UNPKG

@gravityforms/components

Version:

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

354 lines (323 loc) 9.65 kB
import { React, PropTypes, classnames } from '@gravityforms/libraries'; import { spacerClasses } from '@gravityforms/utils'; import Icon from '../../elements/Icon'; import Label from '../../elements/Label'; import ColorPicker from '../ColorPicker'; import { invertColor } from '../../utils/colors'; const { useState, useEffect, useRef, forwardRef } = React; /** * @module Swatch * @description Allows users to select from a pallete of swatches, or add their own using a color picker * * @since 1.1.15 * * @param {object} props Component props. * @param {boolean} props.allowNew Whether to display an icon to add new swatches to the palette. * @param {object} props.customAttributes Custom attributes for the component. * @param {string|Array|object} props.customClasses Custom classes for the component. * @param {object} props.i18n Translated strings for the UI. * @param {string} props.id The ID for the component. * @param {object} props.labelAttributes Custom attributes to apply to the label for each swatch. * @param {string} props.name The name of the component. * @param {Array} props.palette An array of hex color values to display as default palette options. * @param {Array} props.paletteCustom An array of hex color values to display as custom/editable palette options. * @param {string|number|Array|object} props.spacing The spacing for the component, as a string, number, array, or object. * @param {string} props.value The initial hex value to select. * @param {object|null} ref Ref to the component. * * @return {JSX.Element} The Swatch component. * * @example * import Swatch from '@gravityforms/components/react/admin/modules/Swatch'; * * return ( * <Swatch customClasses={ [ 'example-class' ] } palette={ [ '#000', '#111' ] } /> * ); * */ const Swatch = forwardRef( ( { allowNew = true, customAttributes = {}, customClasses = [], i18n = {}, id = '', labelAttributes = { size: 'text-sm', weight: 'medium', }, name = '', palette = [], paletteCustom = [], spacing = '', value = '', }, ref ) => { const [ showPicker, setShowPicker ] = useState( false ); const [ selectedColor, setSelectedColor ] = useState( value ); const [ color, setColor ] = useState( value ); const [ colorBeingModified, setColorBeingModified ] = useState( palette.length + 1 ); const [ customPaletteOptions, setCustomPaletteOptions ] = useState( paletteCustom ); const [ currentRef, setCurrentRef ] = useState( null ); const allInputRef = useRef( null ); const addNewRef = useRef( document.querySelector( `[data-js-setting-name="${ name }"] .gform-input--swatch__option--new` ) ); const swatchRefs = useRef( [] ); useEffect( () => { const newPaletteString = JSON.stringify( customPaletteOptions ); allInputRef.current.value = newPaletteString; if ( customPaletteOptions.length === 0 ) { setSelectedColor( value ); } }, [ customPaletteOptions, value ] ); useEffect( () => { if ( ! currentRef || ! currentRef.current ) { setCurrentRef( addNewRef ); } }, [ setCurrentRef, currentRef ] ); /** * @function handlePickerCancel * @description Handler for the cancel event on the swatch. * * @since 1.1.15 * * @return {void} */ const handlePickerCancel = () => { setShowPicker( false ); }; /** * @function handlePickerDelete * @description Handler for the delete event on the swatch. * * @since 1.1.15 * * @param {number} index Index of the swatch palette. * * @return {void} */ const handlePickerDelete = ( index ) => { setCustomPaletteOptions( ( prevPalette ) => prevPalette.filter( ( item, thisIndex ) => thisIndex !== index ) ); handlePickerCancel(); }; /** * @function handlePickerSave * @description Handler for the save event on the swatch. * * @since 1.1.15 * * @param {string} swatch The swatch value. * * @return {void} */ const handlePickerSave = ( swatch ) => { setColor( swatch ); if ( ! customPaletteOptions.includes( swatch ) ) { setCustomPaletteOptions( ( prevPalette ) => { const newPalette = prevPalette; newPalette[ colorBeingModified ] = swatch; return newPalette; } ); } setSelectedColor( swatch ); setShowPicker( false ); }; /** * @function handleColorChange * @description Handler for the color change event. * * @since 1.1.15 * * @param {object} event Event object. * * @return {void} */ const handleColorChange = ( event ) => { setSelectedColor( event.target.value ); }; /** * @function renderSwatchOption * @description Render the swatch option. * * @since 1.1.15 * * @param {string} swatch The swatch value. * @param {number} index The index of the swatch palette. * @param {boolean} isCustom Whether the swatch is custom or not. * * @return {JSX.Element} The swatch option. */ const renderSwatchOption = ( swatch, index, isCustom = false ) => { const liProps = { className: classnames( { 'gform-input--swatch__option': true, } ), key: index, }; const labelProps = { htmlFor: `${ name }_${ swatch }_${ index }`, label: i18n?.swatch || '', isVisible: false, ...labelAttributes, }; const swatchInputProps = { onChange: handleColorChange, type: 'radio', name, value: swatch, id: `${ name }_${ swatch }_${ index }`, checked: swatch === selectedColor, }; if ( isCustom ) { swatchInputProps.onClick = () => { setColor( swatch ); setCurrentRef( { current: swatchRefs.current[ index ] } ); setColorBeingModified( index ); setShowPicker( true ); }; } const swatchSpanProps = { className: classnames( { 'gform-input--swatch__option-preview': true, } ), style: { backgroundColor: swatch, }, onClick: ( e ) => { if ( e.target.classList.contains( 'gform-input--swatch-delete' ) ) { handlePickerDelete( index ); return; } document.getElementById( `${ name }_${ swatch }_${ index }` ).click(); }, ref: isCustom ? ( el ) => ( swatchRefs.current[ index ] = el ) : null, }; const iconProps = { icon: 'check', customClasses: classnames( { 'gform-input--swatch-selected': true, } ), customAttributes: { style: { color: invertColor( swatch ), }, }, }; const deleteIconProps = { icon: 'delete', customClasses: classnames( { 'gform-input--swatch-delete': true, } ), }; return ( <li { ...liProps }> <Label { ...labelProps } /> <input { ...swatchInputProps } /> <span { ...swatchSpanProps } > { swatch === selectedColor && <Icon { ...iconProps } /> } { isCustom && <Icon { ...deleteIconProps } /> } </span> </li> ); }; /** * @function renderAddNewSwatchOption * @description Render the add new swatch option. * * @since 1.1.15 * * @return {JSX.Element} The add new swatch option. */ const renderAddNewSwatchOption = () => { const liProps = { className: classnames( { 'gform-input--swatch__option': true, 'gform-input--swatch__option--new': true, } ), key: 'add-new', }; const swatchSpanProps = { className: classnames( { 'gform-input--swatch__option-preview': true, 'gform-input--swatch__option-preview--new': true, } ), onClick: () => { setCurrentRef( addNewRef ); setColorBeingModified( customPaletteOptions.length + 1 ); setShowPicker( true ); }, ref: addNewRef, }; const iconProps = { icon: 'plus-regular', }; return ( <li { ...liProps }> <span { ...swatchSpanProps } > <Icon { ...iconProps } /> </span> </li> ); }; const componentProps = { className: classnames( { 'gform-input--swatch': true, ...spacerClasses( spacing ), }, customClasses ), id, 'data-js-setting-name': name, ...customAttributes, }; const swatchOptionsProps = { className: classnames( { 'gform-input--swatch-options': true, } ), }; const allSwatchesInputProps = { name: `${ name }-all-swatches`, defaultValue: JSON.stringify( customPaletteOptions ), id: `${ name }-all-swatches`, type: 'hidden', ref: allInputRef, }; const pickerProps = { value: color || '#ffffff', onSave: handlePickerSave, onCancel: handlePickerCancel, triggerRef: currentRef, i18n: i18n?.colorPicker || {}, }; return ( <div { ...componentProps } ref={ ref }> <div style={ { height: '0' } } /> <ul { ...swatchOptionsProps }> { palette.map( ( swatch, index ) => renderSwatchOption( swatch, index ) ) } { customPaletteOptions.map( ( swatch, index ) => renderSwatchOption( swatch, index, true ) ) } { allowNew && renderAddNewSwatchOption() } </ul> { showPicker && <ColorPicker { ...pickerProps } /> } <input { ...allSwatchesInputProps } /> </div> ); } ); Swatch.propTypes = { allowNew: PropTypes.bool, customAttributes: PropTypes.object, customClasses: PropTypes.oneOfType( [ PropTypes.string, PropTypes.array, PropTypes.object, ] ), i18n: PropTypes.object, id: PropTypes.string, labelAttributes: PropTypes.object, name: PropTypes.string, palette: PropTypes.array, paletteCustom: PropTypes.array, spacing: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number, PropTypes.array, PropTypes.object, ] ), value: PropTypes.string, }; Swatch.displayName = 'Swatch'; export default Swatch;