woocommerce/plugins/woocommerce-blocks/assets/js/components/product-attribute-control/index.js

204 lines
4.9 KiB
JavaScript

/**
* External dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { Fragment } from '@wordpress/element';
import { find } from 'lodash';
import PropTypes from 'prop-types';
import { SearchListControl, SearchListItem } from '@woocommerce/components';
import { SelectControl, Spinner } from '@wordpress/components';
/**
* Internal dependencies
*/
import { withAttributes } from '../../hocs';
import ErrorMessage from '../api-error-placeholder/error-message.js';
import './style.scss';
const ProductAttributeControl = ( { attributes, error, expandedAttribute, onChange, onOperatorChange, isLoading, operator, selected, termsAreLoading, termsList } ) => {
const onExpandAttribute = ( item ) => {
return () => {
onChange( [] );
onExpandAttribute( item.id );
};
};
const renderItem = ( args ) => {
const { item, search, depth = 0 } = args;
const classes = [
'woocommerce-product-attributes__item',
'woocommerce-search-list__item',
];
if ( search.length ) {
classes.push( 'is-searching' );
}
if ( depth === 0 && item.parent ) {
classes.push( 'is-skip-level' );
}
if ( ! item.breadcrumbs.length ) {
return [
<SearchListItem
key={ `attr-${ item.id }` }
{ ...args }
className={ classes.join( ' ' ) }
isSelected={ expandedAttribute === item.id }
onSelect={ onExpandAttribute }
isSingle
disabled={ '0' === item.count }
aria-expanded={ expandedAttribute === item.id }
aria-label={ sprintf(
_n(
'%s, has %d term',
'%s, has %d terms',
item.count,
'woo-gutenberg-products-block'
),
item.name,
item.count
) }
/>,
expandedAttribute === item.id && termsAreLoading && (
<div
key="loading"
className={
'woocommerce-search-list__item woocommerce-product-attributes__item' +
'depth-1 is-loading is-not-active'
}
>
<Spinner />
</div>
),
];
}
return (
<SearchListItem
className={ classes.join( ' ' ) }
{ ...args }
showCount
aria-label={ `${ item.breadcrumbs[ 0 ] }: ${ item.name }` }
/>
);
};
const currentTerms = termsList[ expandedAttribute ] || [];
const currentList = [ ...attributes, ...currentTerms ];
const messages = {
clear: __( 'Clear all product attributes', 'woo-gutenberg-products-block' ),
list: __( 'Product Attributes', 'woo-gutenberg-products-block' ),
noItems: __(
"Your store doesn't have any product attributes.",
'woo-gutenberg-products-block'
),
search: __(
'Search for product attributes',
'woo-gutenberg-products-block'
),
selected: ( n ) =>
sprintf(
_n(
'%d attribute selected',
'%d attributes selected',
n,
'woo-gutenberg-products-block'
),
n
),
updated: __(
'Product attribute search results updated.',
'woo-gutenberg-products-block'
),
};
if ( error ) {
return (
<ErrorMessage error={ error } />
);
}
return (
<Fragment>
<SearchListControl
className="woocommerce-product-attributes"
list={ currentList }
isLoading={ isLoading }
selected={ selected
.map( ( { id } ) => find( currentList, { id } ) )
.filter( Boolean ) }
onChange={ onChange }
renderItem={ renderItem }
messages={ messages }
isHierarchical
/>
{ !! onOperatorChange && (
<div className={ selected.length < 2 ? 'screen-reader-text' : '' }>
<SelectControl
className="woocommerce-product-attributes__operator"
label={ __(
'Display products matching',
'woo-gutenberg-products-block'
) }
help={ __(
'Pick at least two attributes to use this setting.',
'woo-gutenberg-products-block'
) }
value={ operator }
onChange={ onOperatorChange }
options={ [
{
label: __(
'Any selected attributes',
'woo-gutenberg-products-block'
),
value: 'any',
},
{
label: __(
'All selected attributes',
'woo-gutenberg-products-block'
),
value: 'all',
},
] }
/>
</div>
) }
</Fragment>
);
};
ProductAttributeControl.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,
isLoading: PropTypes.bool,
termsAreLoading: PropTypes.bool,
termsList: PropTypes.object,
};
ProductAttributeControl.defaultProps = {
operator: 'any',
};
export default withAttributes( ProductAttributeControl );