UNPKG

@gravityforms/components

Version:

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

496 lines (464 loc) 17.8 kB
import { bodyLock, consoleInfo, focusLoop, getClosest, getNodes, trigger, uniqueId, } from '@gravityforms/utils'; /** * @function dialogTemplate * @description The template function that returns html for the dialog. Options below are passed from constructor and * described there. * * @since 1.0.6 * * @param {object} options The options for the dialog. * @param {string} options.alertButtonText The text for the alert button. * @param {string} options.cancelButtonText The text for the cancel button. * @param {string} options.closeButtonClasses The classes for the close button. Space seperated string. * @param {string} options.closeButtonAriaLabel The aria label for the close button. * @param {string} options.closeButtonSize The size for the close button. * @param {string} options.closeButtonTitle The title for the close button. * @param {string} options.closeButtonType The type for the close button. * @param {string} options.confirmButtonAttributes The attributes for the confirm button. Space seperated string. * @param {string} options.confirmButtonIcon The icon for the confirm button. * @param {string} options.confirmButtonText The text for the confirm button. * @param {string} options.content The content for the dialog. HTML allowed. * @param {string} options.id The id for the dialog. * @param {boolean} options.maskBlur Whether to blur the background when the dialog is open. * @param {string} options.maskClasses The classes for the mask. Space seperated string. * @param {boolean} options.maskTheme Background mask theme, `none`, `light` or `dark`. * @param {string} options.mode The mode for the dialog. `alert`, `confirm` or `dialog`. * @param {string} options.position The position for the dialog. `center`, `top` or `bottom`. * @param {string} options.title The title for the dialog. * @param {string} options.titleIcon The icon for the title. * @param {string} options.titleIconColor The color for the title icon. * @param {string} options.wrapperClasses The classes for the wrapper. Space seperated string. * @param {number} options.zIndex The z-index for the dialog. * * @return string * @example * import { dialogTemplate } from '@gravityforms/components/html/admin/modules/Dialog'; * * function Example() { * const dialogHTML = dialogTemplate( options ); * document.body.insertAdjacentHTML( 'beforeend', dialogHTML ); * } * */ export const dialogTemplate = ( { alertButtonText = '', cancelButtonText = '', closeButtonAriaLabel = '', closeButtonClasses = '', closeButtonSize = 'md', closeButtonTitle = '', closeButtonType = 'circular', confirmButtonAttributes = '', confirmButtonIcon = '', confirmButtonText = '', content = '', id = '', maskBlur = true, maskClasses = '', maskTheme = 'none', mode = '', position = 'fixed', title = '', titleIcon = '', titleIconColor = '', wrapperClasses = '', zIndex = 10, } ) => ` <div class="${ maskClasses } gform-dialog__mask--position-${ position } gform-dialog__mask--theme-${ maskTheme }${ maskBlur ? ` gform-dialog__mask--blur` : '' }" data-js="gform-dialog-mask" style="z-index: ${ zIndex };"> <article id="${ id }" class="${ wrapperClasses }" data-js="${ id }" > <button class="gform-dialog__close ${ closeButtonClasses } gform-button ${ ( closeButtonType === 'circular' ) ? 'gform-button--secondary' : '' } gform-button--${ closeButtonType } gform-button--size-${ closeButtonSize }" data-js="gform-dialog-close" style="z-index: ${ zIndex + 1 };" title="${ closeButtonTitle }" aria-label="${ closeButtonAriaLabel }" > <span class="gform-button__icon gform-common-icon gform-common-icon--x"></span> </button> ${ title ? '<header class="gform-dialog__head" data-js="gform-dialog-header">' : '' } ${ title ? `<h5 class="gform-dialog__title${ titleIcon ? ` gform-dialog__title--has-icon` : '' }">${ titleIcon ? `<span class="gform-dialog__title-icon gform-icon gform-icon--${ titleIcon }"${ titleIconColor ? ` style="color: ${ titleIconColor };"` : '' }></span>` : '' }${ title }</h5>` : '' } ${ title ? '</header>' : '' } <div class="gform-dialog__content" data-js="gform-dialog-content">${ content }</div> ${ ( mode === 'dialog' || mode === 'alert' ) ? '<footer class="gform-dialog__footer" data-js="gform-dialog-footer">' : '' } ${ mode === 'dialog' ? ` <button class="gform-dialog__cancel gform-button gform-button--white" data-js="gform-dialog-cancel" > ${ cancelButtonText } </button> <button id="${ id }-dialog-confirm-button" class="gform-dialog__confirm gform-button gform-button--primary-new${ confirmButtonIcon ? ` gform-button--icon-leading` : '' }" data-js="gform-dialog-confirm" ${ confirmButtonAttributes } > ${ confirmButtonIcon ? `<span class="gform-button__icon gform-icon gform-icon--${ confirmButtonIcon }"></span>` : '' }${ confirmButtonText } </button> ` : '' } ${ mode === 'alert' ? ` <button class="gform-dialog__alert gform-button gform-button--primary-new" data-js="gform-dialog-alert" > ${ alertButtonText } </button> ` : '' } ${ ( mode === 'dialog' || mode === 'alert' ) ? '</footer>' : '' } </article> </div> `; /** * @class Dialog * @description A dialog component to house modals, dialogs or prompts. * * @since 1.0.6 * * @borrows dialogTemplate as dialogTemplate * * @param {object} options The options for the dialog. * @param {string} options.alertButtonText The text for the alert button. * @param {string} options.cancelButtonText The text for the cancel button. * @param {string} options.closeButtonClasses The classes for the close button. Space seperated string. * @param {string} options.closeButtonTitle The title for the close button. * @param {string} options.confirmButtonAttributes The attributes for the confirm button. Space seperated string. * @param {string} options.confirmButtonIcon The icon for the confirm button. * @param {string} options.confirmButtonText The text for the confirm button. * @param {string} options.content The content for the dialog. HTML allowed. * @param {string} options.id The id for the dialog. * @param {boolean} options.lockBody Whether to lock the body behind the dialog * @param {boolean} options.maskBlur Whether to blur the background when the dialog is open. * @param {string} options.maskClasses The classes for the mask. Space seperated string. * @param {boolean} options.maskTheme Background mask theme, `none`, `light` or `dark`. * @param {string} options.mode The mode for the dialog. `alert`, `confirm` or `dialog`. * @param {Function} options.onClose Function to fire when closed * @param {Function} options.onConfirm Function to fire when confirm button is clicked in dialog mode * @param {Function} options.onOpen Function to fire when opened * @param {string} options.position The position for the dialog. `center`, `top` or `bottom`. * @param {boolean} options.renderOnInit Render on initialization? * @param {string} options.target Append target for the dialog and its mask. Uses querySelectorAll * @param {string} options.title The title for the dialog. * @param {string} options.titleIcon The icon for the title. * @param {string} options.titleIconColor The color for the title icon. * @param {string} options.triggers The optional selector[s] of the trigger that shows it * @param {string} options.wrapperClasses The classes for the wrapper. Space seperated string. * @param {number} options.zIndex The z-index for the dialog. * * @return {Class} The class instance. * @example * import Dialog from '@gravityforms/components/html/admin/modules/Dialog'; * * function Example() { * const dialogInstance = new Dialog( { * id: 'example-dialog', * renderOnInit: false, * target: '#example-target', * targetPosition: 'beforeend', * } ); * * // Some time later we can render it. This is only done if we set renderOnInit to false. * // If true it will render on initialization. * dialogInstance.init(); * } * */ export default class Dialog { constructor( options = {} ) { this.options = {}; Object.assign( this.options, { alertButtonText: '', // text for the ok button if in alert mode animationDelay: 250, // animationDelay for the dialog cancelButtonText: '', // text for the cancel button if in dialog mode closeButtonClasses: 'gform-dialog__close', // classes for the close button closeButtonTitle: '', // text for the close button title closeOnMaskClick: true, // does clicking the mask close the dialog? closeOnConfirmClick: true, // does clicking the confirm button close the dialog? confirmButtonAttributes: '', // arbitrary additional attributes for the confirm button. confirmButtonIcon: '', // if in dialog mode, the optional confirmation button icon before the button text confirmButtonText: '', // the text for teh confirmation button if in dialog mode id: uniqueId( 'dialog' ), // id for the dialog lockBody: false, // whether to lock the body behind the dialog maskBlur: true, // whether the background mask has a blur effect or not maskClasses: 'gform-dialog__mask', // classes for the mask maskTheme: 'light', // background mask theme, none, light or dark mode: '', // mode for the dialog: can be modal, alert or dialog onClose: () => {}, // function to fire when closed onConfirm: () => {}, // function to fire when confirm button is clicked in dialog mode onOpen: () => {}, // function to fire when opened position: 'fixed', // fixed or absolute position? renderOnInit: true, // render on initialization? target: 'body', // append target for the dialog and its mask. Uses querySelectorAll title: '', // the optional title for the dialog titleIcon: '', // the optional title icon for the dialog titleIconColor: '', // the optional title icon color for the dialog triggers: '', // the optional selector[s] of the trigger that shows it wrapperClasses: 'gform-dialog', // classes for the wrapper zIndex: 10, // z-index for the dialog }, options ); /** * @event gform/dialog/pre_init * @type {object} * @description Fired before the component has started any internal init functions. A great chance to augment the options. * * @since 1.1.16 * * @property {object} instance The Component class instance. */ trigger( { event: 'gform/dialog/pre_init', native: false, data: { instance: this } } ); this.elements = {}; this.state = { open: false, }; if ( this.options.renderOnInit ) { this.init(); } } /** * @memberof Dialog * @description Opens the dialog and fires the onOpen function that can be passed in. * * @since 1.0.6 * * @return {void} */ showDialog() { const { mask } = this.elements; if ( this.options.lockBody ) { bodyLock.lock(); } this.options.onOpen(); mask.classList.add( 'gform-dialog--anim-in-ready' ); window.setTimeout( () => { mask.classList.add( 'gform-dialog--anim-in-active' ); }, 25 ); this.elements.closeButton.focus(); this.state.open = true; } /** * @memberof Dialog * @description Closes the dialog and fires the onClose function that can be passed in. Can be used by external * developers by firing the method on the instance. Also closes all instances if the event 'gform/dialog/close-all' is * fired on the document. * * @since 1.0.6 * * @return {void} */ closeDialog = () => { const { mask } = this.elements; const { animationDelay, onClose } = this.options; if ( ! mask.classList.contains( 'gform-dialog--anim-in-active' ) ) { return; } mask.classList.remove( 'gform-dialog--anim-in-active' ); window.setTimeout( () => { mask.classList.remove( 'gform-dialog--anim-in-ready' ); }, animationDelay ); this.state.open = false; if ( this.elements.activeTrigger ) { this.elements.activeTrigger.focus(); } if ( this.options.lockBody ) { bodyLock.unlock(); } onClose(); }; /** * @param e * @memberof Dialog * @description Closes all instances except the one that gets passed the activeId when the event 'gform/dialog/close' is * fired on the document. * * @since 1.0.6 * * @return {void} */ maybeCloseDialog = ( e ) => { if ( e.detail?.activeId === this.options.id ) { return; } this.closeDialog(); }; /** * @memberof Dialog * @description Handles accessibility focus looping on the dialog using the focusLoop util. * * @since 1.0.6 * * @param {PointerEvent} e The event object. * * @return {void} */ handleKeyEvents = ( e ) => focusLoop( e, this.elements.activeTrigger, this.elements.dialog, this.closeDialog ); /** * @memberof Dialog * @description Handles opening/closing the dialog on a trigger click. * * @since 1.0.6 * * @param {PointerEvent} e The event object. * * @return {void} */ handleTriggerClick = ( e ) => { this.elements.activeTrigger = e.target; if ( this.state.open ) { this.closeDialog(); } else { this.showDialog(); } }; /** * @memberof Dialog * @description Handles closing the dialog on mask click. * * @since 1.0.6 * * @param {PointerEvent} e The event object. * * @return {void} */ handleMaskClick = ( e ) => { if ( e.target.id === this.options.id || getClosest( e.target, `[data-js="${ this.options.id }"]` ) ) { return; } this.closeDialog(); }; /** * @memberof Dialog * @description Handles a confirm button click and closes if the option is true. * * @since 1.0.6 * * @param {PointerEvent} e The event object. * * @return {void} */ handleConfirm = ( e ) => { const { onConfirm } = this.options; trigger( { event: 'gform/dialog/confirm', native: false, data: { instance: this, button: e.target } } ); if ( this.options.closeOnConfirmClick ) { this.closeDialog(); } onConfirm(); }; /** * @memberof Dialog * @description Stores useful HTMLElements on the instance in the elements namespace after render * * @since 1.0.6 * * @return {void} */ storeElements() { const dialog = getNodes( this.options.id )[ 0 ]; this.elements = { activeTrigger: null, alertButton: getNodes( 'gform-dialog-alert', false, dialog )[ 0 ], content: getNodes( 'gform-dialog-content', false, dialog )[ 0 ], cancelButton: getNodes( 'gform-dialog-cancel', false, dialog )[ 0 ], closeButton: getNodes( 'gform-dialog-close', false, dialog )[ 0 ], confirmButton: getNodes( 'gform-dialog-confirm', false, dialog )[ 0 ], dialog, footer: getNodes( 'gform-dialog-footer', false, dialog )[ 0 ], header: getNodes( 'gform-dialog-header', false, dialog )[ 0 ], mask: dialog.parentNode, triggers: this.options.triggers ? getNodes( this.options.triggers, true, document, true ) : [], }; } /** * @memberof Dialog * @description Renders the component into the dom. * * @since 1.0.6 * * @return {void} */ render() { const { target } = this.options; const renderTarget = getNodes( target, false, document, true )[ 0 ]; renderTarget.insertAdjacentHTML( 'beforeend', dialogTemplate( this.options ) ); } /** * @memberof Dialog * @description Binds the events for this component. * * @since 1.0.6 * * @return {void} */ bindEvents() { this.elements.dialog.addEventListener( 'keydown', this.handleKeyEvents ); this.elements.closeButton.addEventListener( 'click', this.closeDialog ); if ( this.options.triggers ) { getNodes( this.options.triggers, true, document, true ) .forEach( ( t ) => t.addEventListener( 'click', this.handleTriggerClick ) ); } if ( this.options.closeOnMaskClick ) { this.elements.mask.addEventListener( 'click', this.handleMaskClick ); } if ( this.elements.alertButton ) { this.elements.alertButton.addEventListener( 'click', this.closeDialog ); } if ( this.elements.cancelButton ) { this.elements.cancelButton.addEventListener( 'click', this.closeDialog ); } if ( this.elements.confirmButton ) { this.elements.confirmButton.addEventListener( 'click', this.handleConfirm ); } document.addEventListener( 'gform/dialog/close', this.maybeCloseFlyout ); document.addEventListener( 'gform/dialog/close-all', this.closeFlyout ); } /** * @memberof Dialog * @description Initialize the component. * * @fires gform/dialog/post_render * * @since 1.1.16 * * @return {void} */ init() { this.render(); this.storeElements(); this.bindEvents(); /** * @event gform/dialog/post_render * @type {object} * @description Fired when the component has completed rendering and all class init functions have completed. * * @since 1.0.6 * * @property {object} instance The Component class instance. */ trigger( { event: 'gform/dialog/post_render', native: false, data: { instance: this } } ); consoleInfo( `Gravity Forms Admin: Initialized dialog component.` ); } }