Category Selection: Keep selected categories in the list (https://github.com/woocommerce/woocommerce-blocks/pull/182)
* Don’t hide selected elements, instead add selection toggle * Add checked/unchecked icons next to each menu item * Add icon to product category list * Update snapshots with added icons
This commit is contained in:
commit
063f38192f
|
@ -13,6 +13,7 @@ import PropTypes from 'prop-types';
|
|||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
import { CheckedIcon, UncheckedIcon } from '../search-list-control/icons';
|
||||
import SearchListControl from '../search-list-control';
|
||||
|
||||
class ProductCategoryControl extends Component {
|
||||
|
@ -36,7 +37,7 @@ class ProductCategoryControl extends Component {
|
|||
} );
|
||||
}
|
||||
|
||||
renderItem( { getHighlightedName, item, onSelect, search, depth = 0 } ) {
|
||||
renderItem( { getHighlightedName, isSelected, item, onSelect, search, depth = 0 } ) {
|
||||
const classes = [
|
||||
'woocommerce-search-list__item',
|
||||
'woocommerce-product-categories__item',
|
||||
|
@ -56,8 +57,10 @@ class ProductCategoryControl extends Component {
|
|||
return (
|
||||
<MenuItem
|
||||
key={ item.id }
|
||||
role="menuitemcheckbox"
|
||||
className={ classes.join( ' ' ) }
|
||||
onClick={ onSelect( item ) }
|
||||
isSelected={ isSelected }
|
||||
aria-label={ sprintf(
|
||||
_n(
|
||||
'%s, has %d product',
|
||||
|
@ -69,6 +72,9 @@ class ProductCategoryControl extends Component {
|
|||
item.count
|
||||
) }
|
||||
>
|
||||
<span className="woocommerce-search-list__item-state">
|
||||
{ isSelected ? <CheckedIcon /> : <UncheckedIcon /> }
|
||||
</span>
|
||||
<span className="woocommerce-product-categories__item-label">
|
||||
{ !! item.breadcrumbs.length && (
|
||||
<span className="woocommerce-product-categories__item-prefix">
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { SVG } from '@wordpress/components';
|
||||
|
||||
export const CheckedIcon = () => (
|
||||
<SVG
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<path
|
||||
id="checked"
|
||||
d="M15.2222 1H2.7778C1.791 1 1 1.8 1 2.7778v12.4444C1 16.2 1.7911 17 2.7778 17h12.4444C16.209 17 17 16.2 17 15.2222V2.7778C17 1.8 16.2089 1 15.2222 1zm-8 12.4444L2.7778 9 4.031 7.7467l3.1911 3.1822 6.7467-6.7467 1.2533 1.2622-8 8z"
|
||||
/>
|
||||
</defs>
|
||||
<g fill="none" fillRule="evenodd" transform="translate(-1 -1)">
|
||||
<mask id="checked-mask" fill="#fff">
|
||||
<use xlinkHref="#checked" />
|
||||
</mask>
|
||||
<path fill="#1E8CBE" d="M0 0h18v18H0z" mask="url(#checked-mask)" />
|
||||
</g>
|
||||
</SVG>
|
||||
);
|
||||
|
||||
export const UncheckedIcon = () => (
|
||||
<SVG
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<path
|
||||
id="unchecked"
|
||||
d="M15.2222 2.7778v12.4444H2.7778V2.7778h12.4444zm0-1.7778H2.7778C1.8 1 1 1.8 1 2.7778v12.4444C1 16.2 1.8 17 2.7778 17h12.4444C16.2 17 17 16.2 17 15.2222V2.7778C17 1.8 16.2 1 15.2222 1z"
|
||||
/>
|
||||
</defs>
|
||||
<g fill="none" fillRule="evenodd" transform="translate(-1 -1)">
|
||||
<mask id="unchecked-mask" fill="#fff">
|
||||
<use xlinkHref="#unchecked" />
|
||||
</mask>
|
||||
<path fill="#6C7781" d="M0 0h18v18H0z" mask="url(#unchecked-mask)" />
|
||||
</g>
|
||||
</SVG>
|
||||
);
|
|
@ -20,6 +20,7 @@ import { Tag } from '@woocommerce/components';
|
|||
*/
|
||||
import './style.scss';
|
||||
import { buildTermsTree } from './hierarchy';
|
||||
import { CheckedIcon, UncheckedIcon } from './icons';
|
||||
|
||||
const defaultMessages = {
|
||||
clear: __( 'Clear all selected items', 'woocommerce' ),
|
||||
|
@ -41,6 +42,7 @@ export class SearchListControl extends Component {
|
|||
this.onSelect = this.onSelect.bind( this );
|
||||
this.onRemove = this.onRemove.bind( this );
|
||||
this.onClear = this.onClear.bind( this );
|
||||
this.isSelected = this.isSelected.bind( this );
|
||||
this.defaultRenderItem = this.defaultRenderItem.bind( this );
|
||||
this.renderList = this.renderList.bind( this );
|
||||
}
|
||||
|
@ -56,6 +58,10 @@ export class SearchListControl extends Component {
|
|||
onSelect( item ) {
|
||||
const { selected, onChange } = this.props;
|
||||
return () => {
|
||||
if ( this.isSelected( item ) ) {
|
||||
this.onRemove( item.id )();
|
||||
return;
|
||||
}
|
||||
onChange( [ ...selected, item ] );
|
||||
};
|
||||
}
|
||||
|
@ -70,20 +76,15 @@ export class SearchListControl extends Component {
|
|||
|
||||
getFilteredList( list, search ) {
|
||||
const { isHierarchical } = this.props;
|
||||
if ( ! search && isHierarchical ) {
|
||||
return buildTermsTree(
|
||||
list.filter( ( item ) => item && ! this.isSelected( item ) ),
|
||||
list
|
||||
);
|
||||
} else if ( ! search ) {
|
||||
return list.filter( ( item ) => item && ! this.isSelected( item ) );
|
||||
if ( ! search ) {
|
||||
return isHierarchical ? buildTermsTree( list ) : list;
|
||||
}
|
||||
const messages = { ...defaultMessages, ...this.props.messages };
|
||||
const re = new RegExp( escapeRegExp( search ), 'i' );
|
||||
this.props.debouncedSpeak( messages.updated );
|
||||
const filteredList = list
|
||||
.map( ( item ) => ( re.test( item.name ) ? item : false ) )
|
||||
.filter( ( item ) => item && ! this.isSelected( item ) );
|
||||
.filter( Boolean );
|
||||
return isHierarchical ? buildTermsTree( filteredList, list ) : filteredList;
|
||||
}
|
||||
|
||||
|
@ -95,10 +96,15 @@ export class SearchListControl extends Component {
|
|||
return name.replace( re, '<strong>$&</strong>' );
|
||||
}
|
||||
|
||||
defaultRenderItem( { depth = 0, getHighlightedName, item, onSelect, search = '' } ) {
|
||||
const classes = [
|
||||
'woocommerce-search-list__item',
|
||||
];
|
||||
defaultRenderItem( {
|
||||
depth = 0,
|
||||
getHighlightedName,
|
||||
item,
|
||||
isSelected,
|
||||
onSelect,
|
||||
search = '',
|
||||
} ) {
|
||||
const classes = [ 'woocommerce-search-list__item' ];
|
||||
if ( this.props.isHierarchical ) {
|
||||
classes.push( `depth-${ depth }` );
|
||||
}
|
||||
|
@ -106,9 +112,14 @@ export class SearchListControl extends Component {
|
|||
return (
|
||||
<MenuItem
|
||||
key={ item.id }
|
||||
role="menuitemcheckbox"
|
||||
className={ classes.join( ' ' ) }
|
||||
onClick={ onSelect( item ) }
|
||||
isSelected={ isSelected }
|
||||
>
|
||||
<span className="woocommerce-search-list__item-state">
|
||||
{ isSelected ? <CheckedIcon /> : <UncheckedIcon /> }
|
||||
</span>
|
||||
<span
|
||||
className="woocommerce-search-list__item-name"
|
||||
dangerouslySetInnerHTML={ {
|
||||
|
@ -130,6 +141,7 @@ export class SearchListControl extends Component {
|
|||
{ renderItem( {
|
||||
getHighlightedName: this.getHighlightedName,
|
||||
item,
|
||||
isSelected: this.isSelected( item ),
|
||||
onSelect: this.onSelect,
|
||||
search,
|
||||
depth,
|
||||
|
|
|
@ -49,6 +49,8 @@
|
|||
}
|
||||
|
||||
.woocommerce-search-list__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
padding: $gap;
|
||||
background: $white;
|
||||
|
@ -56,6 +58,15 @@
|
|||
border-bottom: 1px solid $core-grey-light-500 !important;
|
||||
color: $core-grey-dark-500;
|
||||
|
||||
.woocommerce-search-list__item-state {
|
||||
flex: 0 0 16px;
|
||||
margin-right: $gap-smaller;
|
||||
}
|
||||
|
||||
.woocommerce-search-list__item-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@include hover-state {
|
||||
background: $core-grey-light-100;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue