bootstrap 5.0.0 → 5.1.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/assets/javascripts/bootstrap/alert.js +77 -106
  4. data/assets/javascripts/bootstrap/base-component.js +126 -7
  5. data/assets/javascripts/bootstrap/button.js +24 -24
  6. data/assets/javascripts/bootstrap/carousel.js +115 -128
  7. data/assets/javascripts/bootstrap/collapse.js +119 -176
  8. data/assets/javascripts/bootstrap/dom/data.js +2 -2
  9. data/assets/javascripts/bootstrap/dom/event-handler.js +3 -4
  10. data/assets/javascripts/bootstrap/dom/manipulator.js +4 -4
  11. data/assets/javascripts/bootstrap/dom/selector-engine.js +47 -5
  12. data/assets/javascripts/bootstrap/dropdown.js +142 -130
  13. data/assets/javascripts/bootstrap/modal.js +376 -171
  14. data/assets/javascripts/bootstrap/offcanvas.js +328 -133
  15. data/assets/javascripts/bootstrap/popover.js +27 -59
  16. data/assets/javascripts/bootstrap/scrollspy.js +51 -56
  17. data/assets/javascripts/bootstrap/tab.js +39 -66
  18. data/assets/javascripts/bootstrap/toast.js +175 -86
  19. data/assets/javascripts/bootstrap/tooltip.js +141 -185
  20. data/assets/javascripts/bootstrap-sprockets.js +6 -6
  21. data/assets/javascripts/bootstrap.js +1031 -1026
  22. data/assets/javascripts/bootstrap.min.js +2 -2
  23. data/assets/stylesheets/_bootstrap-grid.scss +3 -1
  24. data/assets/stylesheets/_bootstrap-reboot.scss +2 -4
  25. data/assets/stylesheets/_bootstrap.scss +2 -1
  26. data/assets/stylesheets/bootstrap/_card.scss +7 -6
  27. data/assets/stylesheets/bootstrap/_carousel.scss +2 -2
  28. data/assets/stylesheets/bootstrap/_dropdown.scss +4 -4
  29. data/assets/stylesheets/bootstrap/_functions.scss +100 -3
  30. data/assets/stylesheets/bootstrap/_grid.scss +11 -0
  31. data/assets/stylesheets/bootstrap/_helpers.scss +2 -0
  32. data/assets/stylesheets/bootstrap/_images.scss +1 -1
  33. data/assets/stylesheets/bootstrap/_list-group.scss +5 -5
  34. data/assets/stylesheets/bootstrap/_mixins.scss +1 -0
  35. data/assets/stylesheets/bootstrap/_modal.scss +7 -26
  36. data/assets/stylesheets/bootstrap/_navbar.scss +30 -1
  37. data/assets/stylesheets/bootstrap/_offcanvas.scss +8 -2
  38. data/assets/stylesheets/bootstrap/_placeholders.scss +51 -0
  39. data/assets/stylesheets/bootstrap/_popover.scss +10 -10
  40. data/assets/stylesheets/bootstrap/_reboot.scss +12 -8
  41. data/assets/stylesheets/bootstrap/_root.scss +40 -2
  42. data/assets/stylesheets/bootstrap/_tables.scss +1 -0
  43. data/assets/stylesheets/bootstrap/_toasts.scss +3 -3
  44. data/assets/stylesheets/bootstrap/_tooltip.scss +4 -4
  45. data/assets/stylesheets/bootstrap/_transitions.scss +6 -0
  46. data/assets/stylesheets/bootstrap/_utilities.scss +44 -8
  47. data/assets/stylesheets/bootstrap/_variables.scss +200 -25
  48. data/assets/stylesheets/bootstrap/bootstrap-utilities.scss +1 -1
  49. data/assets/stylesheets/bootstrap/forms/_floating-labels.scss +3 -1
  50. data/assets/stylesheets/bootstrap/forms/_form-check.scss +1 -1
  51. data/assets/stylesheets/bootstrap/forms/_form-control.scss +6 -6
  52. data/assets/stylesheets/bootstrap/forms/_form-range.scss +1 -1
  53. data/assets/stylesheets/bootstrap/forms/_form-select.scss +3 -0
  54. data/assets/stylesheets/bootstrap/helpers/_stacks.scss +15 -0
  55. data/assets/stylesheets/bootstrap/helpers/_vr.scss +8 -0
  56. data/assets/stylesheets/bootstrap/mixins/_backdrop.scss +14 -0
  57. data/assets/stylesheets/bootstrap/mixins/_buttons.scss +1 -1
  58. data/assets/stylesheets/bootstrap/mixins/_forms.scss +8 -1
  59. data/assets/stylesheets/bootstrap/mixins/_grid.scss +33 -8
  60. data/assets/stylesheets/bootstrap/mixins/_utilities.scss +27 -6
  61. data/assets/stylesheets/bootstrap/vendor/_rfs.scss +55 -13
  62. data/bootstrap.gemspec +3 -3
  63. data/lib/bootstrap/version.rb +2 -2
  64. data/tasks/updater/js.rb +6 -2
  65. metadata +12 -8
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Bootstrap modal.js v5.0.0 (https://getbootstrap.com/)
2
+ * Bootstrap modal.js v5.1.1 (https://getbootstrap.com/)
3
3
  * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
5
  */
@@ -18,7 +18,7 @@
18
18
 
19
19
  /**
20
20
  * --------------------------------------------------------------------------
21
- * Bootstrap (v5.0.0): util/index.js
21
+ * Bootstrap (v5.1.1): util/index.js
22
22
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
23
23
  * --------------------------------------------------------------------------
24
24
  */
@@ -89,24 +89,29 @@
89
89
  element.dispatchEvent(new Event(TRANSITION_END));
90
90
  };
91
91
 
92
- const isElement = obj => (obj[0] || obj).nodeType;
92
+ const isElement = obj => {
93
+ if (!obj || typeof obj !== 'object') {
94
+ return false;
95
+ }
93
96
 
94
- const emulateTransitionEnd = (element, duration) => {
95
- let called = false;
96
- const durationPadding = 5;
97
- const emulatedDuration = duration + durationPadding;
97
+ if (typeof obj.jquery !== 'undefined') {
98
+ obj = obj[0];
99
+ }
98
100
 
99
- function listener() {
100
- called = true;
101
- element.removeEventListener(TRANSITION_END, listener);
101
+ return typeof obj.nodeType !== 'undefined';
102
+ };
103
+
104
+ const getElement = obj => {
105
+ if (isElement(obj)) {
106
+ // it's a jQuery object or a node element
107
+ return obj.jquery ? obj[0] : obj;
102
108
  }
103
109
 
104
- element.addEventListener(TRANSITION_END, listener);
105
- setTimeout(() => {
106
- if (!called) {
107
- triggerTransitionEnd(element);
108
- }
109
- }, emulatedDuration);
110
+ if (typeof obj === 'string' && obj.length > 0) {
111
+ return document.querySelector(obj);
112
+ }
113
+
114
+ return null;
110
115
  };
111
116
 
112
117
  const typeCheckConfig = (componentName, config, configTypes) => {
@@ -122,20 +127,42 @@
122
127
  };
123
128
 
124
129
  const isVisible = element => {
125
- if (!element) {
130
+ if (!isElement(element) || element.getClientRects().length === 0) {
126
131
  return false;
127
132
  }
128
133
 
129
- if (element.style && element.parentNode && element.parentNode.style) {
130
- const elementStyle = getComputedStyle(element);
131
- const parentNodeStyle = getComputedStyle(element.parentNode);
132
- return elementStyle.display !== 'none' && parentNodeStyle.display !== 'none' && elementStyle.visibility !== 'hidden';
134
+ return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
135
+ };
136
+
137
+ const isDisabled = element => {
138
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
139
+ return true;
140
+ }
141
+
142
+ if (element.classList.contains('disabled')) {
143
+ return true;
133
144
  }
134
145
 
135
- return false;
146
+ if (typeof element.disabled !== 'undefined') {
147
+ return element.disabled;
148
+ }
149
+
150
+ return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
136
151
  };
152
+ /**
153
+ * Trick to restart an element's animation
154
+ *
155
+ * @param {HTMLElement} element
156
+ * @return void
157
+ *
158
+ * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
159
+ */
160
+
137
161
 
138
- const reflow = element => element.offsetHeight;
162
+ const reflow = element => {
163
+ // eslint-disable-next-line no-unused-expressions
164
+ element.offsetHeight;
165
+ };
139
166
 
140
167
  const getjQuery = () => {
141
168
  const {
@@ -149,9 +176,18 @@
149
176
  return null;
150
177
  };
151
178
 
179
+ const DOMContentLoadedCallbacks = [];
180
+
152
181
  const onDOMContentLoaded = callback => {
153
182
  if (document.readyState === 'loading') {
154
- document.addEventListener('DOMContentLoaded', callback);
183
+ // add listener on the first call when the document is in loading state
184
+ if (!DOMContentLoadedCallbacks.length) {
185
+ document.addEventListener('DOMContentLoaded', () => {
186
+ DOMContentLoadedCallbacks.forEach(callback => callback());
187
+ });
188
+ }
189
+
190
+ DOMContentLoadedCallbacks.push(callback);
155
191
  } else {
156
192
  callback();
157
193
  }
@@ -159,12 +195,13 @@
159
195
 
160
196
  const isRTL = () => document.documentElement.dir === 'rtl';
161
197
 
162
- const defineJQueryPlugin = (name, plugin) => {
198
+ const defineJQueryPlugin = plugin => {
163
199
  onDOMContentLoaded(() => {
164
200
  const $ = getjQuery();
165
201
  /* istanbul ignore if */
166
202
 
167
203
  if ($) {
204
+ const name = plugin.NAME;
168
205
  const JQUERY_NO_CONFLICT = $.fn[name];
169
206
  $.fn[name] = plugin.jQueryInterface;
170
207
  $.fn[name].Constructor = plugin;
@@ -183,105 +220,166 @@
183
220
  }
184
221
  };
185
222
 
223
+ const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
224
+ if (!waitForTransition) {
225
+ execute(callback);
226
+ return;
227
+ }
228
+
229
+ const durationPadding = 5;
230
+ const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
231
+ let called = false;
232
+
233
+ const handler = ({
234
+ target
235
+ }) => {
236
+ if (target !== transitionElement) {
237
+ return;
238
+ }
239
+
240
+ called = true;
241
+ transitionElement.removeEventListener(TRANSITION_END, handler);
242
+ execute(callback);
243
+ };
244
+
245
+ transitionElement.addEventListener(TRANSITION_END, handler);
246
+ setTimeout(() => {
247
+ if (!called) {
248
+ triggerTransitionEnd(transitionElement);
249
+ }
250
+ }, emulatedDuration);
251
+ };
252
+
186
253
  /**
187
254
  * --------------------------------------------------------------------------
188
- * Bootstrap (v5.0.0): util/scrollBar.js
255
+ * Bootstrap (v5.1.1): util/scrollBar.js
189
256
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
190
257
  * --------------------------------------------------------------------------
191
258
  */
192
259
  const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';
193
260
  const SELECTOR_STICKY_CONTENT = '.sticky-top';
194
261
 
195
- const getWidth = () => {
196
- // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
197
- const documentWidth = document.documentElement.clientWidth;
198
- return Math.abs(window.innerWidth - documentWidth);
199
- };
262
+ class ScrollBarHelper {
263
+ constructor() {
264
+ this._element = document.body;
265
+ }
200
266
 
201
- const hide = (width = getWidth()) => {
202
- _disableOverFlow(); // give padding to element to balances the hidden scrollbar width
267
+ getWidth() {
268
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
269
+ const documentWidth = document.documentElement.clientWidth;
270
+ return Math.abs(window.innerWidth - documentWidth);
271
+ }
203
272
 
273
+ hide() {
274
+ const width = this.getWidth();
204
275
 
205
- _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements, to keep shown fullwidth
276
+ this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width
206
277
 
207
278
 
208
- _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width);
279
+ this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
209
280
 
210
- _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width);
211
- };
212
281
 
213
- const _disableOverFlow = () => {
214
- const actualValue = document.body.style.overflow;
282
+ this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width);
215
283
 
216
- if (actualValue) {
217
- Manipulator__default['default'].setDataAttribute(document.body, 'overflow', actualValue);
284
+ this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width);
218
285
  }
219
286
 
220
- document.body.style.overflow = 'hidden';
221
- };
287
+ _disableOverFlow() {
288
+ this._saveInitialAttribute(this._element, 'overflow');
222
289
 
223
- const _setElementAttributes = (selector, styleProp, callback) => {
224
- const scrollbarWidth = getWidth();
225
- SelectorEngine__default['default'].find(selector).forEach(element => {
226
- if (element !== document.body && window.innerWidth > element.clientWidth + scrollbarWidth) {
227
- return;
228
- }
290
+ this._element.style.overflow = 'hidden';
291
+ }
229
292
 
230
- const actualValue = element.style[styleProp];
231
- const calculatedValue = window.getComputedStyle(element)[styleProp];
232
- Manipulator__default['default'].setDataAttribute(element, styleProp, actualValue);
233
- element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`;
234
- });
235
- };
293
+ _setElementAttributes(selector, styleProp, callback) {
294
+ const scrollbarWidth = this.getWidth();
236
295
 
237
- const reset = () => {
238
- _resetElementAttributes('body', 'overflow');
296
+ const manipulationCallBack = element => {
297
+ if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
298
+ return;
299
+ }
239
300
 
240
- _resetElementAttributes('body', 'paddingRight');
301
+ this._saveInitialAttribute(element, styleProp);
241
302
 
242
- _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight');
303
+ const calculatedValue = window.getComputedStyle(element)[styleProp];
304
+ element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`;
305
+ };
243
306
 
244
- _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight');
245
- };
307
+ this._applyManipulationCallback(selector, manipulationCallBack);
308
+ }
309
+
310
+ reset() {
311
+ this._resetElementAttributes(this._element, 'overflow');
312
+
313
+ this._resetElementAttributes(this._element, 'paddingRight');
246
314
 
247
- const _resetElementAttributes = (selector, styleProp) => {
248
- SelectorEngine__default['default'].find(selector).forEach(element => {
249
- const value = Manipulator__default['default'].getDataAttribute(element, styleProp);
315
+ this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight');
316
+
317
+ this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight');
318
+ }
319
+
320
+ _saveInitialAttribute(element, styleProp) {
321
+ const actualValue = element.style[styleProp];
322
+
323
+ if (actualValue) {
324
+ Manipulator__default['default'].setDataAttribute(element, styleProp, actualValue);
325
+ }
326
+ }
327
+
328
+ _resetElementAttributes(selector, styleProp) {
329
+ const manipulationCallBack = element => {
330
+ const value = Manipulator__default['default'].getDataAttribute(element, styleProp);
331
+
332
+ if (typeof value === 'undefined') {
333
+ element.style.removeProperty(styleProp);
334
+ } else {
335
+ Manipulator__default['default'].removeDataAttribute(element, styleProp);
336
+ element.style[styleProp] = value;
337
+ }
338
+ };
339
+
340
+ this._applyManipulationCallback(selector, manipulationCallBack);
341
+ }
250
342
 
251
- if (typeof value === 'undefined') {
252
- element.style.removeProperty(styleProp);
343
+ _applyManipulationCallback(selector, callBack) {
344
+ if (isElement(selector)) {
345
+ callBack(selector);
253
346
  } else {
254
- Manipulator__default['default'].removeDataAttribute(element, styleProp);
255
- element.style[styleProp] = value;
347
+ SelectorEngine__default['default'].find(selector, this._element).forEach(callBack);
256
348
  }
257
- });
258
- };
349
+ }
350
+
351
+ isOverflowing() {
352
+ return this.getWidth() > 0;
353
+ }
354
+
355
+ }
259
356
 
260
357
  /**
261
358
  * --------------------------------------------------------------------------
262
- * Bootstrap (v5.0.0): util/backdrop.js
359
+ * Bootstrap (v5.1.1): util/backdrop.js
263
360
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
264
361
  * --------------------------------------------------------------------------
265
362
  */
266
- const Default$1 = {
363
+ const Default$2 = {
364
+ className: 'modal-backdrop',
267
365
  isVisible: true,
268
366
  // if false, we use the backdrop helper without adding any element to the dom
269
367
  isAnimated: false,
270
- rootElement: document.body,
368
+ rootElement: 'body',
271
369
  // give the choice to place backdrop under different elements
272
370
  clickCallback: null
273
371
  };
274
- const DefaultType$1 = {
372
+ const DefaultType$2 = {
373
+ className: 'string',
275
374
  isVisible: 'boolean',
276
375
  isAnimated: 'boolean',
277
- rootElement: 'element',
376
+ rootElement: '(element|string)',
278
377
  clickCallback: '(function|null)'
279
378
  };
280
- const NAME$1 = 'backdrop';
281
- const CLASS_NAME_BACKDROP = 'modal-backdrop';
379
+ const NAME$2 = 'backdrop';
282
380
  const CLASS_NAME_FADE$1 = 'fade';
283
381
  const CLASS_NAME_SHOW$1 = 'show';
284
- const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$1}`;
382
+ const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$2}`;
285
383
 
286
384
  class Backdrop {
287
385
  constructor(config) {
@@ -327,7 +425,7 @@
327
425
  _getElement() {
328
426
  if (!this._element) {
329
427
  const backdrop = document.createElement('div');
330
- backdrop.className = CLASS_NAME_BACKDROP;
428
+ backdrop.className = this._config.className;
331
429
 
332
430
  if (this._config.isAnimated) {
333
431
  backdrop.classList.add(CLASS_NAME_FADE$1);
@@ -340,10 +438,12 @@
340
438
  }
341
439
 
342
440
  _getConfig(config) {
343
- config = { ...Default$1,
441
+ config = { ...Default$2,
344
442
  ...(typeof config === 'object' ? config : {})
345
- };
346
- typeCheckConfig(NAME$1, config, DefaultType$1);
443
+ }; // use getElement() with the default "body" to get a fresh Element on each instantiation
444
+
445
+ config.rootElement = getElement(config.rootElement);
446
+ typeCheckConfig(NAME$2, config, DefaultType$2);
347
447
  return config;
348
448
  }
349
449
 
@@ -352,7 +452,7 @@
352
452
  return;
353
453
  }
354
454
 
355
- this._config.rootElement.appendChild(this._getElement());
455
+ this._config.rootElement.append(this._getElement());
356
456
 
357
457
  EventHandler__default['default'].on(this._getElement(), EVENT_MOUSEDOWN, () => {
358
458
  execute(this._config.clickCallback);
@@ -367,27 +467,149 @@
367
467
 
368
468
  EventHandler__default['default'].off(this._element, EVENT_MOUSEDOWN);
369
469
 
370
- this._getElement().parentNode.removeChild(this._element);
470
+ this._element.remove();
371
471
 
372
472
  this._isAppended = false;
373
473
  }
374
474
 
375
475
  _emulateAnimation(callback) {
376
- if (!this._config.isAnimated) {
377
- execute(callback);
476
+ executeAfterTransition(callback, this._getElement(), this._config.isAnimated);
477
+ }
478
+
479
+ }
480
+
481
+ /**
482
+ * --------------------------------------------------------------------------
483
+ * Bootstrap (v5.1.1): util/focustrap.js
484
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
485
+ * --------------------------------------------------------------------------
486
+ */
487
+ const Default$1 = {
488
+ trapElement: null,
489
+ // The element to trap focus inside of
490
+ autofocus: true
491
+ };
492
+ const DefaultType$1 = {
493
+ trapElement: 'element',
494
+ autofocus: 'boolean'
495
+ };
496
+ const NAME$1 = 'focustrap';
497
+ const DATA_KEY$1 = 'bs.focustrap';
498
+ const EVENT_KEY$1 = `.${DATA_KEY$1}`;
499
+ const EVENT_FOCUSIN = `focusin${EVENT_KEY$1}`;
500
+ const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$1}`;
501
+ const TAB_KEY = 'Tab';
502
+ const TAB_NAV_FORWARD = 'forward';
503
+ const TAB_NAV_BACKWARD = 'backward';
504
+
505
+ class FocusTrap {
506
+ constructor(config) {
507
+ this._config = this._getConfig(config);
508
+ this._isActive = false;
509
+ this._lastTabNavDirection = null;
510
+ }
511
+
512
+ activate() {
513
+ const {
514
+ trapElement,
515
+ autofocus
516
+ } = this._config;
517
+
518
+ if (this._isActive) {
519
+ return;
520
+ }
521
+
522
+ if (autofocus) {
523
+ trapElement.focus();
524
+ }
525
+
526
+ EventHandler__default['default'].off(document, EVENT_KEY$1); // guard against infinite focus loop
527
+
528
+ EventHandler__default['default'].on(document, EVENT_FOCUSIN, event => this._handleFocusin(event));
529
+ EventHandler__default['default'].on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));
530
+ this._isActive = true;
531
+ }
532
+
533
+ deactivate() {
534
+ if (!this._isActive) {
378
535
  return;
379
536
  }
380
537
 
381
- const backdropTransitionDuration = getTransitionDurationFromElement(this._getElement());
382
- EventHandler__default['default'].one(this._getElement(), 'transitionend', () => execute(callback));
383
- emulateTransitionEnd(this._getElement(), backdropTransitionDuration);
538
+ this._isActive = false;
539
+ EventHandler__default['default'].off(document, EVENT_KEY$1);
540
+ } // Private
541
+
542
+
543
+ _handleFocusin(event) {
544
+ const {
545
+ target
546
+ } = event;
547
+ const {
548
+ trapElement
549
+ } = this._config;
550
+
551
+ if (target === document || target === trapElement || trapElement.contains(target)) {
552
+ return;
553
+ }
554
+
555
+ const elements = SelectorEngine__default['default'].focusableChildren(trapElement);
556
+
557
+ if (elements.length === 0) {
558
+ trapElement.focus();
559
+ } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {
560
+ elements[elements.length - 1].focus();
561
+ } else {
562
+ elements[0].focus();
563
+ }
564
+ }
565
+
566
+ _handleKeydown(event) {
567
+ if (event.key !== TAB_KEY) {
568
+ return;
569
+ }
570
+
571
+ this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;
572
+ }
573
+
574
+ _getConfig(config) {
575
+ config = { ...Default$1,
576
+ ...(typeof config === 'object' ? config : {})
577
+ };
578
+ typeCheckConfig(NAME$1, config, DefaultType$1);
579
+ return config;
384
580
  }
385
581
 
386
582
  }
387
583
 
388
584
  /**
389
585
  * --------------------------------------------------------------------------
390
- * Bootstrap (v5.0.0): modal.js
586
+ * Bootstrap (v5.1.1): util/component-functions.js
587
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
588
+ * --------------------------------------------------------------------------
589
+ */
590
+
591
+ const enableDismissTrigger = (component, method = 'hide') => {
592
+ const clickEvent = `click.dismiss${component.EVENT_KEY}`;
593
+ const name = component.NAME;
594
+ EventHandler__default['default'].on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
595
+ if (['A', 'AREA'].includes(this.tagName)) {
596
+ event.preventDefault();
597
+ }
598
+
599
+ if (isDisabled(this)) {
600
+ return;
601
+ }
602
+
603
+ const target = getElementFromSelector(this) || this.closest(`.${name}`);
604
+ const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
605
+
606
+ instance[method]();
607
+ });
608
+ };
609
+
610
+ /**
611
+ * --------------------------------------------------------------------------
612
+ * Bootstrap (v5.1.1): modal.js
391
613
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
392
614
  * --------------------------------------------------------------------------
393
615
  */
@@ -417,7 +639,6 @@
417
639
  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
418
640
  const EVENT_SHOW = `show${EVENT_KEY}`;
419
641
  const EVENT_SHOWN = `shown${EVENT_KEY}`;
420
- const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;
421
642
  const EVENT_RESIZE = `resize${EVENT_KEY}`;
422
643
  const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
423
644
  const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
@@ -428,10 +649,10 @@
428
649
  const CLASS_NAME_FADE = 'fade';
429
650
  const CLASS_NAME_SHOW = 'show';
430
651
  const CLASS_NAME_STATIC = 'modal-static';
652
+ const OPEN_SELECTOR = '.modal.show';
431
653
  const SELECTOR_DIALOG = '.modal-dialog';
432
654
  const SELECTOR_MODAL_BODY = '.modal-body';
433
655
  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]';
434
- const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="modal"]';
435
656
  /**
436
657
  * ------------------------------------------------------------------------
437
658
  * Class Definition
@@ -444,9 +665,11 @@
444
665
  this._config = this._getConfig(config);
445
666
  this._dialog = SelectorEngine__default['default'].findOne(SELECTOR_DIALOG, this._element);
446
667
  this._backdrop = this._initializeBackDrop();
668
+ this._focustrap = this._initializeFocusTrap();
447
669
  this._isShown = false;
448
670
  this._ignoreBackdropClick = false;
449
671
  this._isTransitioning = false;
672
+ this._scrollBar = new ScrollBarHelper();
450
673
  } // Getters
451
674
 
452
675
 
@@ -454,8 +677,8 @@
454
677
  return Default;
455
678
  }
456
679
 
457
- static get DATA_KEY() {
458
- return DATA_KEY;
680
+ static get NAME() {
681
+ return NAME;
459
682
  } // Public
460
683
 
461
684
 
@@ -468,20 +691,22 @@
468
691
  return;
469
692
  }
470
693
 
471
- if (this._isAnimated()) {
472
- this._isTransitioning = true;
473
- }
474
-
475
694
  const showEvent = EventHandler__default['default'].trigger(this._element, EVENT_SHOW, {
476
695
  relatedTarget
477
696
  });
478
697
 
479
- if (this._isShown || showEvent.defaultPrevented) {
698
+ if (showEvent.defaultPrevented) {
480
699
  return;
481
700
  }
482
701
 
483
702
  this._isShown = true;
484
- hide();
703
+
704
+ if (this._isAnimated()) {
705
+ this._isTransitioning = true;
706
+ }
707
+
708
+ this._scrollBar.hide();
709
+
485
710
  document.body.classList.add(CLASS_NAME_OPEN);
486
711
 
487
712
  this._adjustDialog();
@@ -490,7 +715,6 @@
490
715
 
491
716
  this._setResizeEvent();
492
717
 
493
- EventHandler__default['default'].on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, event => this.hide(event));
494
718
  EventHandler__default['default'].on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
495
719
  EventHandler__default['default'].one(this._element, EVENT_MOUSEUP_DISMISS, event => {
496
720
  if (event.target === this._element) {
@@ -502,11 +726,7 @@
502
726
  this._showBackdrop(() => this._showElement(relatedTarget));
503
727
  }
504
728
 
505
- hide(event) {
506
- if (event) {
507
- event.preventDefault();
508
- }
509
-
729
+ hide() {
510
730
  if (!this._isShown || this._isTransitioning) {
511
731
  return;
512
732
  }
@@ -529,41 +749,24 @@
529
749
 
530
750
  this._setResizeEvent();
531
751
 
532
- EventHandler__default['default'].off(document, EVENT_FOCUSIN);
752
+ this._focustrap.deactivate();
533
753
 
534
754
  this._element.classList.remove(CLASS_NAME_SHOW);
535
755
 
536
756
  EventHandler__default['default'].off(this._element, EVENT_CLICK_DISMISS);
537
757
  EventHandler__default['default'].off(this._dialog, EVENT_MOUSEDOWN_DISMISS);
538
758
 
539
- if (isAnimated) {
540
- const transitionDuration = getTransitionDurationFromElement(this._element);
541
- EventHandler__default['default'].one(this._element, 'transitionend', event => this._hideModal(event));
542
- emulateTransitionEnd(this._element, transitionDuration);
543
- } else {
544
- this._hideModal();
545
- }
759
+ this._queueCallback(() => this._hideModal(), this._element, isAnimated);
546
760
  }
547
761
 
548
762
  dispose() {
549
763
  [window, this._dialog].forEach(htmlElement => EventHandler__default['default'].off(htmlElement, EVENT_KEY));
550
- super.dispose();
551
- /**
552
- * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API`
553
- * Do not move `document` in `htmlElements` array
554
- * It will remove `EVENT_CLICK_DATA_API` event that should remain
555
- */
556
-
557
- EventHandler__default['default'].off(document, EVENT_FOCUSIN);
558
- this._config = null;
559
- this._dialog = null;
560
764
 
561
765
  this._backdrop.dispose();
562
766
 
563
- this._backdrop = null;
564
- this._isShown = null;
565
- this._ignoreBackdropClick = null;
566
- this._isTransitioning = null;
767
+ this._focustrap.deactivate();
768
+
769
+ super.dispose();
567
770
  }
568
771
 
569
772
  handleUpdate() {
@@ -579,10 +782,16 @@
579
782
  });
580
783
  }
581
784
 
785
+ _initializeFocusTrap() {
786
+ return new FocusTrap({
787
+ trapElement: this._element
788
+ });
789
+ }
790
+
582
791
  _getConfig(config) {
583
792
  config = { ...Default,
584
793
  ...Manipulator__default['default'].getDataAttributes(this._element),
585
- ...config
794
+ ...(typeof config === 'object' ? config : {})
586
795
  };
587
796
  typeCheckConfig(NAME, config, DefaultType);
588
797
  return config;
@@ -595,7 +804,7 @@
595
804
 
596
805
  if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
597
806
  // Don't move modal's DOM position
598
- document.body.appendChild(this._element);
807
+ document.body.append(this._element);
599
808
  }
600
809
 
601
810
  this._element.style.display = 'block';
@@ -618,13 +827,9 @@
618
827
 
619
828
  this._element.classList.add(CLASS_NAME_SHOW);
620
829
 
621
- if (this._config.focus) {
622
- this._enforceFocus();
623
- }
624
-
625
830
  const transitionComplete = () => {
626
831
  if (this._config.focus) {
627
- this._element.focus();
832
+ this._focustrap.activate();
628
833
  }
629
834
 
630
835
  this._isTransitioning = false;
@@ -633,23 +838,7 @@
633
838
  });
634
839
  };
635
840
 
636
- if (isAnimated) {
637
- const transitionDuration = getTransitionDurationFromElement(this._dialog);
638
- EventHandler__default['default'].one(this._dialog, 'transitionend', transitionComplete);
639
- emulateTransitionEnd(this._dialog, transitionDuration);
640
- } else {
641
- transitionComplete();
642
- }
643
- }
644
-
645
- _enforceFocus() {
646
- EventHandler__default['default'].off(document, EVENT_FOCUSIN); // guard against infinite focus loop
647
-
648
- EventHandler__default['default'].on(document, EVENT_FOCUSIN, event => {
649
- if (document !== event.target && this._element !== event.target && !this._element.contains(event.target)) {
650
- this._element.focus();
651
- }
652
- });
841
+ this._queueCallback(transitionComplete, this._dialog, isAnimated);
653
842
  }
654
843
 
655
844
  _setEscapeEvent() {
@@ -691,7 +880,8 @@
691
880
 
692
881
  this._resetAdjustments();
693
882
 
694
- reset();
883
+ this._scrollBar.reset();
884
+
695
885
  EventHandler__default['default'].trigger(this._element, EVENT_HIDDEN);
696
886
  });
697
887
  }
@@ -728,27 +918,32 @@
728
918
  return;
729
919
  }
730
920
 
731
- const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
921
+ const {
922
+ classList,
923
+ scrollHeight,
924
+ style
925
+ } = this._element;
926
+ const isModalOverflowing = scrollHeight > document.documentElement.clientHeight; // return if the following background transition hasn't yet completed
927
+
928
+ if (!isModalOverflowing && style.overflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
929
+ return;
930
+ }
732
931
 
733
932
  if (!isModalOverflowing) {
734
- this._element.style.overflowY = 'hidden';
933
+ style.overflowY = 'hidden';
735
934
  }
736
935
 
737
- this._element.classList.add(CLASS_NAME_STATIC);
936
+ classList.add(CLASS_NAME_STATIC);
738
937
 
739
- const modalTransitionDuration = getTransitionDurationFromElement(this._dialog);
740
- EventHandler__default['default'].off(this._element, 'transitionend');
741
- EventHandler__default['default'].one(this._element, 'transitionend', () => {
742
- this._element.classList.remove(CLASS_NAME_STATIC);
938
+ this._queueCallback(() => {
939
+ classList.remove(CLASS_NAME_STATIC);
743
940
 
744
941
  if (!isModalOverflowing) {
745
- EventHandler__default['default'].one(this._element, 'transitionend', () => {
746
- this._element.style.overflowY = '';
747
- });
748
- emulateTransitionEnd(this._element, modalTransitionDuration);
942
+ this._queueCallback(() => {
943
+ style.overflowY = '';
944
+ }, this._dialog);
749
945
  }
750
- });
751
- emulateTransitionEnd(this._element, modalTransitionDuration);
946
+ }, this._dialog);
752
947
 
753
948
  this._element.focus();
754
949
  } // ----------------------------------------------------------------------
@@ -758,7 +953,9 @@
758
953
 
759
954
  _adjustDialog() {
760
955
  const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
761
- const scrollbarWidth = getWidth();
956
+
957
+ const scrollbarWidth = this._scrollBar.getWidth();
958
+
762
959
  const isBodyOverflowing = scrollbarWidth > 0;
763
960
 
764
961
  if (!isBodyOverflowing && isModalOverflowing && !isRTL() || isBodyOverflowing && !isModalOverflowing && isRTL()) {
@@ -778,7 +975,7 @@
778
975
 
779
976
  static jQueryInterface(config, relatedTarget) {
780
977
  return this.each(function () {
781
- const data = Modal.getInstance(this) || new Modal(this, typeof config === 'object' ? config : {});
978
+ const data = Modal.getOrCreateInstance(this, config);
782
979
 
783
980
  if (typeof config !== 'string') {
784
981
  return;
@@ -818,10 +1015,18 @@
818
1015
  this.focus();
819
1016
  }
820
1017
  });
821
- });
822
- const data = Modal.getInstance(target) || new Modal(target);
1018
+ }); // avoid conflict when clicking moddal toggler while another one is open
1019
+
1020
+ const allReadyOpen = SelectorEngine__default['default'].findOne(OPEN_SELECTOR);
1021
+
1022
+ if (allReadyOpen) {
1023
+ Modal.getInstance(allReadyOpen).hide();
1024
+ }
1025
+
1026
+ const data = Modal.getOrCreateInstance(target);
823
1027
  data.toggle(this);
824
1028
  });
1029
+ enableDismissTrigger(Modal);
825
1030
  /**
826
1031
  * ------------------------------------------------------------------------
827
1032
  * jQuery
@@ -829,7 +1034,7 @@
829
1034
  * add .Modal to jQuery only if jQuery is present
830
1035
  */
831
1036
 
832
- defineJQueryPlugin(NAME, Modal);
1037
+ defineJQueryPlugin(Modal);
833
1038
 
834
1039
  return Modal;
835
1040