@gravityforms/components
Version:
UI components for use in Gravity Forms development. Both React and vanilla js flavors.
157 lines (135 loc) • 4.33 kB
JavaScript
import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { useIdContext, useStoreContext } from '@gravityforms/react-utils';
import { getId } from './utils';
import Pill from '../../elements/Pill';
import { BACKSPACE, DELETE } from '../../utils/keymap';
const { forwardRef, useRef } = React;
const COUNT_PLACEHOLDER = '{{count}}';
/**
* @module DropdownPill
* @description The pill component for the dropdown.
*
* @param {object} props The component props.
* @param {object} props.item The item object.
* @param {boolean} props.isCondensed Whether the pill is condensed.
*
* @return {JSX.Element} The pill component.
*/
const DropdownPill = ( { item, isCondensed = false } ) => {
const selectedItem = useStoreContext( ( state ) => state.selectedItem );
const setSelectedItem = useStoreContext( ( state ) => state.setSelectedItem );
const triggerRef = useStoreContext( ( state ) => state.triggerRef );
const pillRef = useRef( null );
const i18n = useStoreContext( ( state ) => state.i18n );
const removeItem = () => {
// If this is a condensed pill, clear all selected items.
if ( isCondensed ) {
setSelectedItem( [] );
triggerRef.current.focus();
return;
}
// Find index of removed item.
const index = selectedItem.findIndex( ( selItem ) => selItem.value === item.value );
const length = selectedItem.length;
// Remove item from selected items.
setSelectedItem( selectedItem.filter( ( selItem ) => selItem.value !== item.value ) );
// If current item is last one and there are more than 1 item, set focus on previous one.
if ( pillRef.current && length > 1 && index === length - 1 ) {
pillRef.current.previousSibling.focus();
}
// If current item is last one and there is only 1 item, set focus on trigger.
if ( length === 1 ) {
triggerRef.current.focus();
}
};
/**
* @function getCondensedPillLabel
* @description Get the label for the condensed pill.
*
* @since 5.6.3
*
* @return {string|JSX.Element} The label for the condensed pill.
*/
const getCondensedPillLabel = () => {
const label = i18n?.condensedPillLabel;
if ( typeof label === 'string' ) {
if ( label.includes( COUNT_PLACEHOLDER ) ) {
return label.replaceAll( COUNT_PLACEHOLDER, `${ selectedItem.length }` );
}
return label;
}
return `${ selectedItem.length }`;
};
const pillProps = {
content: isCondensed ? getCondensedPillLabel() : item.label,
customClasses: [ 'gform-dropdown__pill' ],
customAttributes: {
role: 'option',
tabIndex: '0',
'aria-keyshortcuts': 'Backspace Delete',
onKeyDown: ( event ) => {
// If not backspace or delete, return early.
if ( ! [ BACKSPACE, DELETE ].includes( event.key ) ) {
return;
}
removeItem();
},
},
tagName: 'div',
onClick: removeItem,
};
return <Pill { ...pillProps } ref={ pillRef } />;
};
DropdownPill.propTypes = {
item: PropTypes.object.isRequired,
isCondensed: PropTypes.bool,
};
/**
* @module DropdownPills
* @description The pills component for the dropdown.
*
* @since 4.5.0
*
* @param {object} props The component props.
* @param {boolean} props.multi Whether the dropdown is multi-select.
* @param {boolean} props.condensePills Whether to condense pills.
* @param {object} ref The ref object.
*
* @return {JSX.Element} The pills component.
*/
const DropdownPills = forwardRef( ( {
multi = false,
condensePills = false,
}, ref ) => {
const id = useIdContext();
const selectedItem = useStoreContext( ( state ) => state.selectedItem );
// If not multi, or selected item is not array, return early.
if ( ! multi || ! Array.isArray( selectedItem ) ) {
return null;
}
const pillsId = getId( id, 'pills' );
const pillsProps = {
className: classnames( {
'gform-dropdown__pills': true,
'gform-dropdown__pills--condensed': condensePills,
} ),
id: pillsId,
role: 'listbox',
};
return (
<div { ...pillsProps } ref={ ref }>
{
condensePills
? <DropdownPill isCondensed={ true } />
: selectedItem.map( ( item, index ) => (
<DropdownPill key={ index } item={ item } />
) )
}
</div>
);
} );
DropdownPills.propTypes = {
multi: PropTypes.bool,
condensePills: PropTypes.bool,
};
export default DropdownPills;