import '../polyfills/Fetch';
import LoadContent from './LoadContent';
import LoadScript from './LoadScript';
import MicroModal from '../vendor/micromodal';
import Cookie from '../vendor/jscookie';
/**
* Uses MicroModal.js library to handle popup functionality.
* Fetches dynamic content and script on open, and resets on close.
*
* @module Popups
* @prop {object} selectors - Set of selectors that can be used to grab popup features.
* @prop {object} states - Set of flags that can be used to toggle various popup states.
*/
const Popups = {
selectors: {
dialog: '.l-popup__inner',
content: '.l-popup__content',
overlay: '.l-popup__overlay',
skeleton: '.l-popup__skeletonTemplate',
dummyTrigger: '.l-popup__dummyTrigger',
onPageLoadPopup: '[data-popup-onpageload]',
lockedPopup: '.l-popup--locked',
},
states: {
init: 'l-popup--init',
locked: 'l-popup--locked',
noScroll: 'is-no-scroll',
fading: {
in: 'is-slid-and-faded-in',
out: 'is-slid-and-faded-out',
},
},
data: {
disablePopupCookie: 'data-popup-disable-oncookie',
},
/**
* Initializes all MicroModal popups on page.
* Binds open and close handlers.
*
* @method init
*/
init() {
// Listen to keydown events when there's an on load popup
// This must be done before setting up MicroModal event listeners
// in order to stop keydown event from propagating to MicroModal
if ( document.querySelectorAll( this.selectors.lockedPopup ).length > 0 ) {
this.lockPage();
}
// bind show / close events for MicroModals
MicroModal.init({
onShow: modal => Popups.show( modal ),
onClose: modal => Popups.hide( modal ),
});
// Select popup that shows up on page load
const $onPageLoadPopup = document.querySelectorAll( this.selectors.onPageLoadPopup );
// Trigger popup automatically on page load.
// This only applies to popup attributed [data-popup-onpageload]
[].forEach.call( $onPageLoadPopup, ( $popup ) => {
const id = $popup.getAttribute( 'id' );
if ( ! this.isDisabled( $popup ) ) {
document.querySelector( `${this.selectors.dummyTrigger}[data-micromodal-trigger=${id}]` ).click();
} else {
this.unlockPage();
}
});
},
/**
* Prevent popup from closing when user hits ESC key
*
* @method lockPage
*/
lockPage() {
document.addEventListener( 'keydown', this.preventEscape );
},
/**
* Allow popup to be closed by user hiting ESC key
*
* @method unlockPage
*/
unlockPage() {
document.removeEventListener( 'keydown', this.preventEscape );
},
/**
* Prevent event propagation to MicroModal when ESC key is hit
*
* @method preventEscape
* @param {event} evt - key down event
*/
preventEscape( evt ) {
if ( 27 === evt.keyCode ) {
evt.stopImmediatePropagation();
}
},
/**
* Animate in popup when it's triggered.
* Show skeleton UI while dynamically fetching content if necessary.
* Dynamically fetch associated script if necessary.
*
* @method show
* @param {element} modal - MicroModal popup element.
*/
show( modal ) {
const $dialog = modal.querySelector( Popups.selectors.dialog );
// fade in popup
$dialog.classList.remove( Popups.states.fading.out );
$dialog.classList.add( Popups.states.fading.in );
// stop body from scrolling
document.querySelector( 'body' ).classList.add( this.states.noScroll );
if ( 'true' !== $dialog.dataset.isInit ) {
const $content = $dialog.querySelector( Popups.selectors.content );
const $skeleton = modal.querySelector( Popups.selectors.skeleton );
// show skeleton ui if available
if ( $skeleton ) {
$content.innerHTML = $skeleton.innerHTML;
}
// load popup content if specified
if ( 'loadContent' in $content.dataset ) {
const promise = LoadContent.load( $content );
promise.then( () => {
// load popup script if specified
if ( 'loadScript' in $content.dataset && 'true' !== $content.dataset.scriptLoaded ) {
LoadScript.load( $content );
}
Popups.focusStates( $content );
Popups.setFocus( $content );
Popups.preventShift( modal );
});
}
$dialog.dataset.isInit = true;
modal.classList.add( Popups.states.init );
}
},
/**
* Animate out popup when close button or overlay is pressed.
* Clear popup content if it's dynamic.
*
* @method hide
* @param {element} modal - MicroModal popup element.
*/
hide( modal ) {
const $dialog = modal.querySelector( Popups.selectors.dialog );
const $content = $dialog.querySelector( Popups.selectors.content );
// fade out popup
$dialog.classList.remove( Popups.states.fading.in );
$dialog.classList.add( Popups.states.fading.out );
// stop body from scrolling
document.querySelector( 'body' ).classList.remove( this.states.noScroll );
// Unlock page when modal locked hte page
if ( modal.classList.contains( this.states.locked ) ) {
this.unlockPage();
}
setTimeout( () => {
// remove any dynamically loaded content
if ( 'loadContent' in $content.dataset ) {
$content.innerHTML = '';
}
$dialog.dataset.isInit = false;
}, 500 );
},
/**
* Scroll input elements into view on focus.
*
* @method focusStates
* @param {element} $content - Popup content wrapper element.
*/
focusStates( $content ) {
const $focusableEls = $content.querySelectorAll( 'input, textarea' );
const config = {
behavior: 'smooth',
block: 'start',
inline: 'nearest',
};
[].forEach.call( $focusableEls, ( $el ) => {
$el.addEventListener( 'focus', ( e ) => {
if ( 'scrollIntoView' in e.target ) {
e.target.scrollIntoView( config );
}
});
});
},
/**
* Focuses in the first input/textarea of the dialog.
*
* @method setFocus
* @param {element} $content - Popup content wrapper element.
*/
setFocus( $content ) {
const $focusableEls = $content.querySelectorAll( 'input, textarea' );
if ( 0 < $focusableEls.length ) {
$focusableEls[0].focus();
}
},
/**
* Prevent page layout from shifting when the keyboard is brought forward in Android
* by input focus.
*
* @method preventShift
* @param {element} modal - MicroModal popup element.
*/
preventShift( modal ) {
const $overlay = modal.querySelector( Popups.selectors.overlay );
$overlay.setAttribute( 'style', 'overflow: hidden;' );
setTimeout( () => {
$overlay.setAttribute( 'style', '' );
}, 1000 );
},
/**
* Check if a popup modal has been disabled by a cookie
*
* @method isDisabled
* @param {element} modal - MicroModal popup element.
*/
isDisabled( modal ) {
const cookieName = modal.getAttribute( this.data.disablePopupCookie );
if ( cookieName && Cookie.get( cookieName ) ) {
return true;
}
return false;
},
};
export default Popups;