/**
* External dependencies
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { escapeRegExp, isEmpty } from 'lodash';
import PropTypes from 'prop-types';
import { SearchListControl, SearchListItem } from '@woocommerce/components';
import { Spinner, MenuItem } from '@wordpress/components';
import classnames from 'classnames';
import {
withProductVariations,
withSearchedProducts,
withTransformSingleSelectToMultipleSelect,
} from '@woocommerce/block-hocs';
/**
* Internal dependencies
*/
import {
IconRadioSelected,
IconRadioUnselected,
} from '@woocommerce/block-components/icons';
import ErrorMessage from '@woocommerce/block-components/error-placeholder/error-message.js';
import './style.scss';
function getHighlightedName( name, search ) {
if ( ! search ) {
return name;
}
const re = new RegExp( escapeRegExp( search ), 'ig' );
return name.replace( re, '$&' );
}
const getInteractionIcon = ( isSelected = false ) => {
return isSelected ? : ;
};
const messages = {
list: __( 'Products', 'woo-gutenberg-products-block' ),
noItems: __(
"Your store doesn't have any products.",
'woo-gutenberg-products-block'
),
search: __(
'Search for a product to display',
'woo-gutenberg-products-block'
),
updated: __(
'Product search results updated.',
'woo-gutenberg-products-block'
),
};
const ProductControl = ( {
expandedProduct,
error,
isLoading,
onChange,
onSearch,
products,
renderItem,
selected,
showVariations,
variations,
variationsLoading,
} ) => {
const renderItemWithVariations = ( args ) => {
const { item, search, depth = 0, isSelected, onSelect } = args;
const variationsCount =
item.variations && Array.isArray( item.variations )
? item.variations.length
: 0;
const classes = classnames(
'woocommerce-search-product__item',
'woocommerce-search-list__item',
`depth-${ depth }`,
{
'is-searching': search.length > 0,
'is-skip-level': depth === 0 && item.parent !== 0,
'is-variable': variationsCount > 0,
}
);
const itemArgs = Object.assign( {}, args );
delete itemArgs.isSingle;
const a11yProps = {
role: 'menuitemradio',
};
if ( item.breadcrumbs.length ) {
a11yProps[ 'aria-label' ] = `${ item.breadcrumbs[ 0 ] }: ${
item.name
}`;
}
if ( variationsCount ) {
a11yProps[ 'aria-expanded' ] = item.id === expandedProduct;
}
// Top level items custom rendering based on SearchListItem.
if ( ! item.breadcrumbs.length ) {
return [
,
expandedProduct === item.id &&
variationsCount > 0 &&
variationsLoading && (
),
];
}
if ( ! isEmpty( item.variation ) ) {
item.name = item.variation;
}
return (
);
};
const getRenderItemFunc = () => {
if ( renderItem ) {
return renderItem;
} else if ( showVariations ) {
return renderItemWithVariations;
}
return null;
};
if ( error ) {
return ;
}
const currentVariations =
variations && variations[ expandedProduct ]
? variations[ expandedProduct ]
: [];
const currentList = [ ...products, ...currentVariations ];
return (
selected.includes( id )
) }
onChange={ onChange }
renderItem={ getRenderItemFunc() }
onSearch={ onSearch }
messages={ messages }
isHierarchical
/>
);
};
ProductControl.propTypes = {
/**
* Callback to update the selected products.
*/
onChange: PropTypes.func.isRequired,
/**
* The ID of the currently expanded product.
*/
expandedProduct: PropTypes.number,
/**
* Callback to search products by their name.
*/
onSearch: PropTypes.func,
/**
* Query args to pass to getProducts.
*/
queryArgs: PropTypes.object,
/**
* Callback to render each item in the selection list, allows any custom object-type rendering.
*/
renderItem: PropTypes.func,
/**
* The ID of the currently selected item (product or variation).
*/
selected: PropTypes.arrayOf( PropTypes.number ),
/**
* Whether to show variations in the list of items available.
*/
showVariations: PropTypes.bool,
};
ProductControl.defaultProps = {
expandedProduct: null,
selected: [],
showVariations: false,
};
export default withTransformSingleSelectToMultipleSelect(
withSearchedProducts( withProductVariations( ProductControl ) )
);