vrembem 1.42.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,288 +1,366 @@
1
- const setInert = (state, selector) => {
2
- if (selector) {
3
- const els = document.querySelectorAll(selector);
4
- els.forEach(el => {
5
- if (state) {
6
- el.inert = true;
7
- el.setAttribute('aria-hidden', true);
8
- } else {
9
- el.inert = null;
10
- el.removeAttribute('aria-hidden');
1
+ function _extends() {
2
+ _extends = Object.assign || function (target) {
3
+ for (var i = 1; i < arguments.length; i++) {
4
+ var source = arguments[i];
5
+
6
+ for (var key in source) {
7
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
8
+ target[key] = source[key];
9
+ }
11
10
  }
11
+ }
12
+
13
+ return target;
14
+ };
15
+
16
+ return _extends.apply(this, arguments);
17
+ }
18
+
19
+ var id = 0;
20
+
21
+ function _classPrivateFieldLooseKey(name) {
22
+ return "__private_" + id++ + "_" + name;
23
+ }
24
+
25
+ function _classPrivateFieldLooseBase(receiver, privateKey) {
26
+ if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
27
+ throw new TypeError("attempted to use private field on non-instance");
28
+ }
29
+
30
+ return receiver;
31
+ }
32
+
33
+ var _handler = /*#__PURE__*/_classPrivateFieldLooseKey("handler");
34
+
35
+ class Breakpoint {
36
+ constructor(value, handler) {
37
+ Object.defineProperty(this, _handler, {
38
+ writable: true,
39
+ value: void 0
12
40
  });
41
+ this.value = value;
42
+ _classPrivateFieldLooseBase(this, _handler)[_handler] = handler;
43
+ this.mql = null;
13
44
  }
14
- };
15
- const setOverflowHidden = (state, selector) => {
16
- if (selector) {
17
- const els = document.querySelectorAll(selector);
18
- els.forEach(el => {
19
- if (state) {
20
- el.style.overflow = 'hidden';
45
+
46
+ get handler() {
47
+ return _classPrivateFieldLooseBase(this, _handler)[_handler];
48
+ } // Unmount existing handler before setting a new one.
49
+
50
+
51
+ set handler(func) {
52
+ if (this.mql) {
53
+ // Conditionally use removeListener() for IE11 support.
54
+ if (typeof this.mql.removeEventListener === 'function') {
55
+ this.mql.removeEventListener('change', _classPrivateFieldLooseBase(this, _handler)[_handler]);
21
56
  } else {
22
- el.style.removeProperty('overflow');
57
+ this.mql.removeListener(_classPrivateFieldLooseBase(this, _handler)[_handler]);
23
58
  }
24
- });
25
- }
26
- };
27
- const setTabindex = selector => {
28
- if (selector) {
29
- const els = document.querySelectorAll(selector);
30
- els.forEach(el => {
31
- el.setAttribute('tabindex', '-1');
32
- });
59
+ }
60
+
61
+ _classPrivateFieldLooseBase(this, _handler)[_handler] = func;
33
62
  }
34
- };
35
63
 
36
- /**
37
- * Adds a class or classes to a Node or NodeList.
38
- * @param {Node || NodeList} el - Element(s) to add class(es) to.
39
- * @param {String || Array} cl - Class(es) to add.
40
- */
41
- const addClass = (el, ...cl) => {
42
- el = el.forEach ? el : [el];
43
- el.forEach(el => {
44
- el.classList.add(...cl);
45
- });
46
- };
64
+ mount(value, handler) {
65
+ // Update passed params.
66
+ if (value) this.value = value;
67
+ if (handler) _classPrivateFieldLooseBase(this, _handler)[_handler] = handler; // Guard if no breakpoint was set.
47
68
 
48
- /**
49
- * Takes a hyphen cased string and converts it to camel case.
50
- * @param {String } str - the string to convert to camel case.
51
- * @returns {Boolean} - returns a camel cased string.
52
- */
53
- const camelCase = str => {
54
- return str.replace(/-([a-z])/g, function (g) {
55
- return g[1].toUpperCase();
56
- });
57
- };
69
+ if (!this.value) return this; // Setup and store the MediaQueryList instance.
58
70
 
59
- var focusableSelectors = ['a[href]:not([tabindex^="-"])', 'area[href]:not([tabindex^="-"])', 'input:not([type="hidden"]):not([type="radio"]):not([disabled]):not([tabindex^="-"])', 'input[type="radio"]:not([disabled]):not([tabindex^="-"])', 'select:not([disabled]):not([tabindex^="-"])', 'textarea:not([disabled]):not([tabindex^="-"])', 'button:not([disabled]):not([tabindex^="-"])', 'iframe:not([tabindex^="-"])', 'audio[controls]:not([tabindex^="-"])', 'video[controls]:not([tabindex^="-"])', '[contenteditable]:not([tabindex^="-"])', '[tabindex]:not([tabindex^="-"])'];
71
+ this.mql = window.matchMedia(`(min-width: ${this.value})`); // Conditionally use addListener() for IE11 support.
60
72
 
61
- const focusTarget = (target, settings) => {
62
- const innerFocus = target.querySelector(`[data-${settings.dataFocus}]`);
73
+ if (typeof this.mql.addEventListener === 'function') {
74
+ this.mql.addEventListener('change', _classPrivateFieldLooseBase(this, _handler)[_handler]);
75
+ } else {
76
+ this.mql.addListener(_classPrivateFieldLooseBase(this, _handler)[_handler]);
77
+ } // Run the handler.
63
78
 
64
- if (innerFocus) {
65
- innerFocus.focus();
66
- } else {
67
- const innerElement = target.querySelector('[tabindex="-1"]');
68
- if (innerElement) innerElement.focus();
79
+
80
+ _classPrivateFieldLooseBase(this, _handler)[_handler](this.mql);
81
+
82
+ return this;
69
83
  }
70
- };
71
- const focusTrigger = (obj = null) => {
72
- if (!obj || !obj.memory || !obj.memory.trigger) return;
73
- obj.memory.trigger.focus();
74
- obj.memory.trigger = null;
75
- };
76
- class FocusTrap {
84
+
85
+ unmount() {
86
+ // Guard if no MediaQueryList instance exists.
87
+ if (!this.mql) return this; // Conditionally use removeListener() for IE11 support.
88
+
89
+ if (typeof this.mql.removeEventListener === 'function') {
90
+ this.mql.removeEventListener('change', _classPrivateFieldLooseBase(this, _handler)[_handler]);
91
+ } else {
92
+ this.mql.removeListener(_classPrivateFieldLooseBase(this, _handler)[_handler]);
93
+ } // Set value, handler and MediaQueryList to null.
94
+
95
+
96
+ this.value = null;
97
+ _classPrivateFieldLooseBase(this, _handler)[_handler] = null;
98
+ this.mql = null;
99
+ return this;
100
+ }
101
+
102
+ }
103
+
104
+ class Collection {
77
105
  constructor() {
78
- this.target = null;
79
- this.__handlerFocusTrap = this.handlerFocusTrap.bind(this);
106
+ this.collection = [];
80
107
  }
81
108
 
82
- init(target) {
83
- this.target = target;
84
- this.inner = this.target.querySelector('[tabindex="-1"]');
85
- this.focusable = this.getFocusable();
109
+ async register(item) {
110
+ await this.deregister(item);
111
+ this.collection.push(item);
112
+ return this.collection;
113
+ }
86
114
 
87
- if (this.focusable.length) {
88
- this.focusableFirst = this.focusable[0];
89
- this.focusableLast = this.focusable[this.focusable.length - 1];
90
- this.target.addEventListener('keydown', this.__handlerFocusTrap);
91
- } else {
92
- this.target.addEventListener('keydown', this.handlerFocusLock);
115
+ async deregister(ref) {
116
+ const index = this.collection.findIndex(entry => {
117
+ return entry === ref;
118
+ });
119
+
120
+ if (index >= 0) {
121
+ const entry = this.collection[index];
122
+ Object.getOwnPropertyNames(entry).forEach(prop => {
123
+ delete entry[prop];
124
+ });
125
+ this.collection.splice(index, 1);
93
126
  }
127
+
128
+ return this.collection;
94
129
  }
95
130
 
96
- destroy() {
97
- if (!this.target) return;
98
- this.inner = null;
99
- this.focusable = null;
100
- this.focusableFirst = null;
101
- this.focusableLast = null;
102
- this.target.removeEventListener('keydown', this.__handlerFocusTrap);
103
- this.target.removeEventListener('keydown', this.handlerFocusLock);
104
- this.target = null;
105
- }
106
-
107
- refresh() {
108
- // Check if a target has been set
109
- if (!this.target) return; // Remove existing events
110
-
111
- this.target.removeEventListener('keydown', this.__handlerFocusTrap);
112
- this.target.removeEventListener('keydown', this.handlerFocusLock); // Get the focusable elements
113
-
114
- this.focusable = this.getFocusable(); // Setup the focus handlers based on focusable length
115
-
116
- if (this.focusable.length) {
117
- // If there are focusable elements, setup focus trap
118
- this.focusableFirst = this.focusable[0];
119
- this.focusableLast = this.focusable[this.focusable.length - 1];
120
- this.target.addEventListener('keydown', this.__handlerFocusTrap);
121
- } else {
122
- // If there are no focusable elements, setup focus lock
123
- this.focusableFirst = null;
124
- this.focusableLast = null;
125
- this.target.addEventListener('keydown', this.handlerFocusLock);
131
+ async registerCollection(items) {
132
+ await Promise.all(Array.from(items, item => {
133
+ this.register(item);
134
+ }));
135
+ return this.collection;
136
+ }
137
+
138
+ async deregisterCollection() {
139
+ while (this.collection.length > 0) {
140
+ await this.deregister(this.collection[0]);
126
141
  }
142
+
143
+ return this.collection;
127
144
  }
128
145
 
129
- handlerFocusTrap(event) {
130
- const isTab = event.key === 'Tab' || event.keyCode === 9;
131
- if (!isTab) return;
146
+ get(value, key = 'id') {
147
+ return this.collection.find(item => {
148
+ return item[key] === value;
149
+ });
150
+ }
132
151
 
133
- if (event.shiftKey) {
134
- if (document.activeElement === this.focusableFirst || document.activeElement === this.inner) {
135
- this.focusableLast.focus();
136
- event.preventDefault();
137
- }
152
+ }
153
+
154
+ var focusableSelectors = ['a[href]:not([tabindex^="-"])', 'area[href]:not([tabindex^="-"])', 'input:not([type="hidden"]):not([type="radio"]):not([disabled]):not([tabindex^="-"])', 'input[type="radio"]:not([disabled]):not([tabindex^="-"])', 'select:not([disabled]):not([tabindex^="-"])', 'textarea:not([disabled]):not([tabindex^="-"])', 'button:not([disabled]):not([tabindex^="-"])', 'iframe:not([tabindex^="-"])', 'audio[controls]:not([tabindex^="-"])', 'video[controls]:not([tabindex^="-"])', '[contenteditable]:not([tabindex^="-"])', '[tabindex]:not([tabindex^="-"])'];
155
+
156
+ var _focusable = /*#__PURE__*/_classPrivateFieldLooseKey("focusable");
157
+
158
+ var _handleFocusTrap = /*#__PURE__*/_classPrivateFieldLooseKey("handleFocusTrap");
159
+
160
+ var _handleFocusLock = /*#__PURE__*/_classPrivateFieldLooseKey("handleFocusLock");
161
+
162
+ class FocusTrap {
163
+ constructor(el = null, selectorFocus = '[data-focus]') {
164
+ Object.defineProperty(this, _focusable, {
165
+ writable: true,
166
+ value: void 0
167
+ });
168
+ Object.defineProperty(this, _handleFocusTrap, {
169
+ writable: true,
170
+ value: void 0
171
+ });
172
+ Object.defineProperty(this, _handleFocusLock, {
173
+ writable: true,
174
+ value: void 0
175
+ });
176
+ this.el = el;
177
+ this.selectorFocus = selectorFocus;
178
+ _classPrivateFieldLooseBase(this, _handleFocusTrap)[_handleFocusTrap] = handleFocusTrap.bind(this);
179
+ _classPrivateFieldLooseBase(this, _handleFocusLock)[_handleFocusLock] = handleFocusLock.bind(this);
180
+ }
181
+
182
+ get focusable() {
183
+ return _classPrivateFieldLooseBase(this, _focusable)[_focusable];
184
+ }
185
+
186
+ set focusable(value) {
187
+ // Update the focusable value.
188
+ _classPrivateFieldLooseBase(this, _focusable)[_focusable] = value; // Apply event listeners based on new focusable array length.
189
+
190
+ if (_classPrivateFieldLooseBase(this, _focusable)[_focusable].length) {
191
+ document.removeEventListener('keydown', _classPrivateFieldLooseBase(this, _handleFocusLock)[_handleFocusLock]);
192
+ document.addEventListener('keydown', _classPrivateFieldLooseBase(this, _handleFocusTrap)[_handleFocusTrap]);
138
193
  } else {
139
- if (document.activeElement === this.focusableLast || document.activeElement === this.inner) {
140
- this.focusableFirst.focus();
141
- event.preventDefault();
142
- }
194
+ document.removeEventListener('keydown', _classPrivateFieldLooseBase(this, _handleFocusTrap)[_handleFocusTrap]);
195
+ document.addEventListener('keydown', _classPrivateFieldLooseBase(this, _handleFocusLock)[_handleFocusLock]);
143
196
  }
144
197
  }
145
198
 
146
- handlerFocusLock(event) {
147
- const isTab = event.key === 'Tab' || event.keyCode === 9;
148
- if (isTab) event.preventDefault();
199
+ get focusableFirst() {
200
+ return this.focusable[0];
149
201
  }
150
202
 
151
- getFocusable() {
152
- const focusable = [];
203
+ get focusableLast() {
204
+ return this.focusable[this.focusable.length - 1];
205
+ }
206
+
207
+ mount(el, selectorFocus) {
208
+ // Update passed params.
209
+ if (el) this.el = el;
210
+ if (selectorFocus) this.selectorFocus = selectorFocus; // Get the focusable elements.
211
+
212
+ this.focusable = this.getFocusable(); // Set the focus on the element.
213
+
214
+ this.focus();
215
+ }
216
+
217
+ unmount() {
218
+ // Set element to null.
219
+ this.el = null; // Apply empty array to focusable.
220
+
221
+ this.focusable = []; // Remove event listeners
222
+
223
+ document.removeEventListener('keydown', _classPrivateFieldLooseBase(this, _handleFocusTrap)[_handleFocusTrap]);
224
+ document.removeEventListener('keydown', _classPrivateFieldLooseBase(this, _handleFocusLock)[_handleFocusLock]);
225
+ }
226
+
227
+ focus(el = this.el, selectorFocus = this.selectorFocus) {
228
+ // Query for the focus selector, otherwise return this element.
229
+ const result = el.querySelector(selectorFocus) || el; // Give the returned element focus.
230
+
231
+ result.focus();
232
+ }
233
+
234
+ getFocusable(el = this.el) {
235
+ // Initialize the focusable array.
236
+ const focusable = []; // Store the initial focus and scroll position.
237
+
153
238
  const initFocus = document.activeElement;
154
- const initScrollTop = this.inner ? this.inner.scrollTop : 0;
155
- this.target.querySelectorAll(focusableSelectors.join(',')).forEach(el => {
156
- el.focus();
239
+ const initScrollTop = el.scrollTop; // Query for all the focusable elements.
240
+
241
+ const els = el.querySelectorAll(focusableSelectors.join(',')); // Loop through all focusable elements.
242
+
243
+ els.forEach(el => {
244
+ // Set them to focus and check
245
+ el.focus(); // Test that the element took focus.
157
246
 
158
- if (el === document.activeElement) {
247
+ if (document.activeElement === el) {
248
+ // Add element to the focusable array.
159
249
  focusable.push(el);
160
250
  }
161
- });
162
- if (this.inner) this.inner.scrollTop = initScrollTop;
163
- initFocus.focus();
251
+ }); // Restore the initial scroll position and focus.
252
+
253
+ el.scrollTop = initScrollTop;
254
+ initFocus.focus(); // Return the focusable array.
255
+
164
256
  return focusable;
165
257
  }
166
258
 
167
259
  }
168
260
 
169
- /**
170
- * Get an element(s) from a selector or return value if not a string.
171
- * @param {String} selector - Selector to query.
172
- * @param {Boolean} single - Whether to return a single or all matches.
173
- */
174
- const getElement = function getElement(selector, single = 0) {
175
- if (typeof selector !== 'string') return selector;
176
- return single ? document.querySelector(selector) : document.querySelectorAll(selector);
177
- };
261
+ function handleFocusTrap(event) {
262
+ // Check if the click was a tab and return if not.
263
+ const isTab = event.key === 'Tab' || event.keyCode === 9;
264
+ if (!isTab) return; // If the shift key is pressed.
178
265
 
179
- /**
180
- * Checks an element or NodeList whether they contain a class or classes.
181
- * Ref: https://davidwalsh.name/nodelist-array
182
- * @param {Node} el - Element(s) to check class(es) on.
183
- * @param {String || Array} c - Class(es) to check.
184
- * @returns {Boolean} - Returns true if class exists, otherwise false.
185
- */
186
- const hasClass = (el, ...cl) => {
187
- el = el.forEach ? el : [el];
188
- el = [].slice.call(el);
189
- return cl.some(cl => {
190
- return el.some(el => {
191
- if (el.classList.contains(cl)) return true;
192
- });
193
- });
194
- };
266
+ if (event.shiftKey) {
267
+ // If the active element is either the root el or first focusable.
268
+ if (document.activeElement === this.focusableFirst || document.activeElement === this.el) {
269
+ // Prevent default and focus the last focusable element instead.
270
+ event.preventDefault();
271
+ this.focusableLast.focus();
272
+ }
273
+ } else {
274
+ // If the active element is either the root el or last focusable.
275
+ if (document.activeElement === this.focusableLast || document.activeElement === this.el) {
276
+ // Prevent default and focus the first focusable element instead.
277
+ event.preventDefault();
278
+ this.focusableFirst.focus();
279
+ }
280
+ }
281
+ }
195
282
 
196
- /**
197
- * Takes a camel cased string and converts it to hyphen case.
198
- * @param {String } str - the string to convert to hyphen case.
199
- * @returns {Boolean} - returns a hyphen cased string.
200
- */
201
- const hyphenCase = str => {
202
- return str.replace(/([a-z][A-Z])/g, function (g) {
203
- return g[0] + '-' + g[1].toLowerCase();
204
- });
205
- };
283
+ function handleFocusLock(event) {
284
+ // Ignore the tab key by preventing default.
285
+ const isTab = event.key === 'Tab' || event.keyCode === 9;
286
+ if (isTab) event.preventDefault();
287
+ }
206
288
 
207
- /**
208
- * Moves element(s) in the DOM based on a reference and move type.
209
- * @param {String} target - The element(s) to move.
210
- * @param {String} type - Move type can be 'after', 'before', 'append' or 'prepend'.
211
- * @param {String} reference - The reference element the move is relative to.
212
- */
289
+ function getConfig$1(el, dataConfig) {
290
+ const string = el.getAttribute(`data-${dataConfig}`) || '';
291
+ const json = string.replace(/'/g, '"');
292
+ return json ? JSON.parse(json) : {};
293
+ }
213
294
 
214
- function moveElement(target, type, reference = false) {
215
- if (reference) {
216
- const els = getElement(target);
217
- if (!els.length) throw new Error(`Move target element "${target}" not found!`);
218
- const ref = getElement(reference, 1);
219
- if (!ref) throw new Error(`Move reference element "${reference}" not found!`);
220
- els.forEach(el => {
221
- switch (type) {
222
- case 'after':
223
- ref.after(el);
224
- return {
225
- ref,
226
- el,
227
- type
228
- };
229
-
230
- case 'before':
231
- ref.before(el);
232
- return {
233
- ref,
234
- el,
235
- type
236
- };
237
-
238
- case 'append':
239
- ref.append(el);
240
- return {
241
- ref,
242
- el,
243
- type
244
- };
245
-
246
- case 'prepend':
247
- ref.prepend(el);
248
- return {
249
- ref,
250
- el,
251
- type
252
- };
253
-
254
- default:
255
- throw new Error(`Move type "${type}" does not exist!`);
256
- }
257
- });
258
- }
295
+ function getPrefix() {
296
+ return getComputedStyle(document.body).getPropertyValue('--vrembem-variable-prefix').trim();
259
297
  }
260
298
 
261
- /**
262
- * Remove a class or classes from an element or NodeList.
263
- * @param {Node || NodeList} el - Element(s) to remove class(es) from.
264
- * @param {String || Array} cl - Class(es) to remove.
265
- */
266
- const removeClass = (el, ...cl) => {
267
- el = el.forEach ? el : [el];
268
- el.forEach(el => {
269
- el.classList.remove(...cl);
299
+ function localStore(key, enable = true) {
300
+ function getStore() {
301
+ const value = localStorage.getItem(key);
302
+ return value ? JSON.parse(value) : {};
303
+ }
304
+
305
+ function setStore(obj) {
306
+ localStorage.setItem(key, JSON.stringify(obj));
307
+ }
308
+
309
+ return new Proxy(getStore(), {
310
+ set: (target, property, value) => {
311
+ target[property] = value;
312
+ if (enable) setStore(target);
313
+ return true;
314
+ },
315
+ deleteProperty: (target, property) => {
316
+ delete target[property];
317
+ if (enable) setStore(target);
318
+ return true;
319
+ }
270
320
  });
271
- };
321
+ }
272
322
 
273
323
  /**
274
- * Toggle a class or classes on an element or NodeList.
275
- * @param {Node || NodeList} el - Element(s) to toggle class(es) on.
276
- * @param {String || Array} cl - Class(es) to toggle.
324
+ * Teleports an element in the DOM based on a reference and teleport method.
325
+ * Provide the comment node as the reference to teleport the element back to its
326
+ * previous location.
327
+ * @param {Node} what - What element to teleport.
328
+ * @param {String || Node} where - Where to teleport the element.
329
+ * @param {String} how - How (method) to teleport the element, e.g: 'after',
330
+ * 'before', 'append' or 'prepend'.
331
+ * @return {Node} Return the return reference if it was teleported else return
332
+ * null if it was returned to a comment reference.
277
333
  */
278
- const toggleClass = (el, ...cl) => {
279
- el = el.forEach ? el : [el];
280
- el.forEach(el => {
281
- cl.forEach(cl => {
282
- el.classList.toggle(cl);
283
- });
284
- });
285
- };
334
+ function teleport(what, where, how) {
335
+ // Check if ref is either a comment or element node.
336
+ const isComment = where.nodeType === Node.COMMENT_NODE;
337
+ const isElement = where.nodeType === Node.ELEMENT_NODE; // Get the reference element.
338
+
339
+ where = isComment || isElement ? where : document.querySelector(where); // If ref is a comment, set teleport type to 'after'.
340
+
341
+ if (isComment) how = 'after'; // Must be a valid reference element and method.
342
+
343
+ if (!where) throw new Error(`Not a valid teleport reference: '${where}'`);
344
+ if (typeof where[how] != 'function') throw new Error(`Not a valid teleport method: '${how}'`); // Initial return ref is null.
345
+
346
+ let returnRef = null; // If ref is not a comment, set a return reference comment.
347
+
348
+ if (!isComment) {
349
+ returnRef = document.createComment('teleported #' + what.id);
350
+ what.before(returnRef);
351
+ } // Teleport the target node.
352
+
353
+
354
+ where[how](what); // Delete the comment node if element was returned to a comment reference.
355
+
356
+ if (isComment) {
357
+ where.remove();
358
+ } // Return the return reference if it was teleported else return null if it was
359
+ // returned to a comment reference.
360
+
361
+
362
+ return returnRef;
363
+ }
286
364
 
287
365
  const openTransition = (el, settings) => {
288
366
  return new Promise(resolve => {
@@ -321,44 +399,55 @@ const closeTransition = (el, settings) => {
321
399
  });
322
400
  };
323
401
 
324
- var index = {
325
- __proto__: null,
326
- setInert: setInert,
327
- setOverflowHidden: setOverflowHidden,
328
- setTabindex: setTabindex,
329
- addClass: addClass,
330
- camelCase: camelCase,
331
- focusTarget: focusTarget,
332
- focusTrigger: focusTrigger,
333
- FocusTrap: FocusTrap,
334
- getElement: getElement,
335
- hasClass: hasClass,
336
- hyphenCase: hyphenCase,
337
- moveElement: moveElement,
338
- removeClass: removeClass,
339
- toggleClass: toggleClass,
340
- openTransition: openTransition,
341
- closeTransition: closeTransition
342
- };
343
-
344
- function _extends() {
345
- _extends = Object.assign || function (target) {
346
- for (var i = 1; i < arguments.length; i++) {
347
- var source = arguments[i];
402
+ function setOverflowHidden(state, selector) {
403
+ if (selector) {
404
+ const els = document.querySelectorAll(selector);
405
+ els.forEach(el => {
406
+ if (state) {
407
+ el.style.overflow = 'hidden';
408
+ } else {
409
+ el.style.removeProperty('overflow');
410
+ }
411
+ });
412
+ }
413
+ }
348
414
 
349
- for (var key in source) {
350
- if (Object.prototype.hasOwnProperty.call(source, key)) {
351
- target[key] = source[key];
352
- }
415
+ function setInert(state, selector) {
416
+ if (selector) {
417
+ const els = document.querySelectorAll(selector);
418
+ els.forEach(el => {
419
+ if (state) {
420
+ el.inert = true;
421
+ el.setAttribute('aria-hidden', true);
422
+ } else {
423
+ el.inert = null;
424
+ el.removeAttribute('aria-hidden');
353
425
  }
354
- }
426
+ });
427
+ }
428
+ }
355
429
 
356
- return target;
357
- };
430
+ function updateGlobalState(param, config) {
431
+ // Set inert state based on if a modal is active.
432
+ setInert(!!param, config.selectorInert); // Set overflow state based on if a modal is active.
358
433
 
359
- return _extends.apply(this, arguments);
434
+ setOverflowHidden(!!param, config.selectorOverflow);
360
435
  }
361
436
 
437
+ var index = {
438
+ __proto__: null,
439
+ Breakpoint: Breakpoint,
440
+ Collection: Collection,
441
+ FocusTrap: FocusTrap,
442
+ getConfig: getConfig$1,
443
+ getPrefix: getPrefix,
444
+ localStore: localStore,
445
+ teleport: teleport,
446
+ openTransition: openTransition,
447
+ closeTransition: closeTransition,
448
+ updateGlobalState: updateGlobalState
449
+ };
450
+
362
451
  var defaults$3 = {
363
452
  autoInit: false,
364
453
  stateAttr: 'aria-checked',
@@ -423,13 +512,17 @@ class Checkbox {
423
512
  var defaults$2 = {
424
513
  autoInit: false,
425
514
  // Data attributes
426
- dataDrawer: 'drawer',
427
- dataDialog: 'drawer-dialog',
428
- dataToggle: 'drawer-toggle',
429
515
  dataOpen: 'drawer-open',
430
516
  dataClose: 'drawer-close',
517
+ dataToggle: 'drawer-toggle',
431
518
  dataBreakpoint: 'drawer-breakpoint',
432
- dataFocus: 'drawer-focus',
519
+ dataConfig: 'drawer-config',
520
+ // Selectors
521
+ selectorDrawer: '.drawer',
522
+ selectorDialog: '.drawer__dialog',
523
+ selectorFocus: '[data-focus]',
524
+ selectorInert: null,
525
+ selectorOverflow: 'body',
433
526
  // State classes
434
527
  stateOpened: 'is-opened',
435
528
  stateOpening: 'is-opening',
@@ -437,447 +530,594 @@ var defaults$2 = {
437
530
  stateClosed: 'is-closed',
438
531
  // Classes
439
532
  classModal: 'drawer_modal',
440
- // Selectors
441
- selectorInert: null,
442
- selectorOverflow: null,
443
533
  // Feature toggles
444
534
  breakpoints: null,
445
535
  customEventPrefix: 'drawer:',
446
536
  eventListeners: true,
447
- stateSave: true,
448
- stateKey: 'DrawerState',
537
+ store: true,
538
+ storeKey: 'VB:DrawerState',
449
539
  setTabindex: true,
450
540
  transition: true
451
541
  };
452
542
 
453
- class Breakpoint {
454
- constructor(parent) {
455
- this.mediaQueryLists = [];
456
- this.parent = parent;
457
- this.prefix = this.getVariablePrefix();
458
- this.__check = this.check.bind(this);
459
- }
460
-
461
- init() {
462
- const drawers = document.querySelectorAll(`[data-${this.parent.settings.dataBreakpoint}]`);
463
- drawers.forEach(drawer => {
464
- // Setup mediaQueryList object
465
- const id = drawer.getAttribute(`data-${this.parent.settings.dataDrawer}`);
466
- const key = drawer.getAttribute(`data-${this.parent.settings.dataBreakpoint}`);
467
- const bp = this.getBreakpoint(key);
468
- const mql = window.matchMedia('(min-width:' + bp + ')'); // Run match check
469
-
470
- this.match(mql, drawer); // Conditionally use addListner() for IE11 support
471
-
472
- if (typeof mql.addEventListener === 'function') {
473
- mql.addEventListener('change', this.__check);
474
- } else {
475
- mql.addListener(this.__check);
476
- } // Push to mediaQueryLists array along with drawer ID
543
+ function handleClick$2(event) {
544
+ // If an open, close or toggle button was clicked, handle the click event.
545
+ const trigger = event.target.closest(`
546
+ [data-${this.settings.dataOpen}],
547
+ [data-${this.settings.dataToggle}],
548
+ [data-${this.settings.dataClose}]
549
+ `);
550
+
551
+ if (trigger) {
552
+ // Prevent the default behavior of the trigger.
553
+ event.preventDefault(); // If it's a toggle trigger...
554
+
555
+ if (trigger.matches(`[data-${this.settings.dataToggle}]`)) {
556
+ const selectors = trigger.getAttribute(`data-${this.settings.dataToggle}`).trim().split(' ');
557
+ selectors.forEach(selector => {
558
+ // Get the entry from collection using the attribute value.
559
+ const entry = this.get(selector); // Store the trigger on the entry.
477
560
 
561
+ entry.trigger = trigger; // Toggle the drawer
478
562
 
479
- this.mediaQueryLists.push({
480
- 'mql': mql,
481
- 'drawer': id
563
+ entry.toggle();
482
564
  });
483
- });
484
- }
565
+ } // If it's a open trigger...
485
566
 
486
- destroy() {
487
- if (this.mediaQueryLists && this.mediaQueryLists.length) {
488
- this.mediaQueryLists.forEach(item => {
489
- item.mql.removeListener(this.__check);
567
+
568
+ if (trigger.matches(`[data-${this.settings.dataOpen}]`)) {
569
+ const selectors = trigger.getAttribute(`data-${this.settings.dataOpen}`).trim().split(' ');
570
+ selectors.forEach(selector => {
571
+ // Get the entry from collection using the attribute value.
572
+ const entry = this.get(selector); // Store the trigger on the entry.
573
+
574
+ entry.trigger = trigger; // Open the drawer.
575
+
576
+ entry.open();
490
577
  });
491
- }
578
+ } // If it's a close trigger...
492
579
 
493
- this.mediaQueryLists = null;
494
- }
495
580
 
496
- check(event = null) {
497
- if (this.mediaQueryLists && this.mediaQueryLists.length) {
498
- this.mediaQueryLists.forEach(item => {
499
- // If an event is passed, filter out drawers that don't match the query
500
- // If event is null, run all drawers through match
501
- let filter = event ? event.media == item.mql.media : true;
502
- if (!filter) return;
503
- const drawer = document.querySelector(`[data-${this.parent.settings.dataDrawer}="${item.drawer}"]`);
504
- if (drawer) this.match(item.mql, drawer);
581
+ if (trigger.matches(`[data-${this.settings.dataClose}]`)) {
582
+ const selectors = trigger.getAttribute(`data-${this.settings.dataClose}`).trim().split(' ');
583
+ selectors.forEach(selector => {
584
+ if (selector) {
585
+ // Get the entry from collection using the attribute value.
586
+ const entry = this.get(selector); // Store the trigger on the entry.
587
+
588
+ entry.trigger = trigger; // Close the drawer.
589
+
590
+ entry.close();
591
+ } else {
592
+ // If no value is set on close trigger, get the parent drawer.
593
+ const parent = event.target.closest(this.settings.selectorDrawer); // If a parent drawer was found, close it.
594
+
595
+ if (parent) this.close(parent);
596
+ }
505
597
  });
506
- document.dispatchEvent(new CustomEvent(this.parent.settings.customEventPrefix + 'breakpoint', {
507
- bubbles: true
508
- }));
509
598
  }
510
- }
511
599
 
512
- match(mql, drawer) {
513
- if (mql.matches) {
514
- this.parent.switchToDefault(drawer);
515
- } else {
516
- this.parent.switchToModal(drawer);
517
- }
600
+ return;
601
+ } // If the modal drawer screen was clicked...
602
+
603
+
604
+ if (event.target.matches(this.settings.selectorDrawer)) {
605
+ // Close the modal drawer.
606
+ this.close(event.target.id);
607
+ }
608
+ }
609
+ function handleKeydown$2(event) {
610
+ if (event.key === 'Escape') {
611
+ const modal = this.activeModal;
612
+ if (modal) this.close(modal);
518
613
  }
614
+ }
519
615
 
520
- getBreakpoint(key) {
521
- let breakpoint = key;
616
+ async function deregister$2(obj, close = true) {
617
+ // Return collection if nothing was passed.
618
+ if (!obj) return this.collection; // Check if entry has been registered in the collection.
522
619
 
523
- if (this.parent.settings.breakpoints && this.parent.settings.breakpoints[key]) {
524
- breakpoint = this.parent.settings.breakpoints[key];
525
- } else if (getComputedStyle(document.body).getPropertyValue(this.prefix + key)) {
526
- breakpoint = getComputedStyle(document.body).getPropertyValue(this.prefix + key);
527
- }
620
+ const index = this.collection.findIndex(entry => {
621
+ return entry.id === obj.id;
622
+ });
528
623
 
529
- return breakpoint;
530
- }
624
+ if (index >= 0) {
625
+ // Get the collection entry.
626
+ const entry = this.collection[index]; // If entry is in the opened state.
531
627
 
532
- getVariablePrefix() {
533
- let prefix = '--';
534
- prefix += getComputedStyle(document.body).getPropertyValue('--vrembem-variable-prefix');
535
- prefix += 'breakpoint-';
536
- return prefix;
537
- }
628
+ if (close && entry.state === 'opened') {
629
+ // Close the drawer.
630
+ await entry.close(false);
631
+ } // Remove entry from local store.
632
+
633
+
634
+ delete this.store[entry.id]; // Unmount the MatchMedia functionality.
635
+
636
+ entry.unmountBreakpoint(); // Delete properties from collection entry.
637
+
638
+ Object.getOwnPropertyNames(entry).forEach(prop => {
639
+ delete entry[prop];
640
+ }); // Remove entry from collection.
538
641
 
642
+ this.collection.splice(index, 1);
643
+ } // Return the modified collection.
644
+
645
+
646
+ return this.collection;
539
647
  }
540
648
 
541
- function getDrawer(drawerKey) {
542
- if (typeof drawerKey !== 'string') return drawerKey;
543
- return document.querySelector(`[data-${this.settings.dataDrawer}="${drawerKey}"]`);
649
+ function L() {
650
+ return getComputedStyle(document.body).getPropertyValue("--vrembem-variable-prefix").trim();
544
651
  }
545
- function drawerNotFound(key) {
546
- return Promise.reject(new Error(`Did not find drawer with key: "${key}"`));
652
+
653
+ function getBreakpoint(drawer) {
654
+ const prefix = L();
655
+ const bp = drawer.getAttribute(`data-${this.settings.dataBreakpoint}`);
656
+
657
+ if (this.settings.breakpoints && this.settings.breakpoints[bp]) {
658
+ return this.settings.breakpoints[bp];
659
+ } else if (getComputedStyle(document.body).getPropertyValue(`--${prefix}breakpoint-${bp}`).trim()) {
660
+ return getComputedStyle(document.body).getPropertyValue(`--${prefix}breakpoint-${bp}`).trim();
661
+ } else {
662
+ return bp;
663
+ }
547
664
  }
548
665
 
549
- async function close$2(drawerKey) {
550
- const drawer = this.getDrawer(drawerKey);
551
- if (!drawer) return drawerNotFound(drawerKey);
666
+ function getDrawer(query) {
667
+ // Get the entry from collection.
668
+ const entry = typeof query === 'string' ? this.get(query) : this.get(query.id); // Return entry if it was resolved, otherwise throw error.
552
669
 
553
- if (hasClass(drawer, this.settings.stateOpened)) {
554
- this.working = true;
670
+ if (entry) {
671
+ return entry;
672
+ } else {
673
+ throw new Error(`Drawer not found in collection with id of "${query.id || query}".`);
674
+ }
675
+ }
555
676
 
556
- if (hasClass(drawer, this.settings.classModal)) {
557
- setInert(false, this.settings.selectorInert);
558
- setOverflowHidden(false, this.settings.selectorOverflow);
677
+ function getDrawerID(obj) {
678
+ // If it's a string, return the string.
679
+ if (typeof obj === 'string') {
680
+ return obj;
681
+ } // If it's an HTML element.
682
+ else if (typeof obj.hasAttribute === 'function') {
683
+ // If it's a drawer open trigger, return data value.
684
+ if (obj.hasAttribute(`data-${this.settings.dataOpen}`)) {
685
+ return obj.getAttribute(`data-${this.settings.dataOpen}`);
686
+ } // If it's a drawer close trigger, return data value or false.
687
+ else if (obj.hasAttribute(`data-${this.settings.dataClose}`)) {
688
+ return obj.getAttribute(`data-${this.settings.dataClose}`) || false;
689
+ } // If it's a drawer toggle trigger, return data value.
690
+ else if (obj.hasAttribute(`data-${this.settings.dataToggle}`)) {
691
+ return obj.getAttribute(`data-${this.settings.dataToggle}`);
692
+ } // If it's a drawer element, return the id.
693
+ else if (obj.closest(this.settings.selectorDrawer)) {
694
+ obj = obj.closest(this.settings.selectorDrawer);
695
+ return obj.id || false;
696
+ } // Return false if no id was found.
697
+ else return false;
698
+ } // If it has an id property, return its value.
699
+ else if (obj.id) {
700
+ return obj.id;
701
+ } // Return false if no id was found.
702
+ else return false;
703
+ }
704
+
705
+ function getDrawerElements(query) {
706
+ const id = getDrawerID.call(this, query);
707
+
708
+ if (id) {
709
+ const drawer = document.querySelector(`#${id}`);
710
+ const dialog = drawer ? drawer.querySelector(this.settings.selectorDialog) : null;
711
+
712
+ if (!drawer && !dialog) {
713
+ return {
714
+ error: new Error(`No drawer elements found using the ID: "${id}".`)
715
+ };
716
+ } else if (!dialog) {
717
+ return {
718
+ error: new Error('Drawer is missing dialog element.')
719
+ };
720
+ } else {
721
+ return {
722
+ drawer,
723
+ dialog
724
+ };
725
+ }
726
+ } else {
727
+ return {
728
+ error: new Error('Could not resolve the drawer ID.')
729
+ };
730
+ }
731
+ }
732
+
733
+ async function initialState(entry) {
734
+ // Setup initial state using the following priority:
735
+ // 1. If a store state is available, restore from local store.
736
+ // 2. If opened state class is set, set state to opened.
737
+ // 3. Else, initialize default state.
738
+ if (this.store[entry.id]) {
739
+ // Restore drawers to saved inline state.
740
+ if (this.store[entry.id] === 'opened') {
741
+ await entry.open(false, false);
742
+ } else {
743
+ await entry.close(false, false);
559
744
  }
745
+ } else if (entry.el.classList.contains(this.settings.stateOpened)) {
746
+ // Update drawer state.
747
+ entry.state = 'opened';
748
+ } else {
749
+ // Remove transition state classes.
750
+ entry.el.classList.remove(this.settings.stateOpening);
751
+ entry.el.classList.remove(this.settings.stateClosing); // Add closed state class.
560
752
 
561
- await closeTransition(drawer, this.settings);
562
- this.stateSave(drawer);
563
- focusTrigger(this);
564
- this.focusTrap.destroy();
565
- drawer.dispatchEvent(new CustomEvent(this.settings.customEventPrefix + 'closed', {
566
- detail: this,
567
- bubbles: true
568
- }));
569
- this.working = false;
570
- return drawer;
753
+ entry.el.classList.add(this.settings.stateClosed);
754
+ }
755
+ }
756
+
757
+ function updateFocusState$1(entry) {
758
+ // Check if there's an active modal
759
+ if (entry.state === 'opened') {
760
+ // Mount the focus trap on the opened drawer.
761
+ if (entry.mode === 'modal') {
762
+ this.focusTrap.mount(entry.dialog, this.settings.selectorFocus);
763
+ } else {
764
+ this.focusTrap.focus(entry.dialog, this.settings.selectorFocus);
765
+ }
571
766
  } else {
572
- return drawer;
767
+ // Set focus to root trigger and unmount the focus trap.
768
+ if (entry.trigger) {
769
+ entry.trigger.focus();
770
+ entry.trigger = null;
771
+ }
772
+
773
+ this.focusTrap.unmount();
573
774
  }
574
775
  }
575
776
 
576
- function handlerClick$2(event) {
577
- // Working catch
578
- if (this.working) return; // Toggle data trigger
777
+ async function open$2(query, transition, focus = true) {
778
+ // Get the drawer from collection.
779
+ const drawer = getDrawer.call(this, query); // Get the modal configuration.
579
780
 
580
- let trigger = event.target.closest(`[data-${this.settings.dataToggle}]`);
781
+ const config = _extends({}, this.settings, drawer.settings); // Add transition parameter to configuration.
581
782
 
582
- if (trigger) {
583
- const selector = trigger.getAttribute(`data-${this.settings.dataToggle}`);
584
- this.memory.trigger = trigger;
585
- this.toggle(selector);
586
- event.preventDefault();
587
- return;
588
- } // Open data trigger
589
783
 
784
+ if (transition !== undefined) config.transition = transition; // If drawer is closed.
590
785
 
591
- trigger = event.target.closest(`[data-${this.settings.dataOpen}]`);
786
+ if (drawer.state === 'closed') {
787
+ // Update drawer state.
788
+ drawer.state = 'opening'; // Run the open transition.
592
789
 
593
- if (trigger) {
594
- const selector = trigger.getAttribute(`data-${this.settings.dataOpen}`);
595
- this.memory.trigger = trigger;
596
- this.open(selector);
597
- event.preventDefault();
598
- return;
599
- } // Close data trigger
790
+ await openTransition(drawer.el, config); // Update the global state if mode is modal.
600
791
 
792
+ if (drawer.mode === 'modal') updateGlobalState(true, config); // Update drawer state.
601
793
 
602
- trigger = event.target.closest(`[data-${this.settings.dataClose}]`);
794
+ drawer.state = 'opened';
795
+ } // Set focus to the drawer element if the focus param is true.
603
796
 
604
- if (trigger) {
605
- const selector = trigger.getAttribute(`data-${this.settings.dataClose}`);
606
797
 
607
- if (selector) {
608
- this.memory.trigger = trigger;
609
- this.close(selector);
610
- } else {
611
- const target = event.target.closest(`[data-${this.settings.dataDrawer}]`);
612
- if (target) this.close(target);
613
- }
798
+ if (focus) {
799
+ updateFocusState$1.call(this, drawer);
800
+ } // Dispatch custom opened event.
614
801
 
615
- event.preventDefault();
616
- return;
617
- } // Screen modal trigger
618
802
 
803
+ drawer.el.dispatchEvent(new CustomEvent(config.customEventPrefix + 'opened', {
804
+ detail: this,
805
+ bubbles: true
806
+ })); // Return the drawer.
619
807
 
620
- if (event.target.hasAttribute(`data-${this.settings.dataDrawer}`)) {
621
- this.close(event.target);
622
- return;
623
- }
808
+ return drawer;
624
809
  }
625
- function handlerKeydown$2(event) {
626
- // Working catch
627
- if (this.working) return;
628
810
 
629
- if (event.key === 'Escape') {
630
- const target = document.querySelector(`.${this.settings.classModal}.${this.settings.stateOpened}`);
811
+ async function close$2(query, transition, focus = true) {
812
+ // Get the drawer from collection.
813
+ const drawer = getDrawer.call(this, query); // Get the modal configuration.
631
814
 
632
- if (target) {
633
- this.close(target);
634
- }
635
- }
636
- }
815
+ const config = _extends({}, this.settings, drawer.settings); // Add transition parameter to configuration.
637
816
 
638
- async function open$2(drawerKey) {
639
- const drawer = this.getDrawer(drawerKey);
640
- if (!drawer) return drawerNotFound(drawerKey);
641
817
 
642
- if (!hasClass(drawer, this.settings.stateOpened)) {
643
- this.working = true;
644
- const isModal = hasClass(drawer, this.settings.classModal);
818
+ if (transition !== undefined) config.transition = transition; // If drawer is opened.
645
819
 
646
- if (isModal) {
647
- setOverflowHidden(true, this.settings.selectorOverflow);
648
- }
820
+ if (drawer.state === 'opened') {
821
+ // Update drawer state.
822
+ drawer.state = 'closing'; // Remove focus from active element.
649
823
 
650
- await openTransition(drawer, this.settings);
651
- this.stateSave(drawer);
824
+ document.activeElement.blur(); // Run the close transition.
652
825
 
653
- if (isModal) {
654
- this.focusTrap.init(drawer);
655
- setInert(true, this.settings.selectorInert);
656
- }
826
+ await closeTransition(drawer.el, config); // Update the global state if mode is modal.
827
+
828
+ if (drawer.mode === 'modal') updateGlobalState(false, config); // Set focus to the trigger element if the focus param is true.
829
+
830
+ if (focus) {
831
+ updateFocusState$1.call(this, drawer);
832
+ } // Update drawer state.
833
+
834
+
835
+ drawer.state = 'closed'; // Dispatch custom closed event.
657
836
 
658
- focusTarget(drawer, this.settings);
659
- drawer.dispatchEvent(new CustomEvent(this.settings.customEventPrefix + 'opened', {
837
+ drawer.el.dispatchEvent(new CustomEvent(config.customEventPrefix + 'closed', {
660
838
  detail: this,
661
839
  bubbles: true
662
840
  }));
663
- this.working = false;
664
- return drawer;
841
+ } // Return the drawer.
842
+
843
+
844
+ return drawer;
845
+ }
846
+
847
+ async function toggle(query, transition, focus) {
848
+ // Get the drawer from collection.
849
+ const drawer = getDrawer.call(this, query); // Open or close the drawer based on its current state.
850
+
851
+ if (drawer.state === 'closed') {
852
+ return open$2.call(this, drawer, transition, focus);
665
853
  } else {
666
- focusTarget(drawer, this.settings);
667
- return drawer;
854
+ return close$2.call(this, drawer, transition, focus);
668
855
  }
669
856
  }
670
857
 
671
- function stateSet(settings) {
672
- // If save state is disabled
673
- if (!settings.stateSave) return stateClear(settings); // If there isn't an existing state to set
858
+ function switchMode(entry) {
859
+ switch (entry.mode) {
860
+ case 'inline':
861
+ return toInline.call(this, entry);
674
862
 
675
- const storageCheck = localStorage.getItem(settings.stateKey);
863
+ case 'modal':
864
+ return toModal.call(this, entry);
676
865
 
677
- if (!storageCheck || storageCheck && Object.keys(JSON.parse(storageCheck)).length === 0) {
678
- return stateSave(null, settings);
679
- } // Set the existing state
866
+ default:
867
+ throw new Error(`"${entry.mode}" is not a valid drawer mode.`);
868
+ }
869
+ }
680
870
 
871
+ async function toInline(entry) {
872
+ // Remove the modal class.
873
+ entry.el.classList.remove(entry.getSetting('classModal')); // Remove the aria-modal attribute.
681
874
 
682
- const state = JSON.parse(localStorage.getItem(settings.stateKey));
683
- Object.keys(state).forEach(key => {
684
- const item = document.querySelector(`[data-${settings.dataDrawer}="${key}"]`);
685
- if (!item) return;
686
- state[key] == settings.stateOpened ? addClass(item, settings.stateOpened) : removeClass(item, settings.stateOpened);
687
- });
688
- return state;
689
- }
690
- function stateSave(target, settings) {
691
- // If save state is disabled
692
- if (!settings.stateSave) return stateClear(settings); // Get the currently saved object if it exists
875
+ entry.dialog.removeAttribute('aria-modal'); // Update the global state.
693
876
 
694
- const state = localStorage.getItem(settings.stateKey) ? JSON.parse(localStorage.getItem(settings.stateKey)) : {}; // Are we saving a single target or the entire suite?
877
+ updateGlobalState(false, _extends({}, this.settings, entry.settings)); // Remove any focus traps.
695
878
 
696
- const drawers = target ? [target] : document.querySelectorAll(`[data-${settings.dataDrawer}]`); // Loop through drawers and save their states
879
+ this.focusTrap.unmount(); // Setup initial state.
697
880
 
698
- drawers.forEach(el => {
699
- if (hasClass(el, settings.classModal)) return;
700
- const drawerKey = el.getAttribute(`data-${settings.dataDrawer}`);
701
- state[drawerKey] = hasClass(el, settings.stateOpened) ? settings.stateOpened : settings.stateClosed;
702
- }); // Save to localStorage and return the state
881
+ await initialState.call(this, entry); // Dispatch custom switch event.
703
882
 
704
- localStorage.setItem(settings.stateKey, JSON.stringify(state));
705
- return state;
706
- }
707
- function stateClear(settings) {
708
- if (localStorage.getItem(settings.stateKey)) {
709
- localStorage.removeItem(settings.stateKey);
710
- }
883
+ entry.el.dispatchEvent(new CustomEvent(entry.getSetting('customEventPrefix') + 'switchMode', {
884
+ detail: this,
885
+ bubbles: true
886
+ })); // Return the entry.
711
887
 
712
- return {};
888
+ return entry;
713
889
  }
714
890
 
715
- async function switchToModal(drawerKey) {
716
- // Initial guards
717
- const drawer = this.getDrawer(drawerKey);
718
- if (!drawer) return drawerNotFound(drawerKey);
719
- if (hasClass(drawer, this.settings.classModal)) return; // Enable modal state
891
+ async function toModal(entry) {
892
+ // Get the drawer configuration.
893
+ // Add the modal class.
894
+ entry.el.classList.add(entry.getSetting('classModal')); // Set aria-modal attribute to true.
895
+
896
+ entry.dialog.setAttribute('aria-modal', 'true'); // If there isn't a stored state but also has the opened state class...
897
+
898
+ if (!this.store[entry.id] && entry.el.classList.contains(entry.getSetting('stateOpened'))) {
899
+ // Save the opened state in local store.
900
+ this.store[entry.id] = 'opened';
901
+ } // Modal drawer defaults to closed state.
902
+
720
903
 
721
- addClass(drawer, this.settings.classModal);
722
- addClass(drawer, this.settings.stateClosed);
723
- removeClass(drawer, this.settings.stateOpened); // Dispatch custom event
904
+ await close$2.call(this, entry, false, false); // Dispatch custom switch event.
724
905
 
725
- drawer.dispatchEvent(new CustomEvent(this.settings.customEventPrefix + 'toModal', {
906
+ entry.el.dispatchEvent(new CustomEvent(entry.getSetting('customEventPrefix') + 'switchMode', {
907
+ detail: this,
726
908
  bubbles: true
727
- }));
728
- return drawer;
909
+ })); // Return the entry.
910
+
911
+ return entry;
729
912
  }
730
- async function switchToDefault(drawerKey) {
731
- // Initial guards
732
- const drawer = this.getDrawer(drawerKey);
733
- if (!drawer) return drawerNotFound(drawerKey);
734
- if (!hasClass(drawer, this.settings.classModal)) return; // Tear down modal state
735
913
 
736
- setInert(false, this.settings.selectorInert);
737
- setOverflowHidden(false, this.settings.selectorOverflow);
738
- removeClass(drawer, this.settings.classModal);
739
- this.focusTrap.destroy(); // Restore drawers saved state
914
+ async function register$2(el, dialog) {
915
+ // Deregister entry incase it has already been registered.
916
+ await deregister$2.call(this, el, false); // Save root this for use inside methods API.
740
917
 
741
- drawerKey = drawer.getAttribute(`data-${this.settings.dataDrawer}`);
742
- const drawerState = this.state[drawerKey];
918
+ const root = this; // Create an instance of the Breakpoint class.
743
919
 
744
- if (drawerState == this.settings.stateOpened) {
745
- addClass(drawer, this.settings.stateOpened);
746
- removeClass(drawer, this.settings.stateClosed);
747
- } // Dispatch custom event
920
+ const breakpoint = new Breakpoint(); // Setup methods API.
748
921
 
922
+ const methods = {
923
+ open(transition, focus) {
924
+ return open$2.call(root, this, transition, focus);
925
+ },
749
926
 
750
- drawer.dispatchEvent(new CustomEvent(this.settings.customEventPrefix + 'toDefault', {
751
- bubbles: true
752
- }));
753
- return drawer;
754
- }
927
+ close(transition, focus) {
928
+ return close$2.call(root, this, transition, focus);
929
+ },
930
+
931
+ toggle(transition, focus) {
932
+ return toggle.call(root, this, transition, focus);
933
+ },
934
+
935
+ deregister() {
936
+ return deregister$2.call(root, this);
937
+ },
938
+
939
+ mountBreakpoint() {
940
+ const value = this.breakpoint;
941
+ const handler = this.handleBreakpoint.bind(this);
942
+ breakpoint.mount(value, handler);
943
+ return this;
944
+ },
945
+
946
+ unmountBreakpoint() {
947
+ breakpoint.unmount();
948
+ return this;
949
+ },
950
+
951
+ handleBreakpoint(event) {
952
+ this.mode = event.matches ? 'inline' : 'modal';
953
+ return this;
954
+ },
955
+
956
+ getSetting(key) {
957
+ return key in this.settings ? this.settings[key] : root.settings[key];
958
+ }
959
+
960
+ }; // Setup the drawer object.
961
+
962
+ const entry = _extends({
963
+ id: el.id,
964
+ el: el,
965
+ dialog: dialog,
966
+ trigger: null,
967
+ settings: getConfig$1(el, this.settings.dataConfig),
968
+
969
+ get breakpoint() {
970
+ return getBreakpoint.call(root, el);
971
+ },
972
+
973
+ get state() {
974
+ return __state;
975
+ },
976
+
977
+ set state(value) {
978
+ __state = value; // Save 'opened' and 'closed' states to store if mode is inline.
979
+
980
+ if (value === 'opened' || value === 'closed') {
981
+ if (this.mode === 'inline') root.store[this.id] = this.state;
982
+ }
983
+ },
984
+
985
+ get mode() {
986
+ return __mode;
987
+ },
988
+
989
+ set mode(value) {
990
+ __mode = value;
991
+ switchMode.call(root, this);
992
+ }
993
+
994
+ }, methods); // Create the state var with the initial state.
995
+
996
+
997
+ let __state = el.classList.contains(entry.getSetting('stateOpened')) ? 'opened' : 'closed'; // Create the mode var with the initial mode.
998
+
999
+
1000
+ let __mode = el.classList.contains(entry.getSetting('classModal')) ? 'modal' : 'inline'; // Setup mode specific attributes.
755
1001
 
756
- async function toggle(drawerKey) {
757
- const drawer = this.getDrawer(drawerKey);
758
- if (!drawer) return drawerNotFound(drawerKey);
759
- const isClosed = !hasClass(drawer, this.settings.stateOpened);
760
1002
 
761
- if (isClosed) {
762
- return this.open(drawer);
1003
+ if (entry.mode === 'modal') {
1004
+ // Set aria-modal attribute to true.
1005
+ entry.dialog.setAttribute('aria-modal', 'true');
763
1006
  } else {
764
- return this.close(drawer);
765
- }
1007
+ // Remove the aria-modal attribute.
1008
+ entry.dialog.removeAttribute('aria-modal');
1009
+ } // Set tabindex="-1" so dialog is focusable via JS or click.
1010
+
1011
+
1012
+ if (entry.getSetting('setTabindex')) {
1013
+ entry.dialog.setAttribute('tabindex', '-1');
1014
+ } // Add entry to collection.
1015
+
1016
+
1017
+ this.collection.push(entry); // If the entry has a breakpoint...
1018
+
1019
+ if (entry.breakpoint) {
1020
+ // Mount media query breakpoint functionality.
1021
+ entry.mountBreakpoint();
1022
+ } else {
1023
+ // Else, Setup initial state.
1024
+ await initialState.call(this, entry);
1025
+ } // Return the registered entry.
1026
+
1027
+
1028
+ return entry;
766
1029
  }
767
1030
 
768
- class Drawer {
1031
+ var _handleClick$1 = /*#__PURE__*/_classPrivateFieldLooseKey("handleClick");
1032
+
1033
+ var _handleKeydown$2 = /*#__PURE__*/_classPrivateFieldLooseKey("handleKeydown");
1034
+
1035
+ class Drawer extends Collection {
769
1036
  constructor(options) {
1037
+ super();
1038
+ Object.defineProperty(this, _handleClick$1, {
1039
+ writable: true,
1040
+ value: void 0
1041
+ });
1042
+ Object.defineProperty(this, _handleKeydown$2, {
1043
+ writable: true,
1044
+ value: void 0
1045
+ });
770
1046
  this.defaults = defaults$2;
771
1047
  this.settings = _extends({}, this.defaults, options);
772
- this.working = false;
773
- this.memory = {};
774
- this.state = {};
775
- this.focusTrap = new FocusTrap();
776
- this.breakpoint = new Breakpoint(this);
777
- this.__handlerClick = handlerClick$2.bind(this);
778
- this.__handlerKeydown = handlerKeydown$2.bind(this);
1048
+ this.focusTrap = new FocusTrap(); // Setup local store for inline drawer state management.
1049
+
1050
+ this.store = localStore(this.settings.storeKey, this.settings.store);
1051
+ _classPrivateFieldLooseBase(this, _handleClick$1)[_handleClick$1] = handleClick$2.bind(this);
1052
+ _classPrivateFieldLooseBase(this, _handleKeydown$2)[_handleKeydown$2] = handleKeydown$2.bind(this);
779
1053
  if (this.settings.autoInit) this.init();
780
1054
  }
781
1055
 
782
- init(options = null) {
783
- if (options) this.settings = _extends({}, this.settings, options);
784
- this.stateSet();
1056
+ get activeModal() {
1057
+ return this.collection.find(entry => {
1058
+ return entry.state === 'opened' && entry.mode === 'modal';
1059
+ });
1060
+ }
785
1061
 
786
- if (this.settings.setTabindex) {
787
- this.setTabindex();
788
- }
1062
+ async init(options = null) {
1063
+ // Update settings with passed options.
1064
+ if (options) this.settings = _extends({}, this.settings, options); // Get all the modals.
1065
+
1066
+ const drawers = document.querySelectorAll(this.settings.selectorDrawer); // Register the collections array with modal instances.
789
1067
 
790
- this.breakpoint.init();
1068
+ await this.registerCollection(drawers); // If eventListeners are enabled, init event listeners.
791
1069
 
792
1070
  if (this.settings.eventListeners) {
793
1071
  this.initEventListeners();
794
1072
  }
1073
+
1074
+ return this;
795
1075
  }
796
1076
 
797
- destroy() {
798
- this.breakpoint.destroy();
799
- this.memory = {};
800
- this.state = {};
801
- localStorage.removeItem(this.settings.stateKey);
1077
+ async destroy() {
1078
+ // Remove all entries from the collection.
1079
+ await this.deregisterCollection(); // If eventListeners are enabled, init event listeners.
802
1080
 
803
1081
  if (this.settings.eventListeners) {
804
1082
  this.destroyEventListeners();
805
1083
  }
806
- }
807
- /**
808
- * Event listeners
809
- */
810
1084
 
1085
+ return this;
1086
+ }
811
1087
 
812
1088
  initEventListeners() {
813
- document.addEventListener('click', this.__handlerClick, false);
814
- document.addEventListener('touchend', this.__handlerClick, false);
815
- document.addEventListener('keydown', this.__handlerKeydown, false);
1089
+ document.addEventListener('click', _classPrivateFieldLooseBase(this, _handleClick$1)[_handleClick$1], false);
1090
+ document.addEventListener('touchend', _classPrivateFieldLooseBase(this, _handleClick$1)[_handleClick$1], false);
1091
+ document.addEventListener('keydown', _classPrivateFieldLooseBase(this, _handleKeydown$2)[_handleKeydown$2], false);
816
1092
  }
817
1093
 
818
1094
  destroyEventListeners() {
819
- document.removeEventListener('click', this.__handlerClick, false);
820
- document.removeEventListener('touchend', this.__handlerClick, false);
821
- document.removeEventListener('keydown', this.__handlerKeydown, false);
822
- }
823
- /**
824
- * Helpers
825
- */
826
-
827
-
828
- getDrawer(drawerKey) {
829
- return getDrawer.call(this, drawerKey);
830
- }
831
-
832
- setTabindex() {
833
- return setTabindex(`
834
- [data-${this.settings.dataDrawer}]
835
- [data-${this.settings.dataDialog}]
836
- `);
1095
+ document.removeEventListener('click', _classPrivateFieldLooseBase(this, _handleClick$1)[_handleClick$1], false);
1096
+ document.removeEventListener('touchend', _classPrivateFieldLooseBase(this, _handleClick$1)[_handleClick$1], false);
1097
+ document.removeEventListener('keydown', _classPrivateFieldLooseBase(this, _handleKeydown$2)[_handleKeydown$2], false);
837
1098
  }
838
- /**
839
- * Save state functionality
840
- */
841
1099
 
842
-
843
- stateSet() {
844
- this.state = stateSet(this.settings);
845
- }
846
-
847
- stateSave(target = null) {
848
- this.state = stateSave(target, this.settings);
849
- }
850
-
851
- stateClear() {
852
- this.state = stateClear(this.settings);
853
- }
854
- /**
855
- * SwitchTo functionality
856
- */
857
-
858
-
859
- switchToDefault(drawerKey) {
860
- return switchToDefault.call(this, drawerKey);
1100
+ register(query) {
1101
+ const els = getDrawerElements.call(this, query);
1102
+ if (els.error) return Promise.reject(els.error);
1103
+ return register$2.call(this, els.drawer, els.dialog);
861
1104
  }
862
1105
 
863
- switchToModal(drawerKey) {
864
- return switchToModal.call(this, drawerKey);
1106
+ deregister(query) {
1107
+ const entry = this.get(getDrawerID.call(this, query));
1108
+ return deregister$2.call(this, entry);
865
1109
  }
866
- /**
867
- * Change state functionality
868
- */
869
1110
 
870
-
871
- toggle(drawerKey) {
872
- return toggle.call(this, drawerKey);
1111
+ open(id, transition, focus) {
1112
+ return open$2.call(this, id, transition, focus);
873
1113
  }
874
1114
 
875
- open(drawerKey) {
876
- return open$2.call(this, drawerKey);
1115
+ close(id, transition, focus) {
1116
+ return close$2.call(this, id, transition, focus);
877
1117
  }
878
1118
 
879
- close(drawerKey) {
880
- return close$2.call(this, drawerKey);
1119
+ toggle(id, transition, focus) {
1120
+ return toggle.call(this, id, transition, focus);
881
1121
  }
882
1122
 
883
1123
  }
@@ -885,416 +1125,565 @@ class Drawer {
885
1125
  var defaults$1 = {
886
1126
  autoInit: false,
887
1127
  // Data attributes
888
- dataModal: 'modal',
889
- dataDialog: 'modal-dialog',
890
1128
  dataOpen: 'modal-open',
891
1129
  dataClose: 'modal-close',
892
- dataFocus: 'modal-focus',
893
- dataRequired: 'modal-required',
1130
+ dataReplace: 'modal-replace',
894
1131
  dataConfig: 'modal-config',
1132
+ // Selectors
1133
+ selectorModal: '.modal',
1134
+ selectorDialog: '.modal__dialog',
1135
+ selectorRequired: '[role="alertdialog"]',
1136
+ selectorFocus: '[data-focus]',
1137
+ selectorInert: null,
1138
+ selectorOverflow: 'body',
895
1139
  // State classes
896
1140
  stateOpened: 'is-opened',
897
1141
  stateOpening: 'is-opening',
898
1142
  stateClosing: 'is-closing',
899
1143
  stateClosed: 'is-closed',
900
- // Selector
901
- selectorInert: null,
902
- selectorOverflow: 'body',
903
- // Feature toggles
1144
+ // Feature settings
904
1145
  customEventPrefix: 'modal:',
905
1146
  eventListeners: true,
906
- moveModals: {
907
- ref: null,
908
- type: null
909
- },
1147
+ teleport: null,
1148
+ teleportMethod: 'append',
910
1149
  setTabindex: true,
911
1150
  transition: true
912
1151
  };
913
1152
 
914
- function getModalConfig(modal) {
915
- const json = modal.getAttribute(`data-${this.settings.dataConfig}`);
1153
+ function getModal(query) {
1154
+ // Get the entry from collection.
1155
+ const entry = typeof query === 'string' ? this.get(query) : this.get(query.id); // Return entry if it was resolved, otherwise throw error.
916
1156
 
917
- if (json) {
918
- const config = JSON.parse(json);
919
- return _extends({}, this.settings, config);
1157
+ if (entry) {
1158
+ return entry;
920
1159
  } else {
921
- return this.settings;
1160
+ throw new Error(`Modal not found in collection with id of "${query.id || query}".`);
922
1161
  }
923
1162
  }
924
- function getModal(modalKey) {
925
- if (typeof modalKey !== 'string') return modalKey;
926
- return document.querySelector(`[data-${this.settings.dataModal}="${modalKey}"]`);
927
- }
928
- function modalNotFound(key) {
929
- return Promise.reject(new Error(`Did not find modal with key: "${key}"`));
930
- }
931
- function moveModals(type = this.settings.moveModals.type, ref = this.settings.moveModals.ref) {
932
- const modals = document.querySelectorAll(`[data-${this.settings.dataModal}]`);
933
- if (modals.length) moveElement(modals, type, ref);
1163
+
1164
+ function getModalID(obj) {
1165
+ // If it's a string, return the string.
1166
+ if (typeof obj === 'string') {
1167
+ return obj;
1168
+ } // If it's an HTML element.
1169
+ else if (typeof obj.hasAttribute === 'function') {
1170
+ // If it's a modal open trigger, return data value.
1171
+ if (obj.hasAttribute(`data-${this.settings.dataOpen}`)) {
1172
+ return obj.getAttribute(`data-${this.settings.dataOpen}`);
1173
+ } // If it's a modal close trigger, return data value or false.
1174
+ else if (obj.hasAttribute(`data-${this.settings.dataClose}`)) {
1175
+ return obj.getAttribute(`data-${this.settings.dataClose}`) || false;
1176
+ } // If it's a modal replace trigger, return data value.
1177
+ else if (obj.hasAttribute(`data-${this.settings.dataReplace}`)) {
1178
+ return obj.getAttribute(`data-${this.settings.dataReplace}`);
1179
+ } // If it's a modal element, return the id.
1180
+ else if (obj.closest(this.settings.selectorModal)) {
1181
+ obj = obj.closest(this.settings.selectorModal);
1182
+ return obj.id || false;
1183
+ } // Return false if no id was found.
1184
+ else return false;
1185
+ } // If it has an id property, return its value.
1186
+ else if (obj.id) {
1187
+ return obj.id;
1188
+ } // Return false if no id was found.
1189
+ else return false;
934
1190
  }
935
1191
 
936
- async function close$1(returnFocus = true) {
937
- const modal = document.querySelector(`[data-${this.settings.dataModal}].${this.settings.stateOpened}`);
1192
+ function getModalElements(query) {
1193
+ const id = getModalID.call(this, query);
938
1194
 
939
- if (modal) {
940
- this.working = true;
941
- const config = getModalConfig.call(this, modal);
942
- setInert(false, config.selectorInert);
943
- setOverflowHidden(false, config.selectorOverflow);
944
- await closeTransition(modal, config);
945
- if (returnFocus) focusTrigger(this);
946
- this.focusTrap.destroy();
947
- modal.dispatchEvent(new CustomEvent(config.customEventPrefix + 'closed', {
948
- detail: this,
949
- bubbles: true
950
- }));
951
- this.working = false;
952
- return modal;
1195
+ if (id) {
1196
+ const modal = document.querySelector(`#${id}`);
1197
+ const dialog = modal ? modal.querySelector(this.settings.selectorDialog) : null;
1198
+
1199
+ if (!modal && !dialog) {
1200
+ return {
1201
+ error: new Error(`No modal elements found using the ID: "${id}".`)
1202
+ };
1203
+ } else if (!dialog) {
1204
+ return {
1205
+ error: new Error('Modal is missing dialog element.')
1206
+ };
1207
+ } else {
1208
+ return {
1209
+ modal,
1210
+ dialog
1211
+ };
1212
+ }
953
1213
  } else {
954
- return modal;
1214
+ return {
1215
+ error: new Error('Could not resolve the modal ID.')
1216
+ };
955
1217
  }
956
1218
  }
957
1219
 
958
- async function handlerClick$1(event) {
959
- // Working catch
960
- if (this.working) return; // Trigger click
1220
+ function updateFocusState() {
1221
+ // Check if there's an active modal
1222
+ if (this.active) {
1223
+ // Mount the focus trap on the active modal.
1224
+ this.focusTrap.mount(this.active.dialog, this.settings.selectorFocus);
1225
+ } else {
1226
+ // Set focus to root trigger and unmount the focus trap.
1227
+ if (this.trigger) {
1228
+ this.trigger.focus();
1229
+ this.trigger = null;
1230
+ }
961
1231
 
962
- const trigger = event.target.closest(`[data-${this.settings.dataOpen}]`);
1232
+ this.focusTrap.unmount();
1233
+ }
1234
+ }
1235
+
1236
+ function updateStackIndex(stack) {
1237
+ stack.forEach((entry, index) => {
1238
+ entry.el.style.zIndex = null;
1239
+ const value = getComputedStyle(entry.el)['z-index'];
1240
+ entry.el.style.zIndex = parseInt(value) + index + 1;
1241
+ });
1242
+ }
1243
+
1244
+ async function handleClick$1(event) {
1245
+ // If an open or replace button was clicked, open or replace the modal.
1246
+ let trigger = event.target.closest(`[data-${this.settings.dataOpen}], [data-${this.settings.dataReplace}]`);
963
1247
 
964
1248
  if (trigger) {
965
- event.preventDefault();
966
- const modalKey = trigger.getAttribute(`data-${this.settings.dataOpen}`);
967
- const fromModal = event.target.closest(`[data-${this.settings.dataModal}]`);
968
- if (!fromModal) this.memory.trigger = trigger;
969
- await this.close(!fromModal);
970
- this.open(modalKey);
971
- return;
972
- } // Close click
1249
+ event.preventDefault(); // Save the trigger if it's not coming from inside a modal.
973
1250
 
1251
+ const fromModal = event.target.closest(this.settings.selectorModal);
1252
+ if (!fromModal) this.trigger = trigger; // Get the modal.
974
1253
 
975
- if (event.target.closest(`[data-${this.settings.dataClose}]`)) {
976
- event.preventDefault();
977
- this.close();
978
- return;
979
- } // Root click
1254
+ const modal = this.get(getModalID.call(this, trigger)); // Depending on the button type, either open or replace the modal.
980
1255
 
1256
+ return trigger.matches(`[data-${this.settings.dataOpen}]`) ? modal.open() : modal.replace();
1257
+ } // If a close button was clicked, close the modal.
981
1258
 
982
- if (event.target.hasAttribute(`data-${this.settings.dataModal}`) && !event.target.hasAttribute(`data-${this.settings.dataRequired}`)) {
983
- this.close();
984
- return;
1259
+
1260
+ trigger = event.target.closest(`[data-${this.settings.dataClose}]`);
1261
+
1262
+ if (trigger) {
1263
+ event.preventDefault(); // Get the value of the data attribute.
1264
+
1265
+ const value = trigger.getAttribute(`data-${this.settings.dataClose}`); // Close all if * wildcard is passed, otherwise close a single modal.
1266
+
1267
+ return value === '*' ? this.closeAll() : this.close(value);
1268
+ } // If the modal screen was clicked, close the modal.
1269
+
1270
+
1271
+ if (event.target.matches(this.settings.selectorModal) && !event.target.querySelector(this.settings.selectorRequired)) {
1272
+ return this.close(getModalID.call(this, event.target));
985
1273
  }
986
1274
  }
987
- function handlerKeydown$1(event) {
988
- // Working catch
989
- if (this.working) return;
990
-
1275
+ function handleKeydown$1(event) {
1276
+ // If escape key was pressed.
991
1277
  if (event.key === 'Escape') {
992
- const target = document.querySelector(`[data-${this.settings.dataModal}].${this.settings.stateOpened}`);
993
-
994
- if (target && !target.hasAttribute(`data-${this.settings.dataRequired}`)) {
995
- this.close();
1278
+ // If a modal is opened and not required, close the modal.
1279
+ if (this.active && !this.active.dialog.matches(this.settings.selectorRequired)) {
1280
+ return this.close();
996
1281
  }
997
1282
  }
998
1283
  }
999
1284
 
1000
- function setInitialState() {
1001
- const modals = document.querySelectorAll(`[data-${this.settings.dataModal}]`);
1002
- modals.forEach(el => {
1003
- // Remove opened state setup
1004
- if (el.classList.contains(this.settings.stateOpened)) {
1005
- setInert(false, this.settings.selectorInert);
1006
- setOverflowHidden(false, this.settings.selectorOverflow);
1007
- focusTrigger(this);
1008
- this.focusTrap.destroy();
1009
- } // Remove all state classes and add the default state (closed)
1010
-
1285
+ async function deregister$1(obj, close = true) {
1286
+ // Return collection if nothing was passed.
1287
+ if (!obj) return this.collection; // Check if entry has been registered in the collection.
1011
1288
 
1012
- removeClass(el, this.settings.stateOpened, this.settings.stateOpening, this.settings.stateClosing);
1013
- addClass(el, this.settings.stateClosed);
1289
+ const index = this.collection.findIndex(entry => {
1290
+ return entry.id === obj.id;
1014
1291
  });
1292
+
1293
+ if (index >= 0) {
1294
+ // Get the collection entry.
1295
+ const entry = this.collection[index]; // If entry is in the opened state, close it.
1296
+
1297
+ if (close && entry.state === 'opened') {
1298
+ await entry.close(false);
1299
+ } else {
1300
+ // Get index of modal in stack array.
1301
+ const stackIndex = this.stack.findIndex(item => {
1302
+ return item.id === entry.id;
1303
+ }); // Remove modal from stack array.
1304
+
1305
+ if (stackIndex >= 0) {
1306
+ this.stack.splice(stackIndex, 1);
1307
+ }
1308
+ } // Return teleported modal if a reference has been set.
1309
+
1310
+
1311
+ if (entry.getSetting('teleport')) {
1312
+ entry.teleportReturn();
1313
+ } // Delete properties from collection entry.
1314
+
1315
+
1316
+ Object.getOwnPropertyNames(entry).forEach(prop => {
1317
+ delete entry[prop];
1318
+ }); // Remove entry from collection.
1319
+
1320
+ this.collection.splice(index, 1);
1321
+ } // Return the modified collection.
1322
+
1323
+
1324
+ return this.collection;
1325
+ }
1326
+
1327
+ async function open$1(query, transition, focus = true) {
1328
+ // Get the modal from collection.
1329
+ const modal = getModal.call(this, query); // Get the modal configuration.
1330
+
1331
+ const config = _extends({}, this.settings, modal.settings); // Add transition parameter to configuration.
1332
+
1333
+
1334
+ if (transition !== undefined) config.transition = transition; // Check if modal is already in the stack.
1335
+
1336
+ const index = this.stack.findIndex(entry => {
1337
+ return entry.id === modal.id;
1338
+ }); // If modal is already open.
1339
+
1340
+ if (index >= 0) {
1341
+ // Remove modal from stack array.
1342
+ this.stack.splice(index, 1); // Move back to end of stack.
1343
+
1344
+ this.stack.push(modal);
1345
+ } // If modal is closed.
1346
+
1347
+
1348
+ if (modal.state === 'closed') {
1349
+ // Update modal state.
1350
+ modal.state = 'opening'; // Apply z-index styles based on stack length.
1351
+
1352
+ modal.el.style.zIndex = null;
1353
+ const value = getComputedStyle(modal.el)['z-index'];
1354
+ modal.el.style.zIndex = parseInt(value) + this.stack.length + 1; // Store modal in stack array.
1355
+
1356
+ this.stack.push(modal); // Run the open transition.
1357
+
1358
+ await openTransition(modal.el, config); // Update modal state.
1359
+
1360
+ modal.state = 'opened';
1361
+ } // Update focus if the focus param is true.
1362
+
1363
+
1364
+ if (focus) {
1365
+ updateFocusState.call(this);
1366
+ } // Dispatch custom opened event.
1367
+
1368
+
1369
+ modal.el.dispatchEvent(new CustomEvent(config.customEventPrefix + 'opened', {
1370
+ detail: this,
1371
+ bubbles: true
1372
+ })); // Return the modal.
1373
+
1374
+ return modal;
1015
1375
  }
1016
1376
 
1017
- async function open$1(modalKey) {
1018
- const modal = getModal.call(this, modalKey);
1019
- if (!modal) return modalNotFound(modalKey);
1020
- const config = getModalConfig.call(this, modal);
1377
+ async function close$1(query, transition, focus = true) {
1378
+ // Get the modal from collection, or top modal in stack if no query is provided.
1379
+ const modal = query ? getModal.call(this, query) : this.active; // If a modal exists and its state is opened.
1380
+
1381
+ if (modal && modal.state === 'opened') {
1382
+ // Update modal state.
1383
+ modal.state = 'closing'; // Get the modal configuration.
1384
+
1385
+ const config = _extends({}, this.settings, modal.settings); // Add transition parameter to configuration.
1386
+
1021
1387
 
1022
- if (hasClass(modal, config.stateClosed)) {
1023
- this.working = true;
1024
- setOverflowHidden(true, config.selectorOverflow);
1025
- await openTransition(modal, config);
1026
- this.focusTrap.init(modal);
1027
- focusTarget(modal, config);
1028
- setInert(true, config.selectorInert);
1029
- modal.dispatchEvent(new CustomEvent(config.customEventPrefix + 'opened', {
1388
+ if (transition !== undefined) config.transition = transition; // Remove focus from active element.
1389
+
1390
+ document.activeElement.blur(); // Run the close transition.
1391
+
1392
+ await closeTransition(modal.el, config); // Remove z-index styles.
1393
+
1394
+ modal.el.style.zIndex = null; // Get index of modal in stack array.
1395
+
1396
+ const index = this.stack.findIndex(entry => {
1397
+ return entry.id === modal.id;
1398
+ }); // Remove modal from stack array.
1399
+
1400
+ this.stack.splice(index, 1); // Update focus if the focus param is true.
1401
+
1402
+ if (focus) {
1403
+ updateFocusState.call(this);
1404
+ } // Update modal state.
1405
+
1406
+
1407
+ modal.state = 'closed'; // Dispatch custom closed event.
1408
+
1409
+ modal.el.dispatchEvent(new CustomEvent(config.customEventPrefix + 'closed', {
1030
1410
  detail: this,
1031
1411
  bubbles: true
1032
1412
  }));
1033
- this.working = false;
1034
- return modal;
1035
- } else {
1036
- return modal;
1037
- }
1038
- }
1413
+ } // Return the modal.
1039
1414
 
1040
- class Modal {
1041
- constructor(options) {
1042
- this.defaults = defaults$1;
1043
- this.settings = _extends({}, this.defaults, options);
1044
- this.working = false;
1045
- this.memory = {};
1046
- this.focusTrap = new FocusTrap();
1047
- this.__handlerClick = handlerClick$1.bind(this);
1048
- this.__handlerKeydown = handlerKeydown$1.bind(this);
1049
- if (this.settings.autoInit) this.init();
1050
- }
1051
1415
 
1052
- init(options = null) {
1053
- if (options) this.settings = _extends({}, this.settings, options);
1054
- this.moveModals();
1055
- this.setInitialState();
1416
+ return modal;
1417
+ }
1056
1418
 
1057
- if (this.settings.setTabindex) {
1058
- this.setTabindex();
1419
+ async function closeAll$1(exclude, transition) {
1420
+ const result = [];
1421
+ await Promise.all(this.stack.map(async modal => {
1422
+ if (exclude && exclude === modal.id) {
1423
+ Promise.resolve();
1424
+ } else {
1425
+ result.push(await close$1.call(this, modal, transition, false));
1059
1426
  }
1060
1427
 
1061
- if (this.settings.eventListeners) {
1062
- this.initEventListeners();
1063
- }
1064
- }
1428
+ modal.trigger = null;
1429
+ }));
1430
+ return result;
1431
+ }
1065
1432
 
1066
- destroy() {
1067
- this.memory = {};
1433
+ async function replace(query, transition, focus = true) {
1434
+ // Get the modal from collection.
1435
+ const modal = getModal.call(this, query); // Setup results for return.
1068
1436
 
1069
- if (this.settings.eventListeners) {
1070
- this.destroyEventListeners();
1071
- }
1072
- }
1073
- /**
1074
- * Event listeners
1075
- */
1437
+ let resultOpened, resultClosed;
1076
1438
 
1439
+ if (modal.state === 'opened') {
1440
+ // If modal is open, close all modals except for replacement.
1441
+ resultOpened = modal;
1442
+ resultClosed = await closeAll$1.call(this, modal.id, transition);
1443
+ } else {
1444
+ // If modal is closed, close all and open replacement at the same time.
1445
+ resultOpened = open$1.call(this, modal, transition, false);
1446
+ resultClosed = closeAll$1.call(this, false, transition);
1447
+ await Promise.all([resultOpened, resultClosed]);
1448
+ } // Update focus if the focus param is true.
1077
1449
 
1078
- initEventListeners() {
1079
- document.addEventListener('click', this.__handlerClick, false);
1080
- document.addEventListener('touchend', this.__handlerClick, false);
1081
- document.addEventListener('keydown', this.__handlerKeydown, false);
1082
- }
1083
1450
 
1084
- destroyEventListeners() {
1085
- document.removeEventListener('click', this.__handlerClick, false);
1086
- document.removeEventListener('touchend', this.__handlerClick, false);
1087
- document.removeEventListener('keydown', this.__handlerKeydown, false);
1088
- }
1089
- /**
1090
- * Helpers
1091
- */
1451
+ if (focus) {
1452
+ updateFocusState.call(this);
1453
+ } // Return the modals there were opened and closed.
1092
1454
 
1093
1455
 
1094
- getModal(modalKey) {
1095
- return getModal.call(this, modalKey);
1096
- }
1456
+ return {
1457
+ opened: resultOpened,
1458
+ closed: resultClosed
1459
+ };
1460
+ }
1097
1461
 
1098
- setTabindex() {
1099
- return setTabindex(`
1100
- [data-${this.settings.dataModal}]
1101
- [data-${this.settings.dataDialog}]
1102
- `);
1103
- }
1462
+ async function register$1(el, dialog) {
1463
+ // Deregister entry incase it has already been registered.
1464
+ await deregister$1.call(this, el, false); // Save root this for use inside methods API.
1104
1465
 
1105
- setInitialState() {
1106
- return setInitialState.call(this);
1107
- }
1466
+ const root = this; // Setup methods API.
1108
1467
 
1109
- moveModals(type, ref) {
1110
- return moveModals.call(this, type, ref);
1111
- }
1112
- /**
1113
- * Change state functionality
1114
- */
1468
+ const methods = {
1469
+ open(transition, focus) {
1470
+ return open$1.call(root, this, transition, focus);
1471
+ },
1115
1472
 
1473
+ close(transition, focus) {
1474
+ return close$1.call(root, this, transition, focus);
1475
+ },
1116
1476
 
1117
- open(modalKey) {
1118
- return open$1.call(this, modalKey);
1119
- }
1477
+ replace(transition, focus) {
1478
+ return replace.call(root, this, transition, focus);
1479
+ },
1120
1480
 
1121
- close(returnFocus) {
1122
- return close$1.call(this, returnFocus);
1123
- }
1481
+ deregister() {
1482
+ return deregister$1.call(root, this);
1483
+ },
1124
1484
 
1125
- }
1485
+ teleport(ref = this.getSetting('teleport'), method = this.getSetting('teleportMethod')) {
1486
+ if (!this.returnRef) {
1487
+ this.returnRef = teleport(this.el, ref, method);
1488
+ return this.el;
1489
+ } else {
1490
+ console.error('Element has already been teleported:', this.el);
1491
+ return false;
1492
+ }
1493
+ },
1126
1494
 
1127
- class Collection {
1128
- constructor() {
1129
- this.collection = [];
1130
- }
1495
+ teleportReturn() {
1496
+ if (this.returnRef) {
1497
+ this.returnRef = teleport(this.el, this.returnRef);
1498
+ return this.el;
1499
+ } else {
1500
+ console.error('No return reference found:', this.el);
1501
+ return false;
1502
+ }
1503
+ },
1131
1504
 
1132
- register(item) {
1133
- this.deregister(item);
1134
- this.collection.push(item);
1135
- return this.collection;
1136
- }
1505
+ getSetting(key) {
1506
+ return key in this.settings ? this.settings[key] : root.settings[key];
1507
+ }
1137
1508
 
1138
- deregister(ref) {
1139
- const index = this.collection.findIndex(entry => {
1140
- return entry === ref;
1141
- });
1509
+ }; // Setup the modal object.
1142
1510
 
1143
- if (index >= 0) {
1144
- const entry = this.collection[index];
1145
- Object.getOwnPropertyNames(entry).forEach(prop => {
1146
- delete entry[prop];
1147
- });
1148
- this.collection.splice(index, 1);
1149
- }
1511
+ const entry = _extends({
1512
+ id: el.id,
1513
+ state: 'closed',
1514
+ el: el,
1515
+ dialog: dialog,
1516
+ returnRef: null,
1517
+ settings: getConfig$1(el, this.settings.dataConfig)
1518
+ }, methods); // Set aria-modal attribute to true.
1150
1519
 
1151
- return this.collection;
1152
- }
1153
1520
 
1154
- registerCollection(items) {
1155
- items.forEach(item => {
1156
- this.register(item);
1157
- });
1158
- return this.collection;
1159
- }
1521
+ entry.dialog.setAttribute('aria-modal', 'true'); // If a role attribute is not set, set it to "dialog" as the default.
1160
1522
 
1161
- deregisterCollection() {
1162
- while (this.collection.length > 0) {
1163
- this.deregister(this.collection[0]);
1164
- }
1523
+ if (!entry.dialog.hasAttribute('role')) {
1524
+ entry.dialog.setAttribute('role', 'dialog');
1525
+ } // Set tabindex="-1" so dialog is focusable via JS or click.
1165
1526
 
1166
- return this.collection;
1167
- }
1168
1527
 
1169
- get(query, key = 'id') {
1170
- const result = this.collection.find(item => {
1171
- return item[key] === query;
1172
- });
1173
- return result || null;
1174
- }
1528
+ if (entry.getSetting('setTabindex')) {
1529
+ entry.dialog.setAttribute('tabindex', '-1');
1530
+ } // Teleport modal if a reference has been set.
1531
+
1532
+
1533
+ if (entry.getSetting('teleport')) {
1534
+ entry.teleport();
1535
+ } // Add entry to collection.
1175
1536
 
1537
+
1538
+ this.collection.push(entry); // Setup initial state.
1539
+
1540
+ if (entry.el.classList.contains(this.settings.stateOpened)) {
1541
+ // Open entry with transitions disabled.
1542
+ await entry.open(false);
1543
+ } else {
1544
+ // Remove transition state classes.
1545
+ entry.el.classList.remove(this.settings.stateOpening);
1546
+ entry.el.classList.remove(this.settings.stateClosing); // Add closed state class.
1547
+
1548
+ entry.el.classList.add(this.settings.stateClosed);
1549
+ } // Return the registered entry.
1550
+
1551
+
1552
+ return entry;
1176
1553
  }
1177
1554
 
1178
- var defaults = {
1179
- autoInit: false,
1180
- // Selectors
1181
- selectorPopover: '.popover',
1182
- selectorArrow: '.popover__arrow',
1183
- // State classes
1184
- stateActive: 'is-active',
1185
- // Feature toggles
1186
- eventListeners: true,
1187
- eventType: 'click',
1188
- placement: 'bottom'
1189
- };
1555
+ var _handleClick = /*#__PURE__*/_classPrivateFieldLooseKey("handleClick");
1556
+
1557
+ var _handleKeydown$1 = /*#__PURE__*/_classPrivateFieldLooseKey("handleKeydown");
1558
+
1559
+ class Modal extends Collection {
1560
+ constructor(options) {
1561
+ super();
1562
+ Object.defineProperty(this, _handleClick, {
1563
+ writable: true,
1564
+ value: void 0
1565
+ });
1566
+ Object.defineProperty(this, _handleKeydown$1, {
1567
+ writable: true,
1568
+ value: void 0
1569
+ });
1570
+ this.defaults = defaults$1;
1571
+ this.settings = _extends({}, this.defaults, options);
1572
+ this.trigger = null;
1573
+ this.focusTrap = new FocusTrap(); // Setup a proxy for stack array.
1190
1574
 
1191
- function close(popover) {
1192
- // Update state class
1193
- popover.target.classList.remove(this.settings.stateActive); // Update a11y attributes
1575
+ this.stack = new Proxy([], {
1576
+ set: (target, property, value) => {
1577
+ target[property] = value; // Update global state if stack length changed.
1194
1578
 
1195
- if (popover.trigger.hasAttribute('aria-controls')) {
1196
- popover.trigger.setAttribute('aria-expanded', 'false');
1197
- } // Disable popper event listeners
1579
+ if (property === 'length') {
1580
+ updateGlobalState(this.active, this.settings);
1581
+ updateStackIndex(this.stack);
1582
+ }
1198
1583
 
1584
+ return true;
1585
+ }
1586
+ });
1587
+ _classPrivateFieldLooseBase(this, _handleClick)[_handleClick] = handleClick$1.bind(this);
1588
+ _classPrivateFieldLooseBase(this, _handleKeydown$1)[_handleKeydown$1] = handleKeydown$1.bind(this);
1589
+ if (this.settings.autoInit) this.init();
1590
+ }
1199
1591
 
1200
- popover.popper.setOptions({
1201
- modifiers: [{
1202
- name: 'eventListeners',
1203
- enabled: false
1204
- }]
1205
- }); // Update popover state
1592
+ get active() {
1593
+ return this.stack[this.stack.length - 1];
1594
+ }
1206
1595
 
1207
- popover.state = 'closed'; // Clear memory if popover trigger matches the one saved in memory
1596
+ async init(options) {
1597
+ // Update settings with passed options.
1598
+ if (options) this.settings = _extends({}, this.settings, options); // Get all the modals.
1208
1599
 
1209
- if (popover.trigger === this.memory.trigger) {
1210
- this.memory.trigger = null;
1211
- } // Return the popover
1600
+ const modals = document.querySelectorAll(this.settings.selectorModal); // Register the collections array with modal instances.
1212
1601
 
1602
+ await this.registerCollection(modals); // If eventListeners are enabled, init event listeners.
1213
1603
 
1214
- return popover;
1215
- }
1216
- function closeAll() {
1217
- this.collection.forEach(popover => {
1218
- if (popover.state === 'opened') {
1219
- popover.close();
1604
+ if (this.settings.eventListeners) {
1605
+ this.initEventListeners();
1220
1606
  }
1221
- }); // Return the collection
1222
1607
 
1223
- return this.collection;
1224
- }
1225
- function closeCheck(popover) {
1226
- // Only run closeCheck if provided popover is currently open
1227
- if (popover.state != 'opened') return; // Needed to correctly check which element is currently being focused
1608
+ return this;
1609
+ }
1228
1610
 
1229
- setTimeout(() => {
1230
- // Check if trigger or target are being hovered
1231
- const isHovered = popover.target.closest(':hover') === popover.target || popover.trigger.closest(':hover') === popover.trigger; // Check if trigger or target are being focused
1611
+ async destroy() {
1612
+ // Clear stored trigger.
1613
+ this.trigger = null; // Remove all entries from the collection.
1232
1614
 
1233
- const isFocused = document.activeElement.closest(`#${popover.id}, [aria-controls="${popover.id}"]`); // Close if the trigger and target are not currently hovered or focused
1615
+ await this.deregisterCollection(); // If eventListeners are enabled, destroy event listeners.
1234
1616
 
1235
- if (!isHovered && !isFocused) {
1236
- popover.close();
1237
- } // Return the popover
1617
+ if (this.settings.eventListeners) {
1618
+ this.destroyEventListeners();
1619
+ }
1238
1620
 
1621
+ return this;
1622
+ }
1239
1623
 
1240
- return popover;
1241
- }, 1);
1242
- }
1624
+ initEventListeners() {
1625
+ document.addEventListener('click', _classPrivateFieldLooseBase(this, _handleClick)[_handleClick], false);
1626
+ document.addEventListener('touchend', _classPrivateFieldLooseBase(this, _handleClick)[_handleClick], false);
1627
+ document.addEventListener('keydown', _classPrivateFieldLooseBase(this, _handleKeydown$1)[_handleKeydown$1], false);
1628
+ }
1243
1629
 
1244
- function handlerClick(popover) {
1245
- if (popover.target.classList.contains(this.settings.stateActive)) {
1246
- popover.close();
1247
- } else {
1248
- this.memory.trigger = popover.trigger;
1249
- popover.open();
1250
- documentClick.call(this, popover);
1630
+ destroyEventListeners() {
1631
+ document.removeEventListener('click', _classPrivateFieldLooseBase(this, _handleClick)[_handleClick], false);
1632
+ document.removeEventListener('touchend', _classPrivateFieldLooseBase(this, _handleClick)[_handleClick], false);
1633
+ document.removeEventListener('keydown', _classPrivateFieldLooseBase(this, _handleKeydown$1)[_handleKeydown$1], false);
1251
1634
  }
1252
- }
1253
- function handlerKeydown(event) {
1254
- switch (event.key) {
1255
- case 'Escape':
1256
- if (this.memory.trigger) {
1257
- this.memory.trigger.focus();
1258
- }
1259
1635
 
1260
- closeAll.call(this);
1261
- return;
1636
+ register(query) {
1637
+ const els = getModalElements.call(this, query);
1638
+ if (els.error) return Promise.reject(els.error);
1639
+ return register$1.call(this, els.modal, els.dialog);
1640
+ }
1262
1641
 
1263
- case 'Tab':
1264
- this.collection.forEach(popover => {
1265
- closeCheck.call(this, popover);
1266
- });
1267
- return;
1642
+ deregister(query) {
1643
+ const modal = this.get(getModalID.call(this, query));
1644
+ return deregister$1.call(this, modal);
1645
+ }
1268
1646
 
1269
- default:
1270
- return;
1647
+ open(id, transition, focus) {
1648
+ return open$1.call(this, id, transition, focus);
1271
1649
  }
1272
- }
1273
- function documentClick(popover) {
1274
- const root = this;
1275
- document.addEventListener('click', function _f(event) {
1276
- // Check if a popover was clicked
1277
- const result = event.target.closest(`#${popover.id}, [aria-controls="${popover.id}"]`);
1278
1650
 
1279
- if (!result) {
1280
- // If it doesn't match and popover is open, close it and remove event listener
1281
- if (popover.target && popover.target.classList.contains(root.settings.stateActive)) {
1282
- popover.close();
1283
- }
1651
+ close(id, transition, focus) {
1652
+ return close$1.call(this, id, transition, focus);
1653
+ }
1284
1654
 
1285
- this.removeEventListener('click', _f);
1286
- } else {
1287
- // If it does match and popover isn't currently active, remove event listener
1288
- if (popover.target && !popover.target.classList.contains(root.settings.stateActive)) {
1289
- this.removeEventListener('click', _f);
1290
- }
1655
+ replace(id, transition, focus) {
1656
+ return replace.call(this, id, transition, focus);
1657
+ }
1658
+
1659
+ async closeAll(exclude = false, transition, focus = true) {
1660
+ const result = await closeAll$1.call(this, exclude, transition); // Update focus if the focus param is true.
1661
+
1662
+ if (focus) {
1663
+ updateFocusState.call(this);
1291
1664
  }
1292
- });
1665
+
1666
+ return result;
1667
+ }
1668
+
1293
1669
  }
1294
1670
 
1671
+ var defaults = {
1672
+ autoInit: false,
1673
+ // Selectors
1674
+ selectorPopover: '.popover',
1675
+ selectorArrow: '.popover__arrow',
1676
+ // State classes
1677
+ stateActive: 'is-active',
1678
+ // Feature settings
1679
+ eventListeners: true,
1680
+ eventType: 'click',
1681
+ placement: 'bottom'
1682
+ };
1683
+
1295
1684
  function getConfig(el, settings) {
1296
- // Get the computed styles of the popover
1297
- const styles = getComputedStyle(el); // Setup the config obj with default values
1685
+ // Get the computed styles of the element.
1686
+ const styles = getComputedStyle(el); // Setup the config obj with default values.
1298
1687
 
1299
1688
  const config = {
1300
1689
  'placement': settings.placement,
@@ -1304,29 +1693,30 @@ function getConfig(el, settings) {
1304
1693
  'flip-padding': 0,
1305
1694
  'arrow-element': settings.selectorArrow,
1306
1695
  'arrow-padding': 0
1307
- }; // Loop through config obj
1696
+ }; // Loop through config obj.
1308
1697
 
1309
1698
  for (const prop in config) {
1310
- // Get the CSS variable property values
1311
- const prefix = getComputedStyle(document.body).getPropertyValue('--vrembem-variable-prefix');
1312
- const value = styles.getPropertyValue(`--${prefix}popover-${prop}`).trim(); // If a value was found, replace the default in config obj
1699
+ // Get the CSS variable property values.
1700
+ const prefix = L();
1701
+ const value = styles.getPropertyValue(`--${prefix}popover-${prop}`).trim(); // If a value was found, replace the default in config obj.
1313
1702
 
1314
1703
  if (value) {
1315
1704
  config[prop] = value;
1316
1705
  }
1317
- } // Return the config obj
1706
+ } // Return the config obj.
1318
1707
 
1319
1708
 
1320
1709
  return config;
1321
1710
  }
1711
+
1322
1712
  function getPadding(value) {
1323
- let padding; // Split the value by spaces if it's a string
1713
+ let padding; // Split the value by spaces if it's a string.
1324
1714
 
1325
- const array = typeof value === 'string' ? value.trim().split(' ') : [value]; // Convert individual values to integers
1715
+ const array = typeof value === 'string' ? value.trim().split(' ') : [value]; // Convert individual values to integers.
1326
1716
 
1327
1717
  array.forEach(function (item, index) {
1328
1718
  array[index] = parseInt(item, 10);
1329
- }); // Build the padding object based on the number of values passed
1719
+ }); // Build the padding object based on the number of values passed.
1330
1720
 
1331
1721
  switch (array.length) {
1332
1722
  case 1:
@@ -1363,11 +1753,12 @@ function getPadding(value) {
1363
1753
  default:
1364
1754
  padding = false;
1365
1755
  break;
1366
- } // Return the padding object
1756
+ } // Return the padding object.
1367
1757
 
1368
1758
 
1369
1759
  return padding;
1370
1760
  }
1761
+
1371
1762
  function getModifiers(options) {
1372
1763
  return [{
1373
1764
  name: 'offset',
@@ -1392,57 +1783,183 @@ function getModifiers(options) {
1392
1783
  }
1393
1784
  }];
1394
1785
  }
1786
+
1787
+ function getPopover(query) {
1788
+ // Get the entry from collection.
1789
+ const entry = typeof query === 'string' ? this.get(query) : this.get(query.id); // Return entry if it was resolved, otherwise throw error.
1790
+
1791
+ if (entry) {
1792
+ return entry;
1793
+ } else {
1794
+ throw new Error(`Popover not found in collection with id of "${query}".`);
1795
+ }
1796
+ }
1797
+
1395
1798
  function getPopoverID(obj) {
1396
- // If it's a string
1799
+ // If it's a string, return the string.
1397
1800
  if (typeof obj === 'string') {
1398
1801
  return obj;
1399
- } // If it's an HTML element
1802
+ } // If it's an HTML element.
1400
1803
  else if (typeof obj.hasAttribute === 'function') {
1401
- // If it's a popover trigger
1402
- if (obj.hasAttribute('aria-controls')) {
1804
+ // If it's a popover element, return the id.
1805
+ if (obj.closest(this.settings.selectorPopover)) {
1806
+ obj = obj.closest(this.settings.selectorPopover);
1807
+ return obj.id;
1808
+ } // If it's a popover trigger, return value of aria-controls.
1809
+ else if (obj.hasAttribute('aria-controls')) {
1403
1810
  return obj.getAttribute('aria-controls');
1404
- } // If it's a popover tooltip trigger
1811
+ } // If it's a popover tooltip trigger, return the value of aria-describedby.
1405
1812
  else if (obj.hasAttribute('aria-describedby')) {
1406
1813
  return obj.getAttribute('aria-describedby');
1407
- } // If it's a popover target
1408
- else if (obj.closest(this.settings.selectorPopover)) {
1409
- return obj.id;
1410
- } // Return false if no id was found
1814
+ } // Return false if no id was found.
1411
1815
  else return false;
1412
- } // If it has an ID property
1816
+ } // If it has an id property, return its value.
1413
1817
  else if (obj.id) {
1414
1818
  return obj.id;
1415
- } // Return false if no id was found
1819
+ } // Return false if no id was found.
1416
1820
  else return false;
1417
1821
  }
1822
+
1418
1823
  function getPopoverElements(query) {
1419
1824
  const id = getPopoverID.call(this, query);
1420
1825
 
1421
1826
  if (id) {
1827
+ const popover = document.querySelector(`#${id}`);
1422
1828
  const trigger = document.querySelector(`[aria-controls="${id}"]`) || document.querySelector(`[aria-describedby="${id}"]`);
1423
- const target = document.querySelector(`#${id}`);
1424
1829
 
1425
- if (!trigger && !target) {
1426
- console.error('No popover elements found using the provided ID:', id);
1830
+ if (!trigger && !popover) {
1831
+ return {
1832
+ error: new Error(`No popover elements found using the ID: "${id}".`)
1833
+ };
1427
1834
  } else if (!trigger) {
1428
- console.error('No popover trigger associated with the provided popover:', target);
1429
- } else if (!target) {
1430
- console.error('No popover associated with the provided popover trigger:', trigger);
1431
- }
1432
-
1433
- if (!trigger || !target) {
1434
- return false;
1835
+ return {
1836
+ error: new Error('No popover trigger associated with the provided popover.')
1837
+ };
1838
+ } else if (!popover) {
1839
+ return {
1840
+ error: new Error('No popover associated with the provided popover trigger.')
1841
+ };
1435
1842
  } else {
1436
1843
  return {
1437
- trigger,
1438
- target
1844
+ popover,
1845
+ trigger
1439
1846
  };
1440
1847
  }
1441
1848
  } else {
1442
- console.error('Could not resolve the popover ID:', query);
1443
- return false;
1849
+ return {
1850
+ error: new Error('Could not resolve the popover ID.')
1851
+ };
1852
+ }
1853
+ }
1854
+
1855
+ async function close(query) {
1856
+ // Get the popover from collection.
1857
+ const popover = query ? getPopover.call(this, query) : await closeAll.call(this); // If a modal exists and its state is opened.
1858
+
1859
+ if (popover && popover.state === 'opened') {
1860
+ // Update state class.
1861
+ popover.el.classList.remove(this.settings.stateActive); // Update accessibility attribute(s).
1862
+
1863
+ if (popover.trigger.hasAttribute('aria-controls')) {
1864
+ popover.trigger.setAttribute('aria-expanded', 'false');
1865
+ } // Disable popper event listeners.
1866
+
1867
+
1868
+ popover.popper.setOptions({
1869
+ modifiers: [{
1870
+ name: 'eventListeners',
1871
+ enabled: false
1872
+ }]
1873
+ }); // Update popover state.
1874
+
1875
+ popover.state = 'closed'; // Clear root trigger if popover trigger matches.
1876
+
1877
+ if (popover.trigger === this.trigger) {
1878
+ this.trigger = null;
1879
+ }
1880
+ } // Return the popover.
1881
+
1882
+
1883
+ return popover;
1884
+ }
1885
+ async function closeAll() {
1886
+ const result = [];
1887
+ await Promise.all(this.collection.map(async popover => {
1888
+ if (popover.state === 'opened') {
1889
+ result.push(await close.call(this, popover));
1890
+ }
1891
+ }));
1892
+ return result;
1893
+ }
1894
+ function closeCheck(popover) {
1895
+ // Only run closeCheck if provided popover is currently open.
1896
+ if (popover.state != 'opened') return; // Needed to correctly check which element is currently being focused.
1897
+
1898
+ setTimeout(() => {
1899
+ // Check if trigger or element are being hovered.
1900
+ const isHovered = popover.el.closest(':hover') === popover.el || popover.trigger.closest(':hover') === popover.trigger; // Check if trigger or element are being focused.
1901
+
1902
+ const isFocused = document.activeElement.closest(`#${popover.id}, [aria-controls="${popover.id}"]`); // Close if the trigger and element are not currently hovered or focused.
1903
+
1904
+ if (!isHovered && !isFocused) {
1905
+ popover.close();
1906
+ } // Return the popover.
1907
+
1908
+
1909
+ return popover;
1910
+ }, 1);
1911
+ }
1912
+
1913
+ function handleClick(popover) {
1914
+ if (popover.state === 'opened') {
1915
+ popover.close();
1916
+ } else {
1917
+ this.trigger = popover.trigger;
1918
+ popover.open();
1919
+ handleDocumentClick.call(this, popover);
1920
+ }
1921
+ }
1922
+ function handleKeydown(event) {
1923
+ switch (event.key) {
1924
+ case 'Escape':
1925
+ if (this.trigger) {
1926
+ this.trigger.focus();
1927
+ }
1928
+
1929
+ closeAll.call(this);
1930
+ return;
1931
+
1932
+ case 'Tab':
1933
+ this.collection.forEach(popover => {
1934
+ closeCheck.call(this, popover);
1935
+ });
1936
+ return;
1937
+
1938
+ default:
1939
+ return;
1444
1940
  }
1445
1941
  }
1942
+ function handleDocumentClick(popover) {
1943
+ const root = this;
1944
+ document.addEventListener('click', function _f(event) {
1945
+ // Check if a popover was clicked.
1946
+ const result = event.target.closest(`#${popover.id}, [aria-controls="${popover.id}"]`);
1947
+
1948
+ if (!result) {
1949
+ // If it doesn't match and popover is open, close it and remove event listener.
1950
+ if (popover.el && popover.el.classList.contains(root.settings.stateActive)) {
1951
+ popover.close();
1952
+ }
1953
+
1954
+ this.removeEventListener('click', _f);
1955
+ } else {
1956
+ // If it does match and popover isn't currently active, remove event listener.
1957
+ if (popover.el && !popover.el.classList.contains(root.settings.stateActive)) {
1958
+ this.removeEventListener('click', _f);
1959
+ }
1960
+ }
1961
+ });
1962
+ }
1446
1963
 
1447
1964
  var top = 'top';
1448
1965
  var bottom = 'bottom';
@@ -1744,6 +2261,10 @@ function getContainingBlock(element) {
1744
2261
 
1745
2262
  var currentNode = getParentNode(element);
1746
2263
 
2264
+ if (isShadowRoot(currentNode)) {
2265
+ currentNode = currentNode.host;
2266
+ }
2267
+
1747
2268
  while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {
1748
2269
  var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that
1749
2270
  // create a containing block.
@@ -1967,7 +2488,7 @@ function mapToStyles(_ref2) {
1967
2488
 
1968
2489
  if (placement === top || (placement === left || placement === right) && variation === end) {
1969
2490
  sideY = bottom;
1970
- var offsetY = isFixed && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]
2491
+ var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]
1971
2492
  offsetParent[heightProp];
1972
2493
  y -= offsetY - popperRect.height;
1973
2494
  y *= gpuAcceleration ? 1 : -1;
@@ -1975,7 +2496,7 @@ function mapToStyles(_ref2) {
1975
2496
 
1976
2497
  if (placement === left || (placement === top || placement === bottom) && variation === end) {
1977
2498
  sideX = right;
1978
- var offsetX = isFixed && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]
2499
+ var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]
1979
2500
  offsetParent[widthProp];
1980
2501
  x -= offsetX - popperRect.width;
1981
2502
  x *= gpuAcceleration ? 1 : -1;
@@ -3227,16 +3748,69 @@ var createPopper = /*#__PURE__*/popperGenerator({
3227
3748
  defaultModifiers: defaultModifiers
3228
3749
  }); // eslint-disable-next-line import/no-unused-modules
3229
3750
 
3230
- function open(popover) {
3231
- // Update state class
3232
- popover.target.classList.add(this.settings.stateActive); // Update a11y attribute
3751
+ async function deregister(obj) {
3752
+ // Return collection if nothing was passed.
3753
+ if (!obj) return this.collection; // Check if entry has been registered in the collection.
3754
+
3755
+ const index = this.collection.findIndex(entry => {
3756
+ return entry.id === obj.id;
3757
+ });
3758
+
3759
+ if (index >= 0) {
3760
+ // Get the collection entry.
3761
+ const entry = this.collection[index]; // If entry is in the opened state, close it.
3762
+
3763
+ if (entry.state === 'opened') {
3764
+ entry.close();
3765
+ } // Clean up the popper instance.
3766
+
3767
+
3768
+ entry.popper.destroy(); // Remove event listeners.
3769
+
3770
+ deregisterEventListeners(entry); // Delete properties from collection entry.
3771
+
3772
+ Object.getOwnPropertyNames(entry).forEach(prop => {
3773
+ delete entry[prop];
3774
+ }); // Remove entry from collection.
3775
+
3776
+ this.collection.splice(index, 1);
3777
+ } // Return the modified collection.
3778
+
3779
+
3780
+ return this.collection;
3781
+ }
3782
+ function deregisterEventListeners(entry) {
3783
+ // If event listeners have been setup.
3784
+ if (entry.__eventListeners) {
3785
+ // Loop through listeners and remove from the appropriate elements.
3786
+ entry.__eventListeners.forEach(evObj => {
3787
+ evObj.el.forEach(el => {
3788
+ evObj.type.forEach(type => {
3789
+ entry[el].removeEventListener(type, evObj.listener, false);
3790
+ });
3791
+ });
3792
+ }); // Remove eventListeners object from collection.
3793
+
3794
+
3795
+ delete entry.__eventListeners;
3796
+ } // Return the entry object.
3797
+
3798
+
3799
+ return entry;
3800
+ }
3801
+
3802
+ async function open(query) {
3803
+ // Get the popover from collection.
3804
+ const popover = getPopover.call(this, query); // Update state class.
3805
+
3806
+ popover.el.classList.add(this.settings.stateActive); // Update accessibility attribute(s).
3233
3807
 
3234
3808
  if (popover.trigger.hasAttribute('aria-controls')) {
3235
3809
  popover.trigger.setAttribute('aria-expanded', 'true');
3236
- } // Update popover config
3810
+ } // Update popover config.
3237
3811
 
3238
3812
 
3239
- popover.config = getConfig(popover.target, this.settings); // Enable popper event listeners and set placement/modifiers
3813
+ popover.config = getConfig(popover.el, this.settings); // Enable popper event listeners and set placement/modifiers.
3240
3814
 
3241
3815
  popover.popper.setOptions({
3242
3816
  placement: popover.config['placement'],
@@ -3244,257 +3818,200 @@ function open(popover) {
3244
3818
  name: 'eventListeners',
3245
3819
  enabled: true
3246
3820
  }, ...getModifiers(popover.config)]
3247
- }); // Update popover position
3821
+ }); // Update popover position.
3248
3822
 
3249
- popover.popper.update(); // Update popover state
3823
+ popover.popper.update(); // Update popover state.
3250
3824
 
3251
- popover.state = 'opened'; // Return the popover
3825
+ popover.state = 'opened'; // Return the popover.
3252
3826
 
3253
3827
  return popover;
3254
3828
  }
3255
3829
 
3256
- function register(trigger, target) {
3257
- // Deregister popover if it already exists in the collection
3258
- this.deregister(target.id); // Create popper instance
3830
+ async function register(el, trigger) {
3831
+ // Deregister entry incase it has already been registered.
3832
+ deregister.call(this, el); // Save root this for use inside methods API.
3259
3833
 
3260
- const popperInstance = createPopper(trigger, target); // Save root this for use inside object & create methods API
3834
+ const root = this; // Setup methods API.
3261
3835
 
3262
- const root = this;
3263
3836
  const methods = {
3264
3837
  open() {
3265
- open.call(root, this);
3838
+ return open.call(root, this);
3266
3839
  },
3267
3840
 
3268
3841
  close() {
3269
- close.call(root, this);
3842
+ return close.call(root, this);
3270
3843
  },
3271
3844
 
3272
3845
  deregister() {
3273
- deregister.call(root, this);
3846
+ return deregister.call(root, this);
3274
3847
  }
3275
3848
 
3276
- }; // Build popover object and push to collection array
3849
+ }; // Setup the popover object.
3277
3850
 
3278
- const popover = _extends({
3279
- id: target.id,
3851
+ const entry = _extends({
3852
+ id: el.id,
3280
3853
  state: 'closed',
3854
+ el: el,
3281
3855
  trigger: trigger,
3282
- target: target,
3283
- popper: popperInstance,
3284
- config: getConfig(target, this.settings)
3285
- }, methods); // Setup event listeners
3286
-
3287
-
3288
- registerEventListeners.call(this, popover); // Set initial state of popover
3289
-
3290
- if (popover.target.classList.contains(this.settings.stateActive)) {
3291
- popover.open();
3292
- documentClick.call(this, popover);
3293
- } else {
3294
- popover.close();
3295
- } // Add item to collection
3296
-
3297
-
3298
- this.collection.push(popover); // Return the popover object
3299
-
3300
- return popover;
3301
- }
3302
- function deregister(popover) {
3303
- // Check if this item has been registered in the collection
3304
- const index = this.collection.findIndex(entry => {
3305
- return entry.id === popover.id;
3306
- }); // If the entry exists in the collection
3307
-
3308
- if (index >= 0) {
3309
- // Get the collection entry
3310
- const entry = this.collection[index]; // Close the collection entry if it's open
3856
+ popper: createPopper(trigger, el),
3857
+ config: getConfig(el, this.settings)
3858
+ }, methods); // Set aria-expanded to false if trigger has aria-controls attribute.
3311
3859
 
3312
- if (entry.state === 'opened') {
3313
- entry.close();
3314
- } // Clean up the popper instance
3315
3860
 
3861
+ if (entry.trigger.hasAttribute('aria-controls')) {
3862
+ entry.trigger.setAttribute('aria-expanded', 'false');
3863
+ } // Setup event listeners.
3316
3864
 
3317
- entry.popper.destroy(); // Remove event listeners
3318
3865
 
3319
- deregisterEventListeners(entry); // Delete properties from collection entry
3866
+ registerEventListeners.call(this, entry); // Add entry to collection.
3320
3867
 
3321
- Object.getOwnPropertyNames(entry).forEach(prop => {
3322
- delete entry[prop];
3323
- }); // Remove entry from collection
3868
+ this.collection.push(entry); // Set initial state.
3324
3869
 
3325
- this.collection.splice(index, 1);
3326
- } // Return the new collection
3870
+ if (entry.el.classList.contains(this.settings.stateActive)) {
3871
+ await entry.open();
3872
+ handleDocumentClick.call(this, entry);
3873
+ } // Return the registered entry.
3327
3874
 
3328
3875
 
3329
- return this.collection;
3876
+ return entry;
3330
3877
  }
3331
- function registerEventListeners(popover) {
3332
- // If event listeners aren't already setup
3333
- if (!popover.__eventListeners) {
3334
- // Add event listeners based on event type
3335
- const eventType = popover.config['event'];
3878
+ function registerEventListeners(entry) {
3879
+ // If event listeners aren't already setup.
3880
+ if (!entry.__eventListeners) {
3881
+ // Add event listeners based on event type.
3882
+ const eventType = entry.config['event']; // If the event type is hover.
3336
3883
 
3337
3884
  if (eventType === 'hover') {
3338
- // Setup event listeners object for hover
3339
- popover.__eventListeners = [{
3885
+ // Setup event listeners object for hover.
3886
+ entry.__eventListeners = [{
3340
3887
  el: ['trigger'],
3341
3888
  type: ['mouseenter', 'focus'],
3342
- listener: open.bind(this, popover)
3889
+ listener: open.bind(this, entry)
3343
3890
  }, {
3344
- el: ['trigger', 'target'],
3891
+ el: ['el', 'trigger'],
3345
3892
  type: ['mouseleave', 'focusout'],
3346
- listener: closeCheck.bind(this, popover)
3347
- }]; // Loop through listeners and apply to appropriate elements
3893
+ listener: closeCheck.bind(this, entry)
3894
+ }]; // Loop through listeners and apply to the appropriate elements.
3348
3895
 
3349
- popover.__eventListeners.forEach(evObj => {
3896
+ entry.__eventListeners.forEach(evObj => {
3350
3897
  evObj.el.forEach(el => {
3351
3898
  evObj.type.forEach(type => {
3352
- popover[el].addEventListener(type, evObj.listener, false);
3899
+ entry[el].addEventListener(type, evObj.listener, false);
3353
3900
  });
3354
3901
  });
3355
3902
  });
3356
- } else {
3357
- // Setup event listeners object for click
3358
- popover.__eventListeners = [{
3903
+ } // Else the event type is click.
3904
+ else {
3905
+ // Setup event listeners object for click.
3906
+ entry.__eventListeners = [{
3359
3907
  el: ['trigger'],
3360
3908
  type: ['click'],
3361
- listener: handlerClick.bind(this, popover)
3362
- }]; // Loop through listeners and apply to appropriate elements
3909
+ listener: handleClick.bind(this, entry)
3910
+ }]; // Loop through listeners and apply to the appropriate elements.
3363
3911
 
3364
- popover.__eventListeners.forEach(evObj => {
3912
+ entry.__eventListeners.forEach(evObj => {
3365
3913
  evObj.el.forEach(el => {
3366
3914
  evObj.type.forEach(type => {
3367
- popover[el].addEventListener(type, evObj.listener, false);
3915
+ entry[el].addEventListener(type, evObj.listener, false);
3368
3916
  });
3369
3917
  });
3370
3918
  });
3371
3919
  }
3372
- } // Return the popover object
3920
+ } // Return the entry object.
3373
3921
 
3374
3922
 
3375
- return popover;
3923
+ return entry;
3376
3924
  }
3377
- function deregisterEventListeners(popover) {
3378
- // If event listeners have been setup
3379
- if (popover.__eventListeners) {
3380
- // Loop through listeners and remove from appropriate elements
3381
- popover.__eventListeners.forEach(evObj => {
3382
- evObj.el.forEach(el => {
3383
- evObj.type.forEach(type => {
3384
- popover[el].removeEventListener(type, evObj.listener, false);
3385
- });
3386
- });
3387
- }); // Remove eventListeners object from collection
3388
-
3389
-
3390
- delete popover.__eventListeners;
3391
- } // Return the popover object
3392
-
3393
3925
 
3394
- return popover;
3395
- }
3926
+ var _handleKeydown = /*#__PURE__*/_classPrivateFieldLooseKey("handleKeydown");
3396
3927
 
3397
3928
  class Popover extends Collection {
3398
3929
  constructor(options) {
3399
3930
  super();
3931
+ Object.defineProperty(this, _handleKeydown, {
3932
+ writable: true,
3933
+ value: void 0
3934
+ });
3400
3935
  this.defaults = defaults;
3401
- this.settings = _extends({}, this.defaults, options); // this.collection = [];
3402
-
3403
- this.memory = {
3404
- trigger: null
3405
- };
3406
- this.__handlerKeydown = handlerKeydown.bind(this);
3936
+ this.settings = _extends({}, this.defaults, options);
3937
+ this.trigger = null;
3938
+ _classPrivateFieldLooseBase(this, _handleKeydown)[_handleKeydown] = handleKeydown.bind(this);
3407
3939
  if (this.settings.autoInit) this.init();
3408
3940
  }
3409
3941
 
3410
- init(options = null) {
3411
- // Update settings with passed options
3412
- if (options) this.settings = _extends({}, this.settings, options); // Get all the popovers
3942
+ async init(options) {
3943
+ // Update settings with passed options.
3944
+ if (options) this.settings = _extends({}, this.settings, options); // Get all the popovers.
3413
3945
 
3414
- const popovers = document.querySelectorAll(this.settings.selectorPopover); // Build the collections array with popover instances
3946
+ const popovers = document.querySelectorAll(this.settings.selectorPopover); // Register the collections array with popover instances.
3415
3947
 
3416
- this.registerCollection(popovers); // If eventListeners is enabled
3948
+ await this.registerCollection(popovers); // If eventListeners are enabled, init event listeners.
3417
3949
 
3418
3950
  if (this.settings.eventListeners) {
3419
3951
  // Pass false to initEventListeners() since registerCollection()
3420
- // already adds event listeners to popovers
3952
+ // already adds event listeners to popovers.
3421
3953
  this.initEventListeners(false);
3422
3954
  }
3955
+
3956
+ return this;
3423
3957
  }
3424
3958
 
3425
- destroy() {
3426
- // Deregister all popovers from collection
3427
- this.deregisterCollection(); // If eventListeners is enabled
3959
+ async destroy() {
3960
+ // Clear stored trigger.
3961
+ this.trigger = null; // Remove all entries from the collection.
3962
+
3963
+ await this.deregisterCollection(); // If eventListeners are enabled, destroy event listeners.
3428
3964
 
3429
3965
  if (this.settings.eventListeners) {
3430
3966
  // Pass false to destroyEventListeners() since deregisterCollection()
3431
- // already removes event listeners from popovers
3967
+ // already removes event listeners from popovers.
3432
3968
  this.destroyEventListeners(false);
3433
3969
  }
3434
- }
3435
- /**
3436
- * Event listeners
3437
- */
3438
3970
 
3971
+ return this;
3972
+ }
3439
3973
 
3440
3974
  initEventListeners(processCollection = true) {
3441
3975
  if (processCollection) {
3442
- // Loop through collection and setup event listeners
3976
+ // Loop through collection and setup event listeners.
3443
3977
  this.collection.forEach(popover => {
3444
3978
  registerEventListeners.call(this, popover);
3445
3979
  });
3446
- } // Add keydown global event listener
3980
+ } // Add keydown global event listener.
3447
3981
 
3448
3982
 
3449
- document.addEventListener('keydown', this.__handlerKeydown, false);
3983
+ document.addEventListener('keydown', _classPrivateFieldLooseBase(this, _handleKeydown)[_handleKeydown], false);
3450
3984
  }
3451
3985
 
3452
3986
  destroyEventListeners(processCollection = true) {
3453
3987
  if (processCollection) {
3454
- // Loop through collection and remove event listeners
3988
+ // Loop through collection and remove event listeners.
3455
3989
  this.collection.forEach(popover => {
3456
3990
  deregisterEventListeners(popover);
3457
3991
  });
3458
- } // Remove keydown global event listener
3992
+ } // Remove keydown global event listener.
3459
3993
 
3460
3994
 
3461
- document.removeEventListener('keydown', this.__handlerKeydown, false);
3995
+ document.removeEventListener('keydown', _classPrivateFieldLooseBase(this, _handleKeydown)[_handleKeydown], false);
3462
3996
  }
3463
- /**
3464
- * Register popover functionality
3465
- */
3466
-
3467
3997
 
3468
3998
  register(query) {
3469
3999
  const els = getPopoverElements.call(this, query);
3470
- if (!els) return false;
3471
- return register.call(this, els.trigger, els.target);
4000
+ if (els.error) return Promise.reject(els.error);
4001
+ return register.call(this, els.popover, els.trigger);
3472
4002
  }
3473
4003
 
3474
4004
  deregister(query) {
3475
- const popover = this.get(getPopoverID(query));
3476
- if (!popover) return false;
4005
+ const popover = this.get(getPopoverID.call(this, query));
3477
4006
  return deregister.call(this, popover);
3478
4007
  }
3479
- /**
3480
- * Change state functionality
3481
- */
3482
-
3483
4008
 
3484
4009
  open(id) {
3485
- const popover = this.get(id);
3486
- if (!popover) return false;
3487
- return popover.open();
4010
+ return open.call(this, id);
3488
4011
  }
3489
4012
 
3490
4013
  close(id) {
3491
- if (id) {
3492
- const popover = this.get(id);
3493
- if (!popover) return false;
3494
- return popover.close();
3495
- } else {
3496
- return closeAll.call(this);
3497
- }
4014
+ return close.call(this, id);
3498
4015
  }
3499
4016
 
3500
4017
  }