initial commit
This commit is contained in:
@ -0,0 +1,248 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __, _n, sprintf } from '@wordpress/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
import { SearchListControl, SearchListItem } from '@woocommerce/components';
|
||||
import { SelectControl } from '@wordpress/components';
|
||||
import { withInstanceId } from '@wordpress/compose';
|
||||
import { withAttributes } from '@woocommerce/block-hocs';
|
||||
import ErrorMessage from '@woocommerce/editor-components/error-placeholder/error-message.js';
|
||||
import classNames from 'classnames';
|
||||
import ExpandableSearchListItem from '@woocommerce/editor-components/expandable-search-list-item/expandable-search-list-item.tsx';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
const ProductAttributeTermControl = ( {
|
||||
attributes,
|
||||
error,
|
||||
expandedAttribute,
|
||||
onChange,
|
||||
onExpandAttribute,
|
||||
onOperatorChange,
|
||||
instanceId,
|
||||
isCompact,
|
||||
isLoading,
|
||||
operator,
|
||||
selected,
|
||||
termsAreLoading,
|
||||
termsList,
|
||||
} ) => {
|
||||
const renderItem = ( args ) => {
|
||||
const { item, search, depth = 0 } = args;
|
||||
const classes = [
|
||||
'woocommerce-product-attributes__item',
|
||||
'woocommerce-search-list__item',
|
||||
{
|
||||
'is-searching': search.length > 0,
|
||||
'is-skip-level': depth === 0 && item.parent !== 0,
|
||||
},
|
||||
];
|
||||
|
||||
if ( ! item.breadcrumbs.length ) {
|
||||
const isSelected = expandedAttribute === item.id;
|
||||
return (
|
||||
<ExpandableSearchListItem
|
||||
{ ...args }
|
||||
className={ classNames( ...classes, {
|
||||
'is-selected': isSelected,
|
||||
} ) }
|
||||
isSelected={ isSelected }
|
||||
item={ item }
|
||||
isLoading={ termsAreLoading }
|
||||
disabled={ item.count === '0' }
|
||||
onSelect={ ( { id } ) => {
|
||||
return () => {
|
||||
onChange( [] );
|
||||
onExpandAttribute( id );
|
||||
};
|
||||
} }
|
||||
name={ `attributes-${ instanceId }` }
|
||||
countLabel={ sprintf(
|
||||
/* translators: %d is the count of terms. */
|
||||
_n(
|
||||
'%d term',
|
||||
'%d terms',
|
||||
item.count,
|
||||
'woocommerce'
|
||||
),
|
||||
item.count
|
||||
) }
|
||||
aria-label={ sprintf(
|
||||
/* translators: %1$s is the item name, %2$d is the count of terms for the item. */
|
||||
_n(
|
||||
'%1$s, has %2$d term',
|
||||
'%1$s, has %2$d terms',
|
||||
item.count,
|
||||
'woocommerce'
|
||||
),
|
||||
item.name,
|
||||
item.count
|
||||
) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const itemName = `${ item.breadcrumbs[ 0 ] }: ${ item.name }`;
|
||||
|
||||
return (
|
||||
<SearchListItem
|
||||
{ ...args }
|
||||
name={ `terms-${ instanceId }` }
|
||||
className={ classNames( ...classes, 'has-count' ) }
|
||||
countLabel={ sprintf(
|
||||
/* translators: %d is the count of products. */
|
||||
_n(
|
||||
'%d product',
|
||||
'%d products',
|
||||
item.count,
|
||||
'woocommerce'
|
||||
),
|
||||
item.count
|
||||
) }
|
||||
aria-label={ sprintf(
|
||||
/* translators: %1$s is the attribute name, %2$d is the count of products for that attribute. */
|
||||
_n(
|
||||
'%1$s, has %2$d product',
|
||||
'%1$s, has %2$d products',
|
||||
item.count,
|
||||
'woocommerce'
|
||||
),
|
||||
itemName,
|
||||
item.count
|
||||
) }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const currentTerms = termsList[ expandedAttribute ] || [];
|
||||
const currentList = [ ...attributes, ...currentTerms ];
|
||||
|
||||
const messages = {
|
||||
clear: __(
|
||||
'Clear all product attributes',
|
||||
'woocommerce'
|
||||
),
|
||||
list: __( 'Product Attributes', 'woocommerce' ),
|
||||
noItems: __(
|
||||
"Your store doesn't have any product attributes.",
|
||||
'woocommerce'
|
||||
),
|
||||
search: __(
|
||||
'Search for product attributes',
|
||||
'woocommerce'
|
||||
),
|
||||
selected: ( n ) =>
|
||||
sprintf(
|
||||
/* translators: %d is the count of attributes selected. */
|
||||
_n(
|
||||
'%d attribute selected',
|
||||
'%d attributes selected',
|
||||
n,
|
||||
'woocommerce'
|
||||
),
|
||||
n
|
||||
),
|
||||
updated: __(
|
||||
'Product attribute search results updated.',
|
||||
'woocommerce'
|
||||
),
|
||||
};
|
||||
|
||||
if ( error ) {
|
||||
return <ErrorMessage error={ error } />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchListControl
|
||||
className="woocommerce-product-attributes"
|
||||
list={ currentList }
|
||||
isLoading={ isLoading }
|
||||
selected={ selected
|
||||
.map( ( { id } ) =>
|
||||
currentList.find(
|
||||
( currentListItem ) => currentListItem.id === id
|
||||
)
|
||||
)
|
||||
.filter( Boolean ) }
|
||||
onChange={ onChange }
|
||||
renderItem={ renderItem }
|
||||
messages={ messages }
|
||||
isCompact={ isCompact }
|
||||
isHierarchical
|
||||
/>
|
||||
{ !! onOperatorChange && (
|
||||
<div hidden={ selected.length < 2 }>
|
||||
<SelectControl
|
||||
className="woocommerce-product-attributes__operator"
|
||||
label={ __(
|
||||
'Display products matching',
|
||||
'woocommerce'
|
||||
) }
|
||||
help={ __(
|
||||
'Pick at least two attributes to use this setting.',
|
||||
'woocommerce'
|
||||
) }
|
||||
value={ operator }
|
||||
onChange={ onOperatorChange }
|
||||
options={ [
|
||||
{
|
||||
label: __(
|
||||
'Any selected attributes',
|
||||
'woocommerce'
|
||||
),
|
||||
value: 'any',
|
||||
},
|
||||
{
|
||||
label: __(
|
||||
'All selected attributes',
|
||||
'woocommerce'
|
||||
),
|
||||
value: 'all',
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</div>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ProductAttributeTermControl.propTypes = {
|
||||
/**
|
||||
* Callback to update the selected product attributes.
|
||||
*/
|
||||
onChange: PropTypes.func.isRequired,
|
||||
/**
|
||||
* Callback to update the category operator. If not passed in, setting is not used.
|
||||
*/
|
||||
onOperatorChange: PropTypes.func,
|
||||
/**
|
||||
* Setting for whether products should match all or any selected categories.
|
||||
*/
|
||||
operator: PropTypes.oneOf( [ 'all', 'any' ] ),
|
||||
/**
|
||||
* The list of currently selected attribute slug/ID pairs.
|
||||
*/
|
||||
selected: PropTypes.array.isRequired,
|
||||
// from withAttributes
|
||||
attributes: PropTypes.array,
|
||||
error: PropTypes.object,
|
||||
expandedAttribute: PropTypes.number,
|
||||
onExpandAttribute: PropTypes.func,
|
||||
isCompact: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
termsAreLoading: PropTypes.bool,
|
||||
termsList: PropTypes.object,
|
||||
};
|
||||
|
||||
ProductAttributeTermControl.defaultProps = {
|
||||
isCompact: false,
|
||||
operator: 'any',
|
||||
};
|
||||
|
||||
export default withAttributes( withInstanceId( ProductAttributeTermControl ) );
|
@ -0,0 +1,56 @@
|
||||
.woocommerce-product-attributes__operator {
|
||||
.components-base-control__help {
|
||||
@include visually-hidden;
|
||||
}
|
||||
|
||||
.components-base-control__label {
|
||||
margin-bottom: 0;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-search-list__item.woocommerce-product-attributes__item {
|
||||
&.is-searching,
|
||||
&.is-skip-level {
|
||||
.woocommerce-search-list__item-prefix::after {
|
||||
content: ":";
|
||||
}
|
||||
}
|
||||
|
||||
&.is-not-active {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
justify-content: center;
|
||||
|
||||
.components-spinner {
|
||||
margin-bottom: $gap-small;
|
||||
}
|
||||
}
|
||||
|
||||
&.depth-0::after {
|
||||
margin-left: $gap-smaller;
|
||||
content: "";
|
||||
height: $gap-large;
|
||||
width: $gap-large;
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" fill="#{encode-color($gray-700)}" /></svg>');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center right;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
&.depth-0.is-selected::after {
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" fill="#{encode-color($gray-700)}" /></svg>');
|
||||
}
|
||||
|
||||
&[disabled].depth-0::after {
|
||||
margin-left: 0;
|
||||
width: auto;
|
||||
background: none;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user