2018-12-20 22:26:51 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
|
|
|
import { __, _n, sprintf } from '@wordpress/i18n';
|
|
|
|
import { addQueryArgs } from '@wordpress/url';
|
|
|
|
import apiFetch from '@wordpress/api-fetch';
|
2019-01-30 21:27:56 +00:00
|
|
|
import { Component, Fragment } from '@wordpress/element';
|
2019-02-07 20:26:47 +00:00
|
|
|
import { debounce, filter, find, uniqBy } from 'lodash';
|
2018-12-20 22:26:51 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2019-01-30 21:27:56 +00:00
|
|
|
import { SelectControl } from '@wordpress/components';
|
2018-12-20 22:26:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import './style.scss';
|
|
|
|
import SearchListControl from '../search-list-control';
|
|
|
|
import SearchListItem from '../search-list-control/item';
|
|
|
|
|
|
|
|
class ProductAttributeControl extends Component {
|
|
|
|
constructor() {
|
|
|
|
super( ...arguments );
|
|
|
|
this.state = {
|
|
|
|
list: [],
|
|
|
|
loading: true,
|
2019-02-07 20:26:47 +00:00
|
|
|
attribute: 0,
|
|
|
|
termsLoading: true,
|
2018-12-20 22:26:51 +00:00
|
|
|
};
|
2019-02-07 20:26:47 +00:00
|
|
|
|
|
|
|
this.debouncedGetTerms = debounce( this.getTerms.bind( this ), 200 );
|
|
|
|
this.renderItem = this.renderItem.bind( this );
|
|
|
|
this.onSelectAttribute = this.onSelectAttribute.bind( this );
|
2018-12-20 22:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
2019-02-07 20:26:47 +00:00
|
|
|
const { selected } = this.props;
|
2018-12-20 22:26:51 +00:00
|
|
|
apiFetch( {
|
|
|
|
path: addQueryArgs( '/wc-pb/v3/products/attributes', { per_page: -1 } ),
|
|
|
|
} )
|
2019-02-07 20:26:47 +00:00
|
|
|
.then( ( list ) => {
|
|
|
|
list = list.map( ( item ) => ( { ...item, parent: 0 } ) );
|
|
|
|
this.setState( ( { attribute } ) => {
|
|
|
|
if ( ! attribute && selected.length > 0 ) {
|
|
|
|
const item = find( list, { slug: selected[ 0 ].attr_slug } );
|
|
|
|
attribute = item ? item.id : 0;
|
|
|
|
}
|
|
|
|
return { list, attribute, loading: false };
|
2018-12-20 22:26:51 +00:00
|
|
|
} );
|
|
|
|
} )
|
|
|
|
.catch( () => {
|
|
|
|
this.setState( { list: [], loading: false } );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2019-02-07 20:26:47 +00:00
|
|
|
componentDidUpdate( prevProps, prevState ) {
|
|
|
|
if ( prevState.attribute !== this.state.attribute ) {
|
|
|
|
this.debouncedGetTerms();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getTerms() {
|
|
|
|
const { attribute } = this.state;
|
|
|
|
if ( ! attribute ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
apiFetch( {
|
|
|
|
path: addQueryArgs( `/wc-pb/v3/products/attributes/${ attribute }/terms`, {
|
|
|
|
per_page: -1,
|
|
|
|
} ),
|
|
|
|
} )
|
|
|
|
.then( ( terms ) => {
|
|
|
|
terms = terms.map( ( term ) => ( { ...term, parent: attribute } ) );
|
|
|
|
this.setState( ( { list } ) => ( {
|
|
|
|
list: uniqBy( [ ...list, ...terms ], 'id' ),
|
|
|
|
termsLoading: false,
|
|
|
|
} ) );
|
|
|
|
} )
|
|
|
|
.catch( () => {
|
|
|
|
this.setState( { termsLoading: false } );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
onSelectAttribute( item ) {
|
|
|
|
return () => {
|
|
|
|
if ( item.id === this.state.attribute ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.props.onChange( [] );
|
|
|
|
this.setState( ( { list } ) => {
|
|
|
|
// Remove all other attribute terms from the list.
|
|
|
|
const updatedList = filter( list, { parent: 0 } );
|
|
|
|
return {
|
|
|
|
list: updatedList,
|
|
|
|
attribute: item.id,
|
|
|
|
};
|
|
|
|
} );
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-12-20 22:26:51 +00:00
|
|
|
renderItem( args ) {
|
|
|
|
const { item, search, depth = 0 } = args;
|
2019-02-07 20:26:47 +00:00
|
|
|
const { attribute } = this.state;
|
2018-12-20 22:26:51 +00:00
|
|
|
const classes = [
|
|
|
|
'woocommerce-product-attributes__item',
|
|
|
|
'woocommerce-search-list__item',
|
|
|
|
];
|
|
|
|
if ( search.length ) {
|
|
|
|
classes.push( 'is-searching' );
|
|
|
|
}
|
|
|
|
if ( depth === 0 && item.parent !== 0 ) {
|
|
|
|
classes.push( 'is-skip-level' );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ! item.breadcrumbs.length ) {
|
|
|
|
classes.push( 'is-not-active' );
|
|
|
|
return (
|
2019-02-07 20:26:47 +00:00
|
|
|
<SearchListItem
|
|
|
|
{ ...args }
|
|
|
|
className={ classes.join( ' ' ) }
|
|
|
|
isSingle
|
|
|
|
isSelected={ attribute === item.id }
|
|
|
|
onSelect={ this.onSelectAttribute }
|
|
|
|
/>
|
2018-12-20 22:26:51 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<SearchListItem
|
|
|
|
className={ classes.join( ' ' ) }
|
|
|
|
{ ...args }
|
|
|
|
showCount
|
|
|
|
aria-label={ `${ item.breadcrumbs[ 0 ] }: ${ item.name }` }
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { list, loading } = this.state;
|
2019-01-31 20:43:55 +00:00
|
|
|
const { onChange, onOperatorChange, operator, selected } = this.props;
|
2018-12-20 22:26:51 +00:00
|
|
|
|
|
|
|
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'
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
2019-01-30 21:27:56 +00:00
|
|
|
<Fragment>
|
|
|
|
<SearchListControl
|
|
|
|
className="woocommerce-product-attributes"
|
|
|
|
list={ list }
|
|
|
|
isLoading={ loading }
|
2019-02-07 20:26:47 +00:00
|
|
|
selected={ selected
|
|
|
|
.map( ( { id } ) => find( list, { id } ) )
|
|
|
|
.filter( Boolean ) }
|
2019-01-30 21:27:56 +00:00
|
|
|
onChange={ onChange }
|
|
|
|
renderItem={ this.renderItem }
|
|
|
|
messages={ messages }
|
|
|
|
isHierarchical
|
|
|
|
/>
|
2019-02-07 20:26:47 +00:00
|
|
|
{ !! onOperatorChange && (
|
2019-01-30 21:27:56 +00:00
|
|
|
<div className={ selected.length < 2 ? 'screen-reader-text' : '' }>
|
|
|
|
<SelectControl
|
|
|
|
className="woocommerce-product-attributes__operator"
|
2019-02-07 20:26:47 +00:00
|
|
|
label={ __(
|
|
|
|
'Display products matching',
|
|
|
|
'woo-gutenberg-products-block'
|
|
|
|
) }
|
|
|
|
help={ __(
|
|
|
|
'Pick at least two attributes to use this setting.',
|
|
|
|
'woo-gutenberg-products-block'
|
|
|
|
) }
|
2019-01-30 21:27:56 +00:00
|
|
|
value={ operator }
|
|
|
|
onChange={ onOperatorChange }
|
|
|
|
options={ [
|
|
|
|
{
|
2019-02-07 20:26:47 +00:00
|
|
|
label: __(
|
|
|
|
'Any selected attributes',
|
|
|
|
'woo-gutenberg-products-block'
|
|
|
|
),
|
2019-01-30 21:27:56 +00:00
|
|
|
value: 'any',
|
|
|
|
},
|
|
|
|
{
|
2019-02-07 20:26:47 +00:00
|
|
|
label: __(
|
|
|
|
'All selected attributes',
|
|
|
|
'woo-gutenberg-products-block'
|
|
|
|
),
|
2019-01-30 21:27:56 +00:00
|
|
|
value: 'all',
|
|
|
|
},
|
|
|
|
] }
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
) }
|
|
|
|
</Fragment>
|
2018-12-20 22:26:51 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ProductAttributeControl.propTypes = {
|
|
|
|
/**
|
|
|
|
* Callback to update the selected product attributes.
|
|
|
|
*/
|
|
|
|
onChange: PropTypes.func.isRequired,
|
2019-01-30 21:27:56 +00:00
|
|
|
/**
|
|
|
|
* 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' ] ),
|
2018-12-20 22:26:51 +00:00
|
|
|
/**
|
|
|
|
* The list of currently selected attribute slug/ID pairs.
|
|
|
|
*/
|
|
|
|
selected: PropTypes.array.isRequired,
|
|
|
|
};
|
|
|
|
|
2019-01-31 20:43:55 +00:00
|
|
|
ProductAttributeControl.defaultProps = {
|
|
|
|
operator: 'any',
|
|
|
|
};
|
|
|
|
|
2018-12-20 22:26:51 +00:00
|
|
|
export default ProductAttributeControl;
|