UNPKG

@gravityforms/components

Version:

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

220 lines (198 loc) 6.5 kB
import { React, PropTypes, classnames } from '@gravityforms/libraries'; import { spacerClasses } from '@gravityforms/utils'; import Button from '../../elements/Button'; import Icon from '../../elements/Icon'; const { forwardRef, useEffect, useState, createContext, useContext } = React; const SnackbarSettingsContext = createContext( {} ); const SnackbarContext = createContext( null ); /** * @module SnackBar * @description Renders a SnackBar module. * * @since 3.4.0 * * @param {object} props Component props. * @param {JSX.Element} props.children React element children. * @param {object} props.customAttributes Custom attributes for the component * @param {string|Array|object} props.customClasses Custom classes for the component. * @param {number} props.delay The delay for the component. * @param {string} props.id The id for the component. * @param {string} props.message The message for the component. * @param {Function} props.onDismiss Callback function to run when the component is dismissed. * @param {string|number|Array|object} props.spacing The spacing for the component, as a string, number, array, or object. * @param {string} props.theme The theme for the component. * @param {string} props.type The type for the component. * @param {object|null} ref Ref to the component. * * @return {JSX.Element} The icon component. * * @example * import SnackBar from '@gravityforms/components/react/admin/modules/SnackBar'; * * return <SnackBar delay={ 2000 } message="Success saving settings!" />; * */ const SnackBar = forwardRef( ( props, ref ) => { const globalSettings = useContext( SnackbarSettingsContext ); const mergedProps = { ...globalSettings, ...props }; const { ariaLive = 'polite', children = null, closeButtonAttributes = {}, closeButtonClasses = [], customAttributes = {}, customClasses = [], delay = 5000, errorIconAttributes = {}, errorIconClasses = [], id = '', interactive = false, message = '', onDismiss = () => {}, spacing = '', successIconAttributes = {}, successIconClasses = [], theme = 'cosmos', type = 'success', ...otherProps } = mergedProps; const [ isVisible, setIsVisible ] = useState( false ); useEffect( () => { setTimeout( () => { setIsVisible( true ); }, 50 ); }, [] ); useEffect( () => { if ( interactive ) { return; } const timer = setTimeout( () => { setIsVisible( false ); }, delay ); const timerForRemove = setTimeout( () => { onDismiss( id ); }, ( delay + 100 ) ); return () => { clearTimeout( timer ); clearTimeout( timerForRemove ); }; }, [ interactive, delay, id, onDismiss ] ); const componentProps = { className: classnames( { 'gform-snackbar': true, 'gform-snackbar--react': true, 'gform-snackbar--interactive': interactive, 'gform-snackbar--visible': isVisible, [ `gform-snackbar--theme-${ theme }` ]: true, [ `gform-snackbar--type-${ type }` ]: true, ...spacerClasses( spacing ), }, customClasses ), style: { ...customAttributes.style, '--gform-snackbar-animation-delay': `${ isVisible ? 0 : delay }ms`, }, 'aria-live': ariaLive, ref, ...customAttributes, }; const closeButtonProps = { customClasses: classnames( { 'gform-snackbar__close': true, }, closeButtonClasses ), iconPosition: 'leading', type: 'unstyled', ...closeButtonAttributes, }; const errorIconProps = { customClasses: classnames( { 'gform-snackbar__type-icon': true, 'gform-snackbar__type-icon--error': true, }, errorIconClasses ), icon: 'delete', iconPrefix: 'gravity-component-icon', spacing: [ 0, 3, 0, 0 ], ...errorIconAttributes, }; const successIconProps = { customClasses: classnames( { 'gform-snackbar__type-icon': true, 'gform-snackbar__type-icon--success': true, }, successIconClasses ), icon: 'check', iconPrefix: 'gravity-component-icon', spacing: [ 0, 3, 0, 0 ], ...successIconAttributes, }; return ( <div { ...componentProps } { ...otherProps }> { type === 'error' && <Icon { ...errorIconProps } /> } { type === 'success' && <Icon { ...successIconProps } /> } { message } { children } { interactive && <Button { ...closeButtonProps } onClick={ () => { onDismiss( id ); setIsVisible( false ); } } /> } </div> ); } ); export const SnackbarProvider = ( { children, defaultSettings = {} } ) => { const [ messages, setMessages ] = useState( [] ); const addMessage = ( message, type = 'success', additionalProps = {} ) => { // Merge the global defaults with the individual message settings const settings = { ...defaultSettings, type, ...additionalProps }; // Generate a unique ID for the message const id = Math.random().toString( 36 ).substring( 7 ); setMessages( ( prevMessages ) => [ ...prevMessages, { id, message, ...settings } ] ); }; const removeMessage = ( id ) => { setMessages( ( prevMessages ) => prevMessages.filter( ( message ) => message.id !== id ) ); }; return ( <SnackbarSettingsContext.Provider value={ defaultSettings }> <SnackbarContext.Provider value={ addMessage }> { children } { messages.map( ( { id, message, type, ...otherProps }, index ) => ( <SnackBar customAttributes={ { style: { '--gform-snackbar-index': index } } } id={ id } key={ id } message={ message } onDismiss={ removeMessage } type={ type } { ...otherProps } /> ) ) } </SnackbarContext.Provider> </SnackbarSettingsContext.Provider> ); }; export const useSnackbar = () => { return useContext( SnackbarContext ); }; SnackBar.propTypes = { children: PropTypes.oneOfType( [ PropTypes.arrayOf( PropTypes.node ), PropTypes.node, ] ), customAttributes: PropTypes.object, customClasses: PropTypes.oneOfType( [ PropTypes.string, PropTypes.array, PropTypes.object, ] ), delay: PropTypes.number, id: PropTypes.string, message: PropTypes.string, onDismiss: PropTypes.func, spacing: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number, PropTypes.array, PropTypes.object, ] ), theme: PropTypes.string, type: PropTypes.string, }; SnackBar.displayName = 'SnackBar'; export default SnackBar;