@ithaka/bonsai
Version:
ITHAKA core styling
185 lines (163 loc) • 6.73 kB
JavaScript
;
import $ from "jquery";
import { Keyboard } from "foundation-sites/js/foundation.util.keyboard";
import { Reveal } from "foundation-sites/js/foundation.reveal";
import { BonsaiBase } from "./bonsai.base";
/** Reveal Module
* @class
* @name BonsaiModal
* @param {jQuery} elements - jQuery object to use for initializing the modal component.
* @param {Object} options - Optional parameters.
*
* @example
* var elem = new BonsaiModal($(".reveal"));
*
* @returns Returns Bonsai.Modals object that has initialized each modal with the incoming `options`
*
*/
class BonsaiModal extends BonsaiBase {
constructor(elements, options = {}) {
super(elements, options);
Bonsai.Modals = Bonsai.hasOwnProperty("Modals") ? Bonsai.Modals : {members: {}, reflow: this.reflow};
this._initializeModals();
}
/**
* Iterates through every element passed into BonsaiModal and initializes it. Store the initlalized Reveal Modal with its elementID into window object Bonsai.Modals
* @private
*/
_initializeModals() {
this.elements.each((index, element) => {
let $element = $(element),
elementID = $element.attr("id"),
theModal = new Reveal($element, this.options);
this._registerModalEvents(theModal);
this._addCircleDesign($element);
if (elementID) {
Bonsai.Modals.members[elementID] = theModal; // Store theModal with elementID after initialized
}
});
}
_registerModalEvents(theModal) {
theModal.$anchor.on("click", (event) => {
theModal.$anchor = $(event.target);
this._onAnchorClick(event, theModal);
});
theModal.$element.find("a.close-button").on("click", (event) => {
this._onCloseButtonClick(event);
});
// gather and combine events for the modal div
let modalEvents = this._getModalEvents(theModal),
additionalModalEvents = this._getAdditionalModalEvents(theModal);
modalEvents = Object.assign(modalEvents, additionalModalEvents);
// register events onto the modal div
theModal.$element.on(modalEvents);
}
/**
* events that are meant to be registered against the modal div
* @param elementID - the ID of the modal div
* @param theModal - the Foundation Reveal object returned from new Reveal()
* @returns an object of functions that triggers custom bonsai events
* @private
*/
_getModalEvents(theModal) {
return {
/** Trigger this to customize the modal as it just started the open call and not visible
* @event bonsai-modal-open
*/
"closeme.zf.reveal": () => {
theModal.$element.trigger("bonsai-modal-open");
},
/** Trigger this to customize the modal as it has successfully opened and is visible
* @event bonsai-modal-opened
*/
"open.zf.reveal": () => {
theModal.$element.trigger("bonsai-modal-opened");
},
/** Trigger this to customize the modal as it just finished closing and is closed
* @event bonsai-modal-closed
*/
"closed.zf.reveal": () => {
theModal.$element.trigger("bonsai-modal-closed");
},
/** Trigger this to reflow the modal and register focusable elements after modal html content change
* @event revealReflow
*/
"revealReflow" : function() {
theModal.focusableElements = Keyboard.findFocusable(theModal.$element);
Keyboard.trapFocus(theModal.$element); // update focusable elements and trap focus in modal
},
// Modal keydown event for tabbing order tweak
"keydown" : function(e) {
if(Keyboard.parseKey(e) === "SHIFT_TAB") { // special case: fix "Cite this Item" shift+tab to copy buttons
if (theModal.focusableElements.eq(3).is(":focus")) {
theModal.focusableElements.eq(2).focus();
e.preventDefault();
e.stopImmediatePropagation(); // stop other events on the element and stop bubbling up the DOM tree
}
}
}
};
}
/**
* Register additional events to be put onto the modal
* @param $element - the jQuery selected div that is the modal
* @param elementID - the ID of the modal
* @param theModal - the Foundation Reveal object returned from new Reveal()
* @returns an object
*/
_getAdditionalModalEvents(theModal) {
return {};
}
/**
* Adds a bonsai specific design to the modal if data-icon and optionally
* data-circle-color are on the modal div
* data-color-circle must be a color in the palette
* data-color-icon must be an icon in bonsai
* @param $element - the jQuery selected modal
* @private
*/
_addCircleDesign($element) {
const elementData = $element.data();
if (elementData.icon) {
let circleClass = elementData.circleColor ? `circle ${elementData.circleColor}-background` : "circle";
$element.prepend(
`<div class="modal-circle">
<div class="${circleClass}"><i class="icon-${elementData.icon}"></i></div>
</div>`
);
}
}
/**
* Runs when the user clicks on the anchor that is meant to launch the modal
* @param event - the click event
* @param theModal - the Bonsai modal object, passed in to be used when subclassing
* @private
*/
_onAnchorClick(event, theModal) {
// to prevent browser from following href
event.preventDefault();
}
/**
* Runs when the user clicks on the anchor that is meant to close the modal
* @param event - the click event
* @private
*/
_onCloseButtonClick(event) {
// to prevent the browser from following href
event.preventDefault();
}
/**
* Reflow method to re-bind jQuery element to a Modal object, and store it back to Bonsai.Modals
*
* @public
*/
reflow() {
$.each(Bonsai.Modals.members, (elementID, theModal) => {
theModal._destroy(); // detroy previous Modal element
let theNewModal = new Reveal($(`#${elementID}`));
BonsaiModal.prototype._registerModalEvents(theNewModal);
Bonsai.Modals.members[elementID] = theNewModal;
});
}
}
export { BonsaiModal };