UNPKG

@gravityforms/components

Version:

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

335 lines (315 loc) 8.89 kB
import { React } from '@gravityforms/libraries'; import { isObject, slugify } from '@gravityforms/utils'; import Icon from '../../elements/Icon'; import Image from '../../elements/Image'; import Text from '../../elements/Text'; /** * @function normalizeText * @description Normalizes the provided text and trims leading and trailing spaces. * * @since 4.5.0 * * @param {string} text The text to normalize. * * @return {string} The normalized text. */ export const normalizeText = ( text = '' ) => { return text .normalize( 'NFD' ) .replace( /[\u0300-\u036f]/g, '' ) .trim(); }; /** * @function getId * @description Get the id from the prefix and key provided. * * @since 4.5.0 * * @param {string} prefix The id prefix. * @param {string} key The id key. * * @return {string} The id. */ export const getId = ( prefix, key ) => slugify( `${ prefix }-${ key }` ); /** * @function getSearchItem * @description Get the search item. * * @since 4.5.0 * * @param {string} idPrefix The id prefix. * * @return {object} The search item. */ export const getSearchItem = ( idPrefix ) => ( { type: 'search', id: getId( idPrefix, 'search' ), } ); /** * @function augmentListItems * @description Augment list items to add id to the item. * * @since 4.5.0 * * @param {Array} listItems The list items. * @param {object} args Arguments to augment list items with. * @param {boolean} args.hasSearch Whether the dropdown has search or not. * @param {string} args.id The id prefix. * * @return {Array} The augmented list items. */ export const augmentListItems = ( listItems, { hasSearch = false, id: idPrefix = '' } ) => { const newListItems = listItems.map( ( item, index ) => { if ( item.type === 'group' ) { return { ...item, id: item?.label?.label ? getId( idPrefix, slugify( `${ item.label?.label || '' }-group` ) ) : getId( idPrefix, `${ index }-group` ), items: [ ...( item?.label ? [ { ...item.label, id: getId( idPrefix, slugify( item.label?.label || '' ) ), } ] : [] ), ...augmentListItems( item.items, { id: idPrefix } ), ], }; } return { ...item, id: getId( idPrefix, item.value ), }; } ); return [ ...newListItems, ...( hasSearch ? [ getSearchItem( idPrefix ) ] : [] ), ]; }; /** * @function getFlatItems * @description Get the flat items from the items provided. * If the item is a group, it will recursively get the flat items. * If the item is a group label, it will skip it. * If the returnProp is provided, it will return the property from the items. * * @since 5.5.0 * * @param {Array} items The items. * @param {string|undefined} returnProp The property to return from the items. * * @return {Array} The item ids. */ export const getFlatItems = ( items, returnProp ) => { return items.flatMap( ( item ) => { if ( item.type === 'group' ) { return getFlatItems( item.items, returnProp ); } if ( item.type === 'groupLabel' ) { return []; } return returnProp ? item[ returnProp ] : item; } ); }; /** * @function getListItemsState * @description Gets the state for list items. * * @since 4.5.0 * * @param {Array} listItems The list items. * @param {object} args Arguments to augment list items with. * @param {boolean} args.hasSearch Whether the dropdown has search or not. * @param {string} args.id The id prefix. * * @return {object} The list items state. */ export const getListItemsState = ( listItems, args = {} ) => { const items = augmentListItems( listItems, args ); return { ids: getFlatItems( items, 'id' ), flatItems: getFlatItems( items ), items, }; }; /** * @function filterListItems * @description Filters list items based on the search value provided. * * @since 4.5.0 * * @param {Array} listItems The list items. * @param {string} searchValue The search value. * * @return {Array} The filtered list items. */ export const filterListItems = ( listItems, searchValue ) => { if ( searchValue === '' ) { return listItems; } return listItems .map( ( item ) => { if ( item.type === 'group' ) { // Recursively filter group items. const filteredItems = filterListItems( item.items, searchValue ); if ( filteredItems.length > 0 ) { return { ...item, items: filteredItems, }; } return null; // Exclude empty groups. } // Non-group item, check if it matches the search. const itemLabel = item.searchValue || item.label || ''; if ( ! itemLabel ) { return null; } const normalizedLabel = normalizeText( itemLabel ).toLowerCase(); return normalizedLabel.indexOf( searchValue.toLowerCase() ) !== -1 ? item : null; } ) .filter( Boolean ); }; /** * @function getComponent * @description Gets the component from the args provided. * * @since 4.5.0 * * @param {JSX.Element|string|object} args The component args. * * @return {JSX.Element|string} The component. */ export const getComponent = ( args ) => { // If args is a string or a React element, return it. if ( typeof args === 'string' || React.isValidElement( args ) ) { return args; } let component = null; // If args is not an object, return null. if ( ! isObject( args ) ) { return component; } switch ( args.component ) { case 'Icon': component = <Icon { ...args.props } />; break; case 'Image': component = <Image { ...args.props } />; break; case 'Text': component = <Text { ...args.props } />; break; default: component = null; } return component; }; /** * @function convertSingleToMultiItem * @description Converts a single item to a multi item. * * @since 4.5.0 * * @param {object} singleItem The single item. * * @return {Array} The multi item. */ export const convertSingleToMultiItem = ( singleItem ) => { if ( ! isObject( singleItem ) ) { // Single item is not object, something is wrong here, return single item. return singleItem; } if ( Object.keys( singleItem ).length === 0 ) { // Single item is empty, return empty array. return []; } // Return single item as multi value. return [ singleItem ]; }; /** * @function convertMultiToSingleItem * @description Converts a multi item to a single item. * * @since 4.5.0 * * @param {Array} multiItem The multi item. * @param {Array} listItems The current list items. * * @return {object} The single item. */ export const convertMultiToSingleItem = ( multiItem, listItems ) => { if ( ! Array.isArray( multiItem ) ) { // multi item is not array, something is wrong here, return multi item. return multiItem; } if ( multiItem.length === 0 ) { // Selected item is empty, return first item in list items. return listItems[ 0 ] || {}; } // Return first item in multi value. return multiItem[ 0 ]; }; /** * @function isListItemShape * @description Check if the item is in the shape of a list item. * * @since 5.4.5 * * @param {*} item The item to check. * * @return {boolean} Whether the item is in the shape of a list item. */ export const isListItemShape = ( item ) => { return isObject( item ) && Object.prototype.hasOwnProperty.call( item, 'label' ) && Object.prototype.hasOwnProperty.call( item, 'value' ); }; /** * @function getSelectedItemFromValue * @description Get the selected item from the value provided. * * @since 5.4.5 * * @param {string|Array|object} value The provided value. * @param {Array} listItems The list items to get the selected item from. * @param {boolean} multi Whether the dropdown is multi or not. * * @return {object|Array} The selected item. */ export const getSelectedItemFromValue = ( value, listItems, multi ) => { // If the value is a string if ( typeof value === 'string' ) { const item = listItems.find( ( listItem ) => listItem.value === value ); if ( multi ) { return item ? [ item ] : []; } return item || {}; } // If the value is an array if ( Array.isArray( value ) ) { if ( multi ) { return value.reduce( ( acc, val ) => { // If val is a string, find the list item with the value and add it to the accumulator. if ( typeof val === 'string' ) { return [ ...acc, listItems.find( ( listItem ) => listItem.value === val ) ]; } // If val is an object, add it to the accumulator. if ( isListItemShape( val ) ) { return [ ...acc, val ]; } // If val is not a string or an object, return the accumulator. return acc; }, [] ); } return convertMultiToSingleItem( value, listItems ); } // If the value is an object if ( isListItemShape( value ) ) { if ( multi ) { return [ value ]; } return value; } // If the value is not a string, an array, or an object, return the default value. return multi ? [] : {}; };