@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.
Files changed (174) hide show
  1. package/components/pds-box2.js +1 -1
  2. package/components/pds-checkbox2.js +1 -1
  3. package/components/pds-chip2.js +1 -1
  4. package/components/pds-combobox.js +1 -1
  5. package/components/pds-copytext.js +1 -1
  6. package/components/pds-drawer.js +1 -1
  7. package/components/pds-dropdown-menu-item.js +1 -1
  8. package/components/pds-dropdown-menu.js +1 -1
  9. package/components/pds-modal2.js +1 -1
  10. package/components/pds-multiselect.js +1 -1
  11. package/components/pds-popover.js +1 -1
  12. package/components/pds-radio.js +1 -1
  13. package/components/pds-row.js +1 -1
  14. package/components/pds-switch.js +1 -1
  15. package/components/pds-table-cell2.js +1 -1
  16. package/components/pds-text2.js +1 -1
  17. package/components/truncation-tooltip.js +1 -1
  18. package/dist/cjs/loader.cjs.js +1 -1
  19. package/dist/cjs/pds-box.cjs.entry.js +96 -96
  20. package/dist/cjs/pds-checkbox.cjs.entry.js +16 -4
  21. package/dist/cjs/pds-chip.cjs.entry.js +34 -9
  22. package/dist/cjs/pds-combobox.cjs.entry.js +47 -4
  23. package/dist/cjs/pds-copytext.cjs.entry.js +7 -2
  24. package/dist/cjs/pds-drawer.cjs.entry.js +23 -10
  25. package/dist/cjs/pds-dropdown-menu-item.cjs.entry.js +2 -2
  26. package/dist/cjs/pds-dropdown-menu.cjs.entry.js +9 -5
  27. package/dist/cjs/pds-modal.cjs.entry.js +175 -49
  28. package/dist/cjs/pds-multiselect.cjs.entry.js +40 -9
  29. package/dist/cjs/pds-popover.cjs.entry.js +170 -26
  30. package/dist/cjs/pds-radio.cjs.entry.js +1 -1
  31. package/dist/cjs/pds-row.cjs.entry.js +7 -7
  32. package/dist/cjs/pds-switch.cjs.entry.js +15 -3
  33. package/dist/cjs/pds-table-cell.cjs.entry.js +24 -4
  34. package/dist/cjs/pds-text.cjs.entry.js +17 -9
  35. package/dist/cjs/pine-core.cjs.js +1 -1
  36. package/dist/cjs/{truncation-tooltip-CYgQfBLt.js → truncation-tooltip-DO9xli2e.js} +7 -7
  37. package/dist/collection/components/pds-box/pds-box.js +96 -96
  38. package/dist/collection/components/pds-checkbox/pds-checkbox.css +4 -0
  39. package/dist/collection/components/pds-checkbox/pds-checkbox.js +15 -3
  40. package/dist/collection/components/pds-chip/pds-chip.js +33 -8
  41. package/dist/collection/components/pds-chip/stories/pds-chip.stories.js +17 -0
  42. package/dist/collection/components/pds-combobox/pds-combobox.js +48 -5
  43. package/dist/collection/components/pds-combobox/stories/pds-combobox.stories.js +1 -1
  44. package/dist/collection/components/pds-copytext/pds-copytext.js +6 -1
  45. package/dist/collection/components/pds-drawer/pds-drawer.css +4 -1
  46. package/dist/collection/components/pds-drawer/pds-drawer.js +54 -17
  47. package/dist/collection/components/pds-drawer/stories/pds-drawer.stories.js +6 -1
  48. package/dist/collection/components/pds-dropdown-menu/pds-dropdown-menu-item/pds-dropdown-menu-item.js +2 -2
  49. package/dist/collection/components/pds-dropdown-menu/pds-dropdown-menu.js +15 -5
  50. package/dist/collection/components/pds-modal/pds-modal.js +180 -50
  51. package/dist/collection/components/pds-multiselect/pds-multiselect.css +4 -0
  52. package/dist/collection/components/pds-multiselect/pds-multiselect.js +42 -11
  53. package/dist/collection/components/pds-popover/pds-popover.js +171 -27
  54. package/dist/collection/components/pds-radio/pds-radio.css +6 -0
  55. package/dist/collection/components/pds-row/pds-row.js +7 -7
  56. package/dist/collection/components/pds-switch/pds-switch.js +15 -3
  57. package/dist/collection/components/pds-table/pds-table-cell/pds-table-cell.js +23 -3
  58. package/dist/collection/components/pds-text/pds-text.js +16 -8
  59. package/dist/collection/utils/truncation-tooltip.js +7 -7
  60. package/dist/docs.json +48 -9
  61. package/dist/esm/loader.js +1 -1
  62. package/dist/esm/pds-box.entry.js +96 -96
  63. package/dist/esm/pds-checkbox.entry.js +16 -4
  64. package/dist/esm/pds-chip.entry.js +34 -9
  65. package/dist/esm/pds-combobox.entry.js +47 -4
  66. package/dist/esm/pds-copytext.entry.js +7 -2
  67. package/dist/esm/pds-drawer.entry.js +23 -10
  68. package/dist/esm/pds-dropdown-menu-item.entry.js +2 -2
  69. package/dist/esm/pds-dropdown-menu.entry.js +9 -5
  70. package/dist/esm/pds-modal.entry.js +175 -49
  71. package/dist/esm/pds-multiselect.entry.js +40 -9
  72. package/dist/esm/pds-popover.entry.js +171 -27
  73. package/dist/esm/pds-radio.entry.js +1 -1
  74. package/dist/esm/pds-row.entry.js +7 -7
  75. package/dist/esm/pds-switch.entry.js +15 -3
  76. package/dist/esm/pds-table-cell.entry.js +24 -4
  77. package/dist/esm/pds-text.entry.js +17 -9
  78. package/dist/esm/pine-core.js +1 -1
  79. package/dist/esm/{truncation-tooltip-_902ENWC.js → truncation-tooltip-DRXsdKk1.js} +7 -7
  80. package/dist/esm-es5/loader.js +1 -1
  81. package/dist/esm-es5/pds-box.entry.js +1 -1
  82. package/dist/esm-es5/pds-checkbox.entry.js +1 -1
  83. package/dist/esm-es5/pds-chip.entry.js +1 -1
  84. package/dist/esm-es5/pds-combobox.entry.js +1 -1
  85. package/dist/esm-es5/pds-copytext.entry.js +1 -1
  86. package/dist/esm-es5/pds-drawer.entry.js +1 -1
  87. package/dist/esm-es5/pds-dropdown-menu-item.entry.js +1 -1
  88. package/dist/esm-es5/pds-dropdown-menu.entry.js +1 -1
  89. package/dist/esm-es5/pds-modal.entry.js +1 -1
  90. package/dist/esm-es5/pds-multiselect.entry.js +1 -1
  91. package/dist/esm-es5/pds-popover.entry.js +1 -1
  92. package/dist/esm-es5/pds-radio.entry.js +1 -1
  93. package/dist/esm-es5/pds-row.entry.js +1 -1
  94. package/dist/esm-es5/pds-switch.entry.js +1 -1
  95. package/dist/esm-es5/pds-table-cell.entry.js +1 -1
  96. package/dist/esm-es5/pds-text.entry.js +1 -1
  97. package/dist/esm-es5/pine-core.js +1 -1
  98. package/dist/esm-es5/truncation-tooltip-DRXsdKk1.js +1 -0
  99. package/dist/pine-core/{p-9af131d6.system.entry.js → p-11d98408.system.entry.js} +1 -1
  100. package/dist/pine-core/p-153ba77b.entry.js +1 -0
  101. package/dist/pine-core/p-355e4cf9.entry.js +1 -0
  102. package/dist/pine-core/p-4435fff6.system.entry.js +1 -0
  103. package/dist/pine-core/p-576cf904.system.entry.js +1 -0
  104. package/dist/pine-core/{p-4f94b4f1.system.entry.js → p-645a482b.system.entry.js} +1 -1
  105. package/dist/pine-core/{p-4fa63723.system.entry.js → p-6d603d16.system.entry.js} +1 -1
  106. package/dist/pine-core/p-74a05bcc.entry.js +1 -0
  107. package/dist/pine-core/p-7fa66cad.entry.js +1 -0
  108. package/dist/pine-core/p-80a698ae.entry.js +1 -0
  109. package/dist/pine-core/{p-bf7ee3b7.system.entry.js → p-80e6f3a0.system.entry.js} +1 -1
  110. package/dist/pine-core/p-88ff1604.entry.js +1 -0
  111. package/dist/pine-core/p-92ae6082.entry.js +1 -0
  112. package/dist/pine-core/{p-6eec23a7.system.entry.js → p-97d0b89e.system.entry.js} +1 -1
  113. package/dist/pine-core/p-993d1043.entry.js +1 -0
  114. package/dist/pine-core/p-CuSNnuFu.system.js +1 -1
  115. package/dist/pine-core/p-JtdHT_W5.js +1 -0
  116. package/dist/pine-core/p-a787b34a.system.entry.js +1 -0
  117. package/dist/pine-core/p-a939b471.entry.js +1 -0
  118. package/dist/pine-core/p-aa032a5e.system.entry.js +1 -0
  119. package/dist/pine-core/{p-646afbca.system.entry.js → p-b60ac7eb.system.entry.js} +1 -1
  120. package/dist/pine-core/p-bb73ad66.entry.js +2 -0
  121. package/dist/pine-core/p-bd26a5e8.entry.js +1 -0
  122. package/dist/pine-core/p-bf5887e8.system.entry.js +1 -0
  123. package/dist/pine-core/p-c36b490e.entry.js +1 -0
  124. package/dist/pine-core/p-c81d8ac8.entry.js +1 -0
  125. package/dist/pine-core/p-ca0a8f5c.entry.js +1 -0
  126. package/dist/pine-core/p-ca0d48e8.entry.js +1 -0
  127. package/dist/pine-core/p-cfc52b95.system.entry.js +1 -0
  128. package/dist/pine-core/{p-ba1f65c2.system.entry.js → p-d8570b4b.system.entry.js} +1 -1
  129. package/dist/pine-core/{p-9e0f920a.system.entry.js → p-e7a95a49.system.entry.js} +1 -1
  130. package/dist/pine-core/p-e803f629.system.entry.js +1 -0
  131. package/dist/pine-core/{p-df539c42.system.entry.js → p-e836f3ec.system.entry.js} +2 -2
  132. package/dist/pine-core/{p-b8e8a4ce.entry.js → p-f474d7de.entry.js} +1 -1
  133. package/dist/pine-core/{p-CBu9UrXr.system.js → p-qhdoK1Jp.system.js} +1 -1
  134. package/dist/pine-core/pine-core.esm.js +1 -1
  135. package/dist/types/components/pds-checkbox/pds-checkbox.d.ts +4 -0
  136. package/dist/types/components/pds-combobox/pds-combobox.d.ts +7 -1
  137. package/dist/types/components/pds-drawer/pds-drawer.d.ts +21 -9
  138. package/dist/types/components/pds-dropdown-menu/pds-dropdown-menu.d.ts +1 -0
  139. package/dist/types/components/pds-modal/pds-modal.d.ts +16 -3
  140. package/dist/types/components/pds-multiselect/pds-multiselect.d.ts +10 -3
  141. package/dist/types/components/pds-popover/pds-popover.d.ts +31 -1
  142. package/dist/types/components/pds-switch/pds-switch.d.ts +4 -0
  143. package/dist/types/components/pds-table/pds-table-cell/pds-table-cell.d.ts +3 -0
  144. package/dist/types/components/pds-text/pds-text.d.ts +1 -0
  145. package/dist/types/components.d.ts +47 -32
  146. package/dist/types/utils/truncation-tooltip.d.ts +15 -2
  147. package/dist/vscode.html-data.json +11 -4
  148. package/hydrate/index.js +698 -253
  149. package/hydrate/index.mjs +698 -253
  150. package/package.json +2 -2
  151. package/dist/esm-es5/truncation-tooltip-_902ENWC.js +0 -1
  152. package/dist/pine-core/p-01c9b8f3.entry.js +0 -1
  153. package/dist/pine-core/p-258d5e83.entry.js +0 -1
  154. package/dist/pine-core/p-3c27088f.system.entry.js +0 -1
  155. package/dist/pine-core/p-4849fda7.entry.js +0 -1
  156. package/dist/pine-core/p-4de8f333.entry.js +0 -1
  157. package/dist/pine-core/p-550c2fd3.entry.js +0 -1
  158. package/dist/pine-core/p-6bf853c9.entry.js +0 -1
  159. package/dist/pine-core/p-6d3610f9.entry.js +0 -1
  160. package/dist/pine-core/p-73eb47fa.entry.js +0 -1
  161. package/dist/pine-core/p-7f367db4.system.entry.js +0 -1
  162. package/dist/pine-core/p-84e9b7da.entry.js +0 -1
  163. package/dist/pine-core/p-9a0083dd.system.entry.js +0 -1
  164. package/dist/pine-core/p-CnM3cCof.js +0 -1
  165. package/dist/pine-core/p-a3735199.entry.js +0 -1
  166. package/dist/pine-core/p-a85a6ab9.system.entry.js +0 -1
  167. package/dist/pine-core/p-cf08a6dd.system.entry.js +0 -1
  168. package/dist/pine-core/p-da103a54.system.entry.js +0 -1
  169. package/dist/pine-core/p-e800a2b1.entry.js +0 -1
  170. package/dist/pine-core/p-eb5465ce.entry.js +0 -1
  171. package/dist/pine-core/p-ec5428fb.entry.js +0 -1
  172. package/dist/pine-core/p-ed4b979e.entry.js +0 -1
  173. package/dist/pine-core/p-f1f17940.entry.js +0 -2
  174. package/dist/pine-core/p-f25e4731.system.entry.js +0 -1
@@ -5,10 +5,11 @@ var closest = require('./closest-D0qyYtJ3.js');
5
5
  var reconnectedContent = require('./reconnected-content-CpnEQcTp.js');
6
6
  require('./keep-hydrated-CiXgkEYd.js');
7
7
 
8
- const pdsDrawerCss = () => `.pds-drawer__modal>.pds-modal__backdrop{-ms-flex-align:stretch;align-items:stretch;background-color:transparent;pointer-events:none}@supports ((-webkit-backdrop-filter: blur(3px)) or (backdrop-filter: blur(3px))){.pds-drawer__modal>.pds-modal__backdrop{-webkit-backdrop-filter:none;backdrop-filter:none}}.pds-drawer__modal>.pds-modal__backdrop[aria-modal]::before{content:none}.pds-drawer__modal>.pds-modal__backdrop>.pds-modal{border-radius:0;height:100%;margin:0;max-height:100%;max-width:100%;pointer-events:auto;width:var(--pds-drawer-width, 500px)}.pds-drawer{display:contents}.pds-drawer.pds-drawer--end .pds-drawer__modal>.pds-modal__backdrop{-ms-flex-pack:end;justify-content:flex-end}.pds-drawer.pds-drawer--start .pds-drawer__modal>.pds-modal__backdrop{-ms-flex-pack:start;justify-content:flex-start}.pds-drawer.pds-drawer--sm{--pds-drawer-width:360px}.pds-drawer.pds-drawer--md{--pds-drawer-width:500px}`;
8
+ const pdsDrawerCss = () => `.pds-drawer__modal>.pds-modal__backdrop{-ms-flex-align:stretch;align-items:stretch}.pds-drawer__modal:not(.pds-drawer__modal--modal)>.pds-modal__backdrop{background-color:transparent;pointer-events:none}@supports ((-webkit-backdrop-filter: blur(3px)) or (backdrop-filter: blur(3px))){.pds-drawer__modal:not(.pds-drawer__modal--modal)>.pds-modal__backdrop{-webkit-backdrop-filter:none;backdrop-filter:none}}.pds-drawer__modal>.pds-modal__backdrop[aria-modal]::before{content:none}.pds-drawer__modal>.pds-modal__backdrop>.pds-modal{border-radius:0;height:100%;margin:0;max-height:100%;max-width:100%;pointer-events:auto;width:var(--pds-drawer-width, 500px)}.pds-drawer{display:contents}.pds-drawer.pds-drawer--end .pds-drawer__modal>.pds-modal__backdrop{-ms-flex-pack:end;justify-content:flex-end}.pds-drawer.pds-drawer--start .pds-drawer__modal>.pds-modal__backdrop{-ms-flex-pack:start;justify-content:flex-start}.pds-drawer.pds-drawer--sm{--pds-drawer-width:360px}.pds-drawer.pds-drawer--md{--pds-drawer-width:500px}`;
9
9
 
10
- // Known selectors for content Pine components portal to document.body rather
11
- // than rendering in place — pds-popover, pds-tooltip, pds-combobox's dropdown.
10
+ // Known selectors for content Pine components portal out of place rather than
11
+ // rendering where they are — pds-tooltip and pds-combobox's dropdown in
12
+ // document.body, pds-popover in the body or the open dialog it's in.
12
13
  // A pointerdown landing on one of these is not "outside" the drawer in the
13
14
  // sense light dismiss cares about: it belongs to an overlay something inside
14
15
  // the drawer opened, even though it is not a DOM descendant of the drawer.
@@ -40,8 +41,18 @@ const PdsDrawer = class {
40
41
  */
41
42
  this.scrollable = true;
42
43
  /**
43
- * Whether the drawer can be dismissed with a pointerdown outside it.
44
- * Replaces `pds-modal`'s `backdropDismiss` — there is no backdrop to click.
44
+ * Whether the drawer is modal. A modal drawer dims the page behind it with a
45
+ * backdrop, makes the page inert, and keeps focus inside the drawer, like
46
+ * `pds-modal`. Use it for a task the page should wait on. Leave it off for a
47
+ * panel people work alongside the page. Read when the drawer opens; changing
48
+ * it while the drawer is open is not supported.
49
+ * @default false
50
+ */
51
+ this.modal = false;
52
+ /**
53
+ * Whether the drawer can be dismissed with a pointerdown outside it (in a
54
+ * `modal` drawer, a click on its backdrop).
55
+ * Replaces `pds-modal`'s `backdropDismiss`.
45
56
  * This also gates Escape, matching how `backdropDismiss` gates Escape on
46
57
  * `pds-modal` today: setting this to `false` means the close button is the
47
58
  * only way out.
@@ -60,7 +71,8 @@ const PdsDrawer = class {
60
71
  * background event while the user is mid-task elsewhere on the page —
61
72
  * moving focus there would interrupt them, and the page stays live under
62
73
  * a non-modal drawer, so it is a genuine data-entry risk, not just an
63
- * annoyance.
74
+ * annoyance. A `modal` drawer ignores `none`: the page behind it is inert,
75
+ * so focus starts in the drawer.
64
76
  * @default 'auto'
65
77
  */
66
78
  this.initialFocus = 'auto';
@@ -106,7 +118,8 @@ const PdsDrawer = class {
106
118
  // page underneath stays genuinely clickable, which means a click there
107
119
  // never reaches pds-modal's backdrop element at all.
108
120
  handleOutsidePointerDown(e) {
109
- if (!this.lightDismiss || !this.open)
121
+ // A modal drawer's backdrop takes the click instead, through pds-modal.
122
+ if (this.modal || !this.lightDismiss || !this.open)
110
123
  return;
111
124
  const target = e.target;
112
125
  if (!target)
@@ -116,7 +129,7 @@ const PdsDrawer = class {
116
129
  if (target.closest(PORTALED_OVERLAY_SELECTOR))
117
130
  return;
118
131
  // A datepicker's portaled calendar counts as inside only when its datepicker is in the drawer,
119
- // or in an overlay portaled from it (a pds-popover in the drawer puts its content on the body).
132
+ // or in an overlay portaled from it (a pds-popover in the drawer puts its content beside it).
120
133
  const calendar = target.closest('.pds-datepicker-calendar-portal');
121
134
  const owner = calendar === null || calendar === void 0 ? void 0 : calendar.pdsDatepicker;
122
135
  if (owner && (closest.containsComposed(this.el, owner) || closest.closest(PORTALED_OVERLAY_SELECTOR, owner)))
@@ -132,11 +145,11 @@ const PdsDrawer = class {
132
145
  this.open = false;
133
146
  }
134
147
  render() {
135
- return (index.h(index.Host, { key: 'ae7e67ed196ef95a39c2b729e859712ce5c41144', class: {
148
+ return (index.h(index.Host, { key: '287bf043c991209488eb1b617031acffb59ff9af', class: {
136
149
  'pds-drawer': true,
137
150
  [`pds-drawer--${this.side}`]: true,
138
151
  [`pds-drawer--${this.size}`]: true,
139
- } }, index.h("pds-modal", { key: '9b35ce377c84612a844a561d5540e98b08c0e499', class: "pds-drawer__modal", ref: (el) => (this.modalEl = el), componentId: this.componentId, open: this.open, scrollable: this.scrollable, disableTopLayer: true, backdropDismiss: this.lightDismiss, disableInitialFocus: this.initialFocus === 'none' }, index.h("slot", { key: 'dda01aa40dc41b3d17073238a8cad8d816571e18' }))));
152
+ } }, index.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' }, index.h("slot", { key: '56bbe9b5c26b10b07c0f503ba147899a57a5de85' }))));
140
153
  }
141
154
  get el() { return index.getElement(this); }
142
155
  };
@@ -65,7 +65,7 @@ const PdsDropdownMenuItem = class {
65
65
  });
66
66
  }
67
67
  renderElement() {
68
- if (this.href !== undefined) {
68
+ if (this.href != null) {
69
69
  return (index.h("pds-link", { download: this.download, href: this.disabled ? null : this.href, external: this.external, target: this.target, class: {
70
70
  'pds-dropdown-menu-item__content': true,
71
71
  'has-focus': this.hasFocus
@@ -77,7 +77,7 @@ const PdsDropdownMenuItem = class {
77
77
  }, 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 }, index.h("slot", null)));
78
78
  }
79
79
  render() {
80
- return (index.h(index.Host, { key: '33bd645d7c9547e3fd07429cf795302e6a482296', 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()));
80
+ return (index.h(index.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()));
81
81
  }
82
82
  get host() { return index.getElement(this); }
83
83
  };
@@ -255,15 +255,19 @@ const PdsDropdownMenu = class {
255
255
  this.focusItemByIndex(prevIndex);
256
256
  }
257
257
  }
258
+ // Escape is taken in document's capture phase, before a modal around the menu hears it on
259
+ // document and closes too.
260
+ handleEscapeKeyDown(event) {
261
+ if (!this.isOpen || event.key !== 'Escape' || event.defaultPrevented)
262
+ return;
263
+ event.preventDefault();
264
+ this.closeDropdown();
265
+ }
258
266
  // Handle keyboard events for the dropdown
259
267
  handleKeyDown(event) {
260
268
  if (!this.isOpen)
261
269
  return;
262
270
  switch (event.key) {
263
- case 'Escape':
264
- event.preventDefault();
265
- this.closeDropdown();
266
- break;
267
271
  case 'ArrowDown':
268
272
  event.preventDefault();
269
273
  this.focusNextItem();
@@ -355,7 +359,7 @@ const PdsDropdownMenu = class {
355
359
  }
356
360
  }
357
361
  render() {
358
- return (index.h(index.Host, { key: '0dd585168e8fa86378c4812ce54fd09108861d5b', id: this.componentId }, index.h("slot", { key: 'c7144f8d6aaebe37b585c6c6230102c013230a9e', name: "trigger", onSlotchange: this.handleTriggerSlotChange }), index.h("pds-box", { key: '82292e051337141bc9fd83b90292b188b0bb3489', "border-radius": "sm", display: "flex", direction: "column", class: "pds-dropdown-menu--panel is-hidden", shadow: "100", part: "menu-panel" }, index.h("div", { key: 'c0f49dca14df0ba3885865c51562c380155bf8a8', class: { 'pds-dropdown-menu__header': true, 'is-hidden': !this.hasHeader } }, index.h("slot", { key: 'dc80c97a8dbb79ab4563524b3dea68d144e8570c', name: "header", onSlotchange: this.handleHeaderSlotChange })), index.h("div", { key: '29cb56bdf28a0438296f33e0fa86c9da7b51902c', class: "pds-dropdown-menu__items", role: "menu", "aria-orientation": "vertical" }, index.h("slot", { key: '8f91ceeb1eedc4acc79f09032dedc69a2ba1f99a', onSlotchange: this.handleSlotChange })))));
362
+ return (index.h(index.Host, { key: '18dac4d59ea236fe171f4c16f6571cb1547059c6', id: this.componentId }, index.h("slot", { key: '95201a75a33ccb70dbd51a5cf9d78c76ebf26f65', name: "trigger", onSlotchange: this.handleTriggerSlotChange }), index.h("pds-box", { key: '7fc588f40f607ad81ad70bf74a525d078d32a80b', "border-radius": "sm", display: "flex", direction: "column", class: "pds-dropdown-menu--panel is-hidden", shadow: "100", part: "menu-panel" }, index.h("div", { key: '73d13d94edccb68db7b703f930b50f729334efa8', class: { 'pds-dropdown-menu__header': true, 'is-hidden': !this.hasHeader } }, index.h("slot", { key: '7066f3a6f71c8961326f38f0509f1c0cc17098ae', name: "header", onSlotchange: this.handleHeaderSlotChange })), index.h("div", { key: '9f07ec488b688c58c6422b575015ccd364075fe8', class: "pds-dropdown-menu__items", role: "menu", "aria-orientation": "vertical" }, index.h("slot", { key: 'd0aad3f9ac3b58779669411523adbb8878dec61c', onSlotchange: this.handleSlotChange })))));
359
363
  }
360
364
  get host() { return index.getElement(this); }
361
365
  };
@@ -78,6 +78,20 @@ function oneRadioPerGroup(elements) {
78
78
  const stops = new Set(groups.map((radios) => { var _a; return (_a = radios.find((radio) => radio.checked)) !== null && _a !== void 0 ? _a : radios[0]; }));
79
79
  return elements.filter((el) => groupedRadio(el) === null || stops.has(el));
80
80
  }
81
+ /** The controls Tab reaches in `root`, in order: visible ones, and one radio per group. */
82
+ function tabbableIn(root) {
83
+ const found = [];
84
+ collectTabbable(root, found);
85
+ // checkVisibility() also drops an element inside a hidden ancestor, whose own computed
86
+ // display isn't none; such an element can't take focus.
87
+ const visible = found.filter(el => {
88
+ if (typeof el.checkVisibility === 'function')
89
+ return el.checkVisibility({ checkVisibilityCSS: true });
90
+ const style = window.getComputedStyle(el);
91
+ return style.display !== 'none' && style.visibility !== 'hidden';
92
+ });
93
+ return oneRadioPerGroup(visible);
94
+ }
81
95
  /** Whether `node` is `ancestor` or inside it, crossing shadow roots, which `Node.contains` doesn't. */
82
96
  function containsComposed(ancestor, node) {
83
97
  for (let current = node; current; current = current instanceof ShadowRoot ? current.host : current.parentNode) {
@@ -86,6 +100,34 @@ function containsComposed(ancestor, node) {
86
100
  }
87
101
  return false;
88
102
  }
103
+ /**
104
+ * Every open modal, with the order it opened in, which ranks top-layer modals and the modals
105
+ * inside them. Kept here rather than queried from the document, which can't see a modal inside
106
+ * another component's shadow root.
107
+ */
108
+ const openModals = new Map();
109
+ let openSequence = 0;
110
+ /** Whether an open auto popover's panel sits in `root`, including inside shadow roots under it. */
111
+ function hasOpenPopover(root) {
112
+ if (root.querySelector('[data-pds-popover-open]'))
113
+ return true;
114
+ return Array.from(root.querySelectorAll('*')).some(el => !!el.shadowRoot && hasOpenPopover(el.shadowRoot));
115
+ }
116
+ /**
117
+ * Whether `a` comes before `b` in the page. `compareDocumentPosition` is arbitrary across a shadow
118
+ * boundary, so each side climbs to its host until both are in the same tree.
119
+ */
120
+ function precedesComposed(a, b) {
121
+ // A document has no host, which ends each climb.
122
+ for (let x = a; x; x = x.getRootNode().host) {
123
+ for (let y = b; y; y = y.getRootNode().host) {
124
+ if (x.getRootNode() === y.getRootNode()) {
125
+ return !!(x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING);
126
+ }
127
+ }
128
+ }
129
+ return false;
130
+ }
89
131
  /** The focused element, followed down through shadow roots to the control that has focus. */
90
132
  function deepActiveElement() {
91
133
  var _a;
@@ -101,7 +143,8 @@ const PdsModal = class {
101
143
  this.pdsModalClose = index.createEvent(this, "pdsModalClose");
102
144
  this.focusableElements = [];
103
145
  /**
104
- * Whether the modal can be dismissed by clicking the backdrop
146
+ * Whether the modal can be dismissed by clicking the backdrop, pressing Escape, or the browser's
147
+ * own close request (such as a back gesture)
105
148
  * @default true
106
149
  */
107
150
  this.backdropDismiss = true;
@@ -149,19 +192,33 @@ const PdsModal = class {
149
192
  * Stores the list of focusable elements in the modal
150
193
  */
151
194
  this.focusableElementsArray = [];
195
+ // The browser's own close request (an Escape nothing prevented, a back gesture) would close the
196
+ // dialog without updating `open`, so close it through hideModal() instead, or not at all.
197
+ this.handleCancel = (e) => {
198
+ e.preventDefault();
199
+ if (!this.open)
200
+ return;
201
+ // The request reaches the top-layer dialog even when a disableTopLayer modal inside it is on
202
+ // top; that one closes first, as it would on Escape.
203
+ const top = this.innermostOpenModal();
204
+ const target = (top && containsComposed(this.el, top) ? top : this.el);
205
+ if (target.backdropDismiss)
206
+ target.hideModal();
207
+ };
152
208
  this.handleBackdropClick = (e) => {
153
209
  if (!this.backdropDismiss || !this.open)
154
210
  return;
155
- if (e.target.classList.contains('pds-modal__backdrop')) {
211
+ // This modal's own backdrop; a nested modal's backdrop handles its own clicks.
212
+ if (e.target === this.modalRef) {
156
213
  e.stopPropagation();
157
214
  // Only close if this is the innermost modal
158
- if (this.isInnermostModal()) {
215
+ if (this.innermostOpenModal() === this.el) {
159
216
  this.hideModal();
160
217
  }
161
218
  }
162
219
  };
163
220
  this.handleKeyDown = (e) => {
164
- var _a, _b, _c;
221
+ var _a, _b, _c, _d;
165
222
  // If the modal is not open, don't handle any keyboard events
166
223
  if (!this.open)
167
224
  return;
@@ -184,24 +241,39 @@ const PdsModal = class {
184
241
  if (!('__pdsActiveElementAtEscape' in eventSnapshot)) {
185
242
  const activeAtDispatch = document.activeElement;
186
243
  eventSnapshot.__pdsActiveElementAtEscape = activeAtDispatch;
244
+ // Followed into shadow roots: for a modal inside one, document.activeElement is only the host.
245
+ eventSnapshot.__pdsDeepActiveElementAtEscape = deepActiveElement();
187
246
  const topLayerDialog = (_a = activeAtDispatch === null || activeAtDispatch === void 0 ? void 0 : activeAtDispatch.closest('dialog')) !== null && _a !== void 0 ? _a : null;
188
247
  eventSnapshot.__pdsTopLayerDialogAtEscape = topLayerDialog;
189
248
  eventSnapshot.__pdsTopLayerDialogWasModalAtEscape = !!(topLayerDialog && topLayerDialog.matches(':modal'));
249
+ // A control that closed on this Escape (a combobox on its own element, a dropdown menu or
250
+ // multiselect in document's capture phase) has prevented it before this bubble listener.
251
+ // Read before any modal prevents it below.
252
+ eventSnapshot.__pdsEscapeHandledInside = e.defaultPrevented;
253
+ // Which modal is innermost is read once too: the inner one closing first would make its
254
+ // outer modal the innermost by the time that modal's listener runs.
255
+ eventSnapshot.__pdsInnermostModalAtEscape = this.innermostOpenModal();
190
256
  }
257
+ if (eventSnapshot.__pdsEscapeHandledInside)
258
+ return;
191
259
  // Leave Escape to a genuinely stacked-above overlay (disableTopLayer's
192
260
  // reason for existing) — not just to anything outside this modal.
193
261
  const active = eventSnapshot.__pdsActiveElementAtEscape;
194
262
  if (this.disableTopLayer &&
195
263
  active &&
196
264
  active !== document.body &&
197
- !this.el.contains(active) &&
198
- this.isStackedAboveOverlay(active, (_b = eventSnapshot.__pdsTopLayerDialogAtEscape) !== null && _b !== void 0 ? _b : null, (_c = eventSnapshot.__pdsTopLayerDialogWasModalAtEscape) !== null && _c !== void 0 ? _c : false)) {
265
+ !containsComposed(this.el, (_b = eventSnapshot.__pdsDeepActiveElementAtEscape) !== null && _b !== void 0 ? _b : active) &&
266
+ this.isStackedAboveOverlay(active, (_c = eventSnapshot.__pdsTopLayerDialogAtEscape) !== null && _c !== void 0 ? _c : null, (_d = eventSnapshot.__pdsTopLayerDialogWasModalAtEscape) !== null && _d !== void 0 ? _d : false)) {
199
267
  return;
200
268
  }
269
+ // An open popover inside takes Escape from its own document listener, which runs after this
270
+ // one, so leave the key for it. Its preventDefault() keeps the native dialog open.
271
+ if (this.modalRef && hasOpenPopover(this.modalRef))
272
+ return;
201
273
  // Always prevent native dialog close behavior
202
274
  e.preventDefault();
203
275
  // Only close if backdropDismiss is enabled and this is the innermost modal
204
- if (this.backdropDismiss && this.isInnermostModal()) {
276
+ if (this.backdropDismiss && eventSnapshot.__pdsInnermostModalAtEscape === this.el) {
205
277
  this.hideModal();
206
278
  }
207
279
  return;
@@ -261,8 +333,6 @@ const PdsModal = class {
261
333
  }
262
334
  componentDidLoad() {
263
335
  this.modalRef = this.el.querySelector('.pds-modal__backdrop');
264
- // Add keyboard event listener
265
- document.addEventListener('keydown', this.handleKeyDown);
266
336
  // @Watch('open') only fires on a later change, not the prop's initial
267
337
  // value — a modal mounted with `open` already true (static markup, or a
268
338
  // framework passing it on first render) would otherwise never call
@@ -278,9 +348,18 @@ const PdsModal = class {
278
348
  componentDidRender() {
279
349
  reconnectedContent.unwrapReconnectedContent(this.el.querySelector('.pds-modal'), 'dialog.pds-modal__backdrop', '.pds-modal');
280
350
  }
351
+ // Here rather than in componentDidLoad, which runs once: a modal taken out of the page and put
352
+ // back (a morph or a framework moving it) gets its keyboard handling, and its place among the
353
+ // open modals, back too. Adding the same listener twice is a no-op.
354
+ connectedCallback() {
355
+ document.addEventListener('keydown', this.handleKeyDown);
356
+ if (this.open && !openModals.has(this.el))
357
+ openModals.set(this.el, ++openSequence);
358
+ }
281
359
  disconnectedCallback() {
282
360
  // Clean up event listener
283
361
  document.removeEventListener('keydown', this.handleKeyDown);
362
+ openModals.delete(this.el);
284
363
  }
285
364
  handleOpenChange(newValue) {
286
365
  if (newValue) {
@@ -296,17 +375,7 @@ const PdsModal = class {
296
375
  updateFocusableElements() {
297
376
  if (!this.modalRef)
298
377
  return;
299
- this.focusableElements = [];
300
- collectTabbable(this.modalRef, this.focusableElements);
301
- // checkVisibility() also drops an element inside a hidden ancestor, whose own computed
302
- // display isn't none; such an element can't take focus, so it can't anchor the trap.
303
- this.focusableElements = this.focusableElements.filter(el => {
304
- if (typeof el.checkVisibility === 'function')
305
- return el.checkVisibility({ checkVisibilityCSS: true });
306
- const style = window.getComputedStyle(el);
307
- return style.display !== 'none' && style.visibility !== 'hidden';
308
- });
309
- this.focusableElements = oneRadioPerGroup(this.focusableElements);
378
+ this.focusableElements = tabbableIn(this.modalRef);
310
379
  }
311
380
  /**
312
381
  * Sets focus to the first focusable element in the modal
@@ -330,10 +399,13 @@ const PdsModal = class {
330
399
  * Opens the modal
331
400
  */
332
401
  async showModal() {
402
+ var _a;
333
403
  if (this.modalRef) {
334
404
  try {
335
- // Store the currently focused element to restore focus when modal closes
336
- this.previousActiveElement = document.activeElement;
405
+ // Store the currently focused element to restore focus when modal closes. Showing an open
406
+ // modal again (the open watcher does) would record a control inside it instead.
407
+ if (!this.modalRef.open)
408
+ this.previousActiveElement = document.activeElement;
337
409
  // showModal() promotes the dialog to the browser top layer (and makes the
338
410
  // rest of the page inert), which prevents any overlay outside the dialog
339
411
  // from ever painting above it. show() opens a non-modal dialog that stays
@@ -345,11 +417,25 @@ const PdsModal = class {
345
417
  this.modalRef.showModal();
346
418
  }
347
419
  this.open = true;
420
+ // Showing an open modal again doesn't move it above one opened after it.
421
+ if (!openModals.has(this.el))
422
+ openModals.set(this.el, ++openSequence);
348
423
  // Update focusable elements and set initial focus
349
424
  // Using a longer timeout to ensure all components are fully rendered
425
+ const openedAs = (_a = openModals.get(this.el)) !== null && _a !== void 0 ? _a : 0;
350
426
  setTimeout(() => {
351
427
  var _a, _b;
352
428
  this.updateFocusableElements();
429
+ // A modal opened above this one in the meantime has focus; don't take it back. Not this
430
+ // modal itself (moving it gives it a new place), and not a disableTopLayer modal outside
431
+ // it while this one is in the top layer, which sits below it.
432
+ const openedAbove = Array.from(openModals).some(([modal, sequence]) => sequence > openedAs &&
433
+ modal !== this.el &&
434
+ (containsComposed(this.el, modal) || !modal.disableTopLayer || this.disableTopLayer));
435
+ if (openedAbove) {
436
+ this.pdsModalOpen.emit();
437
+ return;
438
+ }
353
439
  if (this.disableInitialFocus) {
354
440
  // show()/showModal() also move focus into the dialog natively (the
355
441
  // HTML spec's "dialog focusing steps") — that native step isn't
@@ -393,6 +479,7 @@ const PdsModal = class {
393
479
  * Closes the modal
394
480
  */
395
481
  async hideModal() {
482
+ var _a;
396
483
  if (this.modalRef) {
397
484
  try {
398
485
  // Capture before close(): with the async task queue, a prop change
@@ -407,11 +494,19 @@ const PdsModal = class {
407
494
  const focusAlreadyMovedElsewhere = activeElement !== null && activeElement !== document.body && !this.modalRef.contains(activeElement);
408
495
  this.modalRef.close();
409
496
  this.open = false;
410
- // Restore focus to the element that was focused before the modal was opened
411
- if (!focusAlreadyMovedElsewhere &&
412
- this.previousActiveElement &&
413
- typeof this.previousActiveElement.focus === 'function') {
414
- this.previousActiveElement.focus();
497
+ openModals.delete(this.el);
498
+ // Restore focus to the element that was focused before the modal was opened. If that's
499
+ // gone or can't take focus (disabled, hidden, or nothing), fall back to the open modal now
500
+ // on top, so focus doesn't land on an inert page behind it.
501
+ if (!focusAlreadyMovedElsewhere) {
502
+ const previous = this.previousActiveElement;
503
+ if (previous && previous !== document.body && previous.isConnected)
504
+ (_a = previous.focus) === null || _a === void 0 ? void 0 : _a.call(previous);
505
+ const active = deepActiveElement();
506
+ // document.body isn't a real restore target — it's just where focus lands when nothing
507
+ // was focused before opening, so landing back there never counts as a successful restore.
508
+ if (!previous || previous === document.body || !containsComposed(previous, active) || containsComposed(this.el, active))
509
+ this.focusTopModal();
415
510
  }
416
511
  this.pdsModalClose.emit();
417
512
  }
@@ -420,12 +515,29 @@ const PdsModal = class {
420
515
  }
421
516
  }
422
517
  }
518
+ /**
519
+ * Focuses the first control of the open modal now on top, when the page behind it is inert: that
520
+ * modal is in the top layer (or paints there inside a top-layer one), or it wraps this one. A
521
+ * non-modal one over an interactive page, like a drawer, is left alone.
522
+ */
523
+ focusTopModal() {
524
+ var _a;
525
+ const top = this.innermostOpenModal();
526
+ const dialog = top === null || top === void 0 ? void 0 : top.querySelector('.pds-modal__backdrop');
527
+ if (!dialog)
528
+ return;
529
+ if (!closest.closest('dialog:modal', dialog) && !containsComposed(top, this.el))
530
+ return;
531
+ (_a = tabbableIn(dialog).find(el => !containsComposed(this.el, el))) === null || _a === void 0 ? void 0 : _a.focus();
532
+ }
423
533
  /**
424
534
  * Gets the z-index of a modal's backdrop element
425
535
  */
426
536
  getBackdropZIndex(modal) {
427
537
  const backdrop = modal.querySelector('.pds-modal__backdrop');
428
- return backdrop ? parseInt(getComputedStyle(backdrop).zIndex, 10) : -1;
538
+ // `auto` (no z-index token loaded) parses to NaN, which no comparison can rank.
539
+ const zIndex = backdrop ? parseInt(getComputedStyle(backdrop).zIndex, 10) : -1;
540
+ return Number.isNaN(zIndex) ? 0 : zIndex;
429
541
  }
430
542
  /**
431
543
  * Whether `active` sits inside a surface actually stacked above this modal.
@@ -452,7 +564,8 @@ const PdsModal = class {
452
564
  * lose that evidence depending on listener order.
453
565
  */
454
566
  isStackedAboveOverlay(active, topLayerDialog, topLayerDialogWasModal) {
455
- if (topLayerDialog && topLayerDialog !== this.modalRef && topLayerDialogWasModal) {
567
+ // A top-layer dialog this modal sits inside is underneath it, not an overlay above.
568
+ if (topLayerDialog && topLayerDialog !== this.modalRef && topLayerDialogWasModal && !containsComposed(topLayerDialog, this.el)) {
456
569
  return true;
457
570
  }
458
571
  const ownZIndex = this.getBackdropZIndex(this.el);
@@ -468,24 +581,37 @@ const PdsModal = class {
468
581
  return false;
469
582
  }
470
583
  /**
471
- * Checks if this modal is the innermost (highest z-index) modal
584
+ * The open modal that takes Escape or a backdrop click, among those with no other open modal
585
+ * inside them (a nested modal's backdrop can share its outer modal's z-index). A top-layer dialog
586
+ * (or a modal inside one) paints above anything outside it, and within the top layer the last
587
+ * opened is on top. Modals in the same layer (the page, or one top-layer dialog) then rank by
588
+ * the highest backdrop, then the one later in the page.
472
589
  */
473
- isInnermostModal() {
474
- // Find all open modals
475
- const openModals = Array.from(document.querySelectorAll('pds-modal')).filter(modal => modal.open);
476
- if (openModals.length === 0)
477
- return false;
478
- // Get this modal's backdrop element
479
- const thisBackdrop = this.el.querySelector('.pds-modal__backdrop');
480
- if (!thisBackdrop)
481
- return false;
482
- // Get computed z-index of all open modal backdrops
483
- const modalZIndexes = openModals.map(modal => this.getBackdropZIndex(modal));
484
- // Get the highest z-index
485
- const maxZIndex = Math.max(...modalZIndexes);
486
- // Check if this modal's backdrop has the highest z-index
487
- const thisZIndex = this.getBackdropZIndex(this.el);
488
- return thisZIndex === maxZIndex;
590
+ innermostOpenModal() {
591
+ const open = Array.from(openModals.keys()).filter(modal => modal.isConnected);
592
+ const innermost = open.filter(modal => !open.some(other => other !== modal && containsComposed(modal, other)));
593
+ const byDocumentOrder = [...innermost].sort((a, b) => (precedesComposed(a, b) ? -1 : 1));
594
+ // A modal inside a top-layer dialog paints in that dialog's place in the top layer, even when it
595
+ // isn't in the top layer itself (disableTopLayer), so that dialog's open order ranks it first.
596
+ const rank = (modal) => {
597
+ var _a;
598
+ const backdrop = modal.querySelector('.pds-modal__backdrop');
599
+ const topLayerDialog = backdrop ? closest.closest('dialog:modal', backdrop) : null;
600
+ const topLayerModal = topLayerDialog === null || topLayerDialog === void 0 ? void 0 : topLayerDialog.closest('pds-modal');
601
+ const layer = topLayerDialog ? ((_a = (topLayerModal && openModals.get(topLayerModal))) !== null && _a !== void 0 ? _a : 0) : 0;
602
+ return [topLayerDialog ? 1 : 0, layer, this.getBackdropZIndex(modal), byDocumentOrder.indexOf(modal)];
603
+ };
604
+ let top = null;
605
+ let topRank = [-1, -1, -1, -1];
606
+ for (const modal of innermost) {
607
+ const modalRank = rank(modal);
608
+ const above = modalRank.findIndex((value, i) => value !== topRank[i]);
609
+ if (above >= 0 && modalRank[above] > topRank[above]) {
610
+ top = modal;
611
+ topRank = modalRank;
612
+ }
613
+ }
614
+ return top;
489
615
  }
490
616
  /** Whether a modal dialog other than this one sits between the focused element and this modal. */
491
617
  modalDialogAbove(active) {
@@ -525,14 +651,14 @@ const PdsModal = class {
525
651
  }
526
652
  render() {
527
653
  const [sizeClass = 'md', customSize] = this.resolveSize();
528
- return (index.h("dialog", { key: '12ea38928ea287f16ffa33e246caa2bfde6fc130', class: {
654
+ return (index.h("dialog", { key: '0b187f36910a859afe57b891f1ebffd1d2d49ab7', class: {
529
655
  'pds-modal__backdrop': true,
530
656
  'open': this.open
531
- }, "aria-modal": this.disableTopLayer ? 'false' : 'true', "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick }, index.h("div", { key: 'b9d83cabda56c315d9115f7030e3bdbcf923ec08', class: {
657
+ }, "aria-modal": this.disableTopLayer ? 'false' : 'true', "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick, onCancel: this.handleCancel }, index.h("div", { key: 'f5e652d4dae0e7c272b37b63eb9c65f04c69536a', class: {
532
658
  'pds-modal': true,
533
659
  [`pds-modal--${sizeClass}`]: true,
534
660
  'pds-modal--scrollable': this.scrollable
535
- }, style: customSize !== undefined ? { '--pds-modal-max-width': customSize } : undefined, part: "modal" }, index.h("slot", { key: '3c5edad1482cb124e503197adf411890573b076d' }))));
661
+ }, style: customSize !== undefined ? { '--pds-modal-max-width': customSize } : undefined, part: "modal" }, index.h("slot", { key: 'a954e1694741f78230599c476b270079797a263e' }))));
536
662
  }
537
663
  get el() { return index.getElement(this); }
538
664
  static get watchers() { return {