@lion/ui 0.17.0-menu-system.1 → 0.17.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 (221) hide show
  1. package/collapsible.js +0 -1
  2. package/components/button/src/LionButton.js +0 -4
  3. package/components/button/test-suites/LionButton.suite.js +0 -1
  4. package/components/calendar/src/LionCalendar.js +0 -2
  5. package/components/combobox/test/lion-combobox.test.js +0 -1
  6. package/components/form-core/src/form-group/FormGroupMixin.js +0 -7
  7. package/components/form-core/src/registration/FormRegistrarMixin.js +0 -11
  8. package/components/form-core/test/FormControlMixin.test.js +0 -1
  9. package/components/form-core/test/validate/Validator.test.js +0 -2
  10. package/components/form-core/test-suites/ValidateMixin.suite.js +0 -2
  11. package/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.js +0 -1
  12. package/components/input-datepicker/src/localizeNamespaceLoader.js +0 -1
  13. package/components/input-datepicker/test/lion-input-datepicker.test.js +6 -5
  14. package/components/localize/test/date/formatDate.test.js +0 -1
  15. package/components/overlays/src/OverlayController.js +111 -244
  16. package/components/overlays/src/OverlayMixin.js +11 -58
  17. package/components/overlays/src/OverlaysManager.js +2 -2
  18. package/components/overlays/src/configurations/withDropdownConfig.js +2 -22
  19. package/components/overlays/src/configurations/withPopoverConfig.js +32 -0
  20. package/components/overlays/src/utils/adopt-styles.js +1 -2
  21. package/components/overlays/src/utils/get-focusable-elements.js +0 -4
  22. package/components/overlays/src/utils/is-equal-config.js +6 -10
  23. package/components/overlays/test/OverlayController.test.js +3 -5
  24. package/components/overlays/test/utils-tests/is-equal-config.test.js +1 -1
  25. package/components/overlays/test-suites/OverlayMixin.suite.js +8 -8
  26. package/components/overlays/types/OverlayConfig.ts +5 -16
  27. package/components/select-rich/test/lion-select-rich.test.js +0 -3
  28. package/custom-elements.json +1891 -13818
  29. package/dist-types/components/button/src/LionButton.d.ts +0 -1
  30. package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
  31. package/dist-types/components/button/test-suites/LionButton.suite.d.ts.map +1 -1
  32. package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
  33. package/dist-types/components/dialog/src/LionDialog.d.ts +1 -3
  34. package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
  35. package/dist-types/components/form-core/src/form-group/FormGroupMixin.d.ts.map +1 -1
  36. package/dist-types/components/form-core/src/registration/FormRegistrarMixin.d.ts.map +1 -1
  37. package/dist-types/components/form-core/test-suites/ValidateMixin.suite.d.ts.map +1 -1
  38. package/dist-types/components/form-core/test-suites/ValidateMixinFeedbackPart.suite.d.ts.map +1 -1
  39. package/dist-types/components/input-datepicker/src/localizeNamespaceLoader.d.ts.map +1 -1
  40. package/dist-types/components/overlays/src/OverlayController.d.ts +13 -9
  41. package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
  42. package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
  43. package/dist-types/components/overlays/src/configurations/withDropdownConfig.d.ts +0 -1
  44. package/dist-types/components/overlays/src/configurations/withDropdownConfig.d.ts.map +1 -1
  45. package/dist-types/components/overlays/src/configurations/withPopoverConfig.d.ts +6 -0
  46. package/dist-types/components/overlays/src/configurations/withPopoverConfig.d.ts.map +1 -0
  47. package/dist-types/components/overlays/src/utils/get-focusable-elements.d.ts.map +1 -1
  48. package/dist-types/components/overlays/src/utils/is-equal-config.d.ts.map +1 -1
  49. package/dist-types/components/overlays/types/OverlayConfig.d.ts +2 -10
  50. package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
  51. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +2 -8
  52. package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
  53. package/dist-types/exports/overlays.d.ts +1 -3
  54. package/dist-types/exports/overlays.d.ts.map +1 -1
  55. package/dist-types/tsconfig.tsbuildinfo +1 -1
  56. package/docs/components/checkbox-group/overview.md +2 -2
  57. package/docs/components/collapsible/overview.md +1 -1
  58. package/docs/components/combobox/api-table.md +8 -21
  59. package/docs/components/combobox/overview.md +1 -1
  60. package/docs/components/combobox/use-cases.md +1 -1
  61. package/docs/components/dialog/api-table.md +6 -31
  62. package/docs/components/dialog/overview.md +1 -1
  63. package/docs/components/fieldset/overview.md +5 -5
  64. package/docs/components/form/overview.md +3 -3
  65. package/docs/components/form/use-cases.md +2 -2
  66. package/docs/components/icon/use-cases.md +1 -1
  67. package/docs/components/input/overview.md +1 -1
  68. package/docs/components/input-amount/overview.md +3 -3
  69. package/docs/components/input-date/overview.md +3 -3
  70. package/docs/components/input-datepicker/api-table.md +4 -21
  71. package/docs/components/input-datepicker/overview.md +5 -5
  72. package/docs/components/input-email/overview.md +2 -2
  73. package/docs/components/input-file/overview.md +1 -1
  74. package/docs/components/input-iban/overview.md +2 -2
  75. package/docs/components/input-range/overview.md +2 -2
  76. package/docs/components/input-stepper/overview.md +1 -1
  77. package/docs/components/input-tel/overview.md +1 -1
  78. package/docs/components/input-tel/use-cases.md +6 -6
  79. package/docs/components/input-tel-dropdown/overview.md +1 -1
  80. package/docs/components/listbox/use-cases.md +1 -1
  81. package/docs/components/radio-group/overview.md +3 -3
  82. package/docs/components/select/use-cases.md +1 -1
  83. package/docs/components/select-rich/api-table.md +5 -21
  84. package/docs/components/select-rich/use-cases.md +2 -2
  85. package/docs/components/tabs/examples.md +1 -1
  86. package/docs/components/tooltip/api-table.md +11 -36
  87. package/docs/fundamentals/index.md +4 -4
  88. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  89. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  90. package/docs/fundamentals/rationales/accessibility.md +1 -1
  91. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  92. package/docs/fundamentals/systems/core/overview.md +1 -1
  93. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  94. package/docs/fundamentals/systems/form/model-value.md +1 -1
  95. package/docs/fundamentals/systems/form/overview.md +28 -28
  96. package/docs/fundamentals/systems/form/validate.md +2 -2
  97. package/docs/fundamentals/systems/localize/overview.md +4 -4
  98. package/docs/fundamentals/systems/overlays/api-table.md +34 -34
  99. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  100. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  101. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  102. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  103. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  104. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  105. package/docs/guides/how-to/create-a-form.md +4 -4
  106. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  107. package/docs/guides/how-to/index.md +1 -1
  108. package/docs/guides/index.md +8 -8
  109. package/docs/guides/principles/index.md +5 -5
  110. package/docs/guides/principles/styling.md +1 -1
  111. package/exports/collapsible.js +0 -1
  112. package/exports/overlays.js +1 -6
  113. package/overlays.js +1 -6
  114. package/package.json +1 -1
  115. package/components/collapsible/src/DisclosureMixin.js +0 -313
  116. package/components/collapsible/types/DisclosureMixinTypes.ts +0 -48
  117. package/components/menu/CHANGELOG.md +0 -37
  118. package/components/menu/README.md +0 -335
  119. package/components/menu/index.js +0 -2
  120. package/components/menu/lion-menu-overlay.js +0 -3
  121. package/components/menu/lion-menu.js +0 -3
  122. package/components/menu/lion-spinbutton.js +0 -3
  123. package/components/menu/lion-toolbar.js +0 -3
  124. package/components/menu/lion-tree.js +0 -3
  125. package/components/menu/package.json +0 -46
  126. package/components/menu/requirements.temp.md +0 -12
  127. package/components/menu/src/InteractiveListMixin.js +0 -796
  128. package/components/menu/src/LionMenu.js +0 -250
  129. package/components/menu/src/LionMenuHybrid.js +0 -46
  130. package/components/menu/src/LionMenuOverlay.js +0 -155
  131. package/components/menu/src/LionSpinbutton.js +0 -213
  132. package/components/menu/src/LionToolbar.js +0 -94
  133. package/components/menu/src/LionTree.js +0 -52
  134. package/components/menu/src/MultiLevelListMixin.js +0 -274
  135. package/components/menu/src/utils/isInView.js +0 -26
  136. package/components/menu/src/utils/listItemInteractions.js +0 -104
  137. package/components/menu/src/utils/uuid.js +0 -3
  138. package/components/menu/test/lion-menu.test.js +0 -3
  139. package/components/menu/test-suites/InteractiveListMixin.suite.js +0 -142
  140. package/components/menu/types/InteractiveListMixinTypes.d.ts +0 -82
  141. package/components/navigation-bar/README.md +0 -1
  142. package/components/navigation-bar/src/LionNavigationBar.js +0 -422
  143. package/components/navigation-bar/src/types.js +0 -41
  144. package/components/overlays/NEXT.md +0 -49
  145. package/dist-types/components/collapsible/src/DisclosureMixin.d.ts +0 -3
  146. package/dist-types/components/collapsible/src/DisclosureMixin.d.ts.map +0 -1
  147. package/dist-types/components/collapsible/types/DisclosureMixinTypes.d.ts +0 -24
  148. package/dist-types/components/collapsible/types/DisclosureMixinTypes.d.ts.map +0 -1
  149. package/dist-types/components/menu/index.d.ts +0 -3
  150. package/dist-types/components/menu/index.d.ts.map +0 -1
  151. package/dist-types/components/menu/lion-menu-overlay.d.ts +0 -2
  152. package/dist-types/components/menu/lion-menu-overlay.d.ts.map +0 -1
  153. package/dist-types/components/menu/lion-menu.d.ts +0 -2
  154. package/dist-types/components/menu/lion-menu.d.ts.map +0 -1
  155. package/dist-types/components/menu/lion-spinbutton.d.ts +0 -2
  156. package/dist-types/components/menu/lion-spinbutton.d.ts.map +0 -1
  157. package/dist-types/components/menu/lion-toolbar.d.ts +0 -2
  158. package/dist-types/components/menu/lion-toolbar.d.ts.map +0 -1
  159. package/dist-types/components/menu/lion-tree.d.ts +0 -2
  160. package/dist-types/components/menu/lion-tree.d.ts.map +0 -1
  161. package/dist-types/components/menu/src/InteractiveListMixin.d.ts +0 -611
  162. package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +0 -1
  163. package/dist-types/components/menu/src/LionMenu.d.ts +0 -563
  164. package/dist-types/components/menu/src/LionMenu.d.ts.map +0 -1
  165. package/dist-types/components/menu/src/LionMenuHybrid.d.ts +0 -19
  166. package/dist-types/components/menu/src/LionMenuHybrid.d.ts.map +0 -1
  167. package/dist-types/components/menu/src/LionMenuOverlay.d.ts +0 -1007
  168. package/dist-types/components/menu/src/LionMenuOverlay.d.ts.map +0 -1
  169. package/dist-types/components/menu/src/LionSpinbutton.d.ts +0 -58
  170. package/dist-types/components/menu/src/LionSpinbutton.d.ts.map +0 -1
  171. package/dist-types/components/menu/src/LionToolbar.d.ts +0 -8
  172. package/dist-types/components/menu/src/LionToolbar.d.ts.map +0 -1
  173. package/dist-types/components/menu/src/LionTree.d.ts +0 -445
  174. package/dist-types/components/menu/src/LionTree.d.ts.map +0 -1
  175. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +0 -493
  176. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +0 -1
  177. package/dist-types/components/menu/src/utils/isInView.d.ts +0 -9
  178. package/dist-types/components/menu/src/utils/isInView.d.ts.map +0 -1
  179. package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +0 -42
  180. package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +0 -1
  181. package/dist-types/components/menu/src/utils/uuid.d.ts +0 -2
  182. package/dist-types/components/menu/src/utils/uuid.d.ts.map +0 -1
  183. package/dist-types/components/menu/test/lion-menu.test.d.ts +0 -2
  184. package/dist-types/components/menu/test/lion-menu.test.d.ts.map +0 -1
  185. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +0 -13
  186. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +0 -1
  187. package/dist-types/components/navigation-bar/src/LionNavigationBar.d.ts +0 -238
  188. package/dist-types/components/navigation-bar/src/LionNavigationBar.d.ts.map +0 -1
  189. package/dist-types/components/navigation-bar/src/types.d.ts +0 -85
  190. package/dist-types/components/navigation-bar/src/types.d.ts.map +0 -1
  191. package/dist-types/exports/define/lion-item.d.ts +0 -2
  192. package/dist-types/exports/define/lion-item.d.ts.map +0 -1
  193. package/dist-types/exports/define/lion-menu-overlay.d.ts +0 -2
  194. package/dist-types/exports/define/lion-menu-overlay.d.ts.map +0 -1
  195. package/dist-types/exports/define/lion-menu.d.ts +0 -2
  196. package/dist-types/exports/define/lion-menu.d.ts.map +0 -1
  197. package/dist-types/exports/define/lion-navigation-bar.d.ts +0 -2
  198. package/dist-types/exports/define/lion-navigation-bar.d.ts.map +0 -1
  199. package/dist-types/exports/define/lion-toolbar.d.ts +0 -2
  200. package/dist-types/exports/define/lion-toolbar.d.ts.map +0 -1
  201. package/dist-types/exports/define/lion-tree.d.ts +0 -2
  202. package/dist-types/exports/define/lion-tree.d.ts.map +0 -1
  203. package/dist-types/exports/menu.d.ts +0 -7
  204. package/dist-types/exports/menu.d.ts.map +0 -1
  205. package/dist-types/exports/navigation-bar.d.ts +0 -2
  206. package/dist-types/exports/navigation-bar.d.ts.map +0 -1
  207. package/docs/components/menu/api-table.md +0 -450
  208. package/docs/components/menu/applyMenuDemoStyles.js +0 -173
  209. package/docs/components/menu/extensions.md +0 -216
  210. package/docs/components/menu/index.md +0 -13
  211. package/docs/components/menu/overview.md +0 -422
  212. package/exports/define/lion-item.js +0 -3
  213. package/exports/define/lion-menu-overlay.js +0 -3
  214. package/exports/define/lion-menu.js +0 -3
  215. package/exports/define/lion-navigation-bar.js +0 -3
  216. package/exports/define/lion-toolbar.js +0 -3
  217. package/exports/define/lion-tree.js +0 -3
  218. package/exports/menu.js +0 -6
  219. package/exports/navigation-bar.js +0 -1
  220. package/menu.js +0 -6
  221. package/navigation-bar.js +0 -1
@@ -3,7 +3,6 @@ import { deepContains } from './utils/deep-contains.js';
3
3
  import { overlayShadowDomStyle } from './overlayShadowDomStyle.js';
4
4
  import { _adoptStyleUtils } from './utils/adopt-styles.js';
5
5
  import { getFocusableElements } from './utils/get-focusable-elements.js';
6
- import { isEqualConfig } from './utils/is-equal-config.js';
7
6
 
8
7
  /**
9
8
  * @typedef {import('@lion/ui/types/overlays.js').OverlayPhase} OverlayPhase
@@ -33,37 +32,27 @@ import { isEqualConfig } from './utils/is-equal-config.js';
33
32
  * `<slot name="my-content">` belonging to `<div slot="content"/>` will be wrapped with wrappingDialogNodeL1 and contentWrapperNodeL2
34
33
  * inside shadow dom. With the help of temp markers, `<slot name="my-content">`'s original position will be respected.
35
34
  *
36
- * @param {{
37
- * wrappingDialogNodeL1: HTMLDialogElement|HTMLDivElement;
38
- * contentWrapperNodeL2:Element;
39
- * contentNodeL3: Element;
40
- * }} opts
35
+ * @param {{ wrappingDialogNodeL1:HTMLDialogElement|HTMLDivElement; contentWrapperNodeL2:Element; contentNodeL3: Element }} opts
41
36
  */
42
37
  function rearrangeNodes({ wrappingDialogNodeL1, contentWrapperNodeL2, contentNodeL3 }) {
43
- // if contentWrapperNode is provided by the user,
44
- // we assume it lives in shadow dom around a slot.
45
- const hasLegacyMethodOfProvidingWrapperNode = Boolean(contentWrapperNodeL2.isConnected);
46
- // We could be initialized via a directive (in offline dom). It's important that we know about the parents,
47
- // as we cannot deal with single content nodes
48
- const hasContentNodeAttachmentPoints = Boolean(contentNodeL3.parentNode);
49
-
50
- if (!hasLegacyMethodOfProvidingWrapperNode && !hasContentNodeAttachmentPoints) {
38
+ if (!(contentWrapperNodeL2.isConnected || contentNodeL3.isConnected)) {
51
39
  throw new Error(
52
- '[OverlayController] Could not find a render target, makes sure contentNode has a parent element (or contentWrapperNode is connected)',
40
+ '[OverlayController] Could not find a render target, since the provided contentNode is not connected to the DOM. Make sure that it is connected, e.g. by doing "document.body.appendChild(contentNode)", before passing it on.',
53
41
  );
54
42
  }
55
43
 
56
- /** @type {Node} */
57
44
  let parentElement;
58
- const tempMarker = document.createComment('overlay-insertion-marker');
45
+ const tempMarker = document.createComment('tempMarker');
59
46
 
60
- if (hasLegacyMethodOfProvidingWrapperNode) {
47
+ if (contentWrapperNodeL2.isConnected) {
61
48
  // This is the case when contentWrapperNode (living in shadow dom, wrapping <slot name="my-content-outlet">) is already provided via controller.
62
49
  parentElement = contentWrapperNodeL2.parentElement || contentWrapperNodeL2.getRootNode();
63
50
  parentElement.insertBefore(tempMarker, contentWrapperNodeL2);
64
51
  // Wrap...
65
52
  wrappingDialogNodeL1.appendChild(contentWrapperNodeL2);
66
- } else {
53
+ }
54
+ // if contentNodeL3.isConnected
55
+ else {
67
56
  const contentIsProjected = contentNodeL3.assignedSlot;
68
57
  if (contentIsProjected) {
69
58
  parentElement =
@@ -104,22 +93,7 @@ function rearrangeNodes({ wrappingDialogNodeL1, contentWrapperNodeL2, contentNod
104
93
  * ```
105
94
  */
106
95
  parentElement.insertBefore(wrappingDialogNodeL1, tempMarker);
107
-
108
- return function cleanup() {
109
- if (hasLegacyMethodOfProvidingWrapperNode) {
110
- parentElement?.insertBefore(contentWrapperNodeL2, tempMarker);
111
- } else {
112
- parentElement?.insertBefore(contentNodeL3, tempMarker);
113
- if (parentElement.contains(contentWrapperNodeL2)) {
114
- contentWrapperNodeL2.remove();
115
- }
116
- }
117
- parentElement?.removeChild(tempMarker);
118
-
119
- if (parentElement.contains(wrappingDialogNodeL1)) {
120
- parentElement?.removeChild(wrappingDialogNodeL1);
121
- }
122
- };
96
+ parentElement?.removeChild(tempMarker);
123
97
  }
124
98
 
125
99
  /**
@@ -132,21 +106,6 @@ async function preloadPopper() {
132
106
 
133
107
  const childDialogsClosedInEventLoopWeakmap = new WeakMap();
134
108
 
135
- /**
136
- * @param {HTMLElement | null} el
137
- * @param {string} selector
138
- * @returns {HTMLElement | null}
139
- */
140
- function deepClosest(el, selector) {
141
- // @ts-ignore - comparing HTMLElement to document/window for safety
142
- return (
143
- // @ts-ignore - type comparison check
144
- (el && el !== document && el !== window && el.closest(selector)) ||
145
- // @ts-ignore - host property on ShadowRoot
146
- deepClosest(el.getRootNode().host, selector)
147
- );
148
- }
149
-
150
109
  /**
151
110
  * OverlayController is the fundament for every single type of overlay. With the right
152
111
  * configuration, it can be used to build (modal) dialogs, tooltips, dropdowns, popovers,
@@ -237,21 +196,10 @@ export class OverlayController extends EventTarget {
237
196
  placement: 'center',
238
197
  },
239
198
  zIndex: 9999,
240
- // TODO: rename to isActivated
241
- isOpenable: true,
242
- focusContentOnOpen: false,
243
- // This means that content will have visually-hidden / sr-only styles. This is handy for:
244
- // - hidden menus that should be "indexable" by screen readers (like the Links list in VO: https://support.apple.com/en-gb/guide/voiceover/mchlp2719/mac)
245
- // - "more" menus that put content in a dropdown that do not fit in the current row
246
- // - menus that should generally open on tab.
247
- // @ts-ignore - hideVisually is an extension property
248
- hideVisually: false,
249
- requireConnectedNodes: true,
250
199
  };
251
200
 
252
201
  /** @protected */
253
202
  this._contentId = `overlay-content--${Math.random().toString(36).slice(2, 10)}`;
254
-
255
203
  /** @private */
256
204
  this.__originalAttrs = new Map();
257
205
  /** @private */
@@ -272,8 +220,6 @@ export class OverlayController extends EventTarget {
272
220
  this.__escKeyHandlerCalled = false;
273
221
  }
274
222
 
275
- #hasSetup = false;
276
-
277
223
  /**
278
224
  * The invokerNode
279
225
  * @type {HTMLElement | undefined}
@@ -292,7 +238,7 @@ export class OverlayController extends EventTarget {
292
238
 
293
239
  /**
294
240
  * Determines the connection point in DOM (body vs next to invoker).
295
- * @type {'global' | 'local' | 'custom' | undefined}
241
+ * @type {'global' | 'local' | undefined}
296
242
  */
297
243
  get placementMode() {
298
244
  return this.config?.placementMode;
@@ -517,14 +463,17 @@ export class OverlayController extends EventTarget {
517
463
  * @param { OverlayConfig } cfgToAdd
518
464
  */
519
465
  updateConfig(cfgToAdd) {
466
+ // Teardown all previous configs
467
+ this.teardown();
468
+
520
469
  /**
521
470
  * @type {OverlayConfig}
522
471
  * @private
523
472
  */
524
- const prevConfig = this.config;
473
+ this.__prevConfig = this.config;
525
474
 
526
475
  /** @type {OverlayConfig} */
527
- const newConfig = {
476
+ this.config = {
528
477
  ...this._defaultConfig, // our basic ingredients
529
478
  ...this.__sharedConfig, // the initial configured overlayController
530
479
  ...cfgToAdd, // the added config
@@ -540,18 +489,12 @@ export class OverlayController extends EventTarget {
540
489
  },
541
490
  };
542
491
 
543
- const shouldUpdate = !this.#hasSetup || !isEqualConfig(prevConfig, newConfig);
544
- if (!shouldUpdate) return;
545
- // Teardown all previous configs
546
-
547
- this.teardown();
548
-
549
- this.config = newConfig;
550
-
551
492
  /** @private */
552
493
  this.__validateConfiguration(this.config);
553
494
  /** @protected */
554
495
  this._init();
496
+ /** @private */
497
+ this.__elementToFocusAfterHide = undefined;
555
498
 
556
499
  if (!this.#isRegisteredOnManager()) {
557
500
  this.manager.add(this);
@@ -573,7 +516,7 @@ export class OverlayController extends EventTarget {
573
516
  '[OverlayController] You need to provide a .placementMode ("global"|"local")',
574
517
  );
575
518
  }
576
- if (!['global', 'local', 'custom'].includes(newConfig.placementMode)) {
519
+ if (!['global', 'local'].includes(newConfig.placementMode)) {
577
520
  throw new Error(
578
521
  `[OverlayController] "${newConfig.placementMode}" is not a valid .placementMode, use ("global"|"local")`,
579
522
  );
@@ -592,23 +535,14 @@ export class OverlayController extends EventTarget {
592
535
  * @protected
593
536
  */
594
537
  _init() {
595
- if (!this.config.isOpenable) return;
596
-
597
- // TODO: should not be behind a flag when we are fully tearing down...
598
- // TODO 2: When we move away from dialog (use popover to paint to top layer)
599
- // and popper.js, (use popover and anchor positioning and popperjs as fallback, but we can put styles on contentNode),
600
- // we dont need to create complex wrappers
601
- // if (!this.__contentHasBeenInitialized) {
602
- this.__initContentDomStructure();
603
- // this.__contentHasBeenInitialized = true;
604
- // }
605
- this.__initVisibility();
538
+ if (!this.__contentHasBeenInitialized) {
539
+ this.__initContentDomStructure();
540
+ this.__contentHasBeenInitialized = true;
541
+ }
606
542
 
607
543
  // Reset all positioning styles (local, c.q. Popper) and classes (global)
608
- if (this.contentWrapperNode !== this.contentNode) {
609
- this.contentWrapperNode.removeAttribute('style');
610
- this.contentWrapperNode.removeAttribute('class');
611
- }
544
+ this.contentWrapperNode.removeAttribute('style');
545
+ this.contentWrapperNode.removeAttribute('class');
612
546
 
613
547
  if (this.placementMode === 'local') {
614
548
  // Lazily load Popper as soon as the first local overlay is used...
@@ -618,27 +552,8 @@ export class OverlayController extends EventTarget {
618
552
  }
619
553
  this.__handleOverlayStyles({ phase: 'init' });
620
554
  this._handleFeatures({ phase: 'init' });
621
-
622
- /** @private */
623
- this.__elementToFocusAfterHide = undefined;
624
-
625
- this.contentNode.dataset.content = '';
626
-
627
- // Use event delegation to listen for clicks on close buttons...
628
- this.contentNode.addEventListener('click', this.#hideOnCloseButtonClick);
629
-
630
- this.#hasSetup = true;
631
555
  }
632
556
 
633
- #hideOnCloseButtonClick = (/** @type {{ target: any; }} */ ev) => {
634
- const isOurCloseButton =
635
- ev.target.hasAttribute('data-close') &&
636
- deepClosest(ev.target, '[data-content]') === this.contentNode;
637
- if (!isOurCloseButton) return;
638
-
639
- this.hide();
640
- };
641
-
642
557
  /**
643
558
  * @param {{ phase: OverlayPhase }} config
644
559
  * @private
@@ -679,108 +594,84 @@ export class OverlayController extends EventTarget {
679
594
  * @private
680
595
  */
681
596
  __initContentDomStructure() {
682
- if (this.placementMode !== 'custom') {
683
- const wrappingDialogElement = document.createElement('dialog');
684
- // We use a dialog for its visual capabilities: it renders to the top layer.
685
- // A11y will depend on the type of overlay and is arranged on contentNode level.
686
- // Also see: https://www.scottohara.me/blog/2019/03/05/open-dialog.html
687
- //
688
- // The role="dialog" is set on the contentNode (or another role), so role="none"
689
- // is valid here, although AXE complains about this setup.
690
- // For now we need to add `ignoredRules: ['aria-allowed-role']` in your AXE tests.
691
- // see: https://lion.js.org/fundamentals/systems/overlays/rationale/#considerations
692
- wrappingDialogElement.setAttribute('role', 'none');
693
- wrappingDialogElement.setAttribute('data-overlay-outer-wrapper', '');
694
- // N.B. position: fixed is needed to escape out of 'overflow: hidden'
695
- // We give a high z-index for non-modal dialogs, so that we at least win from all siblings of our
696
- // parent stacking context
697
- // padding reset so we don't get a weird dialog visual square showing up
698
- wrappingDialogElement.style.cssText = `display:none; z-index: ${this.config.zIndex}; padding: 0;`;
699
- // @ts-ignore - 'custom' is a valid extension of placementMode
700
- if (this.config.placementMode === 'custom') {
701
- // The user should have full freedom to control the content node, so its wrapper nodes should remain neutral.
702
- wrappingDialogElement.style.cssText += `position: static;`;
703
- }
704
- this.__wrappingDialogNode = wrappingDialogElement;
597
+ const wrappingDialogElement = document.createElement('dialog');
598
+ // We use a dialog for its visual capabilities: it renders to the top layer.
599
+ // A11y will depend on the type of overlay and is arranged on contentNode level.
600
+ // Also see: https://www.scottohara.me/blog/2019/03/05/open-dialog.html
601
+ //
602
+ // The role="dialog" is set on the contentNode (or another role), so role="none"
603
+ // is valid here, although AXE complains about this setup.
604
+ // For now we need to add `ignoredRules: ['aria-allowed-role']` in your AXE tests.
605
+ // see: https://lion.js.org/fundamentals/systems/overlays/rationale/#considerations
606
+ wrappingDialogElement.setAttribute('role', 'none');
607
+ wrappingDialogElement.setAttribute('data-overlay-outer-wrapper', '');
608
+ // N.B. position: fixed is needed to escape out of 'overflow: hidden'
609
+ // We give a high z-index for non-modal dialogs, so that we at least win from all siblings of our
610
+ // parent stacking context
611
+ // padding reset so we don't get a weird dialog visual square showing up
612
+ wrappingDialogElement.style.cssText = `display:none; z-index: ${this.config.zIndex}; padding: 0;`;
613
+ this.__wrappingDialogNode = wrappingDialogElement;
705
614
 
706
- /**
707
- * Based on the configuration of the developer, multiple scenarios are accounted for
708
- * A. We already have a contentWrapperNode ()
709
- */
710
- if (!this.config?.contentWrapperNode) {
711
- this.__contentWrapperNode = document.createElement('div');
712
- }
713
- this.contentWrapperNode.setAttribute('data-id', 'content-wrapper');
714
- // 'hack' that makes sure popperjs (that is applied one level lower) works correctly in deeply nested shadow roots
715
- this.contentWrapperNode.style.transform = 'translateZ(0px)';
716
-
717
- this.__rearrangeNodesCleanup = rearrangeNodes({
718
- wrappingDialogNodeL1: wrappingDialogElement,
719
- contentWrapperNodeL2: this.contentWrapperNode,
720
- contentNodeL3: this.contentNode,
721
- // requireConnectedNodes: Boolean(this.config.requireConnectedNodes),
722
- });
723
- wrappingDialogElement.open = true;
615
+ /**
616
+ * Based on the configuration of the developer, multiple scenarios are accounted for
617
+ * A. We already have a contentWrapperNode ()
618
+ */
619
+ if (!this.config?.contentWrapperNode) {
620
+ this.__contentWrapperNode = document.createElement('div');
621
+ }
622
+ this.contentWrapperNode.setAttribute('data-id', 'content-wrapper');
623
+ // 'hack' that makes sure popperjs (that is applied one level lower) works correctly in deeply nested shadow roots
624
+ this.contentWrapperNode.style.transform = 'translateZ(0px)';
724
625
 
725
- if (this.isTooltip) {
726
- // needed to prevent tooltip getting focus in Safari and Firefox
727
- wrappingDialogElement.setAttribute('tabindex', '-1');
728
- }
729
- this.contentWrapperNode.style.zIndex = '1';
730
- if (getComputedStyle(this.contentNode).position === 'absolute') {
731
- // Having a _contWrapperNode and a contentNode with 'position:absolute' results in
732
- // computed height of 0...
733
- this.contentNode.style.position = 'static';
734
- }
626
+ rearrangeNodes({
627
+ wrappingDialogNodeL1: wrappingDialogElement,
628
+ contentWrapperNodeL2: this.contentWrapperNode,
629
+ contentNodeL3: this.contentNode,
630
+ });
631
+ wrappingDialogElement.open = true;
735
632
 
736
- // Here we prevent any interference of the native <dialog> element with the keyboard behavior
737
- // as defined by the OverlayController. This is needed until we can configure `closedby="none"`
738
- // on the native dialog for all browsers: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby
739
- const hasClosedBySupport = HTMLDialogElement && 'closedBy' in HTMLDialogElement.prototype;
740
- if (hasClosedBySupport) {
741
- // @ts-ignore - closedBy is a newer property not in all TypeScript versions
742
- wrappingDialogElement.closedBy = 'none';
743
- } else {
744
- wrappingDialogElement.addEventListener(
745
- 'keydown',
746
- (/** @type {* & KeyboardEvent} */ event) => {
747
- if (event.key === 'Escape') {
748
- event.preventDefault();
749
- }
750
- },
751
- );
752
- wrappingDialogElement.addEventListener(
753
- 'keyup',
754
- (/** @type {* & KeyboardEvent} */ event) => {
755
- if (event.key === 'Escape') {
756
- event.preventDefault();
757
- }
758
- },
759
- );
760
- wrappingDialogElement.addEventListener('cancel', event => {
761
- event.stopPropagation();
762
- });
763
- wrappingDialogElement.addEventListener('close', event => {
764
- event.stopPropagation();
765
- });
766
- }
767
- } else {
768
- // quick hack to make none-popper/global flows work...
769
- this.__contentWrapperNode = this.contentNode;
770
- this.__wrappingDialogNode = this.contentNode;
633
+ if (this.isTooltip) {
634
+ // needed to prevent tooltip getting focus in Safari and Firefox
635
+ wrappingDialogElement.setAttribute('tabindex', '-1');
771
636
  }
772
- }
773
637
 
774
- __initVisibility() {
775
- // TODO: allow defaultOpen?
776
- /** @type {HTMLDialogElement} */ (this.__wrappingDialogNode).style.display = 'none';
777
- }
638
+ this.__wrappingDialogNode.style.display = 'none';
639
+ this.contentWrapperNode.style.zIndex = '1';
640
+
641
+ if (getComputedStyle(this.contentNode).position === 'absolute') {
642
+ // Having a _contWrapperNode and a contentNode with 'position:absolute' results in
643
+ // computed height of 0...
644
+ this.contentNode.style.position = 'static';
645
+ }
778
646
 
779
- // Clean up the DOM structure, leaving it as we found it.
780
- // TODO: __teardownContentDomStructure method... use Resettable (rename ro Restorable) found in VisibilitToggleCtrl
781
- __teardownVisibility() {
782
- // TODO: full structure. For now we just reset display prop...
783
- /** @type {HTMLDialogElement} */ (this.__wrappingDialogNode).style.display = '';
647
+ // Here we prevent any interference of the native <dialog> element with the keyboard behavior
648
+ // as defined by the OverlayController. This is needed until we can configure `closedby="none"`
649
+ // on the native dialog for all browsers: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby
650
+ const hasClosedBySupport = HTMLDialogElement && 'closedBy' in HTMLDialogElement.prototype;
651
+ if (hasClosedBySupport) {
652
+ // @ts-expect-error
653
+ wrappingDialogElement.closedBy = 'none';
654
+ } else {
655
+ wrappingDialogElement.addEventListener(
656
+ 'keydown',
657
+ (/** @type {* & KeyboardEvent} */ event) => {
658
+ if (event.key === 'Escape') {
659
+ event.preventDefault();
660
+ }
661
+ },
662
+ );
663
+ wrappingDialogElement.addEventListener('keyup', (/** @type {* & KeyboardEvent} */ event) => {
664
+ if (event.key === 'Escape') {
665
+ event.preventDefault();
666
+ }
667
+ });
668
+ wrappingDialogElement.addEventListener('cancel', event => {
669
+ event.stopPropagation();
670
+ });
671
+ wrappingDialogElement.addEventListener('close', event => {
672
+ event.stopPropagation();
673
+ });
674
+ }
784
675
  }
785
676
 
786
677
  /**
@@ -851,7 +742,6 @@ export class OverlayController extends EventTarget {
851
742
  * @private
852
743
  */
853
744
  __storeOriginalAttrs(node, attrs) {
854
- /** @type {Record<string, string | null>} */
855
745
  const attrMap = {};
856
746
  attrs.forEach(attrName => {
857
747
  attrMap[attrName] = node.getAttribute(attrName);
@@ -908,7 +798,6 @@ export class OverlayController extends EventTarget {
908
798
  if ('HTMLDialogElement' in window && this.__wrappingDialogNode instanceof HTMLDialogElement) {
909
799
  this.__wrappingDialogNode.open = true;
910
800
  }
911
-
912
801
  // @ts-ignore
913
802
  this.__wrappingDialogNode.style.display = '';
914
803
  this._keepBodySize({ phase: 'before-show' });
@@ -922,14 +811,6 @@ export class OverlayController extends EventTarget {
922
811
  contentNode: this.contentNode,
923
812
  });
924
813
  }
925
-
926
- // a styling/debug hook for open state
927
- this.contentNode.setAttribute('data-open', '');
928
-
929
- if (this.config.focusContentOnOpen) {
930
- this.contentNode.focus();
931
- }
932
-
933
814
  /** @type {function} */
934
815
  (this._showResolve)();
935
816
  }
@@ -962,7 +843,6 @@ export class OverlayController extends EventTarget {
962
843
  await this.__createPopperInstance();
963
844
  this._popper.forceUpdate();
964
845
  }
965
- // N.B. leave it up to the user if placementMode === 'custom'
966
846
  }
967
847
 
968
848
  /**
@@ -970,7 +850,9 @@ export class OverlayController extends EventTarget {
970
850
  * @protected
971
851
  */
972
852
  _keepBodySize({ phase }) {
973
- if (!this.preventsScroll) return;
853
+ if (!this.preventsScroll) {
854
+ return;
855
+ }
974
856
 
975
857
  this.manager.requestToKeepBodySize({ phase });
976
858
  }
@@ -980,9 +862,6 @@ export class OverlayController extends EventTarget {
980
862
  * @event hide right after the overlay is hidden
981
863
  */
982
864
  async hide() {
983
- // Function like a no-op for dynamic edge cases...
984
- if (!this.config.isOpenable) return;
985
-
986
865
  this._hideComplete = new Promise(resolve => {
987
866
  this._hideResolve = resolve;
988
867
  });
@@ -1015,6 +894,7 @@ export class OverlayController extends EventTarget {
1015
894
  if ('HTMLDialogElement' in window && this.__wrappingDialogNode instanceof HTMLDialogElement) {
1016
895
  this.__wrappingDialogNode.close();
1017
896
  }
897
+
1018
898
  // @ts-ignore
1019
899
  this.__wrappingDialogNode.style.display = 'none';
1020
900
  this._handleFeatures({ phase: 'hide' });
@@ -1023,8 +903,6 @@ export class OverlayController extends EventTarget {
1023
903
  this._restoreFocus();
1024
904
  }
1025
905
  /** @type {function} */ (this._hideResolve)();
1026
-
1027
- this.contentNode.removeAttribute('data-open');
1028
906
  }
1029
907
 
1030
908
  /**
@@ -1135,24 +1013,23 @@ export class OverlayController extends EventTarget {
1135
1013
  if (this.visibilityTriggerFunction) {
1136
1014
  this._handleVisibilityTriggers({ phase });
1137
1015
  }
1138
-
1139
- if (phase === 'init' && this.config.focusContentOnOpen) {
1140
- this.contentNode?.setAttribute('tabindex', '-1');
1141
- }
1142
-
1143
- if (phase === 'teardown' && this.config.focusContentOnOpen) {
1144
- // TODO: capture initial attr, use Resettable mechanism of VisibilityToggleCtrl in whole Controller
1145
- this.contentNode?.removeAttribute('tabindex');
1146
- }
1147
1016
  }
1148
1017
 
1149
1018
  /**
1150
1019
  * @param {{ phase: OverlayPhase }} config
1151
1020
  */
1152
1021
  _handleVisibilityTriggers({ phase }) {
1153
- if (typeof this.visibilityTriggerFunction !== 'function') return;
1154
-
1155
- this.visibilityTriggerFunction({ controller: this })?.[phase]?.();
1022
+ if (typeof this.visibilityTriggerFunction === 'function') {
1023
+ if (phase === 'init') {
1024
+ this.__visibilityTriggerHandler = this.visibilityTriggerFunction({
1025
+ phase,
1026
+ controller: this,
1027
+ });
1028
+ }
1029
+ if (this.__visibilityTriggerHandler[phase]) {
1030
+ this.__visibilityTriggerHandler[phase]();
1031
+ }
1032
+ }
1156
1033
  }
1157
1034
 
1158
1035
  /**
@@ -1309,9 +1186,7 @@ export class OverlayController extends EventTarget {
1309
1186
  if (phase === 'show') {
1310
1187
  this.#handleShiftKeyPress();
1311
1188
  this.#handleFocusInsideDialog();
1312
- // @ts-ignore - HTMLDialogElement methods
1313
1189
  this.__wrappingDialogNode?.close();
1314
- // @ts-ignore - HTMLDialogElement methods
1315
1190
  this.__wrappingDialogNode?.showModal();
1316
1191
  /**
1317
1192
  * At this moment `#handleFocusInsideDialog` should handle the focus.
@@ -1562,7 +1437,6 @@ export class OverlayController extends EventTarget {
1562
1437
  }
1563
1438
 
1564
1439
  teardown() {
1565
- if (!this.#hasSetup) return;
1566
1440
  this.__handleOverlayStyles({ phase: 'teardown' });
1567
1441
  if (this.isShown) {
1568
1442
  this._keepBodySize({ phase: 'teardown' });
@@ -1571,13 +1445,6 @@ export class OverlayController extends EventTarget {
1571
1445
  if (this.#isRegisteredOnManager()) {
1572
1446
  this.manager.remove(this);
1573
1447
  }
1574
-
1575
- this.contentNode?.removeEventListener('click', this.#hideOnCloseButtonClick);
1576
-
1577
- this.__rearrangeNodesCleanup?.();
1578
- this.__teardownVisibility();
1579
-
1580
- this.#hasSetup = false;
1581
1448
  }
1582
1449
 
1583
1450
  /** @private */