UNPKG

@gravityforms/components

Version:

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

411 lines (384 loc) 14.7 kB
import { consoleInfo, getNode, objectToAttributes, spacerClasses, trigger, uniqueId, viewport, } from '@gravityforms/utils'; /** * @function loaderTemplate * @description Generates the markup for a loader in the admin. * * @since 1.1.16 * * @param {object} options The options for the component template. * @param {string} options.background The background color for the loader. * @param {object} options.customAttributes Any custom attributes. * @param {Array} options.customClasses An array of additional classes for the toggle. * @param {boolean} options.displayText Whether to display the loader text. * @param {string} options.foreground The foreground color for the loader. * @param {string} options.id Id for the loader, auto generated if not passed. * @param {string} options.mask The mask for the loader. * @param {string} options.maskTheme The mask theme for the loader. * @param {string} options.position The position for the loader. * @param {string} options.size The size for the loader. * @param {string|number|Array|object} options.spacing The spacing for the component, string, number, object or array. * @param {string} options.text The text for the loader. * @param {string} options.textColor The text color for the loader. * @param {string} options.theme Theme for the toggle, primary or cosmos. * @param {string} options.type Type for the loader, spinner or bar. * @param {object} options.wrapperCustomAttributes Any custom attributes for the textarea wrapper. * @param {Array} options.wrapperCustomClasses Any custom classes for the textarea wrapper. * @param {string} options.wrapperTagName Tag to use for the textarea wrapper. Defaults to 'div', * * @return {string} * @example * import { loaderTemplate } from '@gravityforms/components/html/admin/elements/Loader'; * * function Example() { * const loaderTemplateHTML = loaderTemplateTemplate( options ); * document.body.insertAdjacentHTML( 'beforeend', loaderTemplateHTML ); * } * */ export const loaderTemplate = ( { background = '#ecedf8', customAttributes = {}, customClasses = [], displayText = true, foreground = '#242748', id = '', mask = true, maskTheme = 'light', position = 'center', size = 5, spacing = '', text = '', textColor = '#000', theme = 'primary', type = 'simple', wrapperCustomAttributes = {}, wrapperCustomClasses = [], wrapperTagName = 'div', } ) => { const wrapperAttrs = objectToAttributes( { ...wrapperCustomAttributes, id: `${ id }-mask`, class: [ 'gform-loader__mask', `gform-loader__mask--theme-${ maskTheme }`, `gform-loader__mask--theme-${ theme }`, `gform-loader__mask--position-${ position }`, ...wrapperCustomClasses, ], role: 'alert', } ); const componentAttrs = objectToAttributes( { ...customAttributes, id, class: [ 'gform-loader', `gform-loader--${ type }`, `gform-loader--theme-${ theme }`, ...Object.keys( spacerClasses( spacing ) ), ...customClasses, ], } ); return ` ${ mask ? `<${ wrapperTagName } ${ wrapperAttrs }>` : '' } ${ mask ? `<div id="${ id }-mask-positioner" class="gform-loader__mask-positioner">` : '' } <span ${ componentAttrs }></span> ${ mask && text && displayText ? `<span id="${ id }-text" class="gform-loader__text">${ text }</span>` : '' } ${ mask && text && ! displayText ? `<span class="gform-visually-hidden">${ text }</span>` : '' } ${ mask ? `</div>` : '' } ${ mask ? `</${ wrapperTagName }>` : '' } <style id="${ id }-style"> #${ id } { ${ type === 'simple' ? ` border-bottom-color: ${ foreground }; border-left-color: ${ foreground }; border-right-color: ${ background }; border-top-color: ${ background }; font-size: ${ size }px; ` : '' } } #${ id }-text { ${ textColor ? `color: ${ textColor };` : '' } } </style> `; }; /** * @class Loader * @description A loader component that can be used as a simple spinner or a full masked element with spinner and text as needed. * * @since 1.1.16 * * @borrows loaderTemplate as loaderTemplate * * @param {object} options The options for the component. * @param {string} options.background The background color for the loader. * @param {object} options.customAttributes Any custom attributes for the component. * @param {Array} options.customClasses An array of additional classes for the component. * @param {boolean} options.displayNoneOnHide On hide should it use display none or just opacity? * @param {boolean} options.displayText Whether to display the loader text. * @param {string} options.foreground The foreground color for the loader. * @param {string} options.id Id for the component, auto generated if not passed. * @param {string} options.mask The mask for the loader. * @param {string} options.maskTheme The mask theme for the loader. * @param {string} options.position The position for the loader. * @param {string} options.rendered Is the component already rendered in the dom, eg by php? * @param {string} options.renderOnInit Render the component on init of the class? * @param {string} options.showOnRender Show the component on render? * @param {string} options.size The size for the loader. * @param {string} options.spacing Spacing for the component. * @param {string} options.target The target to render to. Any valid css selector string. * @param {string} options.targetPosition The insert position for the component relative to the target. * @param {string} options.text Text to show below the loader or for screen readers based on the displayText option if loader mask is enabled. Required for a11y if mask is enabled. * @param {string} options.textColor The text color for the loader. * @param {string} options.theme Theme for the component, primary or cosmos. * @param {string} options.type Type for the loader, spinner or bar. * @param {object} options.wrapperCustomAttributes Any custom attributes for the textarea wrapper. * @param {Array} options.wrapperCustomClasses Any custom classes for the textarea wrapper. * @param {string} options.wrapperTagName Tag to use for the textarea wrapper. Defaults to 'div', * * @return {Class} The class instance. * @example * import Loader from '@gravityforms/components/html/admin/elements/Loader'; * * function Example() { * const loaderInstance = new Loader( { * id: 'example-loader', * renderOnInit: false, * target: '#example-target', * targetPosition: 'beforeend', * theme: 'cosmos', * } ); * * // Some time later we can render it. This is only done if we set renderOnInit to false. * // If true it will render on initialization. * loaderInstance.init(); * } * */ export default class Loader { constructor( options = {} ) { this.options = {}; Object.assign( this.options, { background: '#ecedf8', // background color for the loader customAttributes: {}, customClasses: [], displayNoneOnHide: true, // on hide should it use display none or just opacity? displayText: true, // should we display the text or make it only available to screen readers if loader mask is enabled foreground: '#242748', // the color of the loader id: uniqueId( 'loader' ), // the id for the loader mask: true, // should the loader mask an area? If false we assume the implementation will handle the details (layout, a11y, etc.). maskTheme: 'light', // background mask theme, none, light, or dark position: 'center', // vertical position, auto, top, center, bottom, or sticky if loader mask is enabled rendered: false, // is the loader already rendered in the dom? renderOnInit: true, // should we render this loader on init? showOnRender: true, // visible on render? size: 5, // size of the loader, decimal int values target: '', // node to inject the loader to (uses insertAdjacentHtml) targetPosition: 'afterbegin', // insert position for the loader markup text: '', // text to show below the loader or for screen readers based on the displayText option if loader mask is enabled. Required for a11y if mask is enabled. textColor: '#000', // text color theme: 'cosmos', // theme for the loader, primary or cosmos type: 'simple', // loader type wrapperCustomAttributes: {}, wrapperCustomClasses: [], wrapperTagName: 'div', }, options ); /** * @event gform/loader/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/loader/pre_init', native: false, data: { instance: this } } ); this.elements = {}; if ( this.options.renderOnInit ) { this.init(); } } /** * @memberof Loader * @description Sets the vertical position for the loader. * * @since 1.1.16 * * @return {void} */ positionLoader() { const { position, target } = this.options; const targetNode = getNode( target, document, true ); const { maskPositioner } = this.elements; const targetVisibleHeight = viewport.elVisibleHeight( targetNode ); const viewportHeight = viewport.height(); const { top } = targetNode.getBoundingClientRect(); let windowScroll = 0; // Account for window scroll in our top positioning when: // - position auto, target height greater than window height, and scrolled past top of target // - position fixed, window height greater than target visible height, and haven't scrolled past top of target if ( ( position === 'auto' && ( targetNode.offsetHeight > viewportHeight ) && top < 0 ) || ( position === 'sticky' && ( targetVisibleHeight < viewportHeight ) && top > 0 ) ) { windowScroll = Math.abs( targetNode.getBoundingClientRect().top ); } maskPositioner.style.top = `${ windowScroll + ( ( targetVisibleHeight / 2 ) - ( maskPositioner.offsetHeight / 2 ) ) }px`; } /** * @memberof Loader * @description Removes the injected nodes from the dom. * * @since 1.1.16 * * @return {void} */ removeLoader() { const { loaderEl, style } = this.elements; loaderEl.parentNode.removeChild( loaderEl ); style.parentNode.removeChild( style ); } /** * @memberof Loader * @description Reveal the loader. * * @since 1.1.16 * * @return {void} */ showLoader() { const { mask, position } = this.options; const { loaderEl } = this.elements; loaderEl.style.display = ''; loaderEl.style.opacity = ''; if ( mask && ( position === 'auto' || position === 'sticky' ) ) { this.positionLoader(); } trigger( { event: 'gform/loader/post_show', native: false, data: { instance: this } } ); } /** * @memberof Loader * @description Hide the loader. * * @fires gform/loader/post_hide * * @since 1.1.16 * * @return {void} */ hideLoader() { const { displayNoneOnHide } = this.options; const { loaderEl } = this.elements; if ( displayNoneOnHide ) { loaderEl.style.display = 'none'; } else { loaderEl.style.opacity = '0'; } /** * @event gform/loader/post_hide * @type {object} * @description Fired when the component has completed hiding in the dom. * * @since 1.1.16 * * @property {object} instance The Component class instance. */ trigger( { event: 'gform/loader/post_hide', native: false, data: { instance: this } } ); } /** * @memberof Loader * @description Handles initial visibility for the loader. If showOnRender is false, hides the loader. * * @since 1.1.16 * * @return {void} */ setInitialUI() { const { mask, position, showOnRender } = this.options; if ( ! showOnRender ) { this.hideLoader(); } if ( showOnRender && mask && ( position === 'auto' || position === 'sticky' ) ) { this.positionLoader(); } } /** * @memberof Loader * @description Store the elements on the elements object of this instance. * * @since 1.1.16 * * @return {void} */ storeElements() { const { id } = this.options; this.elements = { loader: getNode( `#${ id }`, document, true ), mask: getNode( `#${ id }-mask`, document, true ), maskPositioner: getNode( `#${ id }-mask-positioner`, document, true ), style: getNode( `#${ id }-style`, document, true ), }; this.elements.loaderEl = this.elements.mask ? this.elements.mask : this.elements.loader; } /** * @memberof Loader * @description Renders the component into the dom. * * @since 1.1.16 * * @return {void} */ render() { const { rendered, target, targetPosition } = this.options; if ( ! rendered ) { const renderTarget = getNode( target, document, true ); if ( this.options.mask ) { renderTarget.style.position = 'relative'; } renderTarget.insertAdjacentHTML( targetPosition, loaderTemplate( this.options ) ); } this.elements.loader = getNode( `#${ this.options.id }`, document, true ); this.elements.wrapper = this.elements.loader.parentNode; } /** * @memberof Loader * @description Initialize the component. * * @fires gform/loader/post_render * * @since 1.1.16 * * @return {void} */ init() { this.render(); this.storeElements(); this.setInitialUI(); /** * @event gform/loader/post_render * @type {object} * @description Fired when the component has completed rendering and all class init functions have completed. * * @since 1.1.16 * * @property {object} instance The Component class instance. */ trigger( { event: 'gform/loader/post_render', native: false, data: { instance: this } } ); consoleInfo( `Gravity Forms Admin: Initialized loader component.` ); } }