/** * External dependencies */ import { css } from '@nelio/popups/css'; import { addClass, isHTMLElement } from '@nelio/popups/utils'; import type { BorderSettings, CssSizeUnit, Popup, ShadowSettings, } from '@nelio/popups/types'; /** * Internal dependencies */ import { VisiblePopup } from '../types'; import { vh, vw } from './pure-functions'; export function renderPopup( popup: Popup ): HTMLElement { const overlay = makePopupOverlay( popup ); const popupBox = makePopupBox( popup ); const wrapper = document.createElement( 'div' ); addClass( 'wrapper', wrapper ); addClass( `wrapper-${ popup.id }`, wrapper ); wrapper.appendChild( overlay ); wrapper.appendChild( popupBox ); return wrapper; } export function renderCloseButton( vp: VisiblePopup ): void { const { el, popup } = vp; const box = el.querySelector( '.nelio-popup-js-popup' ); if ( ! isHTMLElement( box ) ) { return; } addCloseButton( popup, box ); } // ======= // HELPERS // ======= function makePopupBox( popup: Popup ): HTMLElement { const positioner = document.createElement( 'div' ); positioner.style.zIndex = `${ popup.config.display.zIndex }`; addClass( 'positioner', positioner ); addClass( 'js-positioner', positioner ); const box = document.createElement( 'div' ); addClass( box ); addClass( 'js-popup', box ); addClass( `${ popup.id }`, box ); const content = getPopupContent( popup.id ); // Connect elements. positioner.appendChild( box ); box.appendChild( content ); // Style box setPopupLocation( popup, positioner ); setPopupSize( popup, box ); setPopupStyle( popup, box ); setPopupMargin( popup, box ); setPopupPadding( popup, box ); return positioner; } function getPopupContent( id: number ): HTMLElement { const container = document.getElementById( `nelio-popup-content-${ id }` ) || document.createElement( 'div' ); addClass( 'content', container ); container.classList.remove( 'nelio-invisible-popup-wrapper' ); return container; } function makePopupOverlay( popup: Popup ): HTMLElement { const settings = popup.config.overlay; const overlay = document.createElement( 'div' ); if ( ! settings.isEnabled ) { return overlay; } addClass( 'overlay', overlay ); addClass( 'js-overlay', overlay ); overlay.style.backgroundColor = settings.color; overlay.style.zIndex = `${ popup.config.display.zIndex }`; return overlay; } function setPopupLocation( popup: Popup, box: HTMLElement ): void { const location = getLocation( popup ); box.classList.add( css( location ) ); } function setPopupSize( popup: Popup, box: HTMLElement ): void { const { size } = popup.config; if ( 'auto' === size.type ) { addClass( `size--is-${ size.type }-${ size.value }`, box ); } else { addClass( `size--is-${ size.type }`, box ); } if ( 'custom' !== size.type ) { return; } const { width, height: heightSettings } = size; box.style.width = vw( width ); if ( 'custom-height' === heightSettings.type ) { const { isContentScrollable, value: height } = heightSettings; box.style.height = vh( height ); box.style.overflowY = isContentScrollable ? 'auto' : 'hidden'; } } function setPopupMargin( popup: Popup, box: HTMLElement ): void { const { margin: m, padding: p } = popup.config.spacing; const { border: b } = popup.config.wrapper; const border = b.isEnabled ? vw( b.width ) : '0px'; const hm = `${ vw( m.left ) } - ${ vw( m.right ) }`; const vm = `${ vh( m.top ) } - ${ vh( m.bottom ) }`; const hp = `${ vw( p.left ) } - ${ vw( p.right ) }`; const vp = `${ vh( p.top ) } - ${ vh( p.bottom ) }`; const bw = `calc(2 * ${ border })`; box.style.maxWidth = `calc(100vw - ${ hm } - ${ hp } - ${ bw })`; box.style.maxHeight = `calc(100vh - ${ vm } - ${ vp } - ${ bw })`; box.style.marginTop = vh( m.top ); box.style.marginBottom = vh( m.bottom ); box.style.marginLeft = vw( m.left ); box.style.marginRight = vw( m.right ); } function setPopupPadding( popup: Popup, el: HTMLElement ) { const { padding } = popup.config.spacing; const { top: t, bottom: b, left: l, right: r } = padding; el.style.paddingTop = vh( t ); el.style.paddingBottom = vh( b ); el.style.paddingLeft = vw( l ); el.style.paddingRight = vw( r ); } function setPopupStyle( popup: Popup, box: HTMLElement ): void { const { wrapper: settings } = popup.config; setBorderStyle( settings.border, box ); addShadow( settings.shadow, box ); } function setBorderStyle( settings: BorderSettings, box: HTMLElement ): void { if ( ! settings.isEnabled ) { return; } const color = settings.color; const radius = vw( settings.radius ); const width = vw( settings.width ); box.style.border = `${ width } solid ${ color }`; box.style.borderRadius = radius; } function addCloseButton( popup: Popup, box: HTMLElement ): void { const { closeButton: settings } = popup.config.wrapper; if ( ! settings.isEnabled ) { return; } const button = document.createElement( 'button' ); addClass( 'close-button', button ); addClass( `close-button--is-${ settings.position }`, button ); addClass( 'js-close', button ); if ( settings.icon.startsWith( 'dashicons' ) ) { const icon = document.createElement( 'span' ); icon.innerHTML = getIcon( settings.icon, settings.size ); icon.style.display = 'flex'; addClass( 'close-button-icon', icon ); button.appendChild( icon ); } if ( settings.label ) { const label = document.createElement( 'span' ); label.textContent = settings.label; addClass( 'close-button-label', label ); button.appendChild( label ); } button.style.background = settings.backgroundColor; button.style.color = settings.color; button.style.opacity = '0'; button.style.transition = 'opacity 100ms ease-in-out'; box.appendChild( button ); setTimeout( () => { button.style.opacity = '1'; }, settings.delayInMillis ); } export function getIcon( icon: string, size: CssSizeUnit ): string { switch ( icon ) { case 'dashicons-dismiss': return ``; case 'dashicons-no': return ``; case 'dashicons-no-alt': return ``; case 'dashicons-remove': return ``; case 'dashicons-minus': return ``; } return ''; } function addShadow( settings: ShadowSettings, box: HTMLElement ): void { if ( ! settings.isEnabled ) { return; } const color = settings.color; const offsetX = vw( settings.offsetX ); const offsetY = vh( settings.offsetY ); const blur = vw( settings.blur ); box.style.boxShadow = `${ offsetX } ${ offsetY } ${ blur } ${ color }`; } function getLocation( popup: Popup ) { const { location } = popup.config; switch ( location ) { case 'top-left': return { top: '0', left: '0', transform: 'scale(1)', }; case 'top': return { top: '0', left: '50vw', transform: 'translate(-50%, 0) scale(1)', }; case 'top-right': return { top: '0', right: '0', transform: 'scale(1)', }; case 'left': return { top: '50vh', left: '0', transform: 'translate(0, -50%) scale(1)', }; case 'center': return { top: '50vh', left: '50vw', transform: 'translate(-50%, -50%) scale(1)', }; case 'right': return { top: '50vh', right: '0', transform: 'translate(0, -50%) scale(1)', }; case 'bottom-left': return { bottom: '0', left: '0', transform: 'scale(1)', }; case 'bottom': return { bottom: '0', left: '50vw', transform: 'translate(-50%, 0) scale(1)', }; case 'bottom-right': return { bottom: '0', right: '0', transform: 'scale(1)', }; } }