UNPKG

baseframe-js

Version:

A suite of useful Javascript plugins and functions to help with Front-end Development on websites

225 lines (224 loc) 8 kB
import $ from 'cash-dom'; import Store from "./core/Store"; import UrlState from "./core/UrlState"; import trapFocus from './fn/trapFocus'; import { camelCase, getDataOptions, reflow } from './util/helpers'; import { noop } from './util/helpers'; import h from "./fn/hyperScript"; const VERSION = '1.2.0'; const EVENT_NAME = 'modal'; const DATA_NAME = 'Modal'; const DEFAULTS = { enableEvent: 'click', appendTo: document.body, ariaLabelledby: null, ariaLabel: null, cssPrefix: 'modal', closeBtnIconCss: 'ico i-close', closeBtnLabel: 'Close', closeOutDelay: 250, focusInDelay: 0, backDropClose: true, fromDOM: false, modalCss: null, modalID: null, src: '', urlFilterType: 'hash', historyType: 'replace', locationFilter: 'modal', loadLocation: true, onOpenOnce: noop, onOpen: noop, onClose: noop, afterClose: noop }; const hasCb = (cb, modalObj) => { if (cb && typeof cb === 'function') { cb(modalObj); } }; export default class Modal { element; params; modalID; modalObj; modalEvent; trappedFocus; enabledElem; openedOnce; static defaults = DEFAULTS; static version = VERSION; static pluginName = DATA_NAME; constructor(element, options) { const s = this; s.element = element; const dataOptions = getDataOptions(element, EVENT_NAME); s.params = $.extend({}, Modal.defaults, options, dataOptions); s.modalID = s.params.modalID; s.modalObj = s.getModalObj(); s.modalEvent = EVENT_NAME + '_' + s.modalID; s.trappedFocus; s.enabledElem; s.openedOnce = false; s.handleEvents(); s.loadFromUrl(); Store(element, DATA_NAME, s); return s; } static remove(element, plugin) { $(element).each(function () { const s = plugin || Store(this, DATA_NAME); const params = s.params; const { $backdrop, $closeBtn } = s.modalObj; s.modalObj.show && s.modalObj.close(); $(s.element).off(`${s.params.enableEvent}.${s.modalEvent}`); $closeBtn.off(`click.${s.modalEvent}Dismiss`); if (params.backDropClose) $backdrop.off(`click.${s.modalEvent}Dismiss`); $(document) .off(`keydown.${s.modalEvent}Dismiss`) .off(`${s.modalEvent}Dismiss`); $(window).off(`popstate.${s.modalEvent} ${s.modalEvent}`); Store(this, DATA_NAME, null); }); } handleEvents() { const s = this; $(s.element).on(`${s.params.enableEvent}.${s.modalEvent}`, function (e) { s.setDisplayAndEvents(); e.preventDefault(); }); } setDisplayAndEvents() { const s = this; s.enableModal(); $(document).on(`keydown.${s.modalEvent}Dismiss`, function (e) { const ekey = e.code || e.originalEvent.key; //cash-dom || jquery if (ekey === 'Escape') { s.close(); e.preventDefault(); } }); $(document).on(`${s.modalEvent}Dismiss`, s.close); } getModalObj() { const s = this, p = s.params, $closeBtn = h(`button.${p.cssPrefix}__btn-dismiss`, { type: 'button', 'aria-label': p.closeBtnLabel }).append(h(`i.${p.closeBtnIconCss}`)), $dialogContent = h(`div.${p.cssPrefix}__dialog-content`), $dialog = h(`div.${p.cssPrefix}__dialog`).append($closeBtn, $dialogContent), $backdrop = h(`div.${p.cssPrefix}__backdrop`), $modal = h(`div`, { class: p.cssPrefix + (p.modalCss ? ' ' + p.modalCss : ''), 'aria-label': (p.ariaLabel || s.element.dataset.ariaLabel) || null, 'aria-labelledby': (p.ariaLabelledby || s.element.dataset.ariaLabelledby) || null, id: s.modalID }).append($backdrop, $dialog), $content = $(s.params.src || s.element.hash || s.element.dataset.modalSrc); return { $backdrop, $content, contentAppended: false, $dialog, $dialogContent, $closeBtn, id: s.modalID, $modal, close: () => s.close(), show: false }; } enableModal() { const s = this; const { $backdrop, $closeBtn, $content, $modal } = s.modalObj; const p = s.params; s.enabledElem = document.activeElement; if (p.fromDOM) { $content.after(h(`span.${p.cssPrefix}-content-placemarker#${s.modalObj.id}_marker`)); } if (!s.modalObj.contentAppended) { s.modalObj.$dialogContent.append($content); $.extend(s.modalObj, { contentAppended: true }); } $(p.appendTo).append($modal); // attach events after appended to DOM $closeBtn.on(`click.${s.modalEvent}Dismiss`, () => s.close()); if (p.backDropClose) $backdrop.on(`click.${s.modalEvent}Dismiss`, () => s.close()); hasCb(p.onOpen, s.modalObj); if (!s.openedOnce) { hasCb(p.onOpenOnce, s.modalObj); s.openedOnce = true; } $modal.attr({ role: 'dialog', 'aria-modal': 'true' }); reflow($modal[0]); setTimeout(() => { $modal.addClass(p.cssPrefix + '--show'); $.extend(s.modalObj, { show: true }); }, 0); if (p.focusInDelay !== null) { setTimeout(() => { s.trappedFocus = trapFocus($modal, { nameSpace: camelCase(s.modalID) }); }, p.focusInDelay); } $(p.appendTo).addClass(p.cssPrefix + '-open').css({ overflow: 'hidden', 'padding-right': '0px' }); if (p.urlFilterType !== 'none') { UrlState.set(p.urlFilterType, p.locationFilter, s.modalID, p.historyType); } } close() { const s = this; const { $backdrop, $closeBtn, $content, $modal } = s.modalObj; const p = s.params; hasCb(p.onClose, s.modalObj); $modal.addClass(p.cssPrefix + '--dismissing'); $modal.removeClass(p.cssPrefix + '--show'); // detach events $closeBtn.off(`click.${s.modalEvent}Dismiss`); if (p.backDropClose) $backdrop.off(`click.${s.modalEvent}Dismiss`); $(document) .off(`keydown.${s.modalEvent}Dismiss`) .off(`${s.modalEvent}Dismiss`); if (p.urlFilterType !== 'none') { UrlState.set(p.urlFilterType, p.locationFilter, null, p.historyType); } setTimeout(() => { $modal.attr({ role: 'dialog', 'aria-modal': '' }).removeClass(p.cssPrefix + '--dismissing').css({ display: '' }); $(p.appendTo).removeClass(p.cssPrefix + '-open').css({ overflow: '', 'padding-right': '' }); if (p.focusInDelay !== null) { s.trappedFocus.remove(); } if (s.enabledElem && s.enabledElem instanceof HTMLElement) { s.enabledElem.focus(); } if (p.fromDOM) { $('#' + s.modalObj.id + '_marker').after($content).remove(); $.extend(s.modalObj, { contentAppended: false }); } hasCb(p.afterClose, s.modalObj); $modal.remove(); $.extend(s.modalObj, { show: false }); }, p.closeOutDelay); } loadFromUrl() { const s = this; const p = s.params; if (p.locationFilter !== null && p.loadLocation && p.urlFilterType !== 'none') { const filterEl = UrlState.get(p.urlFilterType, p.locationFilter); if (filterEl === s.modalID) { s.modalObj.show ? s.close() : s.setDisplayAndEvents(); } } } }