@lion/ui 0.22.0 → 0.23.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/README.md +35 -0
- package/components/combobox/src/LionCombobox.js +10 -2
- package/components/combobox/test/lion-combobox.test.js +31 -0
- package/components/form-core/src/choice-group/ChoiceGroupMixin.js +7 -0
- package/components/form-core/types/choice-group/ChoiceGroupMixinTypes.ts +5 -0
- package/components/input-amount/src/LionInputAmount.js +17 -6
- package/components/input-amount/src/formatters.js +5 -3
- package/components/input-amount/test/formatters.test.js +88 -74
- package/components/input-amount/test/lion-input-amount.test.js +51 -36
- package/components/input-tel/test/validators.test.js +9 -0
- package/components/listbox/src/ListboxMixin.js +19 -3
- package/components/listbox/types/ListboxMixinTypes.ts +7 -1
- package/components/menu/README.md +360 -0
- package/components/menu/src/InteractiveListMixin.js +867 -0
- package/components/menu/src/LionMenu.js +436 -0
- package/components/menu/src/MoreButtonMenuMixin.js +295 -0
- package/components/menu/src/MultiLevelListMixin.js +312 -0
- package/components/menu/src/utils/isInView.js +26 -0
- package/components/menu/src/utils/listItemInteractions.js +124 -0
- package/components/menu/test/InteractiveListMixin.test.js +3 -0
- package/components/menu/test/MoreButtonMenuMixin.test.js +4 -0
- package/components/menu/test/MultiLevelListMixin.test.js +3 -0
- package/components/menu/test/lion-menu-interactions.test.js +3 -0
- package/components/menu/test/lion-menu.test.js +14 -0
- package/components/menu/test-helpers/mimicKeyPress.js +9 -0
- package/components/menu/test-suites/InteractiveListMixin.suite.js +537 -0
- package/components/menu/test-suites/LionMenu.suite.js +114 -0
- package/components/menu/test-suites/LionMenuInteractions.suite.js +583 -0
- package/components/menu/test-suites/MoreButtonMenuMixin.suite.js +372 -0
- package/components/menu/test-suites/MultiLevelListMixin.suite.js +309 -0
- package/components/menu/types/InteractiveListMixinTypes.ts +126 -0
- package/components/menu/types/MoreButtonMenuMixinTypes.ts +51 -0
- package/components/menu/types/MultiLevelListMixinTypes.ts +28 -0
- package/components/menu/types/OverlayWithListInvokerMixinTypes.ts +61 -0
- package/components/overlays/NEXT.md +49 -0
- package/components/overlays/src/OverlayController.js +304 -125
- package/components/overlays/src/OverlayMixin.js +81 -17
- package/components/overlays/src/utils/adopt-styles.js +1 -1
- package/components/overlays/src/utils/is-equal-config.js +15 -6
- package/components/overlays/test/OverlayController.test.js +223 -25
- package/components/overlays/test-suites/OverlayMixin.suite.js +8 -8
- package/components/overlays/types/ArrowMixinTypes.ts +1 -3
- package/components/overlays/types/OverlayConfig.ts +20 -5
- package/components/overlays/types/OverlayMixinTypes.ts +2 -0
- package/components/select-rich/src/LionSelectRich.js +12 -0
- package/components/select-rich/test/lion-select-rich-interaction.test.js +53 -0
- package/components/select-rich/test/lion-select-rich-upgrade.test.js +30 -0
- package/custom-elements.json +3120 -149
- package/dist-types/components/combobox/src/LionCombobox.d.ts +1 -1
- package/dist-types/components/combobox/src/LionCombobox.d.ts.map +1 -1
- package/dist-types/components/dialog/src/LionDialog.d.ts +6 -2
- package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
- package/dist-types/components/form-core/src/choice-group/ChoiceGroupMixin.d.ts.map +1 -1
- package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts +5 -0
- package/dist-types/components/form-core/types/choice-group/ChoiceGroupMixinTypes.d.ts.map +1 -1
- package/dist-types/components/input-amount/src/LionInputAmount.d.ts +3 -1
- package/dist-types/components/input-amount/src/LionInputAmount.d.ts.map +1 -1
- package/dist-types/components/input-amount/src/formatters.d.ts +6 -4
- package/dist-types/components/input-amount/src/formatters.d.ts.map +1 -1
- package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +1 -1
- package/dist-types/components/listbox/src/ListboxMixin.d.ts.map +1 -1
- package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts +7 -1
- package/dist-types/components/listbox/types/ListboxMixinTypes.d.ts.map +1 -1
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts +35 -0
- package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +1 -0
- package/dist-types/components/menu/src/LionMenu.d.ts +129 -0
- package/dist-types/components/menu/src/LionMenu.d.ts.map +1 -0
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +8 -0
- package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +1 -0
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +7 -0
- package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +1 -0
- package/dist-types/components/menu/src/utils/isInView.d.ts +9 -0
- package/dist-types/components/menu/src/utils/isInView.d.ts.map +1 -0
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts +48 -0
- package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +1 -0
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts +2 -0
- package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts +2 -0
- package/dist-types/components/menu/test/lion-menu-interactions.test.d.ts.map +1 -0
- package/dist-types/components/menu/test/lion-menu.test.d.ts +2 -0
- package/dist-types/components/menu/test/lion-menu.test.d.ts.map +1 -0
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts +7 -0
- package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts +11 -0
- package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts +9 -0
- package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts +5 -0
- package/dist-types/components/menu/test-suites/LionMenuInteractions.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts +11 -0
- package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts +11 -0
- package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts.map +1 -0
- package/dist-types/components/menu/types/InteractiveListMixinTypes.d.ts +72 -0
- package/dist-types/components/menu/types/InteractiveListMixinTypes.d.ts.map +1 -0
- package/dist-types/components/menu/types/MoreButtonMenuMixinTypes.d.ts +30 -0
- package/dist-types/components/menu/types/MoreButtonMenuMixinTypes.d.ts.map +1 -0
- package/dist-types/components/menu/types/MultiLevelListMixinTypes.d.ts +16 -0
- package/dist-types/components/menu/types/MultiLevelListMixinTypes.d.ts.map +1 -0
- package/dist-types/components/menu/types/OverlayWithListInvokerMixinTypes.d.ts +35 -0
- package/dist-types/components/menu/types/OverlayWithListInvokerMixinTypes.d.ts.map +1 -0
- package/dist-types/components/overlays/src/OverlayController.d.ts +22 -5
- 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 +12 -2
- package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +1 -0
- package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts +13 -2
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
- package/dist-types/components/select-rich/test/lion-select-rich-interaction.test.d.ts.map +1 -1
- package/dist-types/components/select-rich/test/lion-select-rich-upgrade.test.d.ts +2 -0
- package/dist-types/components/select-rich/test/lion-select-rich-upgrade.test.d.ts.map +1 -0
- package/dist-types/components/tooltip/src/LionTooltip.d.ts +1 -1
- package/dist-types/exports/define/lion-item.d.ts +2 -0
- package/dist-types/exports/define/lion-item.d.ts.map +1 -0
- package/dist-types/exports/define/lion-menu.d.ts +2 -0
- package/dist-types/exports/define/lion-menu.d.ts.map +1 -0
- package/dist-types/exports/define/lion-menuitem.d.ts +2 -0
- package/dist-types/exports/define/lion-menuitem.d.ts.map +1 -0
- package/dist-types/exports/menu-test-suites.d.ts +4 -0
- package/dist-types/exports/menu-test-suites.d.ts.map +1 -0
- package/dist-types/exports/menu.d.ts +3 -0
- package/dist-types/exports/menu.d.ts.map +1 -0
- package/dist-types/exports/overlays.d.ts +6 -0
- 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 +53 -39
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/api-table.md +32 -6
- 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/api-table.md +9 -7
- package/docs/components/input-amount/overview.md +3 -3
- package/docs/components/input-amount/use-cases.md +15 -4
- package/docs/components/input-amount-dropdown/api-table.md +2 -0
- package/docs/components/input-date/overview.md +3 -3
- package/docs/components/input-datepicker/api-table.md +63 -45
- 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/menu/api-table.md +44 -0
- package/docs/components/menu/applyMenuDemoStyles.js +173 -0
- package/docs/components/menu/extensions.md +262 -0
- package/docs/components/menu/index.md +13 -0
- package/docs/components/menu/migration.md +129 -0
- package/docs/components/menu/overview.md +448 -0
- package/docs/components/menu/src/DemoNavigationBar.js +441 -0
- package/docs/components/menu/src/DemoSpinButton.js +215 -0
- package/docs/components/menu/src/DemoToolbar.js +101 -0
- package/docs/components/menu/src/DemoTree.js +55 -0
- package/docs/components/menu/src/navigation-bar-types.js +41 -0
- package/docs/components/menu/use-cases.md +74 -0
- 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 +51 -33
- 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 +33 -7
- package/docs/fundamentals/index.md +4 -4
- package/docs/fundamentals/node-tools/providence-analytics/overview.md +1 -1
- 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 +46 -43
- 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/define/lion-item.js +3 -0
- package/exports/define/lion-menu.js +3 -0
- package/exports/define/lion-menuitem.js +3 -0
- package/exports/menu-test-suites.js +4 -0
- package/exports/menu.js +3 -0
- package/exports/overlays.js +6 -0
- package/menu-test-suites.js +4 -0
- package/menu.js +3 -0
- package/overlays.js +6 -0
- package/package.json +11 -4
- package/vendor/README.md +61 -0
- package/vendor/awesome-phonenumber-7.9.0-tlouisse.libphonenumber-9.0.41.tgz +0 -0
- package/vscode.html-custom-data.json +76 -8
- package/web-types.json +202 -17
|
@@ -3,6 +3,7 @@ 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';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @typedef {import('@lion/ui/types/overlays.js').OverlayPhase} OverlayPhase
|
|
@@ -32,41 +33,45 @@ import { getFocusableElements } from './utils/get-focusable-elements.js';
|
|
|
32
33
|
* `<slot name="my-content">` belonging to `<div slot="content"/>` will be wrapped with wrappingDialogNodeL1 and contentWrapperNodeL2
|
|
33
34
|
* inside shadow dom. With the help of temp markers, `<slot name="my-content">`'s original position will be respected.
|
|
34
35
|
*
|
|
35
|
-
* @param {{
|
|
36
|
+
* @param {{
|
|
37
|
+
* wrappingDialogNodeL1: HTMLDialogElement|HTMLDivElement;
|
|
38
|
+
* contentWrapperNodeL2:Element;
|
|
39
|
+
* contentNodeL3: Element;
|
|
40
|
+
* }} opts
|
|
36
41
|
*/
|
|
37
42
|
function rearrangeNodes({ wrappingDialogNodeL1, contentWrapperNodeL2, contentNodeL3 }) {
|
|
38
|
-
if
|
|
43
|
+
// if contentWrapperNode is provided by the user,
|
|
44
|
+
// we assume it lives in shadow dom around a slot.
|
|
45
|
+
const hasLegacyMethodOfProvidingWrapperNode = Boolean(contentWrapperNodeL2.parentNode);
|
|
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) {
|
|
39
51
|
throw new Error(
|
|
40
|
-
'[OverlayController] Could not find a render target,
|
|
52
|
+
'[OverlayController] Could not find a render target, makes sure contentNode has a parent element (or contentWrapperNode is connected)',
|
|
41
53
|
);
|
|
42
54
|
}
|
|
43
55
|
|
|
56
|
+
/** @type {Node} */
|
|
44
57
|
let parentElement;
|
|
45
|
-
const tempMarker = document.createComment('
|
|
58
|
+
const tempMarker = document.createComment('overlay-insertion-marker');
|
|
59
|
+
// Moving the wrapper can change slot assignment, so capture the node we relocate up front
|
|
60
|
+
// and restore that same node during cleanup.
|
|
61
|
+
const contentNodeOrSlot = contentNodeL3.assignedSlot || contentNodeL3;
|
|
46
62
|
|
|
47
|
-
if (
|
|
63
|
+
if (hasLegacyMethodOfProvidingWrapperNode) {
|
|
48
64
|
// This is the case when contentWrapperNode (living in shadow dom, wrapping <slot name="my-content-outlet">) is already provided via controller.
|
|
49
65
|
parentElement = contentWrapperNodeL2.parentElement || contentWrapperNodeL2.getRootNode();
|
|
50
66
|
parentElement.insertBefore(tempMarker, contentWrapperNodeL2);
|
|
51
67
|
// Wrap...
|
|
52
68
|
wrappingDialogNodeL1.appendChild(contentWrapperNodeL2);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
contentNodeL3.assignedSlot.parentElement || contentNodeL3.assignedSlot.getRootNode();
|
|
60
|
-
parentElement.insertBefore(tempMarker, contentNodeL3.assignedSlot);
|
|
61
|
-
wrappingDialogNodeL1.appendChild(contentWrapperNodeL2);
|
|
62
|
-
// Important: we do not move around contentNodeL3, but the assigned slot
|
|
63
|
-
contentWrapperNodeL2.appendChild(contentNodeL3.assignedSlot);
|
|
64
|
-
} else {
|
|
65
|
-
parentElement = contentNodeL3.parentElement || contentNodeL3.getRootNode();
|
|
66
|
-
parentElement.insertBefore(tempMarker, contentNodeL3);
|
|
67
|
-
wrappingDialogNodeL1.appendChild(contentWrapperNodeL2);
|
|
68
|
-
contentWrapperNodeL2.appendChild(contentNodeL3);
|
|
69
|
-
}
|
|
69
|
+
} else {
|
|
70
|
+
parentElement = contentNodeOrSlot.parentElement || contentNodeOrSlot.getRootNode();
|
|
71
|
+
parentElement.insertBefore(tempMarker, contentNodeOrSlot);
|
|
72
|
+
wrappingDialogNodeL1.appendChild(contentWrapperNodeL2);
|
|
73
|
+
// Important: for projected content we do not move contentNodeL3, but its assigned slot
|
|
74
|
+
contentWrapperNodeL2.appendChild(contentNodeOrSlot);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
/**
|
|
@@ -93,7 +98,22 @@ function rearrangeNodes({ wrappingDialogNodeL1, contentWrapperNodeL2, contentNod
|
|
|
93
98
|
* ```
|
|
94
99
|
*/
|
|
95
100
|
parentElement.insertBefore(wrappingDialogNodeL1, tempMarker);
|
|
96
|
-
|
|
101
|
+
|
|
102
|
+
return function cleanup() {
|
|
103
|
+
if (hasLegacyMethodOfProvidingWrapperNode) {
|
|
104
|
+
parentElement?.insertBefore(contentWrapperNodeL2, tempMarker);
|
|
105
|
+
} else {
|
|
106
|
+
parentElement?.insertBefore(contentNodeOrSlot, tempMarker);
|
|
107
|
+
if (parentElement.contains(contentWrapperNodeL2)) {
|
|
108
|
+
contentWrapperNodeL2.remove();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
parentElement?.removeChild(tempMarker);
|
|
112
|
+
|
|
113
|
+
if (parentElement.contains(wrappingDialogNodeL1)) {
|
|
114
|
+
parentElement?.removeChild(wrappingDialogNodeL1);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
97
117
|
}
|
|
98
118
|
|
|
99
119
|
/**
|
|
@@ -106,6 +126,21 @@ async function preloadPopper() {
|
|
|
106
126
|
|
|
107
127
|
const childDialogsClosedInEventLoopWeakmap = new WeakMap();
|
|
108
128
|
|
|
129
|
+
/**
|
|
130
|
+
* @param {HTMLElement | null} el
|
|
131
|
+
* @param {string} selector
|
|
132
|
+
* @returns {HTMLElement | null}
|
|
133
|
+
*/
|
|
134
|
+
function deepClosest(el, selector) {
|
|
135
|
+
// @ts-ignore - comparing HTMLElement to document/window for safety
|
|
136
|
+
return (
|
|
137
|
+
// @ts-ignore - type comparison check
|
|
138
|
+
(el && el !== document && el !== window && el.closest(selector)) ||
|
|
139
|
+
// @ts-ignore - host property on ShadowRoot
|
|
140
|
+
deepClosest(el.getRootNode().host, selector)
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
109
144
|
/**
|
|
110
145
|
* OverlayController is the fundament for every single type of overlay. With the right
|
|
111
146
|
* configuration, it can be used to build (modal) dialogs, tooltips, dropdowns, popovers,
|
|
@@ -236,6 +271,20 @@ export class OverlayController extends EventTarget {
|
|
|
236
271
|
placement: 'center',
|
|
237
272
|
},
|
|
238
273
|
zIndex: 9999,
|
|
274
|
+
isActivated: true,
|
|
275
|
+
elementToFocusOnShow: false,
|
|
276
|
+
// This means that content will have visually-hidden / sr-only styles. This is handy for:
|
|
277
|
+
// - 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)
|
|
278
|
+
// - "more" menus that put content in a dropdown that do not fit in the current row
|
|
279
|
+
// - menus that should generally open on tab.
|
|
280
|
+
// @ts-ignore - hideVisually is an extension property
|
|
281
|
+
hideVisually: false,
|
|
282
|
+
requireConnectedNodes: true,
|
|
283
|
+
|
|
284
|
+
// hides children overlays when parent is closed...
|
|
285
|
+
syncChildrenCloseState: false,
|
|
286
|
+
// // In next major, we remove this prop. Now we disable it for backwards compatibility and enable it in the places we need it internally
|
|
287
|
+
// _shouldTeardownDomStructure: false,
|
|
239
288
|
};
|
|
240
289
|
|
|
241
290
|
this._contentId = `overlay-content--${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -249,6 +298,8 @@ export class OverlayController extends EventTarget {
|
|
|
249
298
|
this.__escKeyHandlerCalled = false;
|
|
250
299
|
}
|
|
251
300
|
|
|
301
|
+
__hasSetup = false;
|
|
302
|
+
|
|
252
303
|
/**
|
|
253
304
|
* The invokerNode
|
|
254
305
|
* @type {HTMLElement | undefined}
|
|
@@ -267,7 +318,7 @@ export class OverlayController extends EventTarget {
|
|
|
267
318
|
|
|
268
319
|
/**
|
|
269
320
|
* Determines the connection point in DOM (body vs next to invoker).
|
|
270
|
-
* @type {'global' | 'local' | undefined}
|
|
321
|
+
* @type {'global' | 'local' | 'custom' | undefined}
|
|
271
322
|
*/
|
|
272
323
|
get placementMode() {
|
|
273
324
|
return this.config?.placementMode;
|
|
@@ -492,17 +543,14 @@ export class OverlayController extends EventTarget {
|
|
|
492
543
|
* @param { OverlayConfig } cfgToAdd
|
|
493
544
|
*/
|
|
494
545
|
updateConfig(cfgToAdd) {
|
|
495
|
-
// Teardown all previous configs
|
|
496
|
-
this.teardown();
|
|
497
|
-
|
|
498
546
|
/**
|
|
499
547
|
* @type {OverlayConfig}
|
|
500
548
|
* @private
|
|
501
549
|
*/
|
|
502
|
-
|
|
550
|
+
const prevConfig = this.config;
|
|
503
551
|
|
|
504
552
|
/** @type {OverlayConfig} */
|
|
505
|
-
|
|
553
|
+
const newConfig = {
|
|
506
554
|
...this._defaultConfig, // our basic ingredients
|
|
507
555
|
...this.__sharedConfig, // the initial configured overlayController
|
|
508
556
|
...cfgToAdd, // the added config
|
|
@@ -518,12 +566,18 @@ export class OverlayController extends EventTarget {
|
|
|
518
566
|
},
|
|
519
567
|
};
|
|
520
568
|
|
|
569
|
+
const shouldUpdate = !this.__hasSetup || !isEqualConfig(prevConfig, newConfig);
|
|
570
|
+
if (!shouldUpdate) return;
|
|
571
|
+
// Teardown all previous configs
|
|
572
|
+
|
|
573
|
+
this.teardown();
|
|
574
|
+
|
|
575
|
+
this.config = newConfig;
|
|
576
|
+
|
|
521
577
|
/** @private */
|
|
522
578
|
this.__validateConfiguration(this.config);
|
|
523
579
|
/** @protected */
|
|
524
580
|
this._init();
|
|
525
|
-
/** @private */
|
|
526
|
-
this.__elementToFocusAfterHide = undefined;
|
|
527
581
|
|
|
528
582
|
if (!this.#isRegisteredOnManager()) {
|
|
529
583
|
this.manager.add(this);
|
|
@@ -545,7 +599,7 @@ export class OverlayController extends EventTarget {
|
|
|
545
599
|
'[OverlayController] You need to provide a .placementMode ("global"|"local")',
|
|
546
600
|
);
|
|
547
601
|
}
|
|
548
|
-
if (!['global', 'local'].includes(newConfig.placementMode)) {
|
|
602
|
+
if (!['global', 'local', 'custom'].includes(newConfig.placementMode)) {
|
|
549
603
|
throw new Error(
|
|
550
604
|
`[OverlayController] "${newConfig.placementMode}" is not a valid .placementMode, use ("global"|"local")`,
|
|
551
605
|
);
|
|
@@ -564,14 +618,23 @@ export class OverlayController extends EventTarget {
|
|
|
564
618
|
* @protected
|
|
565
619
|
*/
|
|
566
620
|
_init() {
|
|
567
|
-
if (!this.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
621
|
+
if (!this.config.isActivated) return;
|
|
622
|
+
|
|
623
|
+
// TODO: should not be behind a flag when we are fully tearing down...
|
|
624
|
+
// TODO 2: When we move away from dialog (use popover to paint to top layer)
|
|
625
|
+
// and popper.js, (use popover and anchor positioning and popperjs as fallback, but we can put styles on contentNode),
|
|
626
|
+
// we dont need to create complex wrappers
|
|
627
|
+
// if (!this.__contentHasBeenInitialized) {
|
|
628
|
+
this.__initContentDomStructure();
|
|
629
|
+
// this.__contentHasBeenInitialized = true;
|
|
630
|
+
// }
|
|
631
|
+
this.__initVisibility();
|
|
571
632
|
|
|
572
633
|
// Reset all positioning styles (local, c.q. Popper) and classes (global)
|
|
573
|
-
this.contentWrapperNode.
|
|
574
|
-
|
|
634
|
+
if (this.contentWrapperNode !== this.contentNode) {
|
|
635
|
+
this.contentWrapperNode.removeAttribute('style');
|
|
636
|
+
this.contentWrapperNode.removeAttribute('class');
|
|
637
|
+
}
|
|
575
638
|
|
|
576
639
|
if (this.placementMode === 'local') {
|
|
577
640
|
// Lazily load Popper as soon as the first local overlay is used...
|
|
@@ -581,8 +644,27 @@ export class OverlayController extends EventTarget {
|
|
|
581
644
|
}
|
|
582
645
|
this.__handleOverlayStyles({ phase: 'init' });
|
|
583
646
|
this._handleFeatures({ phase: 'init' });
|
|
647
|
+
|
|
648
|
+
/** @private */
|
|
649
|
+
this.__elementToFocusAfterHide = undefined;
|
|
650
|
+
|
|
651
|
+
this.contentNode.dataset.content = '';
|
|
652
|
+
|
|
653
|
+
// Use event delegation to listen for clicks on close buttons...
|
|
654
|
+
this.contentNode.addEventListener('click', this.#hideOnCloseButtonClick);
|
|
655
|
+
|
|
656
|
+
this.__hasSetup = true;
|
|
584
657
|
}
|
|
585
658
|
|
|
659
|
+
#hideOnCloseButtonClick = (/** @type {{ target: any; }} */ ev) => {
|
|
660
|
+
const isOurCloseButton =
|
|
661
|
+
ev.target.hasAttribute('data-close') &&
|
|
662
|
+
deepClosest(ev.target, '[data-content]') === this.contentNode;
|
|
663
|
+
if (!isOurCloseButton) return;
|
|
664
|
+
|
|
665
|
+
this.hide();
|
|
666
|
+
};
|
|
667
|
+
|
|
586
668
|
/**
|
|
587
669
|
* @param {{ phase: OverlayPhase }} config
|
|
588
670
|
* @private
|
|
@@ -623,84 +705,108 @@ export class OverlayController extends EventTarget {
|
|
|
623
705
|
* @private
|
|
624
706
|
*/
|
|
625
707
|
__initContentDomStructure() {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
708
|
+
if (this.placementMode !== 'custom') {
|
|
709
|
+
const wrappingDialogElement = document.createElement('dialog');
|
|
710
|
+
// We use a dialog for its visual capabilities: it renders to the top layer.
|
|
711
|
+
// A11y will depend on the type of overlay and is arranged on contentNode level.
|
|
712
|
+
// Also see: https://www.scottohara.me/blog/2019/03/05/open-dialog.html
|
|
713
|
+
//
|
|
714
|
+
// The role="dialog" is set on the contentNode (or another role), so role="none"
|
|
715
|
+
// is valid here, although AXE complains about this setup.
|
|
716
|
+
// For now we need to add `ignoredRules: ['aria-allowed-role']` in your AXE tests.
|
|
717
|
+
// see: https://lion.js.org/fundamentals/systems/overlays/rationale/#considerations
|
|
718
|
+
wrappingDialogElement.setAttribute('role', 'none');
|
|
719
|
+
wrappingDialogElement.setAttribute('data-overlay-outer-wrapper', '');
|
|
720
|
+
// N.B. position: fixed is needed to escape out of 'overflow: hidden'
|
|
721
|
+
// We give a high z-index for non-modal dialogs, so that we at least win from all siblings of our
|
|
722
|
+
// parent stacking context
|
|
723
|
+
// padding reset so we don't get a weird dialog visual square showing up
|
|
724
|
+
wrappingDialogElement.style.cssText = `display:none; z-index: ${this.config.zIndex}; padding: 0;`;
|
|
725
|
+
// @ts-ignore - 'custom' is a valid extension of placementMode
|
|
726
|
+
if (this.config.placementMode === 'custom') {
|
|
727
|
+
// The user should have full freedom to control the content node, so its wrapper nodes should remain neutral.
|
|
728
|
+
wrappingDialogElement.style.cssText += `position: static;`;
|
|
729
|
+
}
|
|
730
|
+
this.__wrappingDialogNode = wrappingDialogElement;
|
|
643
731
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
732
|
+
/**
|
|
733
|
+
* Based on the configuration of the developer, multiple scenarios are accounted for
|
|
734
|
+
* A. We already have a contentWrapperNode ()
|
|
735
|
+
*/
|
|
736
|
+
if (!this.config?.contentWrapperNode) {
|
|
737
|
+
this.__contentWrapperNode = document.createElement('div');
|
|
738
|
+
}
|
|
739
|
+
this.contentWrapperNode.setAttribute('data-id', 'content-wrapper');
|
|
740
|
+
// 'hack' that makes sure popperjs (that is applied one level lower) works correctly in deeply nested shadow roots
|
|
741
|
+
this.contentWrapperNode.style.transform = 'translateZ(0px)';
|
|
742
|
+
|
|
743
|
+
this.__rearrangeNodesCleanup = rearrangeNodes({
|
|
744
|
+
wrappingDialogNodeL1: wrappingDialogElement,
|
|
745
|
+
contentWrapperNodeL2: this.contentWrapperNode,
|
|
746
|
+
contentNodeL3: this.contentNode,
|
|
747
|
+
// requireConnectedNodes: Boolean(this.config.requireConnectedNodes),
|
|
748
|
+
});
|
|
749
|
+
wrappingDialogElement.open = true;
|
|
654
750
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
751
|
+
if (this.isTooltip) {
|
|
752
|
+
// needed to prevent tooltip getting focus in Safari and Firefox
|
|
753
|
+
wrappingDialogElement.setAttribute('tabindex', '-1');
|
|
754
|
+
}
|
|
755
|
+
this.contentWrapperNode.style.zIndex = '1';
|
|
756
|
+
if (getComputedStyle(this.contentNode).position === 'absolute') {
|
|
757
|
+
// Having a _contWrapperNode and a contentNode with 'position:absolute' results in
|
|
758
|
+
// computed height of 0...
|
|
759
|
+
this.contentNode.style.position = 'static';
|
|
760
|
+
}
|
|
661
761
|
|
|
662
|
-
|
|
663
|
-
//
|
|
664
|
-
|
|
762
|
+
// Here we prevent any interference of the native <dialog> element with the keyboard behavior
|
|
763
|
+
// as defined by the OverlayController. This is needed until we can configure `closedby="none"`
|
|
764
|
+
// on the native dialog for all browsers: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog#closedby
|
|
765
|
+
const hasClosedBySupport = HTMLDialogElement && 'closedBy' in HTMLDialogElement.prototype;
|
|
766
|
+
if (hasClosedBySupport) {
|
|
767
|
+
// @ts-ignore - closedBy is a newer property not in all TypeScript versions
|
|
768
|
+
wrappingDialogElement.closedBy = 'none';
|
|
769
|
+
} else {
|
|
770
|
+
wrappingDialogElement.addEventListener(
|
|
771
|
+
'keydown',
|
|
772
|
+
(/** @type {* & KeyboardEvent} */ event) => {
|
|
773
|
+
if (event.key === 'Escape') {
|
|
774
|
+
event.preventDefault();
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
);
|
|
778
|
+
wrappingDialogElement.addEventListener(
|
|
779
|
+
'keyup',
|
|
780
|
+
(/** @type {* & KeyboardEvent} */ event) => {
|
|
781
|
+
if (event.key === 'Escape') {
|
|
782
|
+
event.preventDefault();
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
);
|
|
786
|
+
wrappingDialogElement.addEventListener('cancel', event => {
|
|
787
|
+
event.stopPropagation();
|
|
788
|
+
});
|
|
789
|
+
wrappingDialogElement.addEventListener('close', event => {
|
|
790
|
+
event.stopPropagation();
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
} else {
|
|
794
|
+
// quick hack to make none-popper/global flows work...
|
|
795
|
+
this.__contentWrapperNode = this.contentNode;
|
|
796
|
+
this.__wrappingDialogNode = this.contentNode;
|
|
665
797
|
}
|
|
798
|
+
}
|
|
666
799
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
// Having a _contWrapperNode and a contentNode with 'position:absolute' results in
|
|
672
|
-
// computed height of 0...
|
|
673
|
-
this.contentNode.style.position = 'static';
|
|
674
|
-
}
|
|
800
|
+
__initVisibility() {
|
|
801
|
+
// TODO: allow defaultOpen?
|
|
802
|
+
/** @type {HTMLDialogElement} */ (this.__wrappingDialogNode).style.display = 'none';
|
|
803
|
+
}
|
|
675
804
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
-
}
|
|
805
|
+
// Clean up the DOM structure, leaving it as we found it.
|
|
806
|
+
// TODO: __teardownContentDomStructure method... use Resettable (rename ro Restorable) found in VisibilitToggleCtrl
|
|
807
|
+
__teardownVisibility() {
|
|
808
|
+
// TODO: full structure. For now we just reset display prop...
|
|
809
|
+
/** @type {HTMLDialogElement} */ (this.__wrappingDialogNode).style.display = '';
|
|
704
810
|
}
|
|
705
811
|
|
|
706
812
|
/**
|
|
@@ -794,7 +900,7 @@ export class OverlayController extends EventTarget {
|
|
|
794
900
|
}
|
|
795
901
|
|
|
796
902
|
get isShown() {
|
|
797
|
-
return Boolean(this.__wrappingDialogNode
|
|
903
|
+
return Boolean(this.__wrappingDialogNode && this.__wrappingDialogNode.style.display !== 'none');
|
|
798
904
|
}
|
|
799
905
|
|
|
800
906
|
/**
|
|
@@ -812,6 +918,13 @@ export class OverlayController extends EventTarget {
|
|
|
812
918
|
this._showResolve = resolve;
|
|
813
919
|
});
|
|
814
920
|
|
|
921
|
+
// Teardown() removes the content dom structure we created (contentWrapperNode +
|
|
922
|
+
// wrapping <dialog>). Make sure it exists again before the manager (which sets
|
|
923
|
+
// our elevation on the wrapping node) and the show logic below touch it.
|
|
924
|
+
if (!this.__wrappingDialogNode) {
|
|
925
|
+
this._init();
|
|
926
|
+
}
|
|
927
|
+
|
|
815
928
|
if (this.manager) {
|
|
816
929
|
this.manager.show(this);
|
|
817
930
|
}
|
|
@@ -828,6 +941,7 @@ export class OverlayController extends EventTarget {
|
|
|
828
941
|
if ('HTMLDialogElement' in window && this.__wrappingDialogNode instanceof HTMLDialogElement) {
|
|
829
942
|
this.__wrappingDialogNode.open = true;
|
|
830
943
|
}
|
|
944
|
+
|
|
831
945
|
// @ts-ignore
|
|
832
946
|
this.__wrappingDialogNode.style.display = '';
|
|
833
947
|
this._keepBodySize({ phase: 'before-show' });
|
|
@@ -841,6 +955,14 @@ export class OverlayController extends EventTarget {
|
|
|
841
955
|
contentNode: this.contentNode,
|
|
842
956
|
});
|
|
843
957
|
}
|
|
958
|
+
|
|
959
|
+
// a styling/debug hook for open state
|
|
960
|
+
this.contentNode.setAttribute('data-open', '');
|
|
961
|
+
|
|
962
|
+
if (this.config.elementToFocusOnShow) {
|
|
963
|
+
this.contentNode.focus({ preventScroll: true });
|
|
964
|
+
}
|
|
965
|
+
|
|
844
966
|
/** @type {function} */
|
|
845
967
|
(this._showResolve)();
|
|
846
968
|
}
|
|
@@ -873,6 +995,7 @@ export class OverlayController extends EventTarget {
|
|
|
873
995
|
await this.__createPopperInstance();
|
|
874
996
|
/** @type {import('@popperjs/core').Instance} */ (this._popper).forceUpdate();
|
|
875
997
|
}
|
|
998
|
+
// N.B. leave it up to the user if placementMode === 'custom'
|
|
876
999
|
}
|
|
877
1000
|
|
|
878
1001
|
/**
|
|
@@ -880,9 +1003,7 @@ export class OverlayController extends EventTarget {
|
|
|
880
1003
|
* @protected
|
|
881
1004
|
*/
|
|
882
1005
|
_keepBodySize({ phase }) {
|
|
883
|
-
if (!this.preventsScroll)
|
|
884
|
-
return;
|
|
885
|
-
}
|
|
1006
|
+
if (!this.preventsScroll) return;
|
|
886
1007
|
|
|
887
1008
|
this.manager.requestToKeepBodySize({ phase });
|
|
888
1009
|
}
|
|
@@ -892,6 +1013,9 @@ export class OverlayController extends EventTarget {
|
|
|
892
1013
|
* @event hide right after the overlay is hidden
|
|
893
1014
|
*/
|
|
894
1015
|
async hide() {
|
|
1016
|
+
// Function like a no-op for dynamic edge cases...
|
|
1017
|
+
if (!this.config.isActivated) return;
|
|
1018
|
+
|
|
895
1019
|
this._hideComplete = new Promise(resolve => {
|
|
896
1020
|
this._hideResolve = resolve;
|
|
897
1021
|
});
|
|
@@ -924,7 +1048,6 @@ export class OverlayController extends EventTarget {
|
|
|
924
1048
|
if ('HTMLDialogElement' in window && this.__wrappingDialogNode instanceof HTMLDialogElement) {
|
|
925
1049
|
this.__wrappingDialogNode.close();
|
|
926
1050
|
}
|
|
927
|
-
|
|
928
1051
|
// @ts-ignore
|
|
929
1052
|
this.__wrappingDialogNode.style.display = 'none';
|
|
930
1053
|
this._handleFeatures({ phase: 'hide' });
|
|
@@ -933,6 +1056,8 @@ export class OverlayController extends EventTarget {
|
|
|
933
1056
|
this._restoreFocus();
|
|
934
1057
|
}
|
|
935
1058
|
/** @type {function} */ (this._hideResolve)();
|
|
1059
|
+
|
|
1060
|
+
this.contentNode.removeAttribute('data-open');
|
|
936
1061
|
}
|
|
937
1062
|
|
|
938
1063
|
/**
|
|
@@ -1043,23 +1168,53 @@ export class OverlayController extends EventTarget {
|
|
|
1043
1168
|
if (this.visibilityTriggerFunction) {
|
|
1044
1169
|
this._handleVisibilityTriggers({ phase });
|
|
1045
1170
|
}
|
|
1171
|
+
if (this.config.syncChildrenCloseState) {
|
|
1172
|
+
this._handleSyncChildrenCloseState({ phase });
|
|
1173
|
+
}
|
|
1174
|
+
if (this.config.elementToFocusOnShow) {
|
|
1175
|
+
this._handleElementToFocusOnShow({ phase });
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* @param {{phase: OverlayPhase}} opts
|
|
1181
|
+
* @returns {void}
|
|
1182
|
+
*/
|
|
1183
|
+
_handleElementToFocusOnShow({ phase }) {
|
|
1184
|
+
if (phase === 'init') {
|
|
1185
|
+
this.contentNode?.setAttribute('tabindex', '-1');
|
|
1186
|
+
} else if (phase === 'teardown') {
|
|
1187
|
+
// TODO: capture initial attr, use Resettable mechanism of VisibilityToggleCtrl in whole Controller
|
|
1188
|
+
this.contentNode?.removeAttribute('tabindex');
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* @param {{phase: OverlayPhase}} opts
|
|
1194
|
+
* @returns {void}
|
|
1195
|
+
*/
|
|
1196
|
+
_handleSyncChildrenCloseState({ phase }) {
|
|
1197
|
+
if (phase !== 'hide') return;
|
|
1198
|
+
|
|
1199
|
+
const visibleChildren = this.manager.shownList.filter(
|
|
1200
|
+
ctrl => ctrl !== this && deepContains(this.contentNode, ctrl.contentNode),
|
|
1201
|
+
);
|
|
1202
|
+
visibleChildren.forEach(ctrl => ctrl.hide());
|
|
1046
1203
|
}
|
|
1047
1204
|
|
|
1048
1205
|
/**
|
|
1049
1206
|
* @param {{ phase: OverlayPhase }} config
|
|
1050
1207
|
*/
|
|
1051
1208
|
_handleVisibilityTriggers({ phase }) {
|
|
1052
|
-
if (typeof this.visibilityTriggerFunction
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1059
|
-
if (this.__visibilityTriggerHandler[phase]) {
|
|
1060
|
-
this.__visibilityTriggerHandler[phase]();
|
|
1061
|
-
}
|
|
1209
|
+
if (typeof this.visibilityTriggerFunction !== 'function') return;
|
|
1210
|
+
|
|
1211
|
+
// Here we initialize the __visibilityTriggerHandler of our invokerNode. It's important that we ONLY do this on init,
|
|
1212
|
+
// otherwise we risk not being able to properly clean up listeners...
|
|
1213
|
+
if (phase === 'init') {
|
|
1214
|
+
this.__visibilityTriggerHandler = this.visibilityTriggerFunction({ controller: this });
|
|
1062
1215
|
}
|
|
1216
|
+
// Here we run the appropriate lifecycle, if defined in our handler
|
|
1217
|
+
this.__visibilityTriggerHandler[phase]?.();
|
|
1063
1218
|
}
|
|
1064
1219
|
|
|
1065
1220
|
/**
|
|
@@ -1216,7 +1371,9 @@ export class OverlayController extends EventTarget {
|
|
|
1216
1371
|
if (phase === 'show') {
|
|
1217
1372
|
this.#handleShiftKeyPress();
|
|
1218
1373
|
this.#handleFocusInsideDialog();
|
|
1374
|
+
// @ts-ignore - HTMLDialogElement methods
|
|
1219
1375
|
this.__wrappingDialogNode?.close();
|
|
1376
|
+
// @ts-ignore - HTMLDialogElement methods
|
|
1220
1377
|
this.__wrappingDialogNode?.showModal();
|
|
1221
1378
|
/**
|
|
1222
1379
|
* At this moment `#handleFocusInsideDialog` should handle the focus.
|
|
@@ -1471,6 +1628,7 @@ export class OverlayController extends EventTarget {
|
|
|
1471
1628
|
}
|
|
1472
1629
|
|
|
1473
1630
|
teardown() {
|
|
1631
|
+
if (!this.__hasSetup) return;
|
|
1474
1632
|
this.__handleOverlayStyles({ phase: 'teardown' });
|
|
1475
1633
|
if (this.isShown) {
|
|
1476
1634
|
this._keepBodySize({ phase: 'teardown' });
|
|
@@ -1479,6 +1637,27 @@ export class OverlayController extends EventTarget {
|
|
|
1479
1637
|
if (this.#isRegisteredOnManager()) {
|
|
1480
1638
|
this.manager.remove(this);
|
|
1481
1639
|
}
|
|
1640
|
+
|
|
1641
|
+
this.contentNode?.removeEventListener('click', this.#hideOnCloseButtonClick);
|
|
1642
|
+
|
|
1643
|
+
// if (this.config._shouldTeardownDomStructure) {
|
|
1644
|
+
this.__rearrangeNodesCleanup?.();
|
|
1645
|
+
this.__rearrangeNodesCleanup = undefined;
|
|
1646
|
+
// }
|
|
1647
|
+
this.__teardownVisibility();
|
|
1648
|
+
|
|
1649
|
+
// When we created the contentWrapperNode/wrapping <dialog> ourselves, restore the
|
|
1650
|
+
// original dom structure and drop the (now detached) references, so that a
|
|
1651
|
+
// subsequent setup recreates them and `contentWrapperNode`/`content` no longer
|
|
1652
|
+
// expose stale nodes.
|
|
1653
|
+
if (this.contentWrapperNode !== this.contentNode) {
|
|
1654
|
+
this.contentWrapperNode?.removeAttribute('style');
|
|
1655
|
+
this.contentWrapperNode?.removeAttribute('class');
|
|
1656
|
+
this.__contentWrapperNode = undefined;
|
|
1657
|
+
this.__wrappingDialogNode = undefined;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
this.__hasSetup = false;
|
|
1482
1661
|
}
|
|
1483
1662
|
|
|
1484
1663
|
/** @private */
|