UNPKG

@gravityforms/components

Version:

UI components for use in Gravity Forms development. Both React and vanilla js flavors.

265 lines (250 loc) 8.2 kB
import { React, PropTypes, SimpleBar, classnames } from '@gravityforms/libraries'; import { ConditionalWrapper, useIdContext, useStoreContext } from '@gravityforms/react-utils'; import { getId, getComponent, } from './utils'; import RingLoader from '../Loaders/RingLoader'; const { forwardRef } = React; /** * @module DropdownList * @description The list component for the dropdown. * * @since 4.5.0 * * @param {object} props The component props. * @param {boolean} props.ajaxSearch Whether to use ajax search for the dropdown. * @param {Function} props.handleBlur The blur event handler. * @param {Function} props.handleKeyDownCapture The key down capture event handler. * @param {Function} props.handleListKeyDown The list key down event handler. * @param {boolean} props.hasSearch Whether the dropdown has search. * @param {string} props.label The label of the dropdown. * @param {Array} props.listAttributes Custom attributes for the list. * @param {string|Array|object} props.listClasses Custom classes for the list. * @param {boolean} props.multi Whether the dropdown is multi-select. * @param {number} props.popoverMaxHeight The popover max height. * @param {boolean} props.searchIsLoading Whether the dropdown list items are loading from search or not. * @param {string} props.selectedIcon The selected icon. * @param {string} props.selectedIconPrefix The selected icon prefix. * @param {Function} props.selectItem The select item event handler. * @param {boolean} props.simplebar Whether to use simplebar for the dropdown. * @param {object} ref The ref object. * * @return {JSX.Element} The list component. */ const DropdownList = forwardRef( ( { ajaxSearch = false, handleBlur = () => {}, handleKeyDownCapture = () => {}, handleListKeyDown = () => {}, hasSearch = false, label = '', listAttributes = {}, listClasses = [], multi = false, popoverMaxHeight = 0, searchIsLoading = false, selectedIcon = 'check-mark-alt', selectedIconPrefix = 'gravity-component-icon', selectItem = () => {}, simplebar = true, }, ref ) => { const id = useIdContext(); const activeItem = useStoreContext( ( state ) => state.activeItem ); const listItems = useStoreContext( ( state ) => state.listItems ); const selectedItem = useStoreContext( ( state ) => state.selectedItem ); const setActiveItem = useStoreContext( ( state ) => state.setActiveItem ); const baseElRef = useStoreContext( ( state ) => state.baseElRef ); /** * @function getListItems * @description Gets the list items for the dropdown. * * @since 4.5.0 * * @param {Array} items The list items. * * @return {Array} An array of list items. */ const getListItems = ( items ) => { return items.map( ( item ) => { // Return null if search. if ( item.type === 'search' ) { return null; } // Recursively get group items. if ( item.type === 'group' ) { const hasLabel = item.items.some( ( subItem ) => subItem.type === 'groupLabel' ); const groupProps = { className: classnames( { 'gform-dropdown__list-group': true, 'gform-dropdown__list-group--has-label': hasLabel, } ), id: item.id, role: 'group', }; return ( <div { ...groupProps } key={ item.id }> { getListItems( item.items ) } </div> ); } // Get the group label. if ( item.type === 'groupLabel' ) { const groupLabelProps = { className: classnames( [ 'gform-dropdown__list-group-label', 'gform-text--color-comet', 'gform-typography--size-text-xs', 'gform-typography--weight-regular', ] ), id: item.id, role: 'presentation', }; return ( <div { ...groupLabelProps } key={ item.id }> { item.label } </div> ); } // Get the list item. let selected = selectedItem.value === item.value; if ( multi && Array.isArray( selectedItem ) ) { selected = selectedItem.some( ( selItem ) => selItem.value === item.value ); } const isDisabled = ! ! item.disabled; const itemProps = { className: classnames( 'gform-dropdown__list-item', { 'gform-dropdown__list-item--disabled': isDisabled, } ), id: item.id, 'aria-selected': selected ? 'true' : 'false', 'aria-disabled': isDisabled ? 'true' : 'false', tabIndex: '-1', role: 'option', onMouseMove: isDisabled ? null : () => { setActiveItem( item ); }, onClick: isDisabled ? null : ( event ) => selectItem( event, item ), onFocus: () => baseElRef?.current?.focus(), }; if ( ! isDisabled && activeItem.value === item.value ) { itemProps[ 'data-active-item' ] = 'true'; } const itemInnerProps = { className: classnames( [ 'gform-dropdown__list-item-inner', 'gform-text', 'gform-text--color-port', 'gform-typography--size-text-sm', 'gform-typography--weight-regular', ] ), }; return ( <div { ...itemProps } key={ item.id }> <div { ...itemInnerProps }> { item.beforeLabel && ( <span className="gform-dropdown__list-item-before-label"> { getComponent( item.beforeLabel ) } </span> ) } <span className="gform-dropdown__list-item-label">{ item.label }</span> { ( ( item.afterLabel && ! multi ) || ( multi && selected ) ) && ( <span className="gform-dropdown__list-item-after-label"> { multi && selected ? getComponent( { component: 'Icon', props: { iconPrefix: selectedIconPrefix, icon: selectedIcon, }, } ) : getComponent( item.afterLabel ) } </span> ) } </div> </div> ); } ); }; const listId = getId( id, 'list' ); const listProps = { className: classnames( [ 'gform-dropdown__list' ], listClasses ), ...listAttributes, id: listId, onBlur: handleBlur, onKeyDown: ( event ) => { handleListKeyDown( event ); }, onKeyDownCapture: ( event ) => handleKeyDownCapture( event ), role: 'listbox', }; const simpleBarProps = { className: 'gform-dropdown__list-simplebar', }; if ( ! hasSearch ) { listProps.tabIndex = '0'; if ( label ) { const labelId = getId( id, 'label' ); listProps[ 'aria-labelledby' ] = labelId; } if ( listItems.flatItems.length ) { listProps[ 'aria-activedescendant' ] = activeItem.id; } } if ( popoverMaxHeight ) { const listMaxHeight = hasSearch ? Math.max( popoverMaxHeight - 58, 0 ) : popoverMaxHeight; if ( listMaxHeight > 0 ) { const listMaxHeightPx = `${ listMaxHeight }px`; listProps.style = { maxHeight: listMaxHeightPx, }; simpleBarProps.style = { height: listMaxHeightPx, }; } } return ( <div { ...listProps } ref={ ref }> <ConditionalWrapper condition={ simplebar && popoverMaxHeight > 0 } wrapper={ ( ch ) => ( <div { ...simpleBarProps } > <SimpleBar> { ch } </SimpleBar> </div> ) } > { getListItems( listItems.items ) } </ConditionalWrapper> { ajaxSearch && searchIsLoading && ( <div className="gform-dropdown__list-loader-wrapper"> <RingLoader customClasses={ [ 'gform-dropdown__list-loader' ] } foreground="#242748" /> </div> ) } </div> ); } ); DropdownList.propTypes = { ajaxSearch: PropTypes.bool, handleBlur: PropTypes.func, handleKeyDownCapture: PropTypes.func, handleListKeyDown: PropTypes.func, hasSearch: PropTypes.bool, label: PropTypes.string, listAttributes: PropTypes.object, listClasses: PropTypes.oneOfType( [ PropTypes.string, PropTypes.array, PropTypes.object, ] ), multi: PropTypes.bool, popoverMaxHeight: PropTypes.number, searchIsLoading: PropTypes.bool, selectedIcon: PropTypes.string, selectedIconPrefix: PropTypes.string, selectItem: PropTypes.func, simplebar: PropTypes.bool, }; export default DropdownList;