@dso-toolkit/core 52.0.3 → 53.1.0

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 (163) hide show
  1. package/dist/cjs/dso-accordion-section.cjs.entry.js +39 -1404
  2. package/dist/cjs/dso-action-list-item.cjs.entry.js +27 -0
  3. package/dist/cjs/dso-action-list.cjs.entry.js +20 -0
  4. package/dist/cjs/dso-annotation-output.cjs.entry.js +1 -1
  5. package/dist/cjs/dso-card-container.cjs.entry.js +1 -1
  6. package/dist/cjs/dso-date-picker.cjs.entry.js +1 -1
  7. package/dist/cjs/dso-dropdown-menu.cjs.entry.js +18 -3
  8. package/dist/cjs/dso-expandable-heading.cjs.entry.js +6 -2
  9. package/dist/cjs/dso-expandable.cjs.entry.js +1433 -2
  10. package/dist/cjs/dso-header.cjs.entry.js +16 -1
  11. package/dist/cjs/dso-helpcenter-panel.cjs.entry.js +2 -2
  12. package/dist/cjs/dso-image-overlay.cjs.entry.js +2 -2
  13. package/dist/cjs/dso-info_2.cjs.entry.js +3 -2
  14. package/dist/cjs/dso-label.cjs.entry.js +1 -8
  15. package/dist/cjs/dso-list-button.cjs.entry.js +3 -3
  16. package/dist/cjs/dso-modal.cjs.entry.js +2 -2
  17. package/dist/cjs/dso-ozon-content.cjs.entry.js +2 -2
  18. package/dist/cjs/dso-table.cjs.entry.js +2 -2
  19. package/dist/cjs/dso-toolkit.cjs.js +1 -1
  20. package/dist/cjs/dso-tooltip.cjs.entry.js +14 -12
  21. package/dist/cjs/dso-viewer-grid.cjs.entry.js +2 -2
  22. package/dist/cjs/expandable.interfaces-19b608b9.js +7 -0
  23. package/dist/cjs/{focus-trap.esm-c501d382.js → focus-trap.esm-a5b7273f.js} +134 -51
  24. package/dist/cjs/{has-overflow-b1b4f3f3.js → has-overflow-dd552ec8.js} +3 -4
  25. package/dist/cjs/index.cjs.js +67 -0
  26. package/dist/cjs/{index.esm-03a9e0b4.js → index.esm-0e935715.js} +97 -17
  27. package/dist/cjs/loader.cjs.js +1 -1
  28. package/dist/collection/collection-manifest.json +3 -1
  29. package/dist/collection/components/accordion/accordion.js +1 -1
  30. package/dist/collection/components/accordion/components/accordion-section.css +60 -10
  31. package/dist/collection/components/accordion/components/accordion-section.js +41 -100
  32. package/dist/collection/components/action-list/action-list.css +25 -0
  33. package/dist/collection/components/action-list/action-list.js +42 -0
  34. package/dist/collection/components/action-list/components/action-list-item.css +81 -0
  35. package/dist/collection/components/action-list/components/action-list-item.js +120 -0
  36. package/dist/collection/components/annotation-output/annotation-output.js +1 -1
  37. package/dist/collection/components/card-container/card-container.css +15 -1
  38. package/dist/collection/components/date-picker/date-picker.css +1 -1
  39. package/dist/collection/components/date-picker/date-picker.js +1 -1
  40. package/dist/collection/components/dropdown-menu/dropdown-menu.js +36 -1
  41. package/dist/collection/components/expandable/expandable.css +12 -1
  42. package/dist/collection/components/expandable/expandable.interfaces.js +3 -0
  43. package/dist/collection/components/expandable/expandable.js +246 -2
  44. package/dist/collection/components/expandable-heading/expandable-heading.css +34 -0
  45. package/dist/collection/components/expandable-heading/expandable-heading.js +28 -2
  46. package/dist/collection/components/header/header.js +18 -2
  47. package/dist/collection/components/label/label.js +1 -8
  48. package/dist/collection/components/list-button/list-button.css +2 -2
  49. package/dist/collection/components/modal/modal-ref.js +18 -0
  50. package/dist/collection/components/modal/modal.controller.js +47 -0
  51. package/dist/collection/components/modal/modal.js +3 -7
  52. package/dist/collection/components/ozon-content/ozon-content.css +8 -0
  53. package/dist/collection/components/selectable/selectable.css +22 -9
  54. package/dist/collection/components/selectable/selectable.js +2 -1
  55. package/dist/collection/components/tooltip/tooltip.js +13 -11
  56. package/dist/collection/index.js +2 -0
  57. package/dist/components/dropdown-menu.js +17 -1
  58. package/dist/components/dso-accordion-section.js +52 -1407
  59. package/dist/components/dso-action-list-item.d.ts +11 -0
  60. package/dist/components/dso-action-list-item.js +53 -0
  61. package/dist/components/dso-action-list.d.ts +11 -0
  62. package/dist/components/dso-action-list.js +36 -0
  63. package/dist/components/dso-annotation-output.js +1 -1
  64. package/dist/components/dso-card-container.js +1 -1
  65. package/dist/components/dso-date-picker.js +2 -2
  66. package/dist/components/dso-expandable-heading.js +9 -4
  67. package/dist/components/dso-header.js +18 -2
  68. package/dist/components/dso-label.js +1 -8
  69. package/dist/components/dso-list-button.js +1 -1
  70. package/dist/components/dso-ozon-content.js +1 -1
  71. package/dist/components/expandable.js +1440 -4
  72. package/dist/components/focus-trap.esm.js +133 -50
  73. package/dist/components/has-overflow.js +3 -4
  74. package/dist/components/index.d.ts +2 -0
  75. package/dist/components/index.esm.js +97 -17
  76. package/dist/components/index.js +71 -1
  77. package/dist/components/selectable.js +3 -2
  78. package/dist/components/tooltip.js +13 -11
  79. package/dist/dso-toolkit/dso-toolkit.esm.js +1 -1
  80. package/dist/dso-toolkit/index.esm.js +1 -1
  81. package/dist/dso-toolkit/{p-e4f667b3.entry.js → p-0b6fa7d3.entry.js} +1 -1
  82. package/dist/dso-toolkit/p-36224d6f.entry.js +1 -0
  83. package/dist/dso-toolkit/{p-9b07b034.entry.js → p-398a8e0b.entry.js} +1 -1
  84. package/dist/dso-toolkit/p-3b91c3e9.entry.js +1 -0
  85. package/dist/dso-toolkit/p-452b1234.js +1 -0
  86. package/dist/dso-toolkit/p-5950644a.js +5 -0
  87. package/dist/dso-toolkit/p-5de8b79a.entry.js +1 -0
  88. package/dist/dso-toolkit/p-5e50b616.entry.js +1 -0
  89. package/dist/dso-toolkit/p-69f37ab3.entry.js +1 -0
  90. package/dist/dso-toolkit/p-6a99d7f8.entry.js +1 -0
  91. package/dist/dso-toolkit/p-8a77030b.entry.js +1 -0
  92. package/dist/dso-toolkit/p-91963e3d.js +5 -0
  93. package/dist/dso-toolkit/p-96f44d35.entry.js +1 -0
  94. package/dist/dso-toolkit/p-975c172a.entry.js +1 -0
  95. package/dist/dso-toolkit/{p-092dde2f.entry.js → p-a1616935.entry.js} +1 -1
  96. package/dist/dso-toolkit/{p-ba330644.entry.js → p-bb90ea4c.entry.js} +1 -1
  97. package/dist/dso-toolkit/p-bf750b97.js +1 -0
  98. package/dist/dso-toolkit/{p-aab458c4.entry.js → p-c86a5bcb.entry.js} +1 -1
  99. package/dist/dso-toolkit/{p-f2b76233.entry.js → p-cf9b79df.entry.js} +1 -1
  100. package/dist/dso-toolkit/{p-43f3d736.entry.js → p-d10ec2b3.entry.js} +1 -1
  101. package/dist/dso-toolkit/p-e8a6ccf5.entry.js +1 -0
  102. package/dist/dso-toolkit/p-eaae698e.entry.js +1 -0
  103. package/dist/dso-toolkit/p-fa2f1a1c.entry.js +1 -0
  104. package/dist/dso-toolkit/p-fe7ca25f.entry.js +1 -0
  105. package/dist/esm/dso-accordion-section.entry.js +39 -1404
  106. package/dist/esm/dso-action-list-item.entry.js +23 -0
  107. package/dist/esm/dso-action-list.entry.js +16 -0
  108. package/dist/esm/dso-annotation-output.entry.js +1 -1
  109. package/dist/esm/dso-card-container.entry.js +1 -1
  110. package/dist/esm/dso-date-picker.entry.js +1 -1
  111. package/dist/esm/dso-dropdown-menu.entry.js +18 -3
  112. package/dist/esm/dso-expandable-heading.entry.js +7 -3
  113. package/dist/esm/dso-expandable.entry.js +1434 -3
  114. package/dist/esm/dso-header.entry.js +16 -1
  115. package/dist/esm/dso-helpcenter-panel.entry.js +2 -2
  116. package/dist/esm/dso-image-overlay.entry.js +2 -2
  117. package/dist/esm/dso-info_2.entry.js +3 -2
  118. package/dist/esm/dso-label.entry.js +1 -8
  119. package/dist/esm/dso-list-button.entry.js +3 -3
  120. package/dist/esm/dso-modal.entry.js +2 -2
  121. package/dist/esm/dso-ozon-content.entry.js +2 -2
  122. package/dist/esm/dso-table.entry.js +2 -2
  123. package/dist/esm/dso-toolkit.js +1 -1
  124. package/dist/esm/dso-tooltip.entry.js +14 -12
  125. package/dist/esm/dso-viewer-grid.entry.js +2 -2
  126. package/dist/esm/expandable.interfaces-9b1afbe8.js +5 -0
  127. package/dist/esm/{focus-trap.esm-94794d92.js → focus-trap.esm-2a49a38f.js} +134 -51
  128. package/dist/esm/{has-overflow-c44a8a0a.js → has-overflow-fdc85d8f.js} +3 -4
  129. package/dist/esm/{index.esm-8fc07ad8.js → index.esm-3d6c8190.js} +97 -17
  130. package/dist/esm/index.js +69 -0
  131. package/dist/esm/loader.js +1 -1
  132. package/dist/types/components/accordion/accordion.interfaces.d.ts +1 -1
  133. package/dist/types/components/accordion/components/accordion-section.d.ts +4 -8
  134. package/dist/types/components/action-list/action-list.d.ts +5 -0
  135. package/dist/types/components/action-list/components/action-list-item.d.ts +15 -0
  136. package/dist/types/components/dropdown-menu/dropdown-menu.d.ts +5 -0
  137. package/dist/types/components/expandable/expandable.d.ts +25 -1
  138. package/dist/types/components/expandable/expandable.interfaces.d.ts +10 -0
  139. package/dist/types/components/expandable-heading/expandable-heading.d.ts +2 -1
  140. package/dist/types/components/expandable-heading/expandable-heading.interfaces.d.ts +1 -0
  141. package/dist/types/components/header/header.d.ts +3 -0
  142. package/dist/types/components/label/label.d.ts +0 -1
  143. package/dist/types/components/modal/modal-ref.d.ts +8 -0
  144. package/dist/types/components/modal/modal.controller.d.ts +6 -0
  145. package/dist/types/components/modal/modal.d.ts +1 -2
  146. package/dist/types/components/modal/modal.interfaces.d.ts +12 -0
  147. package/dist/types/components.d.ts +82 -4
  148. package/dist/types/index.d.ts +2 -0
  149. package/package.json +11 -11
  150. package/dist/dso-toolkit/p-0fcdc369.entry.js +0 -1
  151. package/dist/dso-toolkit/p-147ec7bd.entry.js +0 -1
  152. package/dist/dso-toolkit/p-22f9240a.entry.js +0 -1
  153. package/dist/dso-toolkit/p-3635427a.js +0 -5
  154. package/dist/dso-toolkit/p-3b83e9c6.entry.js +0 -1
  155. package/dist/dso-toolkit/p-452c7fbb.entry.js +0 -1
  156. package/dist/dso-toolkit/p-4ae40ddc.entry.js +0 -1
  157. package/dist/dso-toolkit/p-4c8426b7.entry.js +0 -1
  158. package/dist/dso-toolkit/p-57ceabab.js +0 -5
  159. package/dist/dso-toolkit/p-9984079e.entry.js +0 -1
  160. package/dist/dso-toolkit/p-cece17a5.entry.js +0 -1
  161. package/dist/dso-toolkit/p-d3ed00f6.js +0 -1
  162. package/dist/dso-toolkit/p-e3bd7689.entry.js +0 -1
  163. package/dist/dso-toolkit/p-f3f0d6c9.entry.js +0 -1
@@ -1,7 +1,7 @@
1
- import { t as tabbable, f as focusable, i as isTabbable, a as isFocusable } from './index.esm-8fc07ad8.js';
1
+ import { t as tabbable, f as focusable, i as isTabbable, a as isFocusable } from './index.esm-3d6c8190.js';
2
2
 
3
3
  /*!
4
- * focus-trap 7.1.0
4
+ * focus-trap 7.4.3
5
5
  * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE
6
6
  */
7
7
 
@@ -27,6 +27,7 @@ function _objectSpread2(target) {
27
27
  return target;
28
28
  }
29
29
  function _defineProperty(obj, key, value) {
30
+ key = _toPropertyKey(key);
30
31
  if (key in obj) {
31
32
  Object.defineProperty(obj, key, {
32
33
  value: value,
@@ -39,8 +40,21 @@ function _defineProperty(obj, key, value) {
39
40
  }
40
41
  return obj;
41
42
  }
43
+ function _toPrimitive(input, hint) {
44
+ if (typeof input !== "object" || input === null) return input;
45
+ var prim = input[Symbol.toPrimitive];
46
+ if (prim !== undefined) {
47
+ var res = prim.call(input, hint || "default");
48
+ if (typeof res !== "object") return res;
49
+ throw new TypeError("@@toPrimitive must return a primitive value.");
50
+ }
51
+ return (hint === "string" ? String : Number)(input);
52
+ }
53
+ function _toPropertyKey(arg) {
54
+ var key = _toPrimitive(arg, "string");
55
+ return typeof key === "symbol" ? key : String(key);
56
+ }
42
57
 
43
- var rooTrapStack = [];
44
58
  var activeFocusTraps = {
45
59
  activateTrap: function activateTrap(trapStack, trap) {
46
60
  if (trapStack.length > 0) {
@@ -77,6 +91,16 @@ var isEscapeEvent = function isEscapeEvent(e) {
77
91
  var isTabEvent = function isTabEvent(e) {
78
92
  return e.key === 'Tab' || e.keyCode === 9;
79
93
  };
94
+
95
+ // checks for TAB by default
96
+ var isKeyForward = function isKeyForward(e) {
97
+ return isTabEvent(e) && !e.shiftKey;
98
+ };
99
+
100
+ // checks for SHIFT+TAB by default
101
+ var isKeyBackward = function isKeyBackward(e) {
102
+ return isTabEvent(e) && e.shiftKey;
103
+ };
80
104
  var delay = function delay(fn) {
81
105
  return setTimeout(fn, 0);
82
106
  };
@@ -120,15 +144,21 @@ var getActualTarget = function getActualTarget(event) {
120
144
  // composedPath()[0] === event.target always).
121
145
  return event.target.shadowRoot && typeof event.composedPath === 'function' ? event.composedPath()[0] : event.target;
122
146
  };
147
+
148
+ // NOTE: this must be _outside_ `createFocusTrap()` to make sure all traps in this
149
+ // current instance use the same stack if `userOptions.trapStack` isn't specified
150
+ var internalTrapStack = [];
123
151
  var createFocusTrap = function createFocusTrap(elements, userOptions) {
124
152
  // SSR: a live trap shouldn't be created in this type of environment so this
125
153
  // should be safe code to execute if the `document` option isn't specified
126
154
  var doc = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.document) || document;
127
- var trapStack = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.trapStack) || rooTrapStack;
155
+ var trapStack = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.trapStack) || internalTrapStack;
128
156
  var config = _objectSpread2({
129
157
  returnFocusOnDeactivate: true,
130
158
  escapeDeactivates: true,
131
- delayInitialFocus: true
159
+ delayInitialFocus: true,
160
+ isKeyForward: isKeyForward,
161
+ isKeyBackward: isKeyBackward
132
162
  }, userOptions);
133
163
  var state = {
134
164
  // containers given to createFocusTrap()
@@ -180,23 +210,24 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
180
210
  /**
181
211
  * Finds the index of the container that contains the element.
182
212
  * @param {HTMLElement} element
213
+ * @param {Event} [event]
183
214
  * @returns {number} Index of the container in either `state.containers` or
184
215
  * `state.containerGroups` (the order/length of these lists are the same); -1
185
216
  * if the element isn't found.
186
217
  */
187
- var findContainerIndex = function findContainerIndex(element) {
218
+ var findContainerIndex = function findContainerIndex(element, event) {
219
+ var composedPath = typeof (event === null || event === void 0 ? void 0 : event.composedPath) === 'function' ? event.composedPath() : undefined;
188
220
  // NOTE: search `containerGroups` because it's possible a group contains no tabbable
189
221
  // nodes, but still contains focusable nodes (e.g. if they all have `tabindex=-1`)
190
222
  // and we still need to find the element in there
191
223
  return state.containerGroups.findIndex(function (_ref) {
192
224
  var container = _ref.container,
193
225
  tabbableNodes = _ref.tabbableNodes;
194
- return container.contains(element) ||
195
- // fall back to explicit tabbable search which will take into consideration any
226
+ return container.contains(element) || ( // fall back to explicit tabbable search which will take into consideration any
196
227
  // web components if the `tabbableOptions.getShadowRoot` option was used for
197
228
  // the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't
198
229
  // look inside web components even if open)
199
- tabbableNodes.find(function (node) {
230
+ composedPath === null || composedPath === void 0 ? void 0 : composedPath.includes(container)) || tabbableNodes.find(function (node) {
200
231
  return node === element;
201
232
  });
202
233
  });
@@ -252,8 +283,8 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
252
283
  if (node === false) {
253
284
  return false;
254
285
  }
255
- if (node === undefined) {
256
- // option not specified: use fallback options
286
+ if (node === undefined || !isFocusable(node, config.tabbableOptions)) {
287
+ // option not specified nor focusable: use fallback options
257
288
  if (findContainerIndex(doc.activeElement) >= 0) {
258
289
  node = doc.activeElement;
259
290
  } else {
@@ -357,25 +388,20 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
357
388
  // so that it precedes the focus event.
358
389
  var checkPointerDown = function checkPointerDown(e) {
359
390
  var target = getActualTarget(e);
360
- if (findContainerIndex(target) >= 0) {
391
+ if (findContainerIndex(target, e) >= 0) {
361
392
  // allow the click since it ocurred inside the trap
362
393
  return;
363
394
  }
364
395
  if (valueOrHandler(config.clickOutsideDeactivates, e)) {
365
396
  // immediately deactivate the trap
366
397
  trap.deactivate({
367
- // if, on deactivation, we should return focus to the node originally-focused
368
- // when the trap was activated (or the configured `setReturnFocus` node),
369
- // then assume it's also OK to return focus to the outside node that was
370
- // just clicked, causing deactivation, as long as that node is focusable;
371
- // if it isn't focusable, then return focus to the original node focused
372
- // on activation (or the configured `setReturnFocus` node)
373
398
  // NOTE: by setting `returnFocus: false`, deactivate() will do nothing,
374
399
  // which will result in the outside click setting focus to the node
375
- // that was clicked, whether it's focusable or not; by setting
400
+ // that was clicked (and if not focusable, to "nothing"); by setting
376
401
  // `returnFocus: true`, we'll attempt to re-focus the node originally-focused
377
- // on activation (or the configured `setReturnFocus` node)
378
- returnFocus: config.returnFocusOnDeactivate && !isFocusable(target, config.tabbableOptions)
402
+ // on activation (or the configured `setReturnFocus` node), whether the
403
+ // outside click was on a focusable node or not
404
+ returnFocus: config.returnFocusOnDeactivate
379
405
  });
380
406
  return;
381
407
  }
@@ -395,7 +421,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
395
421
  // In case focus escapes the trap for some strange reason, pull it back in.
396
422
  var checkFocusIn = function checkFocusIn(e) {
397
423
  var target = getActualTarget(e);
398
- var targetContained = findContainerIndex(target) >= 0;
424
+ var targetContained = findContainerIndex(target, e) >= 0;
399
425
 
400
426
  // In Firefox when you Tab out of an iframe the Document is briefly focused.
401
427
  if (targetContained || target instanceof Document) {
@@ -409,31 +435,32 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
409
435
  }
410
436
  };
411
437
 
412
- // Hijack Tab events on the first and last focusable nodes of the trap,
438
+ // Hijack key nav events on the first and last focusable nodes of the trap,
413
439
  // in order to prevent focus from escaping. If it escapes for even a
414
440
  // moment it can end up scrolling the page and causing confusion so we
415
441
  // kind of need to capture the action at the keydown phase.
416
- var checkTab = function checkTab(e) {
417
- var target = getActualTarget(e);
442
+ var checkKeyNav = function checkKeyNav(event) {
443
+ var isBackward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
444
+ var target = getActualTarget(event);
418
445
  updateTabbableNodes();
419
446
  var destinationNode = null;
420
447
  if (state.tabbableGroups.length > 0) {
421
448
  // make sure the target is actually contained in a group
422
449
  // NOTE: the target may also be the container itself if it's focusable
423
450
  // with tabIndex='-1' and was given initial focus
424
- var containerIndex = findContainerIndex(target);
451
+ var containerIndex = findContainerIndex(target, event);
425
452
  var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : undefined;
426
453
  if (containerIndex < 0) {
427
454
  // target not found in any group: quite possible focus has escaped the trap,
428
- // so bring it back in to...
429
- if (e.shiftKey) {
455
+ // so bring it back into...
456
+ if (isBackward) {
430
457
  // ...the last node in the last group
431
458
  destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode;
432
459
  } else {
433
460
  // ...the first node in the first group
434
461
  destinationNode = state.tabbableGroups[0].firstTabbableNode;
435
462
  }
436
- } else if (e.shiftKey) {
463
+ } else if (isBackward) {
437
464
  // REVERSE
438
465
 
439
466
  // is the target the first tabbable node in a group?
@@ -457,6 +484,10 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
457
484
  var destinationGroupIndex = startOfGroupIndex === 0 ? state.tabbableGroups.length - 1 : startOfGroupIndex - 1;
458
485
  var destinationGroup = state.tabbableGroups[destinationGroupIndex];
459
486
  destinationNode = destinationGroup.lastTabbableNode;
487
+ } else if (!isTabEvent(event)) {
488
+ // user must have customized the nav keys so we have to move focus manually _within_
489
+ // the active group: do this based on the order determined by tabbable()
490
+ destinationNode = containerGroup.nextTabbableNode(target, false);
460
491
  }
461
492
  } else {
462
493
  // FORWARD
@@ -482,33 +513,43 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
482
513
  var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;
483
514
  var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];
484
515
  destinationNode = _destinationGroup.firstTabbableNode;
516
+ } else if (!isTabEvent(event)) {
517
+ // user must have customized the nav keys so we have to move focus manually _within_
518
+ // the active group: do this based on the order determined by tabbable()
519
+ destinationNode = containerGroup.nextTabbableNode(target);
485
520
  }
486
521
  }
487
522
  } else {
523
+ // no groups available
488
524
  // NOTE: the fallbackFocus option does not support returning false to opt-out
489
525
  destinationNode = getNodeForOption('fallbackFocus');
490
526
  }
491
527
  if (destinationNode) {
492
- e.preventDefault();
528
+ if (isTabEvent(event)) {
529
+ // since tab natively moves focus, we wouldn't have a destination node unless we
530
+ // were on the edge of a container and had to move to the next/previous edge, in
531
+ // which case we want to prevent default to keep the browser from moving focus
532
+ // to where it normally would
533
+ event.preventDefault();
534
+ }
493
535
  tryFocus(destinationNode);
494
536
  }
495
537
  // else, let the browser take care of [shift+]tab and move the focus
496
538
  };
497
539
 
498
- var checkKey = function checkKey(e) {
499
- if (isEscapeEvent(e) && valueOrHandler(config.escapeDeactivates, e) !== false) {
500
- e.preventDefault();
540
+ var checkKey = function checkKey(event) {
541
+ if (isEscapeEvent(event) && valueOrHandler(config.escapeDeactivates, event) !== false) {
542
+ event.preventDefault();
501
543
  trap.deactivate();
502
544
  return;
503
545
  }
504
- if (isTabEvent(e)) {
505
- checkTab(e);
506
- return;
546
+ if (config.isKeyForward(event) || config.isKeyBackward(event)) {
547
+ checkKeyNav(event, config.isKeyBackward(event));
507
548
  }
508
549
  };
509
550
  var checkClick = function checkClick(e) {
510
551
  var target = getActualTarget(e);
511
- if (findContainerIndex(target) >= 0) {
552
+ if (findContainerIndex(target, e) >= 0) {
512
553
  return;
513
554
  }
514
555
  if (valueOrHandler(config.clickOutsideDeactivates, e)) {
@@ -569,6 +610,43 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
569
610
  return trap;
570
611
  };
571
612
 
613
+ //
614
+ // MUTATION OBSERVER
615
+ //
616
+
617
+ var checkDomRemoval = function checkDomRemoval(mutations) {
618
+ var isFocusedNodeRemoved = mutations.some(function (mutation) {
619
+ var removedNodes = Array.from(mutation.removedNodes);
620
+ return removedNodes.some(function (node) {
621
+ return node === state.mostRecentlyFocusedNode;
622
+ });
623
+ });
624
+
625
+ // If the currently focused is removed then browsers will move focus to the
626
+ // <body> element. If this happens, try to move focus back into the trap.
627
+ if (isFocusedNodeRemoved) {
628
+ tryFocus(getInitialFocusNode());
629
+ }
630
+ };
631
+
632
+ // Use MutationObserver - if supported - to detect if focused node is removed
633
+ // from the DOM.
634
+ var mutationObserver = typeof window !== 'undefined' && 'MutationObserver' in window ? new MutationObserver(checkDomRemoval) : undefined;
635
+ var updateObservedNodes = function updateObservedNodes() {
636
+ if (!mutationObserver) {
637
+ return;
638
+ }
639
+ mutationObserver.disconnect();
640
+ if (state.active && !state.paused) {
641
+ state.containers.map(function (container) {
642
+ mutationObserver.observe(container, {
643
+ subtree: true,
644
+ childList: true
645
+ });
646
+ });
647
+ }
648
+ };
649
+
572
650
  //
573
651
  // TRAP DEFINITION
574
652
  //
@@ -593,17 +671,14 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
593
671
  state.active = true;
594
672
  state.paused = false;
595
673
  state.nodeFocusedBeforeActivation = doc.activeElement;
596
- if (onActivate) {
597
- onActivate();
598
- }
674
+ onActivate === null || onActivate === void 0 ? void 0 : onActivate();
599
675
  var finishActivation = function finishActivation() {
600
676
  if (checkCanFocusTrap) {
601
677
  updateTabbableNodes();
602
678
  }
603
679
  addListeners();
604
- if (onPostActivate) {
605
- onPostActivate();
606
- }
680
+ updateObservedNodes();
681
+ onPostActivate === null || onPostActivate === void 0 ? void 0 : onPostActivate();
607
682
  };
608
683
  if (checkCanFocusTrap) {
609
684
  checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);
@@ -626,22 +701,19 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
626
701
  removeListeners();
627
702
  state.active = false;
628
703
  state.paused = false;
704
+ updateObservedNodes();
629
705
  activeFocusTraps.deactivateTrap(trapStack, trap);
630
706
  var onDeactivate = getOption(options, 'onDeactivate');
631
707
  var onPostDeactivate = getOption(options, 'onPostDeactivate');
632
708
  var checkCanReturnFocus = getOption(options, 'checkCanReturnFocus');
633
709
  var returnFocus = getOption(options, 'returnFocus', 'returnFocusOnDeactivate');
634
- if (onDeactivate) {
635
- onDeactivate();
636
- }
710
+ onDeactivate === null || onDeactivate === void 0 ? void 0 : onDeactivate();
637
711
  var finishDeactivation = function finishDeactivation() {
638
712
  delay(function () {
639
713
  if (returnFocus) {
640
714
  tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));
641
715
  }
642
- if (onPostDeactivate) {
643
- onPostDeactivate();
644
- }
716
+ onPostDeactivate === null || onPostDeactivate === void 0 ? void 0 : onPostDeactivate();
645
717
  });
646
718
  };
647
719
  if (returnFocus && checkCanReturnFocus) {
@@ -651,21 +723,31 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
651
723
  finishDeactivation();
652
724
  return this;
653
725
  },
654
- pause: function pause() {
726
+ pause: function pause(pauseOptions) {
655
727
  if (state.paused || !state.active) {
656
728
  return this;
657
729
  }
730
+ var onPause = getOption(pauseOptions, 'onPause');
731
+ var onPostPause = getOption(pauseOptions, 'onPostPause');
658
732
  state.paused = true;
733
+ onPause === null || onPause === void 0 ? void 0 : onPause();
659
734
  removeListeners();
735
+ updateObservedNodes();
736
+ onPostPause === null || onPostPause === void 0 ? void 0 : onPostPause();
660
737
  return this;
661
738
  },
662
- unpause: function unpause() {
739
+ unpause: function unpause(unpauseOptions) {
663
740
  if (!state.paused || !state.active) {
664
741
  return this;
665
742
  }
743
+ var onUnpause = getOption(unpauseOptions, 'onUnpause');
744
+ var onPostUnpause = getOption(unpauseOptions, 'onPostUnpause');
666
745
  state.paused = false;
746
+ onUnpause === null || onUnpause === void 0 ? void 0 : onUnpause();
667
747
  updateTabbableNodes();
668
748
  addListeners();
749
+ updateObservedNodes();
750
+ onPostUnpause === null || onPostUnpause === void 0 ? void 0 : onPostUnpause();
669
751
  return this;
670
752
  },
671
753
  updateContainerElements: function updateContainerElements(containerElements) {
@@ -676,6 +758,7 @@ var createFocusTrap = function createFocusTrap(elements, userOptions) {
676
758
  if (state.active) {
677
759
  updateTabbableNodes();
678
760
  }
761
+ updateObservedNodes();
679
762
  return this;
680
763
  }
681
764
  };
@@ -161,7 +161,7 @@ var round = Math.round;
161
161
  function getUAString() {
162
162
  var uaData = navigator.userAgentData;
163
163
 
164
- if (uaData != null && uaData.brands) {
164
+ if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {
165
165
  return uaData.brands.map(function (item) {
166
166
  return item.brand + "/" + item.version;
167
167
  }).join(' ');
@@ -480,10 +480,9 @@ var unsetSides = {
480
480
  // Zooming can change the DPR, but it seems to report a value that will
481
481
  // cleanly divide the values into the appropriate subpixels.
482
482
 
483
- function roundOffsetsByDPR(_ref) {
483
+ function roundOffsetsByDPR(_ref, win) {
484
484
  var x = _ref.x,
485
485
  y = _ref.y;
486
- var win = window;
487
486
  var dpr = win.devicePixelRatio || 1;
488
487
  return {
489
488
  x: round(x * dpr) / dpr || 0,
@@ -566,7 +565,7 @@ function mapToStyles(_ref2) {
566
565
  var _ref4 = roundOffsets === true ? roundOffsetsByDPR({
567
566
  x: x,
568
567
  y: y
569
- }) : {
568
+ }, getWindow(popper)) : {
570
569
  x: x,
571
570
  y: y
572
571
  };
@@ -1,15 +1,64 @@
1
1
  /*!
2
- * tabbable 6.0.1
2
+ * tabbable 6.1.2
3
3
  * @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE
4
4
  */
5
- var candidateSelectors = ['input', 'select', 'textarea', 'a[href]', 'button', '[tabindex]:not(slot)', 'audio[controls]', 'video[controls]', '[contenteditable]:not([contenteditable="false"])', 'details>summary:first-of-type', 'details'];
5
+ // NOTE: separate `:not()` selectors has broader browser support than the newer
6
+ // `:not([inert], [inert] *)` (Feb 2023)
7
+ // CAREFUL: JSDom does not support `:not([inert] *)` as a selector; using it causes
8
+ // the entire query to fail, resulting in no nodes found, which will break a lot
9
+ // of things... so we have to rely on JS to identify nodes inside an inert container
10
+ var candidateSelectors = ['input:not([inert])', 'select:not([inert])', 'textarea:not([inert])', 'a[href]:not([inert])', 'button:not([inert])', '[tabindex]:not(slot):not([inert])', 'audio[controls]:not([inert])', 'video[controls]:not([inert])', '[contenteditable]:not([contenteditable="false"]):not([inert])', 'details>summary:first-of-type:not([inert])', 'details:not([inert])'];
6
11
  var candidateSelector = /* #__PURE__ */candidateSelectors.join(',');
7
12
  var NoElement = typeof Element === 'undefined';
8
13
  var matches = NoElement ? function () {} : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
9
14
  var getRootNode = !NoElement && Element.prototype.getRootNode ? function (element) {
10
- return element.getRootNode();
15
+ var _element$getRootNode;
16
+ return element === null || element === void 0 ? void 0 : (_element$getRootNode = element.getRootNode) === null || _element$getRootNode === void 0 ? void 0 : _element$getRootNode.call(element);
11
17
  } : function (element) {
12
- return element.ownerDocument;
18
+ return element === null || element === void 0 ? void 0 : element.ownerDocument;
19
+ };
20
+
21
+ /**
22
+ * Determines if a node is inert or in an inert ancestor.
23
+ * @param {Element} [node]
24
+ * @param {boolean} [lookUp] If true and `node` is not inert, looks up at ancestors to
25
+ * see if any of them are inert. If false, only `node` itself is considered.
26
+ * @returns {boolean} True if inert itself or by way of being in an inert ancestor.
27
+ * False if `node` is falsy.
28
+ */
29
+ var isInert = function isInert(node, lookUp) {
30
+ var _node$getAttribute;
31
+ if (lookUp === void 0) {
32
+ lookUp = true;
33
+ }
34
+ // CAREFUL: JSDom does not support inert at all, so we can't use the `HTMLElement.inert`
35
+ // JS API property; we have to check the attribute, which can either be empty or 'true';
36
+ // if it's `null` (not specified) or 'false', it's an active element
37
+ var inertAtt = node === null || node === void 0 ? void 0 : (_node$getAttribute = node.getAttribute) === null || _node$getAttribute === void 0 ? void 0 : _node$getAttribute.call(node, 'inert');
38
+ var inert = inertAtt === '' || inertAtt === 'true';
39
+
40
+ // NOTE: this could also be handled with `node.matches('[inert], :is([inert] *)')`
41
+ // if it weren't for `matches()` not being a function on shadow roots; the following
42
+ // code works for any kind of node
43
+ // CAREFUL: JSDom does not appear to support certain selectors like `:not([inert] *)`
44
+ // so it likely would not support `:is([inert] *)` either...
45
+ var result = inert || lookUp && node && isInert(node.parentNode); // recursive
46
+
47
+ return result;
48
+ };
49
+
50
+ /**
51
+ * Determines if a node's content is editable.
52
+ * @param {Element} [node]
53
+ * @returns True if it's content-editable; false if it's not or `node` is falsy.
54
+ */
55
+ var isContentEditable = function isContentEditable(node) {
56
+ var _node$getAttribute2;
57
+ // CAREFUL: JSDom does not support the `HTMLElement.isContentEditable` API so we have
58
+ // to use the attribute directly to check for this, which can either be empty or 'true';
59
+ // if it's `null` (not specified) or 'false', it's a non-editable element
60
+ var attValue = node === null || node === void 0 ? void 0 : (_node$getAttribute2 = node.getAttribute) === null || _node$getAttribute2 === void 0 ? void 0 : _node$getAttribute2.call(node, 'contenteditable');
61
+ return attValue === '' || attValue === 'true';
13
62
  };
14
63
 
15
64
  /**
@@ -19,6 +68,11 @@ var getRootNode = !NoElement && Element.prototype.getRootNode ? function (elemen
19
68
  * @returns {Element[]}
20
69
  */
21
70
  var getCandidates = function getCandidates(el, includeContainer, filter) {
71
+ // even if `includeContainer=false`, we still have to check it for inertness because
72
+ // if it's inert, all its children are inert
73
+ if (isInert(el)) {
74
+ return [];
75
+ }
22
76
  var candidates = Array.prototype.slice.apply(el.querySelectorAll(candidateSelector));
23
77
  if (includeContainer && matches.call(el, candidateSelector)) {
24
78
  candidates.unshift(el);
@@ -66,6 +120,11 @@ var getCandidatesIteratively = function getCandidatesIteratively(elements, inclu
66
120
  var elementsToCheck = Array.from(elements);
67
121
  while (elementsToCheck.length) {
68
122
  var element = elementsToCheck.shift();
123
+ if (isInert(element, false)) {
124
+ // no need to look up since we're drilling down
125
+ // anything inside this container will also be inert
126
+ continue;
127
+ }
69
128
  if (element.tagName === 'SLOT') {
70
129
  // add shadow dom slot scope (slot itself cannot be focusable)
71
130
  var assigned = element.assignedElements();
@@ -90,7 +149,11 @@ var getCandidatesIteratively = function getCandidatesIteratively(elements, inclu
90
149
  var shadowRoot = element.shadowRoot ||
91
150
  // check for an undisclosed shadow
92
151
  typeof options.getShadowRoot === 'function' && options.getShadowRoot(element);
93
- var validShadowRoot = !options.shadowRootFilter || options.shadowRootFilter(element);
152
+
153
+ // no inert look up because we're already drilling down and checking for inertness
154
+ // on the way down, so all containers to this root node should have already been
155
+ // vetted as non-inert
156
+ var validShadowRoot = !isInert(shadowRoot, false) && (!options.shadowRootFilter || options.shadowRootFilter(element));
94
157
  if (shadowRoot && validShadowRoot) {
95
158
  // add shadow dom scope IIF a shadow root node was given; otherwise, an undisclosed
96
159
  // shadow exists, so look at light dom children as fallback BUT create a scope for any
@@ -129,7 +192,7 @@ var getTabindex = function getTabindex(node, isScope) {
129
192
  // isScope is positive for custom element with shadow root or slot that by default
130
193
  // have tabIndex -1, but need to be sorted by document order in order for their
131
194
  // content to be inserted in the correct position
132
- if ((isScope || /^(AUDIO|VIDEO|DETAILS)$/.test(node.tagName) || node.isContentEditable) && isNaN(parseInt(node.getAttribute('tabindex'), 10))) {
195
+ if ((isScope || /^(AUDIO|VIDEO|DETAILS)$/.test(node.tagName) || isContentEditable(node)) && isNaN(parseInt(node.getAttribute('tabindex'), 10))) {
133
196
  return 0;
134
197
  }
135
198
  }
@@ -189,7 +252,7 @@ var isNonTabbableRadio = function isNonTabbableRadio(node) {
189
252
 
190
253
  // determines if a node is ultimately attached to the window's document
191
254
  var isNodeAttached = function isNodeAttached(node) {
192
- var _nodeRootHost;
255
+ var _nodeRoot;
193
256
  // The root node is the shadow root if the node is in a shadow DOM; some document otherwise
194
257
  // (but NOT _the_ document; see second 'If' comment below for more).
195
258
  // If rootNode is shadow root, it'll have a host, which is the element to which the shadow
@@ -209,15 +272,28 @@ var isNodeAttached = function isNodeAttached(node) {
209
272
  // to ignore the rootNode at this point, and use `node.ownerDocument`. Otherwise,
210
273
  // using `rootNode.contains(node)` will _always_ be true we'll get false-positives when
211
274
  // node is actually detached.
212
- var nodeRootHost = getRootNode(node).host;
213
- var attached = !!((_nodeRootHost = nodeRootHost) !== null && _nodeRootHost !== void 0 && _nodeRootHost.ownerDocument.contains(nodeRootHost) || node.ownerDocument.contains(node));
214
- while (!attached && nodeRootHost) {
215
- var _nodeRootHost2;
216
- // since it's not attached and we have a root host, the node MUST be in a nested shadow DOM,
217
- // which means we need to get the host's host and check if that parent host is contained
218
- // in (i.e. attached to) the document
219
- nodeRootHost = getRootNode(nodeRootHost).host;
220
- attached = !!((_nodeRootHost2 = nodeRootHost) !== null && _nodeRootHost2 !== void 0 && _nodeRootHost2.ownerDocument.contains(nodeRootHost));
275
+ // NOTE: If `nodeRootHost` or `node` happens to be the `document` itself (which is possible
276
+ // if a tabbable/focusable node was quickly added to the DOM, focused, and then removed
277
+ // from the DOM as in https://github.com/focus-trap/focus-trap-react/issues/905), then
278
+ // `ownerDocument` will be `null`, hence the optional chaining on it.
279
+ var nodeRoot = node && getRootNode(node);
280
+ var nodeRootHost = (_nodeRoot = nodeRoot) === null || _nodeRoot === void 0 ? void 0 : _nodeRoot.host;
281
+
282
+ // in some cases, a detached node will return itself as the root instead of a document or
283
+ // shadow root object, in which case, we shouldn't try to look further up the host chain
284
+ var attached = false;
285
+ if (nodeRoot && nodeRoot !== node) {
286
+ var _nodeRootHost, _nodeRootHost$ownerDo, _node$ownerDocument;
287
+ attached = !!((_nodeRootHost = nodeRootHost) !== null && _nodeRootHost !== void 0 && (_nodeRootHost$ownerDo = _nodeRootHost.ownerDocument) !== null && _nodeRootHost$ownerDo !== void 0 && _nodeRootHost$ownerDo.contains(nodeRootHost) || node !== null && node !== void 0 && (_node$ownerDocument = node.ownerDocument) !== null && _node$ownerDocument !== void 0 && _node$ownerDocument.contains(node));
288
+ while (!attached && nodeRootHost) {
289
+ var _nodeRoot2, _nodeRootHost2, _nodeRootHost2$ownerD;
290
+ // since it's not attached and we have a root host, the node MUST be in a nested shadow DOM,
291
+ // which means we need to get the host's host and check if that parent host is contained
292
+ // in (i.e. attached to) the document
293
+ nodeRoot = getRootNode(nodeRootHost);
294
+ nodeRootHost = (_nodeRoot2 = nodeRoot) === null || _nodeRoot2 === void 0 ? void 0 : _nodeRoot2.host;
295
+ attached = !!((_nodeRootHost2 = nodeRootHost) !== null && _nodeRootHost2 !== void 0 && (_nodeRootHost2$ownerD = _nodeRootHost2.ownerDocument) !== null && _nodeRootHost2$ownerD !== void 0 && _nodeRootHost2$ownerD.contains(nodeRootHost));
296
+ }
221
297
  }
222
298
  return attached;
223
299
  };
@@ -352,7 +428,11 @@ var isDisabledFromFieldset = function isDisabledFromFieldset(node) {
352
428
  return false;
353
429
  };
354
430
  var isNodeMatchingSelectorFocusable = function isNodeMatchingSelectorFocusable(options, node) {
355
- if (node.disabled || isHiddenInput(node) || isHidden(node, options) ||
431
+ if (node.disabled ||
432
+ // we must do an inert look up to filter out any elements inside an inert ancestor
433
+ // because we're limited in the type of selectors we can use in JSDom (see related
434
+ // note related to `candidateSelectors`)
435
+ isInert(node) || isHiddenInput(node) || isHidden(node, options) ||
356
436
  // For a details element with a summary, the summary element gets the focus
357
437
  isDetailsWithSummary(node) || isDisabledFromFieldset(node)) {
358
438
  return false;
package/dist/esm/index.js CHANGED
@@ -1 +1,70 @@
1
+ export { i as isExpandable } from './expandable.interfaces-9b1afbe8.js';
1
2
  export { t as transitionDuration } from './map-controls.interfaces-2323e8ac.js';
3
+
4
+ class DsoModalRef {
5
+ constructor(modalElement) {
6
+ this.modalElement = modalElement;
7
+ if (!modalElement) {
8
+ throw new Error("unable to add event listener. try opening the modal first");
9
+ }
10
+ }
11
+ /** Removes the modal from the DOM. */
12
+ close() {
13
+ document.body.removeChild(this.modalElement);
14
+ }
15
+ addEventListener(eventName, fn) {
16
+ this.modalElement.addEventListener(eventName, fn);
17
+ }
18
+ removeEventListener(eventName, fn) {
19
+ this.modalElement.removeEventListener(eventName, fn);
20
+ }
21
+ }
22
+
23
+ class DsoModalController {
24
+ open(modal, options) {
25
+ const dsoModalElement = this.createModal(modal, options);
26
+ document.body.appendChild(dsoModalElement);
27
+ return new DsoModalRef(dsoModalElement);
28
+ }
29
+ createModal({ title, body, footer }, options) {
30
+ const element = document.createElement(`dso-modal`);
31
+ if (title) {
32
+ element.setAttribute("modal-title", title);
33
+ }
34
+ if (options) {
35
+ const { role, showCloseButton, initialFocus } = options;
36
+ if (role) {
37
+ element.role = role;
38
+ }
39
+ if (showCloseButton) {
40
+ element.setAttribute("show-close-button", showCloseButton ? "true" : "false");
41
+ }
42
+ if (initialFocus) {
43
+ element.setAttribute("initial-focus", initialFocus);
44
+ }
45
+ }
46
+ const bodyDiv = document.createElement("div");
47
+ bodyDiv.setAttribute("slot", "body");
48
+ if (typeof body === "string") {
49
+ bodyDiv.innerHTML = body;
50
+ }
51
+ else {
52
+ bodyDiv.appendChild(body);
53
+ }
54
+ element.appendChild(bodyDiv);
55
+ if (footer) {
56
+ const footerDiv = document.createElement("div");
57
+ footerDiv.setAttribute("slot", "footer");
58
+ if (typeof footer === "string") {
59
+ footerDiv.innerHTML = footer;
60
+ }
61
+ else {
62
+ footerDiv.appendChild(footer);
63
+ }
64
+ element.appendChild(footerDiv);
65
+ }
66
+ return element;
67
+ }
68
+ }
69
+
70
+ export { DsoModalController };