@lion/ui 0.21.1-menu-system.0 → 0.22.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.
- package/components/button/src/LionButton.js +0 -4
- package/components/form/test/lion-form.test.js +4 -3
- package/components/form-core/src/form-group/FormGroupMixin.js +37 -0
- package/components/form-core/test-suites/form-group/FormGroupMixin.suite.js +25 -0
- package/components/input-amount-dropdown/src/LionInputAmountDropdown.js +2 -2
- package/components/input-amount-dropdown/test/lion-input-amount-dropdown-integrations.test.js +12 -3
- package/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.js +10 -3
- package/components/overlays/src/OverlayController.js +111 -296
- package/components/overlays/src/OverlayMixin.js +17 -76
- package/components/overlays/src/utils/adopt-styles.js +1 -1
- package/components/overlays/src/utils/is-equal-config.js +6 -15
- package/components/overlays/test/OverlayController.test.js +24 -144
- package/components/overlays/test-suites/OverlayMixin.suite.js +8 -8
- package/components/overlays/types/ArrowMixinTypes.ts +3 -1
- package/components/overlays/types/OverlayConfig.ts +5 -20
- package/components/overlays/types/OverlayMixinTypes.ts +0 -2
- package/custom-elements.json +6260 -13587
- package/dist-types/components/button/src/LionButton.d.ts +0 -1
- package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
- package/dist-types/components/combobox/src/LionCombobox.d.ts +1 -1
- package/dist-types/components/dialog/src/LionDialog.d.ts +2 -6
- package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
- package/dist-types/components/form-core/src/form-group/FormGroupMixin.d.ts +1 -0
- package/dist-types/components/form-core/src/form-group/FormGroupMixin.d.ts.map +1 -1
- package/dist-types/components/form-core/test-suites/form-group/FormGroupMixin.suite.d.ts.map +1 -1
- package/dist-types/components/input-amount-dropdown/test-suites/LionInputAmountDropdown.suite.d.ts.map +1 -1
- package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +1 -1
- package/dist-types/components/overlays/src/OverlayController.d.ts +5 -22
- package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
- package/dist-types/components/overlays/src/OverlayMixin.d.ts.map +1 -1
- package/dist-types/components/overlays/src/utils/is-equal-config.d.ts.map +1 -1
- package/dist-types/components/overlays/types/ArrowMixinTypes.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayConfig.d.ts +2 -12
- package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +0 -1
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts +2 -6
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
- package/dist-types/components/tooltip/src/LionTooltip.d.ts +1 -1
- package/dist-types/exports/overlays.d.ts +0 -6
- package/dist-types/exports/overlays.d.ts.map +1 -1
- package/dist-types/tsconfig.tsbuildinfo +1 -1
- package/docs/components/checkbox-group/overview.md +2 -2
- package/docs/components/collapsible/overview.md +1 -1
- package/docs/components/combobox/api-table.md +39 -53
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/api-table.md +6 -32
- package/docs/components/dialog/overview.md +1 -1
- package/docs/components/fieldset/overview.md +5 -5
- package/docs/components/form/overview.md +3 -3
- package/docs/components/form/use-cases.md +2 -2
- package/docs/components/icon/use-cases.md +1 -1
- package/docs/components/input/overview.md +1 -1
- package/docs/components/input-amount/overview.md +3 -3
- package/docs/components/input-date/overview.md +3 -3
- package/docs/components/input-datepicker/api-table.md +45 -63
- package/docs/components/input-datepicker/overview.md +5 -5
- package/docs/components/input-email/overview.md +2 -2
- package/docs/components/input-file/overview.md +1 -1
- package/docs/components/input-iban/overview.md +2 -2
- package/docs/components/input-range/overview.md +2 -2
- package/docs/components/input-stepper/overview.md +1 -1
- package/docs/components/input-tel/overview.md +1 -1
- package/docs/components/input-tel/use-cases.md +6 -6
- package/docs/components/input-tel-dropdown/overview.md +1 -1
- package/docs/components/listbox/use-cases.md +1 -1
- package/docs/components/radio-group/overview.md +3 -3
- package/docs/components/select/use-cases.md +1 -1
- package/docs/components/select-rich/api-table.md +33 -50
- package/docs/components/select-rich/use-cases.md +2 -2
- package/docs/components/tabs/examples.md +1 -1
- package/docs/components/tooltip/api-table.md +7 -33
- package/docs/fundamentals/index.md +4 -4
- package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
- package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
- package/docs/fundamentals/rationales/accessibility.md +1 -1
- package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
- package/docs/fundamentals/systems/core/overview.md +1 -1
- package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
- package/docs/fundamentals/systems/form/model-value.md +1 -1
- package/docs/fundamentals/systems/form/overview.md +28 -28
- package/docs/fundamentals/systems/form/validate.md +2 -2
- package/docs/fundamentals/systems/localize/overview.md +4 -4
- package/docs/fundamentals/systems/overlays/api-table.md +43 -46
- package/docs/fundamentals/systems/overlays/configuration.md +2 -2
- package/docs/fundamentals/systems/overlays/overview.md +5 -5
- package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
- package/docs/fundamentals/tools/helpers/overview.md +1 -1
- package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
- package/docs/guides/how-to/create-a-custom-field.md +5 -5
- package/docs/guides/how-to/create-a-form.md +4 -4
- package/docs/guides/how-to/extend-a-native-input.md +2 -2
- package/docs/guides/how-to/index.md +1 -1
- package/docs/guides/index.md +8 -8
- package/docs/guides/principles/index.md +5 -5
- package/docs/guides/principles/styling.md +1 -1
- package/exports/overlays.js +0 -6
- package/overlays.js +0 -6
- package/package.json +6 -2
- package/vscode.css-custom-data.json +5 -0
- package/vscode.html-custom-data.json +789 -0
- package/web-types.json +2016 -0
- package/components/menu/CHANGELOG.md +0 -37
- package/components/menu/README.md +0 -334
- package/components/menu/index.js +0 -2
- package/components/menu/lion-menu.js +0 -3
- package/components/menu/package.json +0 -46
- package/components/menu/requirements.temp.md +0 -12
- package/components/menu/src/InteractiveListMixin.js +0 -840
- package/components/menu/src/LionMenu.js +0 -421
- package/components/menu/src/MoreButtonMenuMixin.js +0 -278
- package/components/menu/src/MultiLevelListMixin.js +0 -305
- package/components/menu/src/utils/isInView.js +0 -26
- package/components/menu/src/utils/listItemInteractions.js +0 -124
- package/components/menu/test/InteractiveListMixin.test.js +0 -3
- package/components/menu/test/MoreButtonMenuMixin.test.js +0 -4
- package/components/menu/test/MultiLevelListMixin.test.js +0 -3
- package/components/menu/test/lion-menu-interactions.test.js +0 -3
- package/components/menu/test/lion-menu.test.js +0 -14
- package/components/menu/test-helpers/mimicKeyPress.js +0 -9
- package/components/menu/test-suites/InteractiveListMixin.suite.js +0 -454
- package/components/menu/test-suites/LionMenu.suite.js +0 -114
- package/components/menu/test-suites/LionMenuInteractions.suite.js +0 -583
- package/components/menu/test-suites/MoreButtonMenuMixin.suite.js +0 -372
- package/components/menu/test-suites/MultiLevelListMixin.suite.js +0 -303
- package/components/menu/types/InteractiveListMixinTypes.d.ts +0 -84
- package/components/overlays/NEXT.md +0 -49
- package/dist-types/components/menu/index.d.ts +0 -3
- package/dist-types/components/menu/index.d.ts.map +0 -1
- package/dist-types/components/menu/lion-menu.d.ts +0 -2
- package/dist-types/components/menu/lion-menu.d.ts.map +0 -1
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +0 -39
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +0 -1
- package/dist-types/components/menu/src/utils/isInView.d.ts +0 -9
- package/dist-types/components/menu/src/utils/isInView.d.ts.map +0 -1
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +0 -48
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +0 -1
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts +0 -2
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts +0 -2
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts +0 -2
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts +0 -2
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts.map +0 -1
- package/dist-types/components/menu/test/lion-menu.test.d.ts +0 -2
- package/dist-types/components/menu/test/lion-menu.test.d.ts.map +0 -1
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts +0 -7
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +0 -11
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts +0 -9
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts +0 -5
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts +0 -11
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts.map +0 -1
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts +0 -11
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts.map +0 -1
- package/dist-types/exports/define/lion-item.d.ts +0 -2
- package/dist-types/exports/define/lion-item.d.ts.map +0 -1
- package/dist-types/exports/define/lion-menu.d.ts +0 -2
- package/dist-types/exports/define/lion-menu.d.ts.map +0 -1
- package/dist-types/exports/menu-test-suites.d.ts +0 -4
- package/dist-types/exports/menu-test-suites.d.ts.map +0 -1
- package/dist-types/exports/menu.d.ts +0 -3
- package/dist-types/exports/menu.d.ts.map +0 -1
- package/docs/components/menu/api-table.md +0 -121
- package/docs/components/menu/applyMenuDemoStyles.js +0 -173
- package/docs/components/menu/extensions.md +0 -262
- package/docs/components/menu/index.md +0 -13
- package/docs/components/menu/overview.md +0 -422
- package/docs/components/menu/src/DemoNavigationBar.js +0 -441
- package/docs/components/menu/src/DemoSpinButton.js +0 -215
- package/docs/components/menu/src/DemoToolbar.js +0 -101
- package/docs/components/menu/src/DemoTree.js +0 -55
- package/docs/components/menu/src/navigation-bar-types.js +0 -41
- package/docs/components/menu/use-cases.md +0 -74
- package/exports/define/lion-item.js +0 -3
- package/exports/define/lion-menu.js +0 -3
- package/exports/menu-test-suites.js +0 -4
- package/exports/menu.js +0 -3
- package/menu-test-suites.js +0 -4
- package/menu.js +0 -3
|
@@ -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
|
-
|
|
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,
|
|
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('
|
|
45
|
+
const tempMarker = document.createComment('tempMarker');
|
|
59
46
|
|
|
60
|
-
if (
|
|
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
|
-
}
|
|
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,
|
|
@@ -277,20 +236,6 @@ export class OverlayController extends EventTarget {
|
|
|
277
236
|
placement: 'center',
|
|
278
237
|
},
|
|
279
238
|
zIndex: 9999,
|
|
280
|
-
isActivated: true,
|
|
281
|
-
elementToFocusOnShow: false,
|
|
282
|
-
// This means that content will have visually-hidden / sr-only styles. This is handy for:
|
|
283
|
-
// - 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)
|
|
284
|
-
// - "more" menus that put content in a dropdown that do not fit in the current row
|
|
285
|
-
// - menus that should generally open on tab.
|
|
286
|
-
// @ts-ignore - hideVisually is an extension property
|
|
287
|
-
hideVisually: false,
|
|
288
|
-
requireConnectedNodes: true,
|
|
289
|
-
|
|
290
|
-
// hides children overlays when parent is closed...
|
|
291
|
-
syncChildrenCloseState: false,
|
|
292
|
-
// // In next major, we remove this prop. Now we disable it for backwards compatibility and enable it in the places we need it internally
|
|
293
|
-
// _shouldTeardownDomStructure: false,
|
|
294
239
|
};
|
|
295
240
|
|
|
296
241
|
this._contentId = `overlay-content--${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -304,8 +249,6 @@ export class OverlayController extends EventTarget {
|
|
|
304
249
|
this.__escKeyHandlerCalled = false;
|
|
305
250
|
}
|
|
306
251
|
|
|
307
|
-
__hasSetup = false;
|
|
308
|
-
|
|
309
252
|
/**
|
|
310
253
|
* The invokerNode
|
|
311
254
|
* @type {HTMLElement | undefined}
|
|
@@ -324,7 +267,7 @@ export class OverlayController extends EventTarget {
|
|
|
324
267
|
|
|
325
268
|
/**
|
|
326
269
|
* Determines the connection point in DOM (body vs next to invoker).
|
|
327
|
-
* @type {'global' | 'local' |
|
|
270
|
+
* @type {'global' | 'local' | undefined}
|
|
328
271
|
*/
|
|
329
272
|
get placementMode() {
|
|
330
273
|
return this.config?.placementMode;
|
|
@@ -549,14 +492,17 @@ export class OverlayController extends EventTarget {
|
|
|
549
492
|
* @param { OverlayConfig } cfgToAdd
|
|
550
493
|
*/
|
|
551
494
|
updateConfig(cfgToAdd) {
|
|
495
|
+
// Teardown all previous configs
|
|
496
|
+
this.teardown();
|
|
497
|
+
|
|
552
498
|
/**
|
|
553
499
|
* @type {OverlayConfig}
|
|
554
500
|
* @private
|
|
555
501
|
*/
|
|
556
|
-
|
|
502
|
+
this.__prevConfig = this.config;
|
|
557
503
|
|
|
558
504
|
/** @type {OverlayConfig} */
|
|
559
|
-
|
|
505
|
+
this.config = {
|
|
560
506
|
...this._defaultConfig, // our basic ingredients
|
|
561
507
|
...this.__sharedConfig, // the initial configured overlayController
|
|
562
508
|
...cfgToAdd, // the added config
|
|
@@ -572,18 +518,12 @@ export class OverlayController extends EventTarget {
|
|
|
572
518
|
},
|
|
573
519
|
};
|
|
574
520
|
|
|
575
|
-
const shouldUpdate = !this.__hasSetup || !isEqualConfig(prevConfig, newConfig);
|
|
576
|
-
if (!shouldUpdate) return;
|
|
577
|
-
// Teardown all previous configs
|
|
578
|
-
|
|
579
|
-
this.teardown();
|
|
580
|
-
|
|
581
|
-
this.config = newConfig;
|
|
582
|
-
|
|
583
521
|
/** @private */
|
|
584
522
|
this.__validateConfiguration(this.config);
|
|
585
523
|
/** @protected */
|
|
586
524
|
this._init();
|
|
525
|
+
/** @private */
|
|
526
|
+
this.__elementToFocusAfterHide = undefined;
|
|
587
527
|
|
|
588
528
|
if (!this.#isRegisteredOnManager()) {
|
|
589
529
|
this.manager.add(this);
|
|
@@ -605,7 +545,7 @@ export class OverlayController extends EventTarget {
|
|
|
605
545
|
'[OverlayController] You need to provide a .placementMode ("global"|"local")',
|
|
606
546
|
);
|
|
607
547
|
}
|
|
608
|
-
if (!['global', 'local'
|
|
548
|
+
if (!['global', 'local'].includes(newConfig.placementMode)) {
|
|
609
549
|
throw new Error(
|
|
610
550
|
`[OverlayController] "${newConfig.placementMode}" is not a valid .placementMode, use ("global"|"local")`,
|
|
611
551
|
);
|
|
@@ -624,23 +564,14 @@ export class OverlayController extends EventTarget {
|
|
|
624
564
|
* @protected
|
|
625
565
|
*/
|
|
626
566
|
_init() {
|
|
627
|
-
if (!this.
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
// and popper.js, (use popover and anchor positioning and popperjs as fallback, but we can put styles on contentNode),
|
|
632
|
-
// we dont need to create complex wrappers
|
|
633
|
-
// if (!this.__contentHasBeenInitialized) {
|
|
634
|
-
this.__initContentDomStructure();
|
|
635
|
-
// this.__contentHasBeenInitialized = true;
|
|
636
|
-
// }
|
|
637
|
-
this.__initVisibility();
|
|
567
|
+
if (!this.__contentHasBeenInitialized) {
|
|
568
|
+
this.__initContentDomStructure();
|
|
569
|
+
this.__contentHasBeenInitialized = true;
|
|
570
|
+
}
|
|
638
571
|
|
|
639
572
|
// Reset all positioning styles (local, c.q. Popper) and classes (global)
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
this.contentWrapperNode.removeAttribute('class');
|
|
643
|
-
}
|
|
573
|
+
this.contentWrapperNode.removeAttribute('style');
|
|
574
|
+
this.contentWrapperNode.removeAttribute('class');
|
|
644
575
|
|
|
645
576
|
if (this.placementMode === 'local') {
|
|
646
577
|
// Lazily load Popper as soon as the first local overlay is used...
|
|
@@ -650,27 +581,8 @@ export class OverlayController extends EventTarget {
|
|
|
650
581
|
}
|
|
651
582
|
this.__handleOverlayStyles({ phase: 'init' });
|
|
652
583
|
this._handleFeatures({ phase: 'init' });
|
|
653
|
-
|
|
654
|
-
/** @private */
|
|
655
|
-
this.__elementToFocusAfterHide = undefined;
|
|
656
|
-
|
|
657
|
-
this.contentNode.dataset.content = '';
|
|
658
|
-
|
|
659
|
-
// Use event delegation to listen for clicks on close buttons...
|
|
660
|
-
this.contentNode.addEventListener('click', this.#hideOnCloseButtonClick);
|
|
661
|
-
|
|
662
|
-
this.__hasSetup = true;
|
|
663
584
|
}
|
|
664
585
|
|
|
665
|
-
#hideOnCloseButtonClick = (/** @type {{ target: any; }} */ ev) => {
|
|
666
|
-
const isOurCloseButton =
|
|
667
|
-
ev.target.hasAttribute('data-close') &&
|
|
668
|
-
deepClosest(ev.target, '[data-content]') === this.contentNode;
|
|
669
|
-
if (!isOurCloseButton) return;
|
|
670
|
-
|
|
671
|
-
this.hide();
|
|
672
|
-
};
|
|
673
|
-
|
|
674
586
|
/**
|
|
675
587
|
* @param {{ phase: OverlayPhase }} config
|
|
676
588
|
* @private
|
|
@@ -711,108 +623,84 @@ export class OverlayController extends EventTarget {
|
|
|
711
623
|
* @private
|
|
712
624
|
*/
|
|
713
625
|
__initContentDomStructure() {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
// @ts-ignore - 'custom' is a valid extension of placementMode
|
|
732
|
-
if (this.config.placementMode === 'custom') {
|
|
733
|
-
// The user should have full freedom to control the content node, so its wrapper nodes should remain neutral.
|
|
734
|
-
wrappingDialogElement.style.cssText += `position: static;`;
|
|
735
|
-
}
|
|
736
|
-
this.__wrappingDialogNode = wrappingDialogElement;
|
|
626
|
+
const wrappingDialogElement = document.createElement('dialog');
|
|
627
|
+
// We use a dialog for its visual capabilities: it renders to the top layer.
|
|
628
|
+
// A11y will depend on the type of overlay and is arranged on contentNode level.
|
|
629
|
+
// Also see: https://www.scottohara.me/blog/2019/03/05/open-dialog.html
|
|
630
|
+
//
|
|
631
|
+
// The role="dialog" is set on the contentNode (or another role), so role="none"
|
|
632
|
+
// is valid here, although AXE complains about this setup.
|
|
633
|
+
// For now we need to add `ignoredRules: ['aria-allowed-role']` in your AXE tests.
|
|
634
|
+
// see: https://lion.js.org/fundamentals/systems/overlays/rationale/#considerations
|
|
635
|
+
wrappingDialogElement.setAttribute('role', 'none');
|
|
636
|
+
wrappingDialogElement.setAttribute('data-overlay-outer-wrapper', '');
|
|
637
|
+
// N.B. position: fixed is needed to escape out of 'overflow: hidden'
|
|
638
|
+
// We give a high z-index for non-modal dialogs, so that we at least win from all siblings of our
|
|
639
|
+
// parent stacking context
|
|
640
|
+
// padding reset so we don't get a weird dialog visual square showing up
|
|
641
|
+
wrappingDialogElement.style.cssText = `display:none; z-index: ${this.config.zIndex}; padding: 0;`;
|
|
642
|
+
this.__wrappingDialogNode = wrappingDialogElement;
|
|
737
643
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
this.__rearrangeNodesCleanup = rearrangeNodes({
|
|
750
|
-
wrappingDialogNodeL1: wrappingDialogElement,
|
|
751
|
-
contentWrapperNodeL2: this.contentWrapperNode,
|
|
752
|
-
contentNodeL3: this.contentNode,
|
|
753
|
-
// requireConnectedNodes: Boolean(this.config.requireConnectedNodes),
|
|
754
|
-
});
|
|
755
|
-
wrappingDialogElement.open = true;
|
|
644
|
+
/**
|
|
645
|
+
* Based on the configuration of the developer, multiple scenarios are accounted for
|
|
646
|
+
* A. We already have a contentWrapperNode ()
|
|
647
|
+
*/
|
|
648
|
+
if (!this.config?.contentWrapperNode) {
|
|
649
|
+
this.__contentWrapperNode = document.createElement('div');
|
|
650
|
+
}
|
|
651
|
+
this.contentWrapperNode.setAttribute('data-id', 'content-wrapper');
|
|
652
|
+
// 'hack' that makes sure popperjs (that is applied one level lower) works correctly in deeply nested shadow roots
|
|
653
|
+
this.contentWrapperNode.style.transform = 'translateZ(0px)';
|
|
756
654
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
// Having a _contWrapperNode and a contentNode with 'position:absolute' results in
|
|
764
|
-
// computed height of 0...
|
|
765
|
-
this.contentNode.style.position = 'static';
|
|
766
|
-
}
|
|
655
|
+
rearrangeNodes({
|
|
656
|
+
wrappingDialogNodeL1: wrappingDialogElement,
|
|
657
|
+
contentWrapperNodeL2: this.contentWrapperNode,
|
|
658
|
+
contentNodeL3: this.contentNode,
|
|
659
|
+
});
|
|
660
|
+
wrappingDialogElement.open = true;
|
|
767
661
|
|
|
768
|
-
|
|
769
|
-
//
|
|
770
|
-
|
|
771
|
-
const hasClosedBySupport = HTMLDialogElement && 'closedBy' in HTMLDialogElement.prototype;
|
|
772
|
-
if (hasClosedBySupport) {
|
|
773
|
-
// @ts-ignore - closedBy is a newer property not in all TypeScript versions
|
|
774
|
-
wrappingDialogElement.closedBy = 'none';
|
|
775
|
-
} else {
|
|
776
|
-
wrappingDialogElement.addEventListener(
|
|
777
|
-
'keydown',
|
|
778
|
-
(/** @type {* & KeyboardEvent} */ event) => {
|
|
779
|
-
if (event.key === 'Escape') {
|
|
780
|
-
event.preventDefault();
|
|
781
|
-
}
|
|
782
|
-
},
|
|
783
|
-
);
|
|
784
|
-
wrappingDialogElement.addEventListener(
|
|
785
|
-
'keyup',
|
|
786
|
-
(/** @type {* & KeyboardEvent} */ event) => {
|
|
787
|
-
if (event.key === 'Escape') {
|
|
788
|
-
event.preventDefault();
|
|
789
|
-
}
|
|
790
|
-
},
|
|
791
|
-
);
|
|
792
|
-
wrappingDialogElement.addEventListener('cancel', event => {
|
|
793
|
-
event.stopPropagation();
|
|
794
|
-
});
|
|
795
|
-
wrappingDialogElement.addEventListener('close', event => {
|
|
796
|
-
event.stopPropagation();
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
} else {
|
|
800
|
-
// quick hack to make none-popper/global flows work...
|
|
801
|
-
this.__contentWrapperNode = this.contentNode;
|
|
802
|
-
this.__wrappingDialogNode = this.contentNode;
|
|
662
|
+
if (this.isTooltip) {
|
|
663
|
+
// needed to prevent tooltip getting focus in Safari and Firefox
|
|
664
|
+
wrappingDialogElement.setAttribute('tabindex', '-1');
|
|
803
665
|
}
|
|
804
|
-
}
|
|
805
666
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
/** @type {HTMLDialogElement} */ (this.__wrappingDialogNode).style.display = 'none';
|
|
809
|
-
}
|
|
667
|
+
this.__wrappingDialogNode.style.display = 'none';
|
|
668
|
+
this.contentWrapperNode.style.zIndex = '1';
|
|
810
669
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
670
|
+
if (getComputedStyle(this.contentNode).position === 'absolute') {
|
|
671
|
+
// Having a _contWrapperNode and a contentNode with 'position:absolute' results in
|
|
672
|
+
// computed height of 0...
|
|
673
|
+
this.contentNode.style.position = 'static';
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Here we prevent any interference of the native <dialog> element with the keyboard behavior
|
|
677
|
+
// as defined by the OverlayController. This is needed until we can configure `closedby="none"`
|
|
678
|
+
// on the native dialog for all browsers: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby
|
|
679
|
+
const hasClosedBySupport = HTMLDialogElement && 'closedBy' in HTMLDialogElement.prototype;
|
|
680
|
+
if (hasClosedBySupport) {
|
|
681
|
+
// @ts-ignore [closedBy is feature-detected above]
|
|
682
|
+
wrappingDialogElement.closedBy = 'none';
|
|
683
|
+
} else {
|
|
684
|
+
wrappingDialogElement.addEventListener(
|
|
685
|
+
'keydown',
|
|
686
|
+
(/** @type {* & KeyboardEvent} */ event) => {
|
|
687
|
+
if (event.key === 'Escape') {
|
|
688
|
+
event.preventDefault();
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
);
|
|
692
|
+
wrappingDialogElement.addEventListener('keyup', (/** @type {* & KeyboardEvent} */ event) => {
|
|
693
|
+
if (event.key === 'Escape') {
|
|
694
|
+
event.preventDefault();
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
wrappingDialogElement.addEventListener('cancel', event => {
|
|
698
|
+
event.stopPropagation();
|
|
699
|
+
});
|
|
700
|
+
wrappingDialogElement.addEventListener('close', event => {
|
|
701
|
+
event.stopPropagation();
|
|
702
|
+
});
|
|
703
|
+
}
|
|
816
704
|
}
|
|
817
705
|
|
|
818
706
|
/**
|
|
@@ -906,7 +794,7 @@ export class OverlayController extends EventTarget {
|
|
|
906
794
|
}
|
|
907
795
|
|
|
908
796
|
get isShown() {
|
|
909
|
-
return Boolean(this.__wrappingDialogNode
|
|
797
|
+
return Boolean(this.__wrappingDialogNode?.style.display !== 'none');
|
|
910
798
|
}
|
|
911
799
|
|
|
912
800
|
/**
|
|
@@ -924,13 +812,6 @@ export class OverlayController extends EventTarget {
|
|
|
924
812
|
this._showResolve = resolve;
|
|
925
813
|
});
|
|
926
814
|
|
|
927
|
-
// Teardown() removes the content dom structure we created (contentWrapperNode +
|
|
928
|
-
// wrapping <dialog>). Make sure it exists again before the manager (which sets
|
|
929
|
-
// our elevation on the wrapping node) and the show logic below touch it.
|
|
930
|
-
if (!this.__wrappingDialogNode) {
|
|
931
|
-
this._init();
|
|
932
|
-
}
|
|
933
|
-
|
|
934
815
|
if (this.manager) {
|
|
935
816
|
this.manager.show(this);
|
|
936
817
|
}
|
|
@@ -947,7 +828,6 @@ export class OverlayController extends EventTarget {
|
|
|
947
828
|
if ('HTMLDialogElement' in window && this.__wrappingDialogNode instanceof HTMLDialogElement) {
|
|
948
829
|
this.__wrappingDialogNode.open = true;
|
|
949
830
|
}
|
|
950
|
-
|
|
951
831
|
// @ts-ignore
|
|
952
832
|
this.__wrappingDialogNode.style.display = '';
|
|
953
833
|
this._keepBodySize({ phase: 'before-show' });
|
|
@@ -961,14 +841,6 @@ export class OverlayController extends EventTarget {
|
|
|
961
841
|
contentNode: this.contentNode,
|
|
962
842
|
});
|
|
963
843
|
}
|
|
964
|
-
|
|
965
|
-
// a styling/debug hook for open state
|
|
966
|
-
this.contentNode.setAttribute('data-open', '');
|
|
967
|
-
|
|
968
|
-
if (this.config.elementToFocusOnShow) {
|
|
969
|
-
this.contentNode.focus({ preventScroll: true });
|
|
970
|
-
}
|
|
971
|
-
|
|
972
844
|
/** @type {function} */
|
|
973
845
|
(this._showResolve)();
|
|
974
846
|
}
|
|
@@ -1001,7 +873,6 @@ export class OverlayController extends EventTarget {
|
|
|
1001
873
|
await this.__createPopperInstance();
|
|
1002
874
|
/** @type {import('@popperjs/core').Instance} */ (this._popper).forceUpdate();
|
|
1003
875
|
}
|
|
1004
|
-
// N.B. leave it up to the user if placementMode === 'custom'
|
|
1005
876
|
}
|
|
1006
877
|
|
|
1007
878
|
/**
|
|
@@ -1009,7 +880,9 @@ export class OverlayController extends EventTarget {
|
|
|
1009
880
|
* @protected
|
|
1010
881
|
*/
|
|
1011
882
|
_keepBodySize({ phase }) {
|
|
1012
|
-
if (!this.preventsScroll)
|
|
883
|
+
if (!this.preventsScroll) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
1013
886
|
|
|
1014
887
|
this.manager.requestToKeepBodySize({ phase });
|
|
1015
888
|
}
|
|
@@ -1019,9 +892,6 @@ export class OverlayController extends EventTarget {
|
|
|
1019
892
|
* @event hide right after the overlay is hidden
|
|
1020
893
|
*/
|
|
1021
894
|
async hide() {
|
|
1022
|
-
// Function like a no-op for dynamic edge cases...
|
|
1023
|
-
if (!this.config.isActivated) return;
|
|
1024
|
-
|
|
1025
895
|
this._hideComplete = new Promise(resolve => {
|
|
1026
896
|
this._hideResolve = resolve;
|
|
1027
897
|
});
|
|
@@ -1054,6 +924,7 @@ export class OverlayController extends EventTarget {
|
|
|
1054
924
|
if ('HTMLDialogElement' in window && this.__wrappingDialogNode instanceof HTMLDialogElement) {
|
|
1055
925
|
this.__wrappingDialogNode.close();
|
|
1056
926
|
}
|
|
927
|
+
|
|
1057
928
|
// @ts-ignore
|
|
1058
929
|
this.__wrappingDialogNode.style.display = 'none';
|
|
1059
930
|
this._handleFeatures({ phase: 'hide' });
|
|
@@ -1062,8 +933,6 @@ export class OverlayController extends EventTarget {
|
|
|
1062
933
|
this._restoreFocus();
|
|
1063
934
|
}
|
|
1064
935
|
/** @type {function} */ (this._hideResolve)();
|
|
1065
|
-
|
|
1066
|
-
this.contentNode.removeAttribute('data-open');
|
|
1067
936
|
}
|
|
1068
937
|
|
|
1069
938
|
/**
|
|
@@ -1174,53 +1043,23 @@ export class OverlayController extends EventTarget {
|
|
|
1174
1043
|
if (this.visibilityTriggerFunction) {
|
|
1175
1044
|
this._handleVisibilityTriggers({ phase });
|
|
1176
1045
|
}
|
|
1177
|
-
if (this.config.syncChildrenCloseState) {
|
|
1178
|
-
this._handleSyncChildrenCloseState({ phase });
|
|
1179
|
-
}
|
|
1180
|
-
if (this.config.elementToFocusOnShow) {
|
|
1181
|
-
this._handleElementToFocusOnShow({ phase });
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
/**
|
|
1186
|
-
* @param {{phase: OverlayPhase}} opts
|
|
1187
|
-
* @returns {void}
|
|
1188
|
-
*/
|
|
1189
|
-
_handleElementToFocusOnShow({ phase }) {
|
|
1190
|
-
if (phase === 'init') {
|
|
1191
|
-
this.contentNode?.setAttribute('tabindex', '-1');
|
|
1192
|
-
} else if (phase === 'teardown') {
|
|
1193
|
-
// TODO: capture initial attr, use Resettable mechanism of VisibilityToggleCtrl in whole Controller
|
|
1194
|
-
this.contentNode?.removeAttribute('tabindex');
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
/**
|
|
1199
|
-
* @param {{phase: OverlayPhase}} opts
|
|
1200
|
-
* @returns {void}
|
|
1201
|
-
*/
|
|
1202
|
-
_handleSyncChildrenCloseState({ phase }) {
|
|
1203
|
-
if (phase !== 'hide') return;
|
|
1204
|
-
|
|
1205
|
-
const visibleChildren = this.manager.shownList.filter(
|
|
1206
|
-
ctrl => ctrl !== this && deepContains(this.contentNode, ctrl.contentNode),
|
|
1207
|
-
);
|
|
1208
|
-
visibleChildren.forEach(ctrl => ctrl.hide());
|
|
1209
1046
|
}
|
|
1210
1047
|
|
|
1211
1048
|
/**
|
|
1212
1049
|
* @param {{ phase: OverlayPhase }} config
|
|
1213
1050
|
*/
|
|
1214
1051
|
_handleVisibilityTriggers({ phase }) {
|
|
1215
|
-
if (typeof this.visibilityTriggerFunction
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1052
|
+
if (typeof this.visibilityTriggerFunction === 'function') {
|
|
1053
|
+
if (phase === 'init') {
|
|
1054
|
+
this.__visibilityTriggerHandler = this.visibilityTriggerFunction({
|
|
1055
|
+
phase,
|
|
1056
|
+
controller: this,
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
if (this.__visibilityTriggerHandler[phase]) {
|
|
1060
|
+
this.__visibilityTriggerHandler[phase]();
|
|
1061
|
+
}
|
|
1221
1062
|
}
|
|
1222
|
-
// Here we run the appropriate lifecycle, if defined in our handler
|
|
1223
|
-
this.__visibilityTriggerHandler[phase]?.();
|
|
1224
1063
|
}
|
|
1225
1064
|
|
|
1226
1065
|
/**
|
|
@@ -1377,9 +1216,7 @@ export class OverlayController extends EventTarget {
|
|
|
1377
1216
|
if (phase === 'show') {
|
|
1378
1217
|
this.#handleShiftKeyPress();
|
|
1379
1218
|
this.#handleFocusInsideDialog();
|
|
1380
|
-
// @ts-ignore - HTMLDialogElement methods
|
|
1381
1219
|
this.__wrappingDialogNode?.close();
|
|
1382
|
-
// @ts-ignore - HTMLDialogElement methods
|
|
1383
1220
|
this.__wrappingDialogNode?.showModal();
|
|
1384
1221
|
/**
|
|
1385
1222
|
* At this moment `#handleFocusInsideDialog` should handle the focus.
|
|
@@ -1634,7 +1471,6 @@ export class OverlayController extends EventTarget {
|
|
|
1634
1471
|
}
|
|
1635
1472
|
|
|
1636
1473
|
teardown() {
|
|
1637
|
-
if (!this.__hasSetup) return;
|
|
1638
1474
|
this.__handleOverlayStyles({ phase: 'teardown' });
|
|
1639
1475
|
if (this.isShown) {
|
|
1640
1476
|
this._keepBodySize({ phase: 'teardown' });
|
|
@@ -1643,27 +1479,6 @@ export class OverlayController extends EventTarget {
|
|
|
1643
1479
|
if (this.#isRegisteredOnManager()) {
|
|
1644
1480
|
this.manager.remove(this);
|
|
1645
1481
|
}
|
|
1646
|
-
|
|
1647
|
-
this.contentNode?.removeEventListener('click', this.#hideOnCloseButtonClick);
|
|
1648
|
-
|
|
1649
|
-
// if (this.config._shouldTeardownDomStructure) {
|
|
1650
|
-
this.__rearrangeNodesCleanup?.();
|
|
1651
|
-
this.__rearrangeNodesCleanup = undefined;
|
|
1652
|
-
// }
|
|
1653
|
-
this.__teardownVisibility();
|
|
1654
|
-
|
|
1655
|
-
// When we created the contentWrapperNode/wrapping <dialog> ourselves, restore the
|
|
1656
|
-
// original dom structure and drop the (now detached) references, so that a
|
|
1657
|
-
// subsequent setup recreates them and `contentWrapperNode`/`content` no longer
|
|
1658
|
-
// expose stale nodes.
|
|
1659
|
-
if (this.contentWrapperNode !== this.contentNode) {
|
|
1660
|
-
this.contentWrapperNode?.removeAttribute('style');
|
|
1661
|
-
this.contentWrapperNode?.removeAttribute('class');
|
|
1662
|
-
this.__contentWrapperNode = undefined;
|
|
1663
|
-
this.__wrappingDialogNode = undefined;
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
this.__hasSetup = false;
|
|
1667
1482
|
}
|
|
1668
1483
|
|
|
1669
1484
|
/** @private */
|