@pine-ds/core 3.29.0 → 3.31.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 (212) hide show
  1. package/components/form.js +1 -1
  2. package/components/pds-avatar.js +1 -1
  3. package/components/pds-checkbox2.js +1 -1
  4. package/components/pds-combobox.js +2 -2
  5. package/components/pds-input.js +1 -1
  6. package/components/pds-modal-content.js +1 -1
  7. package/components/pds-modal-footer.js +1 -1
  8. package/components/pds-modal-header.js +1 -1
  9. package/components/pds-modal.js +1 -1
  10. package/components/pds-multiselect.js +1 -1
  11. package/components/pds-radio-group.js +1 -1
  12. package/components/pds-radio.js +1 -1
  13. package/components/pds-select.js +1 -1
  14. package/components/pds-sortable-item.js +1 -1
  15. package/components/pds-switch.js +1 -1
  16. package/components/pds-tab.js +1 -1
  17. package/components/pds-tabpanel.js +1 -1
  18. package/components/pds-textarea.js +1 -1
  19. package/components/pds-tooltip.js +1 -1
  20. package/components/reconnected-content.js +1 -0
  21. package/dist/cjs/{form-CpM5Z8q7.js → form-MhwfKTuo.js} +11 -7
  22. package/dist/cjs/loader.cjs.js +1 -1
  23. package/dist/cjs/pds-avatar.cjs.entry.js +2 -2
  24. package/dist/cjs/pds-checkbox.cjs.entry.js +4 -4
  25. package/dist/cjs/pds-combobox.cjs.entry.js +34 -4
  26. package/dist/cjs/pds-input.cjs.entry.js +3 -3
  27. package/dist/cjs/pds-modal-content.cjs.entry.js +7 -2
  28. package/dist/cjs/pds-modal-footer.cjs.entry.js +7 -1
  29. package/dist/cjs/pds-modal-header.cjs.entry.js +7 -1
  30. package/dist/cjs/pds-modal.cjs.entry.js +40 -5
  31. package/dist/cjs/pds-multiselect.cjs.entry.js +5 -5
  32. package/dist/cjs/pds-radio-group.cjs.entry.js +20 -4
  33. package/dist/cjs/pds-radio.cjs.entry.js +17 -5
  34. package/dist/cjs/pds-select.cjs.entry.js +8 -3
  35. package/dist/cjs/pds-sortable-item.cjs.entry.js +14 -1
  36. package/dist/cjs/pds-switch.cjs.entry.js +4 -4
  37. package/dist/cjs/pds-tab.cjs.entry.js +5 -0
  38. package/dist/cjs/pds-tabpanel.cjs.entry.js +6 -1
  39. package/dist/cjs/pds-textarea.cjs.entry.js +4 -4
  40. package/dist/cjs/pds-tooltip.cjs.entry.js +32 -2
  41. package/dist/cjs/pine-core.cjs.js +1 -1
  42. package/dist/cjs/reconnected-content-CpnEQcTp.js +29 -0
  43. package/dist/collection/components/pds-avatar/pds-avatar.js +3 -2
  44. package/dist/collection/components/pds-checkbox/pds-checkbox.js +3 -3
  45. package/dist/collection/components/pds-combobox/pds-combobox.css +33 -1
  46. package/dist/collection/components/pds-combobox/pds-combobox.js +107 -3
  47. package/dist/collection/components/pds-combobox/stories/pds-combobox.stories.js +60 -0
  48. package/dist/collection/components/pds-input/pds-input.css +1 -1
  49. package/dist/collection/components/pds-input/pds-input.js +1 -1
  50. package/dist/collection/components/pds-modal/pds-modal-content/pds-modal-content.js +7 -2
  51. package/dist/collection/components/pds-modal/pds-modal-footer/pds-modal-footer.js +7 -1
  52. package/dist/collection/components/pds-modal/pds-modal-header/pds-modal-header.js +7 -1
  53. package/dist/collection/components/pds-modal/pds-modal.js +63 -5
  54. package/dist/collection/components/pds-modal/stories/pds-modal.stories.js +77 -0
  55. package/dist/collection/components/pds-multiselect/pds-multiselect.css +2 -2
  56. package/dist/collection/components/pds-multiselect/pds-multiselect.js +3 -3
  57. package/dist/collection/components/pds-radio/pds-radio.css +6 -0
  58. package/dist/collection/components/pds-radio/pds-radio.js +15 -3
  59. package/dist/collection/components/pds-radio-group/pds-radio-group.js +19 -3
  60. package/dist/collection/components/pds-select/pds-select.css +1 -1
  61. package/dist/collection/components/pds-select/pds-select.js +7 -2
  62. package/dist/collection/components/pds-sortable/pds-sortable-item/pds-sortable-item.js +14 -1
  63. package/dist/collection/components/pds-switch/pds-switch.js +3 -3
  64. package/dist/collection/components/pds-tabs/pds-tab/pds-tab.js +5 -0
  65. package/dist/collection/components/pds-tabs/pds-tabpanel/pds-tabpanel.js +6 -1
  66. package/dist/collection/components/pds-textarea/pds-textarea.js +3 -3
  67. package/dist/collection/components/pds-tooltip/pds-tooltip.js +32 -2
  68. package/dist/collection/utils/form.js +11 -7
  69. package/dist/collection/utils/reconnected-content.js +25 -0
  70. package/dist/collection/utils/test/reconnect-safety.js +31 -0
  71. package/dist/docs.json +129 -1
  72. package/dist/esm/{form-C3xDZBme.js → form-BuRbS7Hp.js} +11 -7
  73. package/dist/esm/loader.js +1 -1
  74. package/dist/esm/pds-avatar.entry.js +2 -2
  75. package/dist/esm/pds-checkbox.entry.js +4 -4
  76. package/dist/esm/pds-combobox.entry.js +34 -4
  77. package/dist/esm/pds-input.entry.js +3 -3
  78. package/dist/esm/pds-modal-content.entry.js +7 -2
  79. package/dist/esm/pds-modal-footer.entry.js +8 -2
  80. package/dist/esm/pds-modal-header.entry.js +8 -2
  81. package/dist/esm/pds-modal.entry.js +40 -5
  82. package/dist/esm/pds-multiselect.entry.js +5 -5
  83. package/dist/esm/pds-radio-group.entry.js +20 -4
  84. package/dist/esm/pds-radio.entry.js +17 -5
  85. package/dist/esm/pds-select.entry.js +8 -3
  86. package/dist/esm/pds-sortable-item.entry.js +14 -1
  87. package/dist/esm/pds-switch.entry.js +4 -4
  88. package/dist/esm/pds-tab.entry.js +5 -0
  89. package/dist/esm/pds-tabpanel.entry.js +6 -1
  90. package/dist/esm/pds-textarea.entry.js +4 -4
  91. package/dist/esm/pds-tooltip.entry.js +32 -2
  92. package/dist/esm/pine-core.js +1 -1
  93. package/dist/esm/reconnected-content-RFpTWV6r.js +27 -0
  94. package/dist/esm-es5/form-BuRbS7Hp.js +1 -0
  95. package/dist/esm-es5/loader.js +1 -1
  96. package/dist/esm-es5/pds-avatar.entry.js +1 -1
  97. package/dist/esm-es5/pds-checkbox.entry.js +1 -1
  98. package/dist/esm-es5/pds-combobox.entry.js +2 -2
  99. package/dist/esm-es5/pds-input.entry.js +1 -1
  100. package/dist/esm-es5/pds-modal-content.entry.js +1 -1
  101. package/dist/esm-es5/pds-modal-footer.entry.js +1 -1
  102. package/dist/esm-es5/pds-modal-header.entry.js +1 -1
  103. package/dist/esm-es5/pds-modal.entry.js +1 -1
  104. package/dist/esm-es5/pds-multiselect.entry.js +1 -1
  105. package/dist/esm-es5/pds-radio-group.entry.js +1 -1
  106. package/dist/esm-es5/pds-radio.entry.js +1 -1
  107. package/dist/esm-es5/pds-select.entry.js +1 -1
  108. package/dist/esm-es5/pds-sortable-item.entry.js +1 -1
  109. package/dist/esm-es5/pds-switch.entry.js +1 -1
  110. package/dist/esm-es5/pds-tab.entry.js +1 -1
  111. package/dist/esm-es5/pds-tabpanel.entry.js +1 -1
  112. package/dist/esm-es5/pds-textarea.entry.js +1 -1
  113. package/dist/esm-es5/pds-tooltip.entry.js +1 -1
  114. package/dist/esm-es5/pine-core.js +1 -1
  115. package/dist/esm-es5/reconnected-content-RFpTWV6r.js +1 -0
  116. package/dist/pine-core/p-08b0d298.entry.js +1 -0
  117. package/dist/pine-core/p-0aa85901.entry.js +2 -0
  118. package/dist/pine-core/p-16f13b17.entry.js +1 -0
  119. package/dist/pine-core/p-17d4a99a.system.entry.js +1 -0
  120. package/dist/pine-core/p-18ae43e3.entry.js +1 -0
  121. package/dist/pine-core/p-1b1ec833.system.entry.js +3 -0
  122. package/dist/pine-core/p-1b8ac76c.system.entry.js +1 -0
  123. package/dist/pine-core/p-1e87e72a.entry.js +1 -0
  124. package/dist/pine-core/p-2c5fe3d9.system.entry.js +1 -0
  125. package/dist/pine-core/{p-978b848b.system.entry.js → p-31679dbc.system.entry.js} +1 -1
  126. package/dist/pine-core/p-36fc1ade.system.entry.js +1 -0
  127. package/dist/pine-core/{p-5f4d2c5c.system.entry.js → p-3cece186.system.entry.js} +1 -1
  128. package/dist/pine-core/p-419e711c.entry.js +1 -0
  129. package/dist/pine-core/p-43268325.system.entry.js +1 -0
  130. package/dist/pine-core/p-5d1228cb.entry.js +1 -0
  131. package/dist/pine-core/p-6630f428.system.entry.js +1 -0
  132. package/dist/pine-core/{p-82af7d58.system.entry.js → p-69372e72.system.entry.js} +1 -1
  133. package/dist/pine-core/p-73619cfc.system.entry.js +1 -0
  134. package/dist/pine-core/p-73b40545.system.entry.js +1 -0
  135. package/dist/pine-core/p-7ad6f313.entry.js +1 -0
  136. package/dist/pine-core/p-7e146a45.entry.js +1 -0
  137. package/dist/pine-core/{p-ba54470a.system.entry.js → p-86d12f3c.system.entry.js} +1 -1
  138. package/dist/pine-core/p-950dd70e.system.entry.js +1 -0
  139. package/dist/pine-core/p-B1rytYxP.system.js +1 -1
  140. package/dist/pine-core/p-BbXp4qBM.system.js +1 -0
  141. package/dist/pine-core/p-BuRbS7Hp.js +1 -0
  142. package/dist/pine-core/p-BxIzb-Wc.system.js +1 -0
  143. package/dist/pine-core/p-RFpTWV6r.js +1 -0
  144. package/dist/pine-core/p-a0edb4ad.entry.js +1 -0
  145. package/dist/pine-core/p-af1e2778.entry.js +1 -0
  146. package/dist/pine-core/p-b1e5e8b1.system.entry.js +1 -0
  147. package/dist/pine-core/p-b3b44b80.entry.js +1 -0
  148. package/dist/pine-core/p-b759dc21.entry.js +1 -0
  149. package/dist/pine-core/{p-dc48f603.entry.js → p-caf73216.entry.js} +1 -1
  150. package/dist/pine-core/p-cd2370d0.system.entry.js +1 -0
  151. package/dist/pine-core/{p-3c6300a8.system.entry.js → p-cd407bed.system.entry.js} +1 -1
  152. package/dist/pine-core/p-d12a93d6.entry.js +1 -0
  153. package/dist/pine-core/p-dfa70d20.entry.js +1 -0
  154. package/dist/pine-core/p-f145e018.entry.js +1 -0
  155. package/dist/pine-core/p-f7cbdb81.system.entry.js +1 -0
  156. package/dist/pine-core/p-fef6694d.entry.js +1 -0
  157. package/dist/pine-core/pine-core.esm.js +1 -1
  158. package/dist/types/components/pds-avatar/pds-avatar.d.ts +1 -0
  159. package/dist/types/components/pds-combobox/pds-combobox.d.ts +18 -0
  160. package/dist/types/components/pds-modal/pds-modal-content/pds-modal-content.d.ts +1 -0
  161. package/dist/types/components/pds-modal/pds-modal-footer/pds-modal-footer.d.ts +2 -0
  162. package/dist/types/components/pds-modal/pds-modal-header/pds-modal-header.d.ts +2 -0
  163. package/dist/types/components/pds-modal/pds-modal.d.ts +11 -0
  164. package/dist/types/components/pds-radio/pds-radio.d.ts +1 -0
  165. package/dist/types/components/pds-radio-group/pds-radio-group.d.ts +1 -0
  166. package/dist/types/components/pds-select/pds-select.d.ts +1 -0
  167. package/dist/types/components/pds-sortable/pds-sortable-item/pds-sortable-item.d.ts +1 -0
  168. package/dist/types/components/pds-tabs/pds-tab/pds-tab.d.ts +1 -0
  169. package/dist/types/components/pds-tabs/pds-tabpanel/pds-tabpanel.d.ts +1 -0
  170. package/dist/types/components/pds-tooltip/pds-tooltip.d.ts +3 -0
  171. package/dist/types/components.d.ts +42 -0
  172. package/dist/types/{home/runner/work/pine/pine → private/tmp/claude-501/-Users-qjason-Documents-GitHub/dc3bfd4f-fc9e-49b8-b152-4257ab4ca83c/scratchpad/pine-publish-backfill}/libs/core/.stencil/scripts/vscode-custom-data-generator.d.ts +1 -1
  173. package/dist/types/utils/form.d.ts +4 -1
  174. package/dist/types/utils/reconnected-content.d.ts +14 -0
  175. package/dist/types/utils/test/reconnect-safety.d.ts +11 -0
  176. package/dist/vscode.html-data.json +10909 -0
  177. package/hydrate/index.js +248 -50
  178. package/hydrate/index.mjs +248 -50
  179. package/package.json +2 -2
  180. package/dist/esm-es5/form-C3xDZBme.js +0 -1
  181. package/dist/pine-core/p-02805a93.system.entry.js +0 -1
  182. package/dist/pine-core/p-02977c6f.entry.js +0 -1
  183. package/dist/pine-core/p-054f7011.entry.js +0 -1
  184. package/dist/pine-core/p-092a72d2.system.entry.js +0 -1
  185. package/dist/pine-core/p-0d6daa35.entry.js +0 -1
  186. package/dist/pine-core/p-12154189.system.entry.js +0 -1
  187. package/dist/pine-core/p-1deded6e.system.entry.js +0 -1
  188. package/dist/pine-core/p-1fb985bb.entry.js +0 -1
  189. package/dist/pine-core/p-2a74b396.entry.js +0 -1
  190. package/dist/pine-core/p-39ad92fc.entry.js +0 -1
  191. package/dist/pine-core/p-507e831f.system.entry.js +0 -1
  192. package/dist/pine-core/p-5c229a7f.entry.js +0 -1
  193. package/dist/pine-core/p-69e47ffc.system.entry.js +0 -1
  194. package/dist/pine-core/p-6fb68ad1.entry.js +0 -1
  195. package/dist/pine-core/p-72a1b563.system.entry.js +0 -1
  196. package/dist/pine-core/p-7a7954b2.system.entry.js +0 -1
  197. package/dist/pine-core/p-7d15b65e.entry.js +0 -1
  198. package/dist/pine-core/p-902b5790.entry.js +0 -2
  199. package/dist/pine-core/p-9aaee1e9.system.entry.js +0 -1
  200. package/dist/pine-core/p-9ba86e34.entry.js +0 -1
  201. package/dist/pine-core/p-9f041a63.system.entry.js +0 -3
  202. package/dist/pine-core/p-B1u-QvFn.system.js +0 -1
  203. package/dist/pine-core/p-C3xDZBme.js +0 -1
  204. package/dist/pine-core/p-b0674b8d.entry.js +0 -1
  205. package/dist/pine-core/p-c1932447.entry.js +0 -1
  206. package/dist/pine-core/p-ce715993.entry.js +0 -1
  207. package/dist/pine-core/p-d037308b.entry.js +0 -1
  208. package/dist/pine-core/p-e0fb06ea.system.entry.js +0 -1
  209. package/dist/pine-core/p-e4b69d10.entry.js +0 -1
  210. package/dist/pine-core/p-e81b4d04.system.entry.js +0 -1
  211. package/dist/pine-core/p-fb10caff.system.entry.js +0 -1
  212. package/dist/pine-core/p-ff3399ed.entry.js +0 -1
@@ -58,13 +58,45 @@
58
58
  .pds-combobox__input::placeholder {
59
59
  color: var(--pine-color-text-placeholder);
60
60
  }
61
+ .pds-combobox__input.pds-combobox__input--invalid {
62
+ background: var(--pine-color-red-050);
63
+ border-color: var(--pine-color-border-danger);
64
+ }
65
+ :host-context([data-theme=dark]) .pds-combobox__input.pds-combobox__input--invalid {
66
+ background: var(--pine-color-red-950);
67
+ }
68
+ .pds-combobox__input.pds-combobox__input--invalid:hover:not(:disabled) {
69
+ border-color: var(--pine-color-border-danger);
70
+ }
71
+ .pds-combobox__input.pds-combobox__input--invalid:focus-visible {
72
+ outline-color: var(--pine-color-focus-ring-danger);
73
+ }
61
74
 
62
75
  .pds-combobox__input-icon {
63
76
  color: var(--pine-color-text-secondary);
64
77
  inset-inline-end: var(--pine-dimension-150);
65
78
  pointer-events: none;
66
79
  position: absolute;
67
- z-index: var(--pine-z-index-raised);
80
+ z-index: calc(var(--pine-z-index-raised) - 1);
81
+ }
82
+
83
+ .pds-combobox__helper {
84
+ color: var(--pine-color-text-message);
85
+ font: var(--pine-typography-body-sm-medium);
86
+ margin-block-start: var(--pine-dimension-2xs);
87
+ }
88
+
89
+ .pds-combobox__error {
90
+ align-items: flex-start;
91
+ color: var(--pine-color-text-message-danger);
92
+ display: flex;
93
+ font: var(--pine-typography-body-sm-medium);
94
+ gap: var(--pine-dimension-2xs);
95
+ margin-block-start: var(--pine-dimension-2xs);
96
+ }
97
+ .pds-combobox__error pds-icon {
98
+ flex-shrink: 0;
99
+ margin-block-start: var(--pine-dimension-025);
68
100
  }
69
101
 
70
102
  .pds-combobox__listbox {
@@ -1,5 +1,5 @@
1
1
  import { h, Host } from "@stencil/core";
2
- import { isSpecTest } from "../../utils/form";
2
+ import { assignDescription, isSpecTest, messageId } from "../../utils/form";
3
3
  import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
4
4
  import { debounceEvent } from "../../utils/utils";
5
5
  import DOMPurify from "dompurify";
@@ -1182,6 +1182,20 @@ export class PdsCombobox {
1182
1182
  async getSelectedValue() {
1183
1183
  return this.selectedOption ? this.selectedOption.value : null;
1184
1184
  }
1185
+ /**
1186
+ * Clears the selected value and resets the component.
1187
+ */
1188
+ async clear() {
1189
+ const hadValue = this.value !== '' || this.displayText !== '';
1190
+ this.displayText = '';
1191
+ this.setSelectedOption(null);
1192
+ this.value = '';
1193
+ this.filterOptions();
1194
+ this.updateFormValue('');
1195
+ if (hadValue) {
1196
+ this.pdsComboboxChange.emit({ value: '' });
1197
+ }
1198
+ }
1185
1199
  // Get the label of the selected option
1186
1200
  get selectedLabel() {
1187
1201
  return this.displayText || '';
@@ -1450,6 +1464,19 @@ export class PdsCombobox {
1450
1464
  ,
1451
1465
  large: chipProps.large, icon: chipProps.icon, dot: chipProps.dot, class: "pds-combobox__option-chip" }, this.getOptionLabel(option)));
1452
1466
  }
1467
+ // Mirrors pds-multiselect and pds-select: an error message on its own puts the
1468
+ // field in an invalid state, so assistive tech agrees with the visual treatment.
1469
+ isInvalid() {
1470
+ return Boolean(this.invalid) || Boolean(this.errorMessage);
1471
+ }
1472
+ renderMessages() {
1473
+ const hasErrorMessage = Boolean(this.errorMessage);
1474
+ const showHelperMessage = Boolean(this.helperMessage) && !hasErrorMessage;
1475
+ return [
1476
+ showHelperMessage && (h("p", { class: "pds-combobox__helper", id: messageId(this.componentId, 'helper') }, this.helperMessage)),
1477
+ hasErrorMessage && (h("p", { class: "pds-combobox__error", id: messageId(this.componentId, 'error'), "aria-live": "assertive" }, h("pds-icon", { icon: "danger", size: "small", "aria-hidden": "true" }), this.errorMessage)),
1478
+ ];
1479
+ }
1453
1480
  render() {
1454
1481
  const triggerClass = [
1455
1482
  'pds-combobox__button-trigger',
@@ -1458,10 +1485,13 @@ export class PdsCombobox {
1458
1485
  ]
1459
1486
  .filter(Boolean)
1460
1487
  .join(' ');
1461
- return (h(Host, { key: '3de5711e53460545e3cb962d446880a99edcd44c', "aria-disabled": this.disabled ? 'true' : null }, h("div", { key: 'cce16999fc6be08b99a113ae58cb2be0fbe9c577', class: "pds-combobox", tabIndex: -1, onFocusout: this.onComboboxFocusOut, part: "combobox" }, this.label && !this.hideLabel && (h("label", { key: '355650eba712d6c91694752daed4cf18008a920d', htmlFor: this.componentId, class: "pds-combobox__label" }, this.label)), this.trigger === 'input' ? (h("div", { class: "pds-combobox__input-wrapper", style: { width: this.triggerWidth } }, h("input", { ref: el => {
1488
+ return (h(Host, { key: '25ce0fc749a909555762a44ff7ff089ab65465d4', "aria-disabled": this.disabled ? 'true' : null }, h("div", { key: '8f10f691452c1b793fd31b719f43de04b1ea064b', class: "pds-combobox", tabIndex: -1, onFocusout: this.onComboboxFocusOut, part: "combobox" }, this.label && !this.hideLabel && (h("label", { key: 'af6ee018cc15d1bc0b75b7d33530e2678fe6017b', htmlFor: this.componentId, class: "pds-combobox__label" }, this.label)), this.trigger === 'input' ? (h("div", { class: "pds-combobox__input-wrapper", style: { width: this.triggerWidth } }, h("input", { ref: el => {
1462
1489
  this.inputEl = el;
1463
1490
  this.triggerEl = el;
1464
- }, class: "pds-combobox__input", type: "text", role: "combobox", "aria-autocomplete": "list", "aria-controls": this.listboxId, "aria-activedescendant": this.isOpen && this.highlightedIndex >= 0 ? this.optionDomId(this.highlightedIndex) : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, value: this.displayText, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput, onClick: this.handleInputClick, onKeyDown: this.handleKeyDown, autocomplete: "off", part: "input" }), h("pds-icon", { icon: "enlarge", "aria-hidden": "true", class: "pds-combobox__input-icon" }))) : this.trigger === 'chip' ? (h("div", { class: this.getChipTriggerClass(), style: { width: this.triggerWidth }, role: "combobox", "aria-haspopup": "listbox", "aria-controls": this.listboxId, "aria-activedescendant": this.isOpen && this.highlightedIndex >= 0 ? this.optionDomId(this.highlightedIndex) : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, ref: el => (this.triggerEl = el), part: "chip-trigger" }, this.renderChipTriggerContent())) : (h("div", { class: triggerClass, style: { width: this.triggerWidth }, role: "combobox", "aria-haspopup": "listbox", "aria-controls": this.listboxId, "aria-activedescendant": this.isOpen && this.highlightedIndex >= 0 ? this.optionDomId(this.highlightedIndex) : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, ref: el => (this.triggerEl = el), part: "button-trigger" }, this.renderButtonTriggerContent())), h("div", { key: '87ffd9a2472b7459343daf4c0f7d46f260365180', style: { display: 'none' } }, h("slot", { key: 'fd0e21c876fbf21761ad63c9529a304912aecd1d', onSlotchange: () => this.updateOptions() })), this.renderDropdown())));
1491
+ }, class: {
1492
+ 'pds-combobox__input': true,
1493
+ 'pds-combobox__input--invalid': this.isInvalid(),
1494
+ }, type: "text", role: "combobox", "aria-autocomplete": "list", "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.isOpen && this.highlightedIndex >= 0 ? this.optionDomId(this.highlightedIndex) : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, value: this.displayText, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput, onClick: this.handleInputClick, onKeyDown: this.handleKeyDown, autocomplete: "off", part: "input" }), h("pds-icon", { icon: "enlarge", "aria-hidden": "true", class: "pds-combobox__input-icon" }))) : this.trigger === 'chip' ? (h("div", { class: this.getChipTriggerClass(), style: { width: this.triggerWidth }, role: "combobox", "aria-haspopup": "listbox", "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.isOpen && this.highlightedIndex >= 0 ? this.optionDomId(this.highlightedIndex) : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, ref: el => (this.triggerEl = el), part: "chip-trigger" }, this.renderChipTriggerContent())) : (h("div", { class: triggerClass, style: { width: this.triggerWidth }, role: "combobox", "aria-haspopup": "listbox", "aria-controls": this.listboxId, "aria-describedby": assignDescription(this.componentId, this.isInvalid(), this.helperMessage, this.errorMessage), "aria-invalid": this.isInvalid() ? 'true' : undefined, "aria-activedescendant": this.isOpen && this.highlightedIndex >= 0 ? this.optionDomId(this.highlightedIndex) : undefined, "aria-expanded": this.isOpen ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.hideLabel ? this.label : undefined, id: this.componentId, tabIndex: this.disabled ? -1 : 0, onClick: this.onButtonTriggerClick, "data-layout": this.customTriggerContent, onKeyDown: this.onButtonTriggerKeyDown, onKeyUp: this.onButtonTriggerKeyUp, ref: el => (this.triggerEl = el), part: "button-trigger" }, this.renderButtonTriggerContent())), this.renderMessages(), h("div", { key: '586e443fb97fbe74888d6c296db63a0ed7c95787', style: { display: 'none' } }, h("slot", { key: '9a287c316d09bc4cc7fd94ad7dbdbf5458753162', onSlotchange: () => this.updateOptions() })), this.renderDropdown())));
1465
1495
  }
1466
1496
  static get is() { return "pds-combobox"; }
1467
1497
  static get encapsulation() { return "shadow"; }
@@ -1582,6 +1612,25 @@ export class PdsCombobox {
1582
1612
  "attribute": "disabled",
1583
1613
  "defaultValue": "false"
1584
1614
  },
1615
+ "errorMessage": {
1616
+ "type": "string",
1617
+ "mutable": false,
1618
+ "complexType": {
1619
+ "original": "string",
1620
+ "resolved": "string",
1621
+ "references": {}
1622
+ },
1623
+ "required": false,
1624
+ "optional": true,
1625
+ "docs": {
1626
+ "tags": [],
1627
+ "text": "Error message to display."
1628
+ },
1629
+ "getter": false,
1630
+ "setter": false,
1631
+ "reflect": false,
1632
+ "attribute": "error-message"
1633
+ },
1585
1634
  "dropdownPlacement": {
1586
1635
  "type": "string",
1587
1636
  "mutable": false,
@@ -1651,6 +1700,25 @@ export class PdsCombobox {
1651
1700
  "attribute": "dropdown-mount",
1652
1701
  "defaultValue": "'host'"
1653
1702
  },
1703
+ "helperMessage": {
1704
+ "type": "string",
1705
+ "mutable": false,
1706
+ "complexType": {
1707
+ "original": "string",
1708
+ "resolved": "string",
1709
+ "references": {}
1710
+ },
1711
+ "required": false,
1712
+ "optional": true,
1713
+ "docs": {
1714
+ "tags": [],
1715
+ "text": "Helper message to display below the combobox."
1716
+ },
1717
+ "getter": false,
1718
+ "setter": false,
1719
+ "reflect": false,
1720
+ "attribute": "helper-message"
1721
+ },
1654
1722
  "hideLabel": {
1655
1723
  "type": "boolean",
1656
1724
  "mutable": false,
@@ -1671,6 +1739,25 @@ export class PdsCombobox {
1671
1739
  "attribute": "hide-label",
1672
1740
  "defaultValue": "false"
1673
1741
  },
1742
+ "invalid": {
1743
+ "type": "boolean",
1744
+ "mutable": false,
1745
+ "complexType": {
1746
+ "original": "boolean",
1747
+ "resolved": "boolean",
1748
+ "references": {}
1749
+ },
1750
+ "required": false,
1751
+ "optional": true,
1752
+ "docs": {
1753
+ "tags": [],
1754
+ "text": "If true, the combobox is in an invalid state."
1755
+ },
1756
+ "getter": false,
1757
+ "setter": false,
1758
+ "reflect": false,
1759
+ "attribute": "invalid"
1760
+ },
1674
1761
  "label": {
1675
1762
  "type": "string",
1676
1763
  "mutable": false,
@@ -2218,6 +2305,23 @@ export class PdsCombobox {
2218
2305
  "text": "Gets the value of the currently selected option.",
2219
2306
  "tags": []
2220
2307
  }
2308
+ },
2309
+ "clear": {
2310
+ "complexType": {
2311
+ "signature": "() => Promise<void>",
2312
+ "parameters": [],
2313
+ "references": {
2314
+ "Promise": {
2315
+ "location": "global",
2316
+ "id": "global::Promise"
2317
+ }
2318
+ },
2319
+ "return": "Promise<void>"
2320
+ },
2321
+ "docs": {
2322
+ "text": "Clears the selected value and resets the component.",
2323
+ "tags": []
2324
+ }
2221
2325
  }
2222
2326
  };
2223
2327
  }
@@ -10,7 +10,10 @@ export default {
10
10
  dropdownPlacement: 'bottom-start',
11
11
  dropdownMount: 'host',
12
12
  dropdownWidth: '236px',
13
+ errorMessage: null,
14
+ helperMessage: null,
13
15
  hideLabel: false,
16
+ invalid: false,
14
17
  maxHeight: null,
15
18
  mode: 'filter',
16
19
  trigger: 'input',
@@ -38,7 +41,10 @@ const BaseTemplate = (args) => html`
38
41
  dropdown-placement=${args.dropdownPlacement}
39
42
  dropdown-mount=${args.dropdownMount}
40
43
  dropdown-width=${args.dropdownWidth}
44
+ error-message=${args.errorMessage}
45
+ helper-message=${args.helperMessage}
41
46
  hide-label=${args.hideLabel}
47
+ invalid=${args.invalid}
42
48
  max-height=${args.maxHeight}
43
49
  label=${args.label}
44
50
  placeholder=${args.placeholder}
@@ -70,6 +76,25 @@ Default.args = {
70
76
  value: 'panda',
71
77
  };
72
78
 
79
+ export const HelperMessage = BaseTemplate.bind();
80
+ HelperMessage.args = {
81
+ componentId: 'combobox-helper-story',
82
+ name: 'favoriteAnimal',
83
+ label: 'Favorite Animal',
84
+ placeholder: 'placeholder_text',
85
+ helperMessage: 'Start typing to filter the list.',
86
+ };
87
+
88
+ export const ErrorMessage = BaseTemplate.bind();
89
+ ErrorMessage.args = {
90
+ componentId: 'combobox-error-story',
91
+ name: 'favoriteAnimal',
92
+ label: 'Favorite Animal',
93
+ placeholder: 'placeholder_text',
94
+ errorMessage: 'Please choose an animal.',
95
+ invalid: true,
96
+ };
97
+
73
98
  export const ButtonTrigger = BaseTemplate.bind();
74
99
  ButtonTrigger.args = {
75
100
  componentId: 'combobox-button-trigger-story',
@@ -83,6 +108,41 @@ ButtonTrigger.args = {
83
108
  value: 'dog',
84
109
  };
85
110
 
111
+ export const ProgrammaticClear = (args) => html`
112
+ <pds-box direction="column" gap="sm" align-items="start">
113
+ <pds-combobox
114
+ id="combobox-clear-story"
115
+ component-id=${args.componentId}
116
+ name=${args.name}
117
+ label=${args.label}
118
+ placeholder=${args.placeholder}
119
+ trigger=${args.trigger}
120
+ mode=${args.mode}
121
+ value=${args.value}
122
+ >
123
+ <option value="cat">Cat</option>
124
+ <option value="dog">Dog</option>
125
+ <option value="panda">Panda</option>
126
+ <option value="snake">Snake</option>
127
+ </pds-combobox>
128
+ <pds-button
129
+ variant="secondary"
130
+ size="sm"
131
+ @click=${() => document.querySelector('#combobox-clear-story').clear()}
132
+ >
133
+ Reset
134
+ </pds-button>
135
+ </pds-box>`;
136
+ ProgrammaticClear.args = {
137
+ componentId: 'combobox-clear-story',
138
+ name: 'favoriteAnimal',
139
+ label: 'Favorite Animal',
140
+ placeholder: 'Select an animal',
141
+ trigger: 'input',
142
+ mode: 'filter',
143
+ value: 'panda',
144
+ };
145
+
86
146
  export const ChipTriggerAndLayout = (args) => html`
87
147
  <pds-box min-width="100%" direction="row" gap="sm" wrap>
88
148
  <pds-combobox
@@ -37,7 +37,7 @@
37
37
  top: 50%;
38
38
  transform: translateY(-50%);
39
39
  white-space: nowrap;
40
- z-index: var(--pine-z-index-raised);
40
+ z-index: calc(var(--pine-z-index-raised) - 1);
41
41
  }
42
42
  :host::part(prefix) {
43
43
  border-end-start-radius: var(--pds-input-border-radius);
@@ -289,7 +289,7 @@ export class PdsInput {
289
289
  return (h(Host, { key: 'ecf4cc212e5c3ebc18082b790803e6b2ccb8ca77', "aria-disabled": this.disabled ? 'true' : null, "aria-readonly": this.readonly ? 'true' : null, "has-prefix": this.hasPrefix ? 'true' : null, "has-suffix": this.hasSuffix ? 'true' : null, "has-prepend": this.hasPrepend ? 'true' : null, "has-append": this.hasAppend ? 'true' : null, "has-action": this.hasAction && !this.hideLabel ? 'true' : null, "full-width": this.fullWidth ? 'true' : null }, h("div", { key: 'acdf92a30068fc04a17816ec6d85295b47e512ee', class: "pds-input" }, label && (h("div", { key: '4569f0b3761db89e85a090eff6af1c47cb562115', class: {
290
290
  'pds-input__label-wrapper': true,
291
291
  'visually-hidden': this.hideLabel,
292
- } }, h("label", { key: '90b0a863d78115fa158429db11548fc2a9ee5ae6', htmlFor: componentId, class: "pds-input__label" }, h("span", { key: '3d440aaafc4daeffe45152cd23d27472d7ebf85a' }, label, this.required && h("span", { key: 'c7bf6ef6191e961a106b8885cab604aa89730eba', class: "pds-input__required-indicator" }, " *"))), !this.hideLabel && this.renderAction())), h("div", { key: '81f0b8a347bc9a06c753b9f31f5e30ac78e45adb', class: inputWrapperClasses }, this.renderPrepend(), this.renderPrefix(), h("input", Object.assign({ key: '3785b396bf9174766973e8b19028e4dcecde19d9', ref: (input) => (this.nativeInput = input), class: "pds-input__field", "aria-describedby": assignDescription(componentId, invalid, helperMessage), "aria-invalid": invalid ? "true" : undefined, autocomplete: this.autocomplete, disabled: disabled, id: componentId, max: this.max, maxlength: this.maxlength, min: this.min, minlength: this.minlength, name: this.name, pattern: this.pattern, placeholder: this.placeholder, readOnly: this.readonly, required: this.required, step: this.step, type: this.type, value: value, onInput: this.onInputEvent, onChange: this.onChangeEvent, onBlur: this.onBlurEvent, onFocus: this.onFocusEvent, onKeyDown: this.onKeyDownEvent, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd }, this.inheritedAttributes)), this.renderSuffix(), this.renderAppend()), helperMessage && (h("p", { key: 'e9e4aa936346811e0a5ca0fa5107f548b52fcc99', class: "pds-input__helper-message", id: messageId(componentId, 'helper') }, helperMessage)), errorMessage && (h("p", { key: 'ef3694246fa3d8f818da438e7ec4411dd56b084c', class: "pds-input__error-message", id: messageId(componentId, 'error') }, h("pds-icon", { key: 'f1e488b33c8338f54cd83a59e219311a844e3c97', "aria-hidden": "true", icon: danger, size: "small" }), errorMessage)))));
292
+ } }, h("label", { key: '90b0a863d78115fa158429db11548fc2a9ee5ae6', htmlFor: componentId, class: "pds-input__label" }, h("span", { key: '3d440aaafc4daeffe45152cd23d27472d7ebf85a' }, label, this.required && h("span", { key: 'c7bf6ef6191e961a106b8885cab604aa89730eba', class: "pds-input__required-indicator" }, " *"))), !this.hideLabel && this.renderAction())), h("div", { key: '81f0b8a347bc9a06c753b9f31f5e30ac78e45adb', class: inputWrapperClasses }, this.renderPrepend(), this.renderPrefix(), h("input", Object.assign({ key: '41ed8dab6f7c71c6c382627ca4dc48bb95c1161d', ref: (input) => (this.nativeInput = input), class: "pds-input__field", "aria-describedby": assignDescription(componentId, invalid, helperMessage, errorMessage), "aria-invalid": invalid ? "true" : undefined, autocomplete: this.autocomplete, disabled: disabled, id: componentId, max: this.max, maxlength: this.maxlength, min: this.min, minlength: this.minlength, name: this.name, pattern: this.pattern, placeholder: this.placeholder, readOnly: this.readonly, required: this.required, step: this.step, type: this.type, value: value, onInput: this.onInputEvent, onChange: this.onChangeEvent, onBlur: this.onBlurEvent, onFocus: this.onFocusEvent, onKeyDown: this.onKeyDownEvent, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd }, this.inheritedAttributes)), this.renderSuffix(), this.renderAppend()), helperMessage && (h("p", { key: '4791ddeb01b758a94a2fddfec6093b9b39eefafa', class: "pds-input__helper-message", id: messageId(componentId, 'helper') }, helperMessage)), errorMessage && (h("p", { key: 'd0a22ae4b0e400e1d5df55e35c0aaaa5c5ea3ab7', class: "pds-input__error-message", id: messageId(componentId, 'error') }, h("pds-icon", { key: '69975cdcc15e5ab6df9e2c354977e2c6dae18f6e', "aria-hidden": "true", icon: danger, size: "small" }), errorMessage)))));
293
293
  }
294
294
  static get is() { return "pds-input"; }
295
295
  static get encapsulation() { return "shadow"; }
@@ -1,4 +1,5 @@
1
1
  import { h, Host } from "@stencil/core";
2
+ import { unwrapReconnectedContent } from "../../../utils/reconnected-content";
2
3
  export class PdsModalContent {
3
4
  constructor() {
4
5
  /**
@@ -12,6 +13,10 @@ export class PdsModalContent {
12
13
  */
13
14
  this.userSetBorder = false;
14
15
  }
16
+ // Unwraps a nested .pds-modal-content left by a page-cache (Turbo, bfcache) reconnect.
17
+ componentDidRender() {
18
+ unwrapReconnectedContent(this.el.querySelector('.pds-modal-content'), '.pds-modal-content');
19
+ }
15
20
  componentWillLoad() {
16
21
  // Check if border was explicitly set via attribute or property
17
22
  const borderAttr = this.el.getAttribute('border');
@@ -173,10 +178,10 @@ export class PdsModalContent {
173
178
  render() {
174
179
  // Only apply max-height style if it's not 'none'
175
180
  const styleObj = this.contentMaxHeight !== 'none' ? { maxHeight: this.contentMaxHeight } : {};
176
- return (h(Host, { key: 'fae42c1fc151e4855cd2c731802c15851473be79' }, h("div", { key: 'feb61d6a7227d8113b931df6280358cd9b4fe583', class: {
181
+ return (h(Host, { key: 'f3e75b90be56bb27ba5f4fde473fac474846f346' }, h("div", { key: '5035d5fa319fe2aaa8adffb825f0d2babfd11a26', class: {
177
182
  'pds-modal-content': true,
178
183
  [`pds-modal-content--border-${this.border}`]: true
179
- }, style: styleObj, tabindex: "-1" }, h("slot", { key: '1c9e55ecd1ad353699e5a4590ee04c9005719f6d' }))));
184
+ }, style: styleObj, tabindex: "-1" }, h("slot", { key: '73299f6f7045ea50a5bf134da04f1e30cd9f233c' }))));
180
185
  }
181
186
  static get is() { return "pds-modal-content"; }
182
187
  static get originalStyleUrls() {
@@ -1,7 +1,12 @@
1
1
  import { h } from "@stencil/core";
2
+ import { unwrapReconnectedContent } from "../../../utils/reconnected-content";
2
3
  export class PdsModalFooter {
4
+ // Unwraps a nested .pds-modal__footer left by a page-cache (Turbo, bfcache) reconnect.
5
+ componentDidRender() {
6
+ unwrapReconnectedContent(this.el.querySelector('.pds-modal__footer'), '.pds-modal__footer');
7
+ }
3
8
  render() {
4
- return (h("footer", { key: '0fa58ce225ce31a1f40d6ce1d3276e716b36de8d', class: "pds-modal__footer" }, h("slot", { key: 'df0bbdb56c711c7eb34db7181f940de11d4449ef' })));
9
+ return (h("footer", { key: '1220d6319585df0128924ebac52569af7645f525', class: "pds-modal__footer" }, h("slot", { key: '1bbbf4897f41c802fc5773154cee3930290c906a' })));
5
10
  }
6
11
  static get is() { return "pds-modal-footer"; }
7
12
  static get originalStyleUrls() {
@@ -14,4 +19,5 @@ export class PdsModalFooter {
14
19
  "$": ["pds-modal-footer.css"]
15
20
  };
16
21
  }
22
+ static get elementRef() { return "el"; }
17
23
  }
@@ -1,7 +1,12 @@
1
1
  import { h } from "@stencil/core";
2
+ import { unwrapReconnectedContent } from "../../../utils/reconnected-content";
2
3
  export class PdsModalHeader {
4
+ // Unwraps a nested .pds-modal__header left by a page-cache (Turbo, bfcache) reconnect.
5
+ componentDidRender() {
6
+ unwrapReconnectedContent(this.el.querySelector('.pds-modal__header'), '.pds-modal__header');
7
+ }
3
8
  render() {
4
- return (h("header", { key: '1a471bd6dfa2b654f5a6ebdf8fb5baef728324b3', class: "pds-modal__header" }, h("slot", { key: '946a365ab3c2ca627d618188890d385c054b4c16' })));
9
+ return (h("header", { key: '0fd8a216ae8dc149b59cd29c8706d5a45a21de15', class: "pds-modal__header" }, h("slot", { key: '4bf075dfd4870621e6e0b710fcdb42aac53810b3' })));
5
10
  }
6
11
  static get is() { return "pds-modal-header"; }
7
12
  static get originalStyleUrls() {
@@ -14,4 +19,5 @@ export class PdsModalHeader {
14
19
  "$": ["pds-modal-header.css"]
15
20
  };
16
21
  }
22
+ static get elementRef() { return "el"; }
17
23
  }
@@ -1,4 +1,5 @@
1
1
  import { h } from "@stencil/core";
2
+ import { unwrapReconnectedContent } from "../../utils/reconnected-content";
2
3
  export class PdsModal {
3
4
  constructor() {
4
5
  this.focusableElements = [];
@@ -22,6 +23,16 @@ export class PdsModal {
22
23
  * @default true
23
24
  */
24
25
  this.scrollable = true;
26
+ /**
27
+ * Whether the modal opens outside the browser top layer as a non-modal dialog.
28
+ * When `true` it opens with `dialog.show()` instead of `dialog.showModal()`, so
29
+ * overlays rendered elsewhere in the DOM (file pickers, editor menus) can display
30
+ * above it via `z-index`. The page is not made inert and focus is not trapped in
31
+ * this mode. Read when the modal opens; changing it while the modal is open is
32
+ * not supported.
33
+ * @default false
34
+ */
35
+ this.disableTopLayer = false;
25
36
  /**
26
37
  * Stores the list of focusable elements in the modal
27
38
  */
@@ -43,6 +54,13 @@ export class PdsModal {
43
54
  return;
44
55
  // Handle Escape key to close the modal
45
56
  if (e.key === 'Escape') {
57
+ // In non-top-layer mode, focus can move into an overlay stacked above the
58
+ // modal (the reason disableTopLayer exists). If that overlay owns focus,
59
+ // leave Escape to it rather than dismissing this modal out from under it.
60
+ const active = document.activeElement;
61
+ if (this.disableTopLayer && active && active !== document.body && !this.el.contains(active)) {
62
+ return;
63
+ }
46
64
  // Always prevent native dialog close behavior
47
65
  e.preventDefault();
48
66
  // Only close if backdropDismiss is enabled and this is the innermost modal
@@ -53,6 +71,11 @@ export class PdsModal {
53
71
  }
54
72
  // Handle Tab key for focus trapping
55
73
  if (e.key === 'Tab') {
74
+ // In non-top-layer mode the modal is deliberately not focus-isolated: focus
75
+ // must be able to leave it into overlays stacked above (the whole point of
76
+ // disableTopLayer), so do not trap Tab here.
77
+ if (this.disableTopLayer)
78
+ return;
56
79
  // If there are no focusable elements, do nothing
57
80
  if (this.focusableElements.length === 0)
58
81
  return;
@@ -84,6 +107,10 @@ export class PdsModal {
84
107
  // Add keyboard event listener
85
108
  document.addEventListener('keydown', this.handleKeyDown);
86
109
  }
110
+ // Unwraps a nested dialog.pds-modal__backdrop left by a page-cache (Turbo, bfcache) reconnect.
111
+ componentDidRender() {
112
+ unwrapReconnectedContent(this.el.querySelector('.pds-modal'), 'dialog.pds-modal__backdrop', '.pds-modal');
113
+ }
87
114
  disconnectedCallback() {
88
115
  // Clean up event listener
89
116
  document.removeEventListener('keydown', this.handleKeyDown);
@@ -170,8 +197,16 @@ export class PdsModal {
170
197
  try {
171
198
  // Store the currently focused element to restore focus when modal closes
172
199
  this.previousActiveElement = document.activeElement;
173
- // Use native dialog showModal method which makes the rest of the page inert
174
- this.modalRef.showModal();
200
+ // showModal() promotes the dialog to the browser top layer (and makes the
201
+ // rest of the page inert), which prevents any overlay outside the dialog
202
+ // from ever painting above it. show() opens a non-modal dialog that stays
203
+ // in the normal stacking context so those overlays can stack above it.
204
+ if (this.disableTopLayer) {
205
+ this.modalRef.show();
206
+ }
207
+ else {
208
+ this.modalRef.showModal();
209
+ }
175
210
  this.open = true;
176
211
  // Update focusable elements and set initial focus
177
212
  // Using a longer timeout to ensure all components are fully rendered
@@ -233,14 +268,14 @@ export class PdsModal {
233
268
  return thisZIndex === maxZIndex;
234
269
  }
235
270
  render() {
236
- return (h("dialog", { key: 'cf2710a889d8f0e80a3551088ab38a88dc44ad1e', class: {
271
+ return (h("dialog", { key: '134e913f0bf5d7bf77e67f00707a399fae986fcb', class: {
237
272
  'pds-modal__backdrop': true,
238
273
  'open': this.open
239
- }, "aria-modal": "true", "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick }, h("div", { key: 'a06d017153e67ad38bc1ced56ba22aeade977aaf', class: {
274
+ }, "aria-modal": this.disableTopLayer ? 'false' : 'true', "aria-labelledby": `${this.componentId}-heading`, onClick: this.handleBackdropClick }, h("div", { key: '6cb819a92c00b5c8676b4d1a84301896373305f3', class: {
240
275
  'pds-modal': true,
241
276
  [`pds-modal--${this.size}`]: true,
242
277
  'pds-modal--scrollable': this.scrollable
243
- }, part: "modal" }, h("slot", { key: 'd995c586809a4940c091912244a86cb4605a961c' }))));
278
+ }, part: "modal" }, h("slot", { key: 'aff0f88a7b9e1b4b681a6a14636153ce3a6907d5' }))));
244
279
  }
245
280
  static get is() { return "pds-modal"; }
246
281
  static get originalStyleUrls() {
@@ -365,6 +400,29 @@ export class PdsModal {
365
400
  "reflect": false,
366
401
  "attribute": "scrollable",
367
402
  "defaultValue": "true"
403
+ },
404
+ "disableTopLayer": {
405
+ "type": "boolean",
406
+ "mutable": false,
407
+ "complexType": {
408
+ "original": "boolean",
409
+ "resolved": "boolean",
410
+ "references": {}
411
+ },
412
+ "required": false,
413
+ "optional": false,
414
+ "docs": {
415
+ "tags": [{
416
+ "name": "default",
417
+ "text": "false"
418
+ }],
419
+ "text": "Whether the modal opens outside the browser top layer as a non-modal dialog.\nWhen `true` it opens with `dialog.show()` instead of `dialog.showModal()`, so\noverlays rendered elsewhere in the DOM (file pickers, editor menus) can display\nabove it via `z-index`. The page is not made inert and focus is not trapped in\nthis mode. Read when the modal opens; changing it while the modal is open is\nnot supported."
420
+ },
421
+ "getter": false,
422
+ "setter": false,
423
+ "reflect": false,
424
+ "attribute": "disable-top-layer",
425
+ "defaultValue": "false"
368
426
  }
369
427
  };
370
428
  }
@@ -8,6 +8,7 @@ export default {
8
8
  args: {
9
9
  backdropDismiss: true,
10
10
  componentId: 'modal-demo',
11
+ disableTopLayer: false,
11
12
  open: false,
12
13
  scrollable: true,
13
14
  size: 'md',
@@ -26,6 +27,7 @@ const BaseTemplate = (args) => html`
26
27
  component-id="${args.componentId}"
27
28
  size="${args.size}"
28
29
  ?backdrop-dismiss=${args.backdropDismiss}
30
+ ?disable-top-layer=${args.disableTopLayer}
29
31
  scrollable="${args.scrollable}"
30
32
  ?open=${args.open}
31
33
  key="${args.scrollable ? 'scrollable' : 'non-scrollable'}"
@@ -652,3 +654,78 @@ export const CustomBorders = CustomBordersTemplate.bind({});
652
654
  CustomBorders.args = {
653
655
  componentId: 'custom-borders-modal',
654
656
  };
657
+
658
+ // Demonstrates `disable-top-layer`: the modal opens as a non-modal dialog
659
+ // (dialog.show()) so an overlay appended to document.body can display above it.
660
+ // With the default top-layer behavior the same overlay would render behind the
661
+ // modal and be unreachable.
662
+ const DisableTopLayerTemplate = (args) => html`
663
+ <div style="padding: 1rem;">
664
+ <pds-button onClick="document.querySelector('#${args.componentId}').open = true">
665
+ Open Modal (disable-top-layer)
666
+ </pds-button>
667
+
668
+ <pds-modal
669
+ id="${args.componentId}"
670
+ component-id="${args.componentId}"
671
+ size="${args.size}"
672
+ ?backdrop-dismiss=${args.backdropDismiss}
673
+ ?disable-top-layer=${args.disableTopLayer}
674
+ ?open=${args.open}
675
+ >
676
+ <pds-modal-header>
677
+ <pds-box direction="column" fit padding="md">
678
+ <pds-box align-items="center" fit justify-content="space-between">
679
+ <pds-text tag="h2" size="h3">Modal Title</pds-text>
680
+ <pds-button
681
+ class="pds-modal__close"
682
+ variant="unstyled"
683
+ icon-only="true"
684
+ onclick="document.querySelector('#${args.componentId}').open = false"
685
+ aria-label="Close modal"
686
+ part="close-button"
687
+ >
688
+ <pds-icon slot="start" name="remove" aria-hidden="true"></pds-icon>
689
+ </pds-button>
690
+ </pds-box>
691
+ </pds-box>
692
+ </pds-modal-header>
693
+
694
+ <pds-modal-content>
695
+ <pds-box fit direction="column" padding-inline-start="md" padding-inline-end="md" gap="sm">
696
+ <p>
697
+ This modal uses <code>disable-top-layer</code>, so it renders in the normal stacking
698
+ context instead of the browser top layer.
699
+ </p>
700
+ <p>
701
+ An overlay appended to <code>document.body</code> with a higher <code>z-index</code> can
702
+ therefore display above it &mdash; useful for file pickers, rich-text editor menus, and
703
+ other body-mounted overlays that would otherwise be trapped behind a top-layer dialog.
704
+ </p>
705
+ <pds-button
706
+ variant="secondary"
707
+ onclick="(function(){var o=document.createElement('button');o.type='button';o.textContent='Overlay above the modal — focusable, keyboard-dismissable. Click or press Enter.';o.setAttribute('style','position:fixed;inset:auto 2rem 2rem auto;max-width:20rem;padding:1rem;border:0;text-align:start;border-radius:8px;background:#111;color:#fff;z-index:2147483647;cursor:pointer;box-shadow:0 8px 24px rgba(0,0,0,.4)');var modal=document.querySelector('#${args.componentId}');var remove=function(){o.remove()};o.onclick=remove;o.addEventListener('keydown',function(e){if(e.key==='Escape')remove()});if(modal)modal.addEventListener('pdsModalClose',remove,{once:true});document.body.appendChild(o);o.focus();})()"
708
+ >
709
+ Show overlay above modal
710
+ </pds-button>
711
+ </pds-box>
712
+ </pds-modal-content>
713
+
714
+ <pds-modal-footer>
715
+ <pds-box justify-content="end" fit padding="md" gap="sm">
716
+ <pds-button variant="secondary" onclick="document.querySelector('#${args.componentId}').open = false">
717
+ Close
718
+ </pds-button>
719
+ </pds-box>
720
+ </pds-modal-footer>
721
+ </pds-modal>
722
+ </div>
723
+ `;
724
+
725
+ export const DisableTopLayer = DisableTopLayerTemplate.bind({});
726
+ DisableTopLayer.args = {
727
+ componentId: 'disable-top-layer-modal',
728
+ disableTopLayer: true,
729
+ size: 'md',
730
+ open: false,
731
+ };
@@ -291,7 +291,7 @@
291
291
 
292
292
  .pds-multiselect__helper {
293
293
  color: var(--pine-color-text-message);
294
- font: var(--pine-typography-body-sm);
294
+ font: var(--pine-typography-body-sm-medium);
295
295
  margin-block-start: var(--pine-dimension-2xs);
296
296
  margin-inline-start: var(--pine-dimension-none);
297
297
  }
@@ -300,7 +300,7 @@
300
300
  align-items: flex-start;
301
301
  color: var(--pine-color-text-message-danger);
302
302
  display: flex;
303
- font: var(--pine-typography-body-sm);
303
+ font: var(--pine-typography-body-sm-medium);
304
304
  gap: var(--pine-dimension-2xs);
305
305
  margin-block-start: var(--pine-dimension-2xs);
306
306
  margin-inline-start: var(--pine-dimension-none);