@pine-ds/core 4.6.0 → 4.7.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.
- package/components/pds-box2.js +1 -1
- package/components/pds-checkbox2.js +1 -1
- package/components/pds-chip2.js +1 -1
- package/components/pds-combobox.js +1 -1
- package/components/pds-copytext.js +1 -1
- package/components/pds-drawer.js +1 -1
- package/components/pds-dropdown-menu-item.js +1 -1
- package/components/pds-dropdown-menu.js +1 -1
- package/components/pds-modal2.js +1 -1
- package/components/pds-multiselect.js +1 -1
- package/components/pds-popover.js +1 -1
- package/components/pds-radio.js +1 -1
- package/components/pds-row.js +1 -1
- package/components/pds-switch.js +1 -1
- package/components/pds-table-cell2.js +1 -1
- package/components/pds-text2.js +1 -1
- package/components/truncation-tooltip.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/pds-box.cjs.entry.js +96 -96
- package/dist/cjs/pds-checkbox.cjs.entry.js +16 -4
- package/dist/cjs/pds-chip.cjs.entry.js +34 -9
- package/dist/cjs/pds-combobox.cjs.entry.js +47 -4
- package/dist/cjs/pds-copytext.cjs.entry.js +7 -2
- package/dist/cjs/pds-drawer.cjs.entry.js +23 -10
- package/dist/cjs/pds-dropdown-menu-item.cjs.entry.js +2 -2
- package/dist/cjs/pds-dropdown-menu.cjs.entry.js +9 -5
- package/dist/cjs/pds-modal.cjs.entry.js +175 -49
- package/dist/cjs/pds-multiselect.cjs.entry.js +40 -9
- package/dist/cjs/pds-popover.cjs.entry.js +170 -26
- package/dist/cjs/pds-radio.cjs.entry.js +1 -1
- package/dist/cjs/pds-row.cjs.entry.js +7 -7
- package/dist/cjs/pds-switch.cjs.entry.js +15 -3
- package/dist/cjs/pds-table-cell.cjs.entry.js +24 -4
- package/dist/cjs/pds-text.cjs.entry.js +17 -9
- package/dist/cjs/pine-core.cjs.js +1 -1
- package/dist/cjs/{truncation-tooltip-CYgQfBLt.js → truncation-tooltip-DO9xli2e.js} +7 -7
- package/dist/collection/components/pds-box/pds-box.js +96 -96
- package/dist/collection/components/pds-checkbox/pds-checkbox.css +4 -0
- package/dist/collection/components/pds-checkbox/pds-checkbox.js +15 -3
- package/dist/collection/components/pds-chip/pds-chip.js +33 -8
- package/dist/collection/components/pds-chip/stories/pds-chip.stories.js +17 -0
- package/dist/collection/components/pds-combobox/pds-combobox.js +48 -5
- package/dist/collection/components/pds-combobox/stories/pds-combobox.stories.js +1 -1
- package/dist/collection/components/pds-copytext/pds-copytext.js +6 -1
- package/dist/collection/components/pds-drawer/pds-drawer.css +4 -1
- package/dist/collection/components/pds-drawer/pds-drawer.js +54 -17
- package/dist/collection/components/pds-drawer/stories/pds-drawer.stories.js +6 -1
- package/dist/collection/components/pds-dropdown-menu/pds-dropdown-menu-item/pds-dropdown-menu-item.js +2 -2
- package/dist/collection/components/pds-dropdown-menu/pds-dropdown-menu.js +15 -5
- package/dist/collection/components/pds-modal/pds-modal.js +180 -50
- package/dist/collection/components/pds-multiselect/pds-multiselect.css +4 -0
- package/dist/collection/components/pds-multiselect/pds-multiselect.js +42 -11
- package/dist/collection/components/pds-popover/pds-popover.js +171 -27
- package/dist/collection/components/pds-radio/pds-radio.css +6 -0
- package/dist/collection/components/pds-row/pds-row.js +7 -7
- package/dist/collection/components/pds-switch/pds-switch.js +15 -3
- package/dist/collection/components/pds-table/pds-table-cell/pds-table-cell.js +23 -3
- package/dist/collection/components/pds-text/pds-text.js +16 -8
- package/dist/collection/utils/truncation-tooltip.js +7 -7
- package/dist/docs.json +48 -9
- package/dist/esm/loader.js +1 -1
- package/dist/esm/pds-box.entry.js +96 -96
- package/dist/esm/pds-checkbox.entry.js +16 -4
- package/dist/esm/pds-chip.entry.js +34 -9
- package/dist/esm/pds-combobox.entry.js +47 -4
- package/dist/esm/pds-copytext.entry.js +7 -2
- package/dist/esm/pds-drawer.entry.js +23 -10
- package/dist/esm/pds-dropdown-menu-item.entry.js +2 -2
- package/dist/esm/pds-dropdown-menu.entry.js +9 -5
- package/dist/esm/pds-modal.entry.js +175 -49
- package/dist/esm/pds-multiselect.entry.js +40 -9
- package/dist/esm/pds-popover.entry.js +171 -27
- package/dist/esm/pds-radio.entry.js +1 -1
- package/dist/esm/pds-row.entry.js +7 -7
- package/dist/esm/pds-switch.entry.js +15 -3
- package/dist/esm/pds-table-cell.entry.js +24 -4
- package/dist/esm/pds-text.entry.js +17 -9
- package/dist/esm/pine-core.js +1 -1
- package/dist/esm/{truncation-tooltip-_902ENWC.js → truncation-tooltip-DRXsdKk1.js} +7 -7
- package/dist/esm-es5/loader.js +1 -1
- package/dist/esm-es5/pds-box.entry.js +1 -1
- package/dist/esm-es5/pds-checkbox.entry.js +1 -1
- package/dist/esm-es5/pds-chip.entry.js +1 -1
- package/dist/esm-es5/pds-combobox.entry.js +1 -1
- package/dist/esm-es5/pds-copytext.entry.js +1 -1
- package/dist/esm-es5/pds-drawer.entry.js +1 -1
- package/dist/esm-es5/pds-dropdown-menu-item.entry.js +1 -1
- package/dist/esm-es5/pds-dropdown-menu.entry.js +1 -1
- package/dist/esm-es5/pds-modal.entry.js +1 -1
- package/dist/esm-es5/pds-multiselect.entry.js +1 -1
- package/dist/esm-es5/pds-popover.entry.js +1 -1
- package/dist/esm-es5/pds-radio.entry.js +1 -1
- package/dist/esm-es5/pds-row.entry.js +1 -1
- package/dist/esm-es5/pds-switch.entry.js +1 -1
- package/dist/esm-es5/pds-table-cell.entry.js +1 -1
- package/dist/esm-es5/pds-text.entry.js +1 -1
- package/dist/esm-es5/pine-core.js +1 -1
- package/dist/esm-es5/truncation-tooltip-DRXsdKk1.js +1 -0
- package/dist/pine-core/{p-9af131d6.system.entry.js → p-11d98408.system.entry.js} +1 -1
- package/dist/pine-core/p-153ba77b.entry.js +1 -0
- package/dist/pine-core/p-355e4cf9.entry.js +1 -0
- package/dist/pine-core/p-4435fff6.system.entry.js +1 -0
- package/dist/pine-core/p-576cf904.system.entry.js +1 -0
- package/dist/pine-core/{p-4f94b4f1.system.entry.js → p-645a482b.system.entry.js} +1 -1
- package/dist/pine-core/{p-4fa63723.system.entry.js → p-6d603d16.system.entry.js} +1 -1
- package/dist/pine-core/p-74a05bcc.entry.js +1 -0
- package/dist/pine-core/p-7fa66cad.entry.js +1 -0
- package/dist/pine-core/p-80a698ae.entry.js +1 -0
- package/dist/pine-core/{p-bf7ee3b7.system.entry.js → p-80e6f3a0.system.entry.js} +1 -1
- package/dist/pine-core/p-88ff1604.entry.js +1 -0
- package/dist/pine-core/p-92ae6082.entry.js +1 -0
- package/dist/pine-core/{p-6eec23a7.system.entry.js → p-97d0b89e.system.entry.js} +1 -1
- package/dist/pine-core/p-993d1043.entry.js +1 -0
- package/dist/pine-core/p-CuSNnuFu.system.js +1 -1
- package/dist/pine-core/p-JtdHT_W5.js +1 -0
- package/dist/pine-core/p-a787b34a.system.entry.js +1 -0
- package/dist/pine-core/p-a939b471.entry.js +1 -0
- package/dist/pine-core/p-aa032a5e.system.entry.js +1 -0
- package/dist/pine-core/{p-646afbca.system.entry.js → p-b60ac7eb.system.entry.js} +1 -1
- package/dist/pine-core/p-bb73ad66.entry.js +2 -0
- package/dist/pine-core/p-bd26a5e8.entry.js +1 -0
- package/dist/pine-core/p-bf5887e8.system.entry.js +1 -0
- package/dist/pine-core/p-c36b490e.entry.js +1 -0
- package/dist/pine-core/p-c81d8ac8.entry.js +1 -0
- package/dist/pine-core/p-ca0a8f5c.entry.js +1 -0
- package/dist/pine-core/p-ca0d48e8.entry.js +1 -0
- package/dist/pine-core/p-cfc52b95.system.entry.js +1 -0
- package/dist/pine-core/{p-ba1f65c2.system.entry.js → p-d8570b4b.system.entry.js} +1 -1
- package/dist/pine-core/{p-9e0f920a.system.entry.js → p-e7a95a49.system.entry.js} +1 -1
- package/dist/pine-core/p-e803f629.system.entry.js +1 -0
- package/dist/pine-core/{p-df539c42.system.entry.js → p-e836f3ec.system.entry.js} +2 -2
- package/dist/pine-core/{p-b8e8a4ce.entry.js → p-f474d7de.entry.js} +1 -1
- package/dist/pine-core/{p-CBu9UrXr.system.js → p-qhdoK1Jp.system.js} +1 -1
- package/dist/pine-core/pine-core.esm.js +1 -1
- package/dist/types/components/pds-checkbox/pds-checkbox.d.ts +4 -0
- package/dist/types/components/pds-combobox/pds-combobox.d.ts +7 -1
- package/dist/types/components/pds-drawer/pds-drawer.d.ts +21 -9
- package/dist/types/components/pds-dropdown-menu/pds-dropdown-menu.d.ts +1 -0
- package/dist/types/components/pds-modal/pds-modal.d.ts +16 -3
- package/dist/types/components/pds-multiselect/pds-multiselect.d.ts +10 -3
- package/dist/types/components/pds-popover/pds-popover.d.ts +31 -1
- package/dist/types/components/pds-switch/pds-switch.d.ts +4 -0
- package/dist/types/components/pds-table/pds-table-cell/pds-table-cell.d.ts +3 -0
- package/dist/types/components/pds-text/pds-text.d.ts +1 -0
- package/dist/types/components.d.ts +47 -32
- package/dist/types/utils/truncation-tooltip.d.ts +15 -2
- package/dist/vscode.html-data.json +11 -4
- package/hydrate/index.js +698 -253
- package/hydrate/index.mjs +698 -253
- package/package.json +2 -2
- package/dist/esm-es5/truncation-tooltip-_902ENWC.js +0 -1
- package/dist/pine-core/p-01c9b8f3.entry.js +0 -1
- package/dist/pine-core/p-258d5e83.entry.js +0 -1
- package/dist/pine-core/p-3c27088f.system.entry.js +0 -1
- package/dist/pine-core/p-4849fda7.entry.js +0 -1
- package/dist/pine-core/p-4de8f333.entry.js +0 -1
- package/dist/pine-core/p-550c2fd3.entry.js +0 -1
- package/dist/pine-core/p-6bf853c9.entry.js +0 -1
- package/dist/pine-core/p-6d3610f9.entry.js +0 -1
- package/dist/pine-core/p-73eb47fa.entry.js +0 -1
- package/dist/pine-core/p-7f367db4.system.entry.js +0 -1
- package/dist/pine-core/p-84e9b7da.entry.js +0 -1
- package/dist/pine-core/p-9a0083dd.system.entry.js +0 -1
- package/dist/pine-core/p-CnM3cCof.js +0 -1
- package/dist/pine-core/p-a3735199.entry.js +0 -1
- package/dist/pine-core/p-a85a6ab9.system.entry.js +0 -1
- package/dist/pine-core/p-cf08a6dd.system.entry.js +0 -1
- package/dist/pine-core/p-da103a54.system.entry.js +0 -1
- package/dist/pine-core/p-e800a2b1.entry.js +0 -1
- package/dist/pine-core/p-eb5465ce.entry.js +0 -1
- package/dist/pine-core/p-ec5428fb.entry.js +0 -1
- package/dist/pine-core/p-ed4b979e.entry.js +0 -1
- package/dist/pine-core/p-f1f17940.entry.js +0 -2
- package/dist/pine-core/p-f25e4731.system.entry.js +0 -1
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { Host, h } from "@stencil/core";
|
|
2
2
|
import { closest, containsComposed } from "../../utils/closest";
|
|
3
3
|
import { unwrapReconnectedContent } from "../../utils/reconnected-content";
|
|
4
|
-
// Known selectors for content Pine components portal
|
|
5
|
-
//
|
|
4
|
+
// Known selectors for content Pine components portal out of place rather than
|
|
5
|
+
// rendering where they are — pds-tooltip and pds-combobox's dropdown in
|
|
6
|
+
// document.body, pds-popover in the body or the open dialog it's in.
|
|
6
7
|
// A pointerdown landing on one of these is not "outside" the drawer in the
|
|
7
8
|
// sense light dismiss cares about: it belongs to an overlay something inside
|
|
8
9
|
// the drawer opened, even though it is not a DOM descendant of the drawer.
|
|
9
10
|
const PORTALED_OVERLAY_SELECTOR = '.pds-popover, .pds-tooltip, .pds-combobox-dropdown-portal';
|
|
10
11
|
/**
|
|
11
|
-
* A
|
|
12
|
+
* A side panel composed from `pds-modal`, non-modal by default.
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
-
* dimming, no blur, no scroll lock, no click blocking.
|
|
15
|
-
* `pds-modal` internally with `disableTopLayer`
|
|
16
|
-
* suppressed, and adds the edge, width and dismiss behavior a
|
|
17
|
-
* needs on top.
|
|
14
|
+
* By default, unlike `pds-modal`, the page stays interactive while a drawer
|
|
15
|
+
* is open: no dimming, no blur, no scroll lock, no click blocking.
|
|
16
|
+
* `pds-drawer` renders a `pds-modal` internally with `disableTopLayer` on and
|
|
17
|
+
* its backdrop suppressed, and adds the edge, width and dismiss behavior a
|
|
18
|
+
* side panel needs on top. With `modal`, it opens that `pds-modal` in the top
|
|
19
|
+
* layer instead and keeps its backdrop. It does not reimplement focus, Escape or dialog semantics —
|
|
18
20
|
* those come from `pds-modal` unchanged.
|
|
19
21
|
* @lightDom renders-markup
|
|
20
22
|
*/
|
|
@@ -42,8 +44,18 @@ export class PdsDrawer {
|
|
|
42
44
|
*/
|
|
43
45
|
this.scrollable = true;
|
|
44
46
|
/**
|
|
45
|
-
* Whether the drawer
|
|
46
|
-
*
|
|
47
|
+
* Whether the drawer is modal. A modal drawer dims the page behind it with a
|
|
48
|
+
* backdrop, makes the page inert, and keeps focus inside the drawer, like
|
|
49
|
+
* `pds-modal`. Use it for a task the page should wait on. Leave it off for a
|
|
50
|
+
* panel people work alongside the page. Read when the drawer opens; changing
|
|
51
|
+
* it while the drawer is open is not supported.
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
this.modal = false;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the drawer can be dismissed with a pointerdown outside it (in a
|
|
57
|
+
* `modal` drawer, a click on its backdrop).
|
|
58
|
+
* Replaces `pds-modal`'s `backdropDismiss`.
|
|
47
59
|
* This also gates Escape, matching how `backdropDismiss` gates Escape on
|
|
48
60
|
* `pds-modal` today: setting this to `false` means the close button is the
|
|
49
61
|
* only way out.
|
|
@@ -62,7 +74,8 @@ export class PdsDrawer {
|
|
|
62
74
|
* background event while the user is mid-task elsewhere on the page —
|
|
63
75
|
* moving focus there would interrupt them, and the page stays live under
|
|
64
76
|
* a non-modal drawer, so it is a genuine data-entry risk, not just an
|
|
65
|
-
* annoyance.
|
|
77
|
+
* annoyance. A `modal` drawer ignores `none`: the page behind it is inert,
|
|
78
|
+
* so focus starts in the drawer.
|
|
66
79
|
* @default 'auto'
|
|
67
80
|
*/
|
|
68
81
|
this.initialFocus = 'auto';
|
|
@@ -108,7 +121,8 @@ export class PdsDrawer {
|
|
|
108
121
|
// page underneath stays genuinely clickable, which means a click there
|
|
109
122
|
// never reaches pds-modal's backdrop element at all.
|
|
110
123
|
handleOutsidePointerDown(e) {
|
|
111
|
-
|
|
124
|
+
// A modal drawer's backdrop takes the click instead, through pds-modal.
|
|
125
|
+
if (this.modal || !this.lightDismiss || !this.open)
|
|
112
126
|
return;
|
|
113
127
|
const target = e.target;
|
|
114
128
|
if (!target)
|
|
@@ -118,7 +132,7 @@ export class PdsDrawer {
|
|
|
118
132
|
if (target.closest(PORTALED_OVERLAY_SELECTOR))
|
|
119
133
|
return;
|
|
120
134
|
// A datepicker's portaled calendar counts as inside only when its datepicker is in the drawer,
|
|
121
|
-
// or in an overlay portaled from it (a pds-popover in the drawer puts its content
|
|
135
|
+
// or in an overlay portaled from it (a pds-popover in the drawer puts its content beside it).
|
|
122
136
|
const calendar = target.closest('.pds-datepicker-calendar-portal');
|
|
123
137
|
const owner = calendar === null || calendar === void 0 ? void 0 : calendar.pdsDatepicker;
|
|
124
138
|
if (owner && (containsComposed(this.el, owner) || closest(PORTALED_OVERLAY_SELECTOR, owner)))
|
|
@@ -134,11 +148,11 @@ export class PdsDrawer {
|
|
|
134
148
|
this.open = false;
|
|
135
149
|
}
|
|
136
150
|
render() {
|
|
137
|
-
return (h(Host, { key: '
|
|
151
|
+
return (h(Host, { key: '287bf043c991209488eb1b617031acffb59ff9af', class: {
|
|
138
152
|
'pds-drawer': true,
|
|
139
153
|
[`pds-drawer--${this.side}`]: true,
|
|
140
154
|
[`pds-drawer--${this.size}`]: true,
|
|
141
|
-
} }, h("pds-modal", { key: '
|
|
155
|
+
} }, h("pds-modal", { key: 'cd86fb552353f5cc12fb36fc956033432a91847c', class: { 'pds-drawer__modal': true, 'pds-drawer__modal--modal': this.modal }, ref: (el) => (this.modalEl = el), componentId: this.componentId, open: this.open, scrollable: this.scrollable, disableTopLayer: !this.modal, backdropDismiss: this.lightDismiss, disableInitialFocus: !this.modal && this.initialFocus === 'none' }, h("slot", { key: '56bbe9b5c26b10b07c0f503ba147899a57a5de85' }))));
|
|
142
156
|
}
|
|
143
157
|
static get is() { return "pds-drawer"; }
|
|
144
158
|
static get originalStyleUrls() {
|
|
@@ -264,6 +278,29 @@ export class PdsDrawer {
|
|
|
264
278
|
"attribute": "scrollable",
|
|
265
279
|
"defaultValue": "true"
|
|
266
280
|
},
|
|
281
|
+
"modal": {
|
|
282
|
+
"type": "boolean",
|
|
283
|
+
"mutable": false,
|
|
284
|
+
"complexType": {
|
|
285
|
+
"original": "boolean",
|
|
286
|
+
"resolved": "boolean",
|
|
287
|
+
"references": {}
|
|
288
|
+
},
|
|
289
|
+
"required": false,
|
|
290
|
+
"optional": false,
|
|
291
|
+
"docs": {
|
|
292
|
+
"tags": [{
|
|
293
|
+
"name": "default",
|
|
294
|
+
"text": "false"
|
|
295
|
+
}],
|
|
296
|
+
"text": "Whether the drawer is modal. A modal drawer dims the page behind it with a\nbackdrop, makes the page inert, and keeps focus inside the drawer, like\n`pds-modal`. Use it for a task the page should wait on. Leave it off for a\npanel people work alongside the page. Read when the drawer opens; changing\nit while the drawer is open is not supported."
|
|
297
|
+
},
|
|
298
|
+
"getter": false,
|
|
299
|
+
"setter": false,
|
|
300
|
+
"reflect": false,
|
|
301
|
+
"attribute": "modal",
|
|
302
|
+
"defaultValue": "false"
|
|
303
|
+
},
|
|
267
304
|
"lightDismiss": {
|
|
268
305
|
"type": "boolean",
|
|
269
306
|
"mutable": false,
|
|
@@ -279,7 +316,7 @@ export class PdsDrawer {
|
|
|
279
316
|
"name": "default",
|
|
280
317
|
"text": "true"
|
|
281
318
|
}],
|
|
282
|
-
"text": "Whether the drawer can be dismissed with a pointerdown outside it.\nReplaces `pds-modal`'s `backdropDismiss
|
|
319
|
+
"text": "Whether the drawer can be dismissed with a pointerdown outside it (in a\n`modal` drawer, a click on its backdrop).\nReplaces `pds-modal`'s `backdropDismiss`.\nThis also gates Escape, matching how `backdropDismiss` gates Escape on\n`pds-modal` today: setting this to `false` means the close button is the\nonly way out.\n\nA trigger outside the drawer should only ever set `open` to `true` and\nleave closing to the drawer itself. A toggle-style trigger (`open =\n!open`) fights light dismiss: clicking it while open closes the drawer\non `pointerdown`, then the trigger's own click handler re-opens it."
|
|
283
320
|
},
|
|
284
321
|
"getter": false,
|
|
285
322
|
"setter": false,
|
|
@@ -302,7 +339,7 @@ export class PdsDrawer {
|
|
|
302
339
|
"name": "default",
|
|
303
340
|
"text": "'auto'"
|
|
304
341
|
}],
|
|
305
|
-
"text": "Whether to move focus into the drawer when it opens. `auto` is right for\na drawer opened by a direct user action (a click, a deep link the user\njust navigated to). Set to `none` for a drawer that can open from a\nbackground event while the user is mid-task elsewhere on the page \u2014\nmoving focus there would interrupt them, and the page stays live under\na non-modal drawer, so it is a genuine data-entry risk, not just an\nannoyance."
|
|
342
|
+
"text": "Whether to move focus into the drawer when it opens. `auto` is right for\na drawer opened by a direct user action (a click, a deep link the user\njust navigated to). Set to `none` for a drawer that can open from a\nbackground event while the user is mid-task elsewhere on the page \u2014\nmoving focus there would interrupt them, and the page stays live under\na non-modal drawer, so it is a genuine data-entry risk, not just an\nannoyance. A `modal` drawer ignores `none`: the page behind it is inert,\nso focus starts in the drawer."
|
|
306
343
|
},
|
|
307
344
|
"getter": false,
|
|
308
345
|
"setter": false,
|
|
@@ -9,6 +9,7 @@ export default {
|
|
|
9
9
|
size: 'md',
|
|
10
10
|
scrollable: true,
|
|
11
11
|
lightDismiss: true,
|
|
12
|
+
modal: false,
|
|
12
13
|
initialFocus: 'auto',
|
|
13
14
|
open: false,
|
|
14
15
|
},
|
|
@@ -28,12 +29,13 @@ const BaseTemplate = (args) => html`
|
|
|
28
29
|
size="${args.size}"
|
|
29
30
|
scrollable="${args.scrollable}"
|
|
30
31
|
light-dismiss="${args.lightDismiss}"
|
|
32
|
+
?modal=${args.modal}
|
|
31
33
|
initial-focus="${args.initialFocus}"
|
|
32
34
|
?open=${args.open}
|
|
33
35
|
>
|
|
34
36
|
<pds-drawer-header>
|
|
35
37
|
<pds-box align-items="center" justify-content="space-between" fit>
|
|
36
|
-
<pds-text tag="h2" size="h4">Drawer Title</pds-text>
|
|
38
|
+
<pds-text tag="h2" size="h4" id="${args.componentId}-heading">Drawer Title</pds-text>
|
|
37
39
|
<pds-button
|
|
38
40
|
class="pds-drawer__close"
|
|
39
41
|
variant="unstyled"
|
|
@@ -70,3 +72,6 @@ StartSide.args = { side: 'start' };
|
|
|
70
72
|
|
|
71
73
|
export const Small = BaseTemplate.bind({});
|
|
72
74
|
Small.args = { size: 'sm' };
|
|
75
|
+
|
|
76
|
+
export const Modal = BaseTemplate.bind({});
|
|
77
|
+
Modal.args = { modal: true, componentId: 'drawer-modal-demo' };
|
|
@@ -60,7 +60,7 @@ export class PdsDropdownMenuItem {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
renderElement() {
|
|
63
|
-
if (this.href
|
|
63
|
+
if (this.href != null) {
|
|
64
64
|
return (h("pds-link", { download: this.download, href: this.disabled ? null : this.href, external: this.external, target: this.target, class: {
|
|
65
65
|
'pds-dropdown-menu-item__content': true,
|
|
66
66
|
'has-focus': this.hasFocus
|
|
@@ -72,7 +72,7 @@ export class PdsDropdownMenuItem {
|
|
|
72
72
|
}, tabIndex: this.disabled ? -1 : 0, type: "button", part: "button", onKeyDown: this.handleKeyDown, onFocus: this.handleFocus, onBlur: this.handleBlur, disabled: this.disabled, "aria-disabled": this.disabled ? 'true' : null }, h("slot", null)));
|
|
73
73
|
}
|
|
74
74
|
render() {
|
|
75
|
-
return (h(Host, { key: '
|
|
75
|
+
return (h(Host, { key: '2d499a06768bdc4b9a34774d16c1f1e10804b3d5', id: this.componentId, class: { 'is-disabled': this.disabled, 'destructive': !this.disabled && this.destructive }, onClick: () => !this.disabled && this.handleClick(), role: "none", tabIndex: -1, "aria-disabled": this.disabled ? 'true' : null }, this.renderElement()));
|
|
76
76
|
}
|
|
77
77
|
static get is() { return "pds-dropdown-menu-item"; }
|
|
78
78
|
static get encapsulation() { return "shadow"; }
|
|
@@ -253,15 +253,19 @@ export class PdsDropdownMenu {
|
|
|
253
253
|
this.focusItemByIndex(prevIndex);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
|
+
// Escape is taken in document's capture phase, before a modal around the menu hears it on
|
|
257
|
+
// document and closes too.
|
|
258
|
+
handleEscapeKeyDown(event) {
|
|
259
|
+
if (!this.isOpen || event.key !== 'Escape' || event.defaultPrevented)
|
|
260
|
+
return;
|
|
261
|
+
event.preventDefault();
|
|
262
|
+
this.closeDropdown();
|
|
263
|
+
}
|
|
256
264
|
// Handle keyboard events for the dropdown
|
|
257
265
|
handleKeyDown(event) {
|
|
258
266
|
if (!this.isOpen)
|
|
259
267
|
return;
|
|
260
268
|
switch (event.key) {
|
|
261
|
-
case 'Escape':
|
|
262
|
-
event.preventDefault();
|
|
263
|
-
this.closeDropdown();
|
|
264
|
-
break;
|
|
265
269
|
case 'ArrowDown':
|
|
266
270
|
event.preventDefault();
|
|
267
271
|
this.focusNextItem();
|
|
@@ -353,7 +357,7 @@ export class PdsDropdownMenu {
|
|
|
353
357
|
}
|
|
354
358
|
}
|
|
355
359
|
render() {
|
|
356
|
-
return (h(Host, { key: '
|
|
360
|
+
return (h(Host, { key: '18dac4d59ea236fe171f4c16f6571cb1547059c6', id: this.componentId }, h("slot", { key: '95201a75a33ccb70dbd51a5cf9d78c76ebf26f65', name: "trigger", onSlotchange: this.handleTriggerSlotChange }), h("pds-box", { key: '7fc588f40f607ad81ad70bf74a525d078d32a80b', "border-radius": "sm", display: "flex", direction: "column", class: "pds-dropdown-menu--panel is-hidden", shadow: "100", part: "menu-panel" }, h("div", { key: '73d13d94edccb68db7b703f930b50f729334efa8', class: { 'pds-dropdown-menu__header': true, 'is-hidden': !this.hasHeader } }, h("slot", { key: '7066f3a6f71c8961326f38f0509f1c0cc17098ae', name: "header", onSlotchange: this.handleHeaderSlotChange })), h("div", { key: '9f07ec488b688c58c6422b575015ccd364075fe8', class: "pds-dropdown-menu__items", role: "menu", "aria-orientation": "vertical" }, h("slot", { key: 'd0aad3f9ac3b58779669411523adbb8878dec61c', onSlotchange: this.handleSlotChange })))));
|
|
357
361
|
}
|
|
358
362
|
static get is() { return "pds-dropdown-menu"; }
|
|
359
363
|
static get encapsulation() { return "shadow"; }
|
|
@@ -426,6 +430,12 @@ export class PdsDropdownMenu {
|
|
|
426
430
|
static get elementRef() { return "host"; }
|
|
427
431
|
static get listeners() {
|
|
428
432
|
return [{
|
|
433
|
+
"name": "keydown",
|
|
434
|
+
"method": "handleEscapeKeyDown",
|
|
435
|
+
"target": "document",
|
|
436
|
+
"capture": true,
|
|
437
|
+
"passive": false
|
|
438
|
+
}, {
|
|
429
439
|
"name": "keydown",
|
|
430
440
|
"method": "handleKeyDown",
|
|
431
441
|
"target": "window",
|
|
@@ -72,6 +72,20 @@ function oneRadioPerGroup(elements) {
|
|
|
72
72
|
const stops = new Set(groups.map((radios) => { var _a; return (_a = radios.find((radio) => radio.checked)) !== null && _a !== void 0 ? _a : radios[0]; }));
|
|
73
73
|
return elements.filter((el) => groupedRadio(el) === null || stops.has(el));
|
|
74
74
|
}
|
|
75
|
+
/** The controls Tab reaches in `root`, in order: visible ones, and one radio per group. */
|
|
76
|
+
function tabbableIn(root) {
|
|
77
|
+
const found = [];
|
|
78
|
+
collectTabbable(root, found);
|
|
79
|
+
// checkVisibility() also drops an element inside a hidden ancestor, whose own computed
|
|
80
|
+
// display isn't none; such an element can't take focus.
|
|
81
|
+
const visible = found.filter(el => {
|
|
82
|
+
if (typeof el.checkVisibility === 'function')
|
|
83
|
+
return el.checkVisibility({ checkVisibilityCSS: true });
|
|
84
|
+
const style = window.getComputedStyle(el);
|
|
85
|
+
return style.display !== 'none' && style.visibility !== 'hidden';
|
|
86
|
+
});
|
|
87
|
+
return oneRadioPerGroup(visible);
|
|
88
|
+
}
|
|
75
89
|
/** Whether `node` is `ancestor` or inside it, crossing shadow roots, which `Node.contains` doesn't. */
|
|
76
90
|
function containsComposed(ancestor, node) {
|
|
77
91
|
for (let current = node; current; current = current instanceof ShadowRoot ? current.host : current.parentNode) {
|
|
@@ -80,6 +94,34 @@ function containsComposed(ancestor, node) {
|
|
|
80
94
|
}
|
|
81
95
|
return false;
|
|
82
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Every open modal, with the order it opened in, which ranks top-layer modals and the modals
|
|
99
|
+
* inside them. Kept here rather than queried from the document, which can't see a modal inside
|
|
100
|
+
* another component's shadow root.
|
|
101
|
+
*/
|
|
102
|
+
const openModals = new Map();
|
|
103
|
+
let openSequence = 0;
|
|
104
|
+
/** Whether an open auto popover's panel sits in `root`, including inside shadow roots under it. */
|
|
105
|
+
function hasOpenPopover(root) {
|
|
106
|
+
if (root.querySelector('[data-pds-popover-open]'))
|
|
107
|
+
return true;
|
|
108
|
+
return Array.from(root.querySelectorAll('*')).some(el => !!el.shadowRoot && hasOpenPopover(el.shadowRoot));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Whether `a` comes before `b` in the page. `compareDocumentPosition` is arbitrary across a shadow
|
|
112
|
+
* boundary, so each side climbs to its host until both are in the same tree.
|
|
113
|
+
*/
|
|
114
|
+
function precedesComposed(a, b) {
|
|
115
|
+
// A document has no host, which ends each climb.
|
|
116
|
+
for (let x = a; x; x = x.getRootNode().host) {
|
|
117
|
+
for (let y = b; y; y = y.getRootNode().host) {
|
|
118
|
+
if (x.getRootNode() === y.getRootNode()) {
|
|
119
|
+
return !!(x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
83
125
|
/** The focused element, followed down through shadow roots to the control that has focus. */
|
|
84
126
|
function deepActiveElement() {
|
|
85
127
|
var _a;
|
|
@@ -95,7 +137,8 @@ export class PdsModal {
|
|
|
95
137
|
constructor() {
|
|
96
138
|
this.focusableElements = [];
|
|
97
139
|
/**
|
|
98
|
-
* Whether the modal can be dismissed by clicking the backdrop
|
|
140
|
+
* Whether the modal can be dismissed by clicking the backdrop, pressing Escape, or the browser's
|
|
141
|
+
* own close request (such as a back gesture)
|
|
99
142
|
* @default true
|
|
100
143
|
*/
|
|
101
144
|
this.backdropDismiss = true;
|
|
@@ -143,19 +186,33 @@ export class PdsModal {
|
|
|
143
186
|
* Stores the list of focusable elements in the modal
|
|
144
187
|
*/
|
|
145
188
|
this.focusableElementsArray = [];
|
|
189
|
+
// The browser's own close request (an Escape nothing prevented, a back gesture) would close the
|
|
190
|
+
// dialog without updating `open`, so close it through hideModal() instead, or not at all.
|
|
191
|
+
this.handleCancel = (e) => {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
if (!this.open)
|
|
194
|
+
return;
|
|
195
|
+
// The request reaches the top-layer dialog even when a disableTopLayer modal inside it is on
|
|
196
|
+
// top; that one closes first, as it would on Escape.
|
|
197
|
+
const top = this.innermostOpenModal();
|
|
198
|
+
const target = (top && containsComposed(this.el, top) ? top : this.el);
|
|
199
|
+
if (target.backdropDismiss)
|
|
200
|
+
target.hideModal();
|
|
201
|
+
};
|
|
146
202
|
this.handleBackdropClick = (e) => {
|
|
147
203
|
if (!this.backdropDismiss || !this.open)
|
|
148
204
|
return;
|
|
149
|
-
|
|
205
|
+
// This modal's own backdrop; a nested modal's backdrop handles its own clicks.
|
|
206
|
+
if (e.target === this.modalRef) {
|
|
150
207
|
e.stopPropagation();
|
|
151
208
|
// Only close if this is the innermost modal
|
|
152
|
-
if (this.
|
|
209
|
+
if (this.innermostOpenModal() === this.el) {
|
|
153
210
|
this.hideModal();
|
|
154
211
|
}
|
|
155
212
|
}
|
|
156
213
|
};
|
|
157
214
|
this.handleKeyDown = (e) => {
|
|
158
|
-
var _a, _b, _c;
|
|
215
|
+
var _a, _b, _c, _d;
|
|
159
216
|
// If the modal is not open, don't handle any keyboard events
|
|
160
217
|
if (!this.open)
|
|
161
218
|
return;
|
|
@@ -178,24 +235,39 @@ export class PdsModal {
|
|
|
178
235
|
if (!('__pdsActiveElementAtEscape' in eventSnapshot)) {
|
|
179
236
|
const activeAtDispatch = document.activeElement;
|
|
180
237
|
eventSnapshot.__pdsActiveElementAtEscape = activeAtDispatch;
|
|
238
|
+
// Followed into shadow roots: for a modal inside one, document.activeElement is only the host.
|
|
239
|
+
eventSnapshot.__pdsDeepActiveElementAtEscape = deepActiveElement();
|
|
181
240
|
const topLayerDialog = (_a = activeAtDispatch === null || activeAtDispatch === void 0 ? void 0 : activeAtDispatch.closest('dialog')) !== null && _a !== void 0 ? _a : null;
|
|
182
241
|
eventSnapshot.__pdsTopLayerDialogAtEscape = topLayerDialog;
|
|
183
242
|
eventSnapshot.__pdsTopLayerDialogWasModalAtEscape = !!(topLayerDialog && topLayerDialog.matches(':modal'));
|
|
243
|
+
// A control that closed on this Escape (a combobox on its own element, a dropdown menu or
|
|
244
|
+
// multiselect in document's capture phase) has prevented it before this bubble listener.
|
|
245
|
+
// Read before any modal prevents it below.
|
|
246
|
+
eventSnapshot.__pdsEscapeHandledInside = e.defaultPrevented;
|
|
247
|
+
// Which modal is innermost is read once too: the inner one closing first would make its
|
|
248
|
+
// outer modal the innermost by the time that modal's listener runs.
|
|
249
|
+
eventSnapshot.__pdsInnermostModalAtEscape = this.innermostOpenModal();
|
|
184
250
|
}
|
|
251
|
+
if (eventSnapshot.__pdsEscapeHandledInside)
|
|
252
|
+
return;
|
|
185
253
|
// Leave Escape to a genuinely stacked-above overlay (disableTopLayer's
|
|
186
254
|
// reason for existing) — not just to anything outside this modal.
|
|
187
255
|
const active = eventSnapshot.__pdsActiveElementAtEscape;
|
|
188
256
|
if (this.disableTopLayer &&
|
|
189
257
|
active &&
|
|
190
258
|
active !== document.body &&
|
|
191
|
-
!this.el.
|
|
192
|
-
this.isStackedAboveOverlay(active, (
|
|
259
|
+
!containsComposed(this.el, (_b = eventSnapshot.__pdsDeepActiveElementAtEscape) !== null && _b !== void 0 ? _b : active) &&
|
|
260
|
+
this.isStackedAboveOverlay(active, (_c = eventSnapshot.__pdsTopLayerDialogAtEscape) !== null && _c !== void 0 ? _c : null, (_d = eventSnapshot.__pdsTopLayerDialogWasModalAtEscape) !== null && _d !== void 0 ? _d : false)) {
|
|
193
261
|
return;
|
|
194
262
|
}
|
|
263
|
+
// An open popover inside takes Escape from its own document listener, which runs after this
|
|
264
|
+
// one, so leave the key for it. Its preventDefault() keeps the native dialog open.
|
|
265
|
+
if (this.modalRef && hasOpenPopover(this.modalRef))
|
|
266
|
+
return;
|
|
195
267
|
// Always prevent native dialog close behavior
|
|
196
268
|
e.preventDefault();
|
|
197
269
|
// Only close if backdropDismiss is enabled and this is the innermost modal
|
|
198
|
-
if (this.backdropDismiss && this.
|
|
270
|
+
if (this.backdropDismiss && eventSnapshot.__pdsInnermostModalAtEscape === this.el) {
|
|
199
271
|
this.hideModal();
|
|
200
272
|
}
|
|
201
273
|
return;
|
|
@@ -255,8 +327,6 @@ export class PdsModal {
|
|
|
255
327
|
}
|
|
256
328
|
componentDidLoad() {
|
|
257
329
|
this.modalRef = this.el.querySelector('.pds-modal__backdrop');
|
|
258
|
-
// Add keyboard event listener
|
|
259
|
-
document.addEventListener('keydown', this.handleKeyDown);
|
|
260
330
|
// @Watch('open') only fires on a later change, not the prop's initial
|
|
261
331
|
// value — a modal mounted with `open` already true (static markup, or a
|
|
262
332
|
// framework passing it on first render) would otherwise never call
|
|
@@ -272,9 +342,18 @@ export class PdsModal {
|
|
|
272
342
|
componentDidRender() {
|
|
273
343
|
unwrapReconnectedContent(this.el.querySelector('.pds-modal'), 'dialog.pds-modal__backdrop', '.pds-modal');
|
|
274
344
|
}
|
|
345
|
+
// Here rather than in componentDidLoad, which runs once: a modal taken out of the page and put
|
|
346
|
+
// back (a morph or a framework moving it) gets its keyboard handling, and its place among the
|
|
347
|
+
// open modals, back too. Adding the same listener twice is a no-op.
|
|
348
|
+
connectedCallback() {
|
|
349
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
|
350
|
+
if (this.open && !openModals.has(this.el))
|
|
351
|
+
openModals.set(this.el, ++openSequence);
|
|
352
|
+
}
|
|
275
353
|
disconnectedCallback() {
|
|
276
354
|
// Clean up event listener
|
|
277
355
|
document.removeEventListener('keydown', this.handleKeyDown);
|
|
356
|
+
openModals.delete(this.el);
|
|
278
357
|
}
|
|
279
358
|
handleOpenChange(newValue) {
|
|
280
359
|
if (newValue) {
|
|
@@ -290,17 +369,7 @@ export class PdsModal {
|
|
|
290
369
|
updateFocusableElements() {
|
|
291
370
|
if (!this.modalRef)
|
|
292
371
|
return;
|
|
293
|
-
this.focusableElements =
|
|
294
|
-
collectTabbable(this.modalRef, this.focusableElements);
|
|
295
|
-
// checkVisibility() also drops an element inside a hidden ancestor, whose own computed
|
|
296
|
-
// display isn't none; such an element can't take focus, so it can't anchor the trap.
|
|
297
|
-
this.focusableElements = this.focusableElements.filter(el => {
|
|
298
|
-
if (typeof el.checkVisibility === 'function')
|
|
299
|
-
return el.checkVisibility({ checkVisibilityCSS: true });
|
|
300
|
-
const style = window.getComputedStyle(el);
|
|
301
|
-
return style.display !== 'none' && style.visibility !== 'hidden';
|
|
302
|
-
});
|
|
303
|
-
this.focusableElements = oneRadioPerGroup(this.focusableElements);
|
|
372
|
+
this.focusableElements = tabbableIn(this.modalRef);
|
|
304
373
|
}
|
|
305
374
|
/**
|
|
306
375
|
* Sets focus to the first focusable element in the modal
|
|
@@ -324,10 +393,13 @@ export class PdsModal {
|
|
|
324
393
|
* Opens the modal
|
|
325
394
|
*/
|
|
326
395
|
async showModal() {
|
|
396
|
+
var _a;
|
|
327
397
|
if (this.modalRef) {
|
|
328
398
|
try {
|
|
329
|
-
// Store the currently focused element to restore focus when modal closes
|
|
330
|
-
|
|
399
|
+
// Store the currently focused element to restore focus when modal closes. Showing an open
|
|
400
|
+
// modal again (the open watcher does) would record a control inside it instead.
|
|
401
|
+
if (!this.modalRef.open)
|
|
402
|
+
this.previousActiveElement = document.activeElement;
|
|
331
403
|
// showModal() promotes the dialog to the browser top layer (and makes the
|
|
332
404
|
// rest of the page inert), which prevents any overlay outside the dialog
|
|
333
405
|
// from ever painting above it. show() opens a non-modal dialog that stays
|
|
@@ -339,11 +411,25 @@ export class PdsModal {
|
|
|
339
411
|
this.modalRef.showModal();
|
|
340
412
|
}
|
|
341
413
|
this.open = true;
|
|
414
|
+
// Showing an open modal again doesn't move it above one opened after it.
|
|
415
|
+
if (!openModals.has(this.el))
|
|
416
|
+
openModals.set(this.el, ++openSequence);
|
|
342
417
|
// Update focusable elements and set initial focus
|
|
343
418
|
// Using a longer timeout to ensure all components are fully rendered
|
|
419
|
+
const openedAs = (_a = openModals.get(this.el)) !== null && _a !== void 0 ? _a : 0;
|
|
344
420
|
setTimeout(() => {
|
|
345
421
|
var _a, _b;
|
|
346
422
|
this.updateFocusableElements();
|
|
423
|
+
// A modal opened above this one in the meantime has focus; don't take it back. Not this
|
|
424
|
+
// modal itself (moving it gives it a new place), and not a disableTopLayer modal outside
|
|
425
|
+
// it while this one is in the top layer, which sits below it.
|
|
426
|
+
const openedAbove = Array.from(openModals).some(([modal, sequence]) => sequence > openedAs &&
|
|
427
|
+
modal !== this.el &&
|
|
428
|
+
(containsComposed(this.el, modal) || !modal.disableTopLayer || this.disableTopLayer));
|
|
429
|
+
if (openedAbove) {
|
|
430
|
+
this.pdsModalOpen.emit();
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
347
433
|
if (this.disableInitialFocus) {
|
|
348
434
|
// show()/showModal() also move focus into the dialog natively (the
|
|
349
435
|
// HTML spec's "dialog focusing steps") — that native step isn't
|
|
@@ -387,6 +473,7 @@ export class PdsModal {
|
|
|
387
473
|
* Closes the modal
|
|
388
474
|
*/
|
|
389
475
|
async hideModal() {
|
|
476
|
+
var _a;
|
|
390
477
|
if (this.modalRef) {
|
|
391
478
|
try {
|
|
392
479
|
// Capture before close(): with the async task queue, a prop change
|
|
@@ -401,11 +488,19 @@ export class PdsModal {
|
|
|
401
488
|
const focusAlreadyMovedElsewhere = activeElement !== null && activeElement !== document.body && !this.modalRef.contains(activeElement);
|
|
402
489
|
this.modalRef.close();
|
|
403
490
|
this.open = false;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
491
|
+
openModals.delete(this.el);
|
|
492
|
+
// Restore focus to the element that was focused before the modal was opened. If that's
|
|
493
|
+
// gone or can't take focus (disabled, hidden, or nothing), fall back to the open modal now
|
|
494
|
+
// on top, so focus doesn't land on an inert page behind it.
|
|
495
|
+
if (!focusAlreadyMovedElsewhere) {
|
|
496
|
+
const previous = this.previousActiveElement;
|
|
497
|
+
if (previous && previous !== document.body && previous.isConnected)
|
|
498
|
+
(_a = previous.focus) === null || _a === void 0 ? void 0 : _a.call(previous);
|
|
499
|
+
const active = deepActiveElement();
|
|
500
|
+
// document.body isn't a real restore target — it's just where focus lands when nothing
|
|
501
|
+
// was focused before opening, so landing back there never counts as a successful restore.
|
|
502
|
+
if (!previous || previous === document.body || !containsComposed(previous, active) || containsComposed(this.el, active))
|
|
503
|
+
this.focusTopModal();
|
|
409
504
|
}
|
|
410
505
|
this.pdsModalClose.emit();
|
|
411
506
|
}
|
|
@@ -414,12 +509,29 @@ export class PdsModal {
|
|
|
414
509
|
}
|
|
415
510
|
}
|
|
416
511
|
}
|
|
512
|
+
/**
|
|
513
|
+
* Focuses the first control of the open modal now on top, when the page behind it is inert: that
|
|
514
|
+
* modal is in the top layer (or paints there inside a top-layer one), or it wraps this one. A
|
|
515
|
+
* non-modal one over an interactive page, like a drawer, is left alone.
|
|
516
|
+
*/
|
|
517
|
+
focusTopModal() {
|
|
518
|
+
var _a;
|
|
519
|
+
const top = this.innermostOpenModal();
|
|
520
|
+
const dialog = top === null || top === void 0 ? void 0 : top.querySelector('.pds-modal__backdrop');
|
|
521
|
+
if (!dialog)
|
|
522
|
+
return;
|
|
523
|
+
if (!closest('dialog:modal', dialog) && !containsComposed(top, this.el))
|
|
524
|
+
return;
|
|
525
|
+
(_a = tabbableIn(dialog).find(el => !containsComposed(this.el, el))) === null || _a === void 0 ? void 0 : _a.focus();
|
|
526
|
+
}
|
|
417
527
|
/**
|
|
418
528
|
* Gets the z-index of a modal's backdrop element
|
|
419
529
|
*/
|
|
420
530
|
getBackdropZIndex(modal) {
|
|
421
531
|
const backdrop = modal.querySelector('.pds-modal__backdrop');
|
|
422
|
-
|
|
532
|
+
// `auto` (no z-index token loaded) parses to NaN, which no comparison can rank.
|
|
533
|
+
const zIndex = backdrop ? parseInt(getComputedStyle(backdrop).zIndex, 10) : -1;
|
|
534
|
+
return Number.isNaN(zIndex) ? 0 : zIndex;
|
|
423
535
|
}
|
|
424
536
|
/**
|
|
425
537
|
* Whether `active` sits inside a surface actually stacked above this modal.
|
|
@@ -446,7 +558,8 @@ export class PdsModal {
|
|
|
446
558
|
* lose that evidence depending on listener order.
|
|
447
559
|
*/
|
|
448
560
|
isStackedAboveOverlay(active, topLayerDialog, topLayerDialogWasModal) {
|
|
449
|
-
|
|
561
|
+
// A top-layer dialog this modal sits inside is underneath it, not an overlay above.
|
|
562
|
+
if (topLayerDialog && topLayerDialog !== this.modalRef && topLayerDialogWasModal && !containsComposed(topLayerDialog, this.el)) {
|
|
450
563
|
return true;
|
|
451
564
|
}
|
|
452
565
|
const ownZIndex = this.getBackdropZIndex(this.el);
|
|
@@ -462,24 +575,37 @@ export class PdsModal {
|
|
|
462
575
|
return false;
|
|
463
576
|
}
|
|
464
577
|
/**
|
|
465
|
-
*
|
|
578
|
+
* The open modal that takes Escape or a backdrop click, among those with no other open modal
|
|
579
|
+
* inside them (a nested modal's backdrop can share its outer modal's z-index). A top-layer dialog
|
|
580
|
+
* (or a modal inside one) paints above anything outside it, and within the top layer the last
|
|
581
|
+
* opened is on top. Modals in the same layer (the page, or one top-layer dialog) then rank by
|
|
582
|
+
* the highest backdrop, then the one later in the page.
|
|
466
583
|
*/
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
//
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
584
|
+
innermostOpenModal() {
|
|
585
|
+
const open = Array.from(openModals.keys()).filter(modal => modal.isConnected);
|
|
586
|
+
const innermost = open.filter(modal => !open.some(other => other !== modal && containsComposed(modal, other)));
|
|
587
|
+
const byDocumentOrder = [...innermost].sort((a, b) => (precedesComposed(a, b) ? -1 : 1));
|
|
588
|
+
// A modal inside a top-layer dialog paints in that dialog's place in the top layer, even when it
|
|
589
|
+
// isn't in the top layer itself (disableTopLayer), so that dialog's open order ranks it first.
|
|
590
|
+
const rank = (modal) => {
|
|
591
|
+
var _a;
|
|
592
|
+
const backdrop = modal.querySelector('.pds-modal__backdrop');
|
|
593
|
+
const topLayerDialog = backdrop ? closest('dialog:modal', backdrop) : null;
|
|
594
|
+
const topLayerModal = topLayerDialog === null || topLayerDialog === void 0 ? void 0 : topLayerDialog.closest('pds-modal');
|
|
595
|
+
const layer = topLayerDialog ? ((_a = (topLayerModal && openModals.get(topLayerModal))) !== null && _a !== void 0 ? _a : 0) : 0;
|
|
596
|
+
return [topLayerDialog ? 1 : 0, layer, this.getBackdropZIndex(modal), byDocumentOrder.indexOf(modal)];
|
|
597
|
+
};
|
|
598
|
+
let top = null;
|
|
599
|
+
let topRank = [-1, -1, -1, -1];
|
|
600
|
+
for (const modal of innermost) {
|
|
601
|
+
const modalRank = rank(modal);
|
|
602
|
+
const above = modalRank.findIndex((value, i) => value !== topRank[i]);
|
|
603
|
+
if (above >= 0 && modalRank[above] > topRank[above]) {
|
|
604
|
+
top = modal;
|
|
605
|
+
topRank = modalRank;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return top;
|
|
483
609
|
}
|
|
484
610
|
/** Whether a modal dialog other than this one sits between the focused element and this modal. */
|
|
485
611
|
modalDialogAbove(active) {
|
|
@@ -519,14 +645,14 @@ export class PdsModal {
|
|
|
519
645
|
}
|
|
520
646
|
render() {
|
|
521
647
|
const [sizeClass = 'md', customSize] = this.resolveSize();
|
|
522
|
-
return (h("dialog", { key: '
|
|
648
|
+
return (h("dialog", { key: '0b187f36910a859afe57b891f1ebffd1d2d49ab7', class: {
|
|
523
649
|
'pds-modal__backdrop': true,
|
|
524
650
|
'open': this.open
|
|
525
|
-
}, "aria-modal": this.disableTopLayer ? 'false' : 'true', "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick }, h("div", { key: '
|
|
651
|
+
}, "aria-modal": this.disableTopLayer ? 'false' : 'true', "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick, onCancel: this.handleCancel }, h("div", { key: 'f5e652d4dae0e7c272b37b63eb9c65f04c69536a', class: {
|
|
526
652
|
'pds-modal': true,
|
|
527
653
|
[`pds-modal--${sizeClass}`]: true,
|
|
528
654
|
'pds-modal--scrollable': this.scrollable
|
|
529
|
-
}, style: customSize !== undefined ? { '--pds-modal-max-width': customSize } : undefined, part: "modal" }, h("slot", { key: '
|
|
655
|
+
}, style: customSize !== undefined ? { '--pds-modal-max-width': customSize } : undefined, part: "modal" }, h("slot", { key: 'a954e1694741f78230599c476b270079797a263e' }))));
|
|
530
656
|
}
|
|
531
657
|
static get is() { return "pds-modal"; }
|
|
532
658
|
static get originalStyleUrls() {
|
|
@@ -556,7 +682,7 @@ export class PdsModal {
|
|
|
556
682
|
"name": "default",
|
|
557
683
|
"text": "true"
|
|
558
684
|
}],
|
|
559
|
-
"text": "Whether the modal can be dismissed by clicking the backdrop"
|
|
685
|
+
"text": "Whether the modal can be dismissed by clicking the backdrop, pressing Escape, or the browser's\nown close request (such as a back gesture)"
|
|
560
686
|
},
|
|
561
687
|
"getter": false,
|
|
562
688
|
"setter": false,
|
|
@@ -763,6 +889,10 @@ export class PdsModal {
|
|
|
763
889
|
"HTMLElement": {
|
|
764
890
|
"location": "global",
|
|
765
891
|
"id": "global::HTMLElement"
|
|
892
|
+
},
|
|
893
|
+
"HTMLPdsModalElement": {
|
|
894
|
+
"location": "global",
|
|
895
|
+
"id": "global::HTMLPdsModalElement"
|
|
766
896
|
}
|
|
767
897
|
},
|
|
768
898
|
"return": "Promise<void>"
|