PluginProbe
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder / 3.1.4
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder v3.1.4
3.1.4 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 All 93 releases
ultimate-store-kit / src / vendor / js / micromodal.js

micromodal.js in Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder 3.1.4, at src/vendor/js/micromodal.js

549 lines 17.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function (global, factory) {
2 typeof exports === "object" && typeof module !== "undefined"
3 ? (module.exports = factory())
4 : typeof define === "function" && define.amd
5 ? define(factory)
6 : ((global = global || self), (global.MicroModal = factory()));
7 })(this, function () {
8 "use strict";
9
10 function _classCallCheck(instance, Constructor) {
11 if (!(instance instanceof Constructor)) {
12 throw new TypeError("Cannot call a class as a function");
13 }
14 }
15
16 function _defineProperties(target, props) {
17 for (var i = 0; i < props.length; i++) {
18 var descriptor = props[i];
19 descriptor.enumerable = descriptor.enumerable || false;
20 descriptor.configurable = true;
21 if ("value" in descriptor) descriptor.writable = true;
22 Object.defineProperty(target, descriptor.key, descriptor);
23 }
24 }
25
26 function _createClass(Constructor, protoProps, staticProps) {
27 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
28 if (staticProps) _defineProperties(Constructor, staticProps);
29 return Constructor;
30 }
31
32 function _toConsumableArray(arr) {
33 return (
34 _arrayWithoutHoles(arr) ||
35 _iterableToArray(arr) ||
36 _unsupportedIterableToArray(arr) ||
37 _nonIterableSpread()
38 );
39 }
40
41 function _arrayWithoutHoles(arr) {
42 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
43 }
44
45 function _iterableToArray(iter) {
46 if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter))
47 return Array.from(iter);
48 }
49
50 function _unsupportedIterableToArray(o, minLen) {
51 if (!o) return;
52 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
53 var n = Object.prototype.toString.call(o).slice(8, -1);
54 if (n === "Object" && o.constructor) n = o.constructor.name;
55 if (n === "Map" || n === "Set") return Array.from(n);
56 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))
57 return _arrayLikeToArray(o, minLen);
58 }
59
60 function _arrayLikeToArray(arr, len) {
61 if (len == null || len > arr.length) len = arr.length;
62
63 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
64
65 return arr2;
66 }
67
68 function _nonIterableSpread() {
69 throw new TypeError(
70 "Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."
71 );
72 }
73
74 var MicroModal = (function () {
75 var FOCUSABLE_ELEMENTS = [
76 "a[href]",
77 "area[href]",
78 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
79 "select:not([disabled]):not([aria-hidden])",
80 "textarea:not([disabled]):not([aria-hidden])",
81 "button:not([disabled]):not([aria-hidden])",
82 "iframe",
83 "object",
84 "embed",
85 "[contenteditable]",
86 '[tabindex]:not([tabindex^="-"])',
87 ];
88
89 var Modal = /*#__PURE__*/ (function () {
90 function Modal(_ref) {
91 var targetModal = _ref.targetModal,
92 _ref$triggers = _ref.triggers,
93 triggers = _ref$triggers === void 0 ? [] : _ref$triggers,
94 _ref$onShow = _ref.onShow,
95 onShow = _ref$onShow === void 0 ? function () {} : _ref$onShow,
96 _ref$onClose = _ref.onClose,
97 onClose = _ref$onClose === void 0 ? function () {} : _ref$onClose,
98 _ref$openTrigger = _ref.openTrigger,
99 openTrigger =
100 _ref$openTrigger === void 0
101 ? "data-micromodal-trigger"
102 : _ref$openTrigger,
103 _ref$closeTrigger = _ref.closeTrigger,
104 closeTrigger =
105 _ref$closeTrigger === void 0
106 ? "data-micromodal-close"
107 : _ref$closeTrigger,
108 _ref$openClass = _ref.openClass,
109 openClass = _ref$openClass === void 0 ? "is-open" : _ref$openClass,
110 _ref$disableScroll = _ref.disableScroll,
111 disableScroll =
112 _ref$disableScroll === void 0 ? false : _ref$disableScroll,
113 _ref$disableFocus = _ref.disableFocus,
114 disableFocus =
115 _ref$disableFocus === void 0 ? false : _ref$disableFocus,
116 _ref$awaitCloseAnimat = _ref.awaitCloseAnimation,
117 awaitCloseAnimation =
118 _ref$awaitCloseAnimat === void 0 ? false : _ref$awaitCloseAnimat,
119 _ref$awaitOpenAnimati = _ref.awaitOpenAnimation,
120 awaitOpenAnimation =
121 _ref$awaitOpenAnimati === void 0 ? false : _ref$awaitOpenAnimati,
122 _ref$debugMode = _ref.debugMode,
123 debugMode = _ref$debugMode === void 0 ? false : _ref$debugMode;
124
125 _classCallCheck(this, Modal);
126
127 // Save a reference of the modal
128 this.modal = document.getElementById(targetModal); // Save a reference to the passed config
129
130 this.config = {
131 debugMode: debugMode,
132 disableScroll: disableScroll,
133 openTrigger: openTrigger,
134 closeTrigger: closeTrigger,
135 openClass: openClass,
136 onShow: onShow,
137 onClose: onClose,
138 awaitCloseAnimation: awaitCloseAnimation,
139 awaitOpenAnimation: awaitOpenAnimation,
140 disableFocus: disableFocus,
141 }; // Register click events only if pre binding eventListeners
142
143 if (triggers.length > 0)
144 this.registerTriggers.apply(this, _toConsumableArray(triggers)); // pre bind functions for event listeners
145
146 this.onClick = this.onClick.bind(this);
147 this.onKeydown = this.onKeydown.bind(this);
148 }
149 /**
150 * Loops through all openTriggers and binds click event
151 * @param {array} triggers [Array of node elements]
152 * @return {void}
153 */
154
155 _createClass(Modal, [
156 {
157 key: "registerTriggers",
158 value: function registerTriggers() {
159 var _this = this;
160
161 for (
162 var _len = arguments.length, triggers = new Array(_len), _key = 0;
163 _key < _len;
164 _key++
165 ) {
166 triggers[_key] = arguments[_key];
167 }
168
169 triggers.filter(Boolean).forEach(function (trigger) {
170 trigger.addEventListener("click", function (event) {
171 return _this.showModal(event);
172 });
173 });
174 },
175 },
176 {
177 key: "showModal",
178 value: function showModal() {
179 var _this2 = this;
180
181 var event =
182 arguments.length > 0 && arguments[0] !== undefined
183 ? arguments[0]
184 : null;
185 this.activeElement = document.activeElement;
186 this.modal.setAttribute("aria-hidden", "false");
187 this.modal.classList.add(this.config.openClass);
188 this.scrollBehaviour("disable");
189 this.addEventListeners();
190
191 if (this.config.awaitOpenAnimation) {
192 var handler = function handler() {
193 _this2.modal.removeEventListener(
194 "animationend",
195 handler,
196 false
197 );
198
199 _this2.setFocusToFirstNode();
200 };
201
202 this.modal.addEventListener("animationend", handler, false);
203 } else {
204 this.setFocusToFirstNode();
205 }
206
207 this.config.onShow(this.modal, this.activeElement, event);
208 },
209 },
210 {
211 key: "closeModal",
212 value: function closeModal() {
213 var event =
214 arguments.length > 0 && arguments[0] !== undefined
215 ? arguments[0]
216 : null;
217 var modal = this.modal;
218 this.modal.setAttribute("aria-hidden", "true");
219 this.removeEventListeners();
220 this.scrollBehaviour("enable");
221
222 if (this.activeElement && this.activeElement.focus) {
223 this.activeElement.focus();
224 }
225
226 this.config.onClose(this.modal, this.activeElement, event);
227
228 if (this.config.awaitCloseAnimation) {
229 var openClass = this.config.openClass; // <- old school ftw
230
231 this.modal.addEventListener(
232 "animationend",
233 function handler() {
234 modal.classList.remove(openClass);
235 modal.removeEventListener("animationend", handler, false);
236 },
237 false
238 );
239 } else {
240 modal.classList.remove(this.config.openClass);
241 }
242 },
243 },
244 {
245 key: "closeModalById",
246 value: function closeModalById(targetModal) {
247 this.modal = document.getElementById(targetModal);
248 if (this.modal) this.closeModal();
249 },
250 },
251 {
252 key: "scrollBehaviour",
253 value: function scrollBehaviour(toggle) {
254 if (!this.config.disableScroll) return;
255 var body = document.querySelector("body");
256
257 switch (toggle) {
258 case "enable":
259 Object.assign(body.style, {
260 overflow: "",
261 });
262 break;
263
264 case "disable":
265 Object.assign(body.style, {
266 overflow: "hidden",
267 });
268 break;
269 }
270 },
271 },
272 {
273 key: "addEventListeners",
274 value: function addEventListeners() {
275 this.modal.addEventListener("touchstart", this.onClick);
276 this.modal.addEventListener("click", this.onClick);
277 document.addEventListener("keydown", this.onKeydown);
278 },
279 },
280 {
281 key: "removeEventListeners",
282 value: function removeEventListeners() {
283 this.modal.removeEventListener("touchstart", this.onClick);
284 this.modal.removeEventListener("click", this.onClick);
285 document.removeEventListener("keydown", this.onKeydown);
286 },
287 },
288 {
289 key: "onClick",
290 value: function onClick(event) {
291 if (event.target.hasAttribute(this.config.closeTrigger)) {
292 this.closeModal(event);
293 }
294 },
295 },
296 {
297 key: "onKeydown",
298 value: function onKeydown(event) {
299 if (event.keyCode === 27) this.closeModal(event); // esc
300
301 if (event.keyCode === 9) this.retainFocus(event); // tab
302 },
303 },
304 {
305 key: "getFocusableNodes",
306 value: function getFocusableNodes() {
307 var nodes = this.modal.querySelectorAll(FOCUSABLE_ELEMENTS);
308 return Array.apply(void 0, _toConsumableArray(nodes));
309 },
310 /**
311 * Tries to set focus on a node which is not a close trigger
312 * if no other nodes exist then focuses on first close trigger
313 */
314 },
315 {
316 key: "setFocusToFirstNode",
317 value: function setFocusToFirstNode() {
318 var _this3 = this;
319
320 if (this.config.disableFocus) return;
321 var focusableNodes = this.getFocusableNodes(); // no focusable nodes
322
323 if (focusableNodes.length === 0) return; // remove nodes on whose click, the modal closes
324 // could not think of a better name :(
325
326 var nodesWhichAreNotCloseTargets = focusableNodes.filter(function (
327 node
328 ) {
329 return !node.hasAttribute(_this3.config.closeTrigger);
330 });
331 if (nodesWhichAreNotCloseTargets.length > 0)
332 nodesWhichAreNotCloseTargets[0].focus();
333 if (nodesWhichAreNotCloseTargets.length === 0)
334 focusableNodes[0].focus();
335 },
336 },
337 {
338 key: "retainFocus",
339 value: function retainFocus(event) {
340 var focusableNodes = this.getFocusableNodes(); // no focusable nodes
341
342 if (focusableNodes.length === 0) return;
343 /**
344 * Filters nodes which are hidden to prevent
345 * focus leak outside modal
346 */
347
348 focusableNodes = focusableNodes.filter(function (node) {
349 return node.offsetParent !== null;
350 }); // if disableFocus is true
351
352 if (!this.modal.contains(document.activeElement)) {
353 focusableNodes[0].focus();
354 } else {
355 var focusedItemIndex = focusableNodes.indexOf(
356 document.activeElement
357 );
358
359 if (event.shiftKey && focusedItemIndex === 0) {
360 focusableNodes[focusableNodes.length - 1].focus();
361 event.preventDefault();
362 }
363
364 if (
365 !event.shiftKey &&
366 focusableNodes.length > 0 &&
367 focusedItemIndex === focusableNodes.length - 1
368 ) {
369 focusableNodes[0].focus();
370 event.preventDefault();
371 }
372 }
373 },
374 },
375 ]);
376
377 return Modal;
378 })();
379 /**
380 * Modal prototype ends.
381 * Here on code is responsible for detecting and
382 * auto binding event handlers on modal triggers
383 */
384 // Keep a reference to the opened modal
385
386 var activeModal = null;
387 /**
388 * Generates an associative array of modals and it's
389 * respective triggers
390 * @param {array} triggers An array of all triggers
391 * @param {string} triggerAttr The data-attribute which triggers the module
392 * @return {array}
393 */
394
395 var generateTriggerMap = function generateTriggerMap(
396 triggers,
397 triggerAttr
398 ) {
399 var triggerMap = [];
400 triggers.forEach(function (trigger) {
401 var targetModal = trigger.attributes[triggerAttr].value;
402 if (triggerMap[targetModal] === undefined) triggerMap[targetModal] = [];
403 triggerMap[targetModal].push(trigger);
404 });
405 return triggerMap;
406 };
407 /**
408 * Validates whether a modal of the given id exists
409 * in the DOM
410 * @param {number} id The id of the modal
411 * @return {boolean}
412 */
413
414 var validateModalPresence = function validateModalPresence(id) {
415 if (!document.getElementById(id)) {
416 console.warn(
417 "MicroModal: \u2757Seems like you have missed %c'".concat(id, "'"),
418 "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
419 "ID somewhere in your code. Refer example below to resolve it."
420 );
421 console.warn(
422 "%cExample:",
423 "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
424 '<div class="modal" id="'.concat(id, '"></div>')
425 );
426 return false;
427 }
428 };
429 /**
430 * Validates if there are modal triggers present
431 * in the DOM
432 * @param {array} triggers An array of data-triggers
433 * @return {boolean}
434 */
435
436 var validateTriggerPresence = function validateTriggerPresence(triggers) {
437 if (triggers.length <= 0) {
438 console.warn(
439 "MicroModal: \u2757Please specify at least one %c'micromodal-trigger'",
440 "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
441 "data attribute."
442 );
443 console.warn(
444 "%cExample:",
445 "background-color: #f8f9fa;color: #50596c;font-weight: bold;",
446 '<a href="#" data-micromodal-trigger="my-modal"></a>'
447 );
448 return false;
449 }
450 };
451 /**
452 * Checks if triggers and their corresponding modals
453 * are present in the DOM
454 * @param {array} triggers Array of DOM nodes which have data-triggers
455 * @param {array} triggerMap Associative array of modals and their triggers
456 * @return {boolean}
457 */
458
459 var validateArgs = function validateArgs(triggers, triggerMap) {
460 validateTriggerPresence(triggers);
461 if (!triggerMap) return true;
462
463 for (var id in triggerMap) {
464 validateModalPresence(id);
465 }
466
467 return true;
468 };
469 /**
470 * Binds click handlers to all modal triggers
471 * @param {object} config [description]
472 * @return void
473 */
474
475 var init = function init(config) {
476 // Create an config object with default openTrigger
477 var options = Object.assign(
478 {},
479 {
480 openTrigger: "data-micromodal-trigger",
481 },
482 config
483 ); // Collects all the nodes with the trigger
484
485 var triggers = _toConsumableArray(
486 document.querySelectorAll("[".concat(options.openTrigger, "]"))
487 ); // Makes a mappings of modals with their trigger nodes
488
489 var triggerMap = generateTriggerMap(triggers, options.openTrigger); // Checks if modals and triggers exist in dom
490
491 if (
492 options.debugMode === true &&
493 validateArgs(triggers, triggerMap) === false
494 )
495 return; // For every target modal creates a new instance
496
497 for (var key in triggerMap) {
498 var value = triggerMap[key];
499 options.targetModal = key;
500 options.triggers = _toConsumableArray(value);
501 activeModal = new Modal(options); // eslint-disable-line no-new
502 }
503 };
504 /**
505 * Shows a particular modal
506 * @param {string} targetModal [The id of the modal to display]
507 * @param {object} config [The configuration object to pass]
508 * @return {void}
509 */
510
511 var show = function show(targetModal, config) {
512 var options = config || {};
513 options.targetModal = targetModal; // Checks if modals and triggers exist in dom
514
515 if (
516 options.debugMode === true &&
517 validateModalPresence(targetModal) === false
518 )
519 return; // clear events in case previous modal wasn't close
520
521 if (activeModal) activeModal.removeEventListeners(); // stores reference to active modal
522
523 activeModal = new Modal(options); // eslint-disable-line no-new
524
525 activeModal.showModal();
526 };
527 /**
528 * Closes the active modal
529 * @param {string} targetModal [The id of the modal to close]
530 * @return {void}
531 */
532
533 var close = function close(targetModal) {
534 targetModal
535 ? activeModal.closeModalById(targetModal)
536 : activeModal.closeModal();
537 };
538
539 return {
540 init: init,
541 show: show,
542 close: close,
543 };
544 })();
545 window.MicroModal = MicroModal;
546
547 return MicroModal;
548 });
549