@pure-ds/core 0.4.37 → 0.5.2

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 (146) hide show
  1. package/dist/types/pds.d.ts +34 -11
  2. package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts +9 -0
  3. package/dist/types/public/assets/auto-definer-XWHRBQPU.d.ts.map +1 -0
  4. package/dist/types/public/assets/chunk-746HIXIK.d.ts +52 -0
  5. package/dist/types/public/assets/chunk-746HIXIK.d.ts.map +1 -0
  6. package/dist/types/public/assets/chunk-APJV5T3J.d.ts +106 -0
  7. package/dist/types/public/assets/chunk-APJV5T3J.d.ts.map +1 -0
  8. package/dist/types/public/assets/chunk-BEPKFFM7.d.ts +398 -0
  9. package/dist/types/public/assets/chunk-BEPKFFM7.d.ts.map +1 -0
  10. package/dist/types/public/assets/chunk-ISS7UH5H.d.ts +2424 -0
  11. package/dist/types/public/assets/chunk-ISS7UH5H.d.ts.map +1 -0
  12. package/dist/types/public/assets/chunk-RUPLQUDG.d.ts +582 -0
  13. package/dist/types/public/assets/chunk-RUPLQUDG.d.ts.map +1 -0
  14. package/dist/types/public/assets/chunk-USML4NYF.d.ts +18 -0
  15. package/dist/types/public/assets/chunk-USML4NYF.d.ts.map +1 -0
  16. package/dist/types/public/assets/chunk-Z47A3HLT.d.ts +3 -0
  17. package/dist/types/public/assets/chunk-Z47A3HLT.d.ts.map +1 -0
  18. package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts +9 -0
  19. package/dist/types/public/assets/js/auto-definer-HZLD2XF4.d.ts.map +1 -0
  20. package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts +88 -0
  21. package/dist/types/public/assets/js/chunk-6A6DFAIG.d.ts.map +1 -0
  22. package/dist/types/public/assets/js/chunk-746HIXIK.d.ts +52 -0
  23. package/dist/types/public/assets/js/chunk-746HIXIK.d.ts.map +1 -0
  24. package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts +4 -0
  25. package/dist/types/public/assets/js/chunk-A3TZGIYX.d.ts.map +1 -0
  26. package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts +398 -0
  27. package/dist/types/public/assets/js/chunk-BEPKFFM7.d.ts.map +1 -0
  28. package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts +1695 -0
  29. package/dist/types/public/assets/js/chunk-OTTRJ5MB.d.ts.map +1 -0
  30. package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts +747 -0
  31. package/dist/types/public/assets/js/chunk-RBPKHG76.d.ts.map +1 -0
  32. package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts +582 -0
  33. package/dist/types/public/assets/js/chunk-RUPLQUDG.d.ts.map +1 -0
  34. package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts +68 -0
  35. package/dist/types/public/assets/js/chunk-SMD2R3CX.d.ts.map +1 -0
  36. package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts +15 -0
  37. package/dist/types/public/assets/js/chunk-Y73DA2D5.d.ts.map +1 -0
  38. package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts +9 -0
  39. package/dist/types/public/assets/js/chunks/auto-definer-X7MSXKTU.d.ts.map +1 -0
  40. package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts +485 -0
  41. package/dist/types/public/assets/js/chunks/chunk-7BDQH5CT.d.ts.map +1 -0
  42. package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts +3 -0
  43. package/dist/types/public/assets/js/chunks/chunk-MWB3S7NG.d.ts.map +1 -0
  44. package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts +5 -0
  45. package/dist/types/public/assets/js/chunks/chunk-WIMLORAU.d.ts.map +1 -0
  46. package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts +833 -0
  47. package/dist/types/public/assets/js/chunks/chunk-WN4Y2ELN.d.ts.map +1 -0
  48. package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts +1687 -0
  49. package/dist/types/public/assets/js/chunks/chunk-XQOUIBLO.d.ts.map +1 -0
  50. package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts +5 -0
  51. package/dist/types/public/assets/js/chunks/font-loader-VN5SRNOD.d.ts.map +1 -0
  52. package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts +38 -0
  53. package/dist/types/public/assets/js/chunks/pds-live-validation-BQPWN5JG.d.ts.map +1 -0
  54. package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts +4 -0
  55. package/dist/types/public/assets/js/common-WIAC4WAJ.d.ts.map +1 -0
  56. package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts +4 -0
  57. package/dist/types/public/assets/js/pds-config-WEBAXXSM.d.ts.map +1 -0
  58. package/dist/types/public/assets/js/pds-core/pds-generator.d.ts +700 -0
  59. package/dist/types/public/assets/js/pds-core/pds-generator.d.ts.map +1 -0
  60. package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts +27 -0
  61. package/dist/types/public/assets/js/pds-core/pds-utilities.d.ts.map +1 -0
  62. package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts +3 -0
  63. package/dist/types/public/assets/js/pds-enums-DCBZHS64.d.ts.map +1 -0
  64. package/dist/types/public/assets/js/pds-gen.d.ts +106 -0
  65. package/dist/types/public/assets/js/pds-gen.d.ts.map +1 -0
  66. package/dist/types/public/assets/js/pds-live.d.ts +11 -0
  67. package/dist/types/public/assets/js/pds-live.d.ts.map +1 -0
  68. package/dist/types/public/assets/js/pds-manager.d.ts +1047 -0
  69. package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -0
  70. package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts +9 -0
  71. package/dist/types/public/assets/js/pds-ontology-2DICJXHO.d.ts.map +1 -0
  72. package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts +70 -0
  73. package/dist/types/public/assets/js/pds-query-B54LBKKR.d.ts.map +1 -0
  74. package/dist/types/public/assets/js/pds.d.ts +1 -18
  75. package/dist/types/public/assets/js/pds.d.ts.map +1 -1
  76. package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts +9 -0
  77. package/dist/types/public/assets/pds-ontology-ZO6TJHO3.d.ts.map +1 -0
  78. package/dist/types/src/js/common/pds-core/pds-config.d.ts +757 -0
  79. package/dist/types/src/js/common/pds-core/pds-config.d.ts.map +1 -0
  80. package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts +28 -0
  81. package/dist/types/src/js/common/pds-core/pds-enhancers.d.ts.map +1 -0
  82. package/dist/types/src/js/common/pds-core/pds-enums.d.ts +87 -0
  83. package/dist/types/src/js/common/pds-core/pds-enums.d.ts.map +1 -0
  84. package/dist/types/src/js/common/pds-core/pds-generator.d.ts +700 -0
  85. package/dist/types/src/js/common/pds-core/pds-generator.d.ts.map +1 -0
  86. package/dist/types/src/js/common/pds-core/pds-ontology.d.ts +380 -0
  87. package/dist/types/src/js/common/pds-core/pds-ontology.d.ts.map +1 -0
  88. package/dist/types/src/js/common/pds-core/pds-paths.d.ts +37 -0
  89. package/dist/types/src/js/common/pds-core/pds-paths.d.ts.map +1 -0
  90. package/dist/types/src/js/common/pds-core/pds-query.d.ts +102 -0
  91. package/dist/types/src/js/common/pds-core/pds-query.d.ts.map +1 -0
  92. package/dist/types/src/js/common/pds-core/pds-registry.d.ts +35 -0
  93. package/dist/types/src/js/common/pds-core/pds-registry.d.ts.map +1 -0
  94. package/dist/types/src/js/common/pds-core/pds-utilities.d.ts +27 -0
  95. package/dist/types/src/js/common/pds-core/pds-utilities.d.ts.map +1 -0
  96. package/dist/types/src/js/pds-core/pds-generator.d.ts +38 -46
  97. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
  98. package/dist/types/src/js/pds-core/pds-live.d.ts +39 -0
  99. package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -0
  100. package/dist/types/src/js/pds-core/pds-runtime.d.ts +39 -0
  101. package/dist/types/src/js/pds-core/pds-runtime.d.ts.map +1 -0
  102. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts +60 -0
  103. package/dist/types/src/js/pds-core/pds-start-helpers.d.ts.map +1 -0
  104. package/dist/types/src/js/pds-core/pds-utilities.d.ts +27 -0
  105. package/dist/types/src/js/pds-core/pds-utilities.d.ts.map +1 -0
  106. package/dist/types/src/js/pds-gen.d.ts +48 -0
  107. package/dist/types/src/js/pds-gen.d.ts.map +1 -0
  108. package/dist/types/src/js/pds-live-runtime.d.ts +7 -0
  109. package/dist/types/src/js/pds-live-runtime.d.ts.map +1 -0
  110. package/dist/types/src/js/pds-live-validation.d.ts +44 -0
  111. package/dist/types/src/js/pds-live-validation.d.ts.map +1 -0
  112. package/dist/types/src/js/pds-live.d.ts +11 -0
  113. package/dist/types/src/js/pds-live.d.ts.map +1 -0
  114. package/dist/types/src/js/pds-manager.d.ts +2 -0
  115. package/dist/types/src/js/pds-manager.d.ts.map +1 -0
  116. package/dist/types/src/js/pds.d.ts +6 -33
  117. package/dist/types/src/js/pds.d.ts.map +1 -1
  118. package/package.json +11 -12
  119. package/packages/pds-cli/bin/{generate-css-data.mjs → generate-css-data.js} +563 -563
  120. package/packages/pds-cli/bin/{generate-manifest.mjs → generate-manifest.js} +352 -352
  121. package/packages/pds-cli/bin/{pds-build-icons.mjs → pds-build-icons.js} +152 -152
  122. package/packages/pds-cli/bin/{pds-dx.mjs → pds-dx.js} +114 -114
  123. package/packages/pds-cli/bin/{pds-init-config.mjs → pds-init-config.js} +34 -34
  124. package/packages/pds-cli/bin/{pds-setup-copilot.mjs → pds-setup-copilot.js} +106 -106
  125. package/packages/pds-cli/bin/{pds-static.mjs → pds-static.js} +597 -581
  126. package/packages/pds-cli/bin/{pds.mjs → pds.js} +127 -127
  127. package/packages/pds-cli/bin/postinstall.mjs +522 -563
  128. package/packages/pds-cli/bin/{sync-assets.mjs → sync-assets.js} +251 -251
  129. package/packages/pds-cli/lib/{asset-roots.mjs → asset-roots.js} +47 -47
  130. package/packages/pds-cli/lib/{fs-writer.mjs → fs-writer.js} +75 -75
  131. package/public/assets/js/app.js +95 -118
  132. package/public/assets/js/pds-manager.js +3251 -0
  133. package/public/assets/js/pds.js +10 -3201
  134. package/readme.md +2014 -2016
  135. package/src/js/pds-core/pds-enhancers.js +518 -518
  136. package/src/js/pds-core/pds-enums.js +86 -86
  137. package/src/js/pds-core/pds-generator.js +255 -185
  138. package/src/js/pds-core/pds-live.js +434 -0
  139. package/src/js/pds-core/pds-paths.js +109 -109
  140. package/src/js/pds-core/pds-registry.js +79 -79
  141. package/src/js/pds-core/pds-runtime.js +184 -0
  142. package/src/js/pds-core/pds-start-helpers.js +405 -0
  143. package/src/js/pds.d.ts +34 -11
  144. package/src/js/pds.js +43 -1182
  145. package/getting-started.md +0 -626
  146. package/src/js/pds-core/pds.d.ts +0 -129
@@ -1,518 +1,518 @@
1
- /**
2
- * PDS Enhancers - Single Source of Truth
3
- *
4
- * This file defines all progressive enhancements for the Pure Design System.
5
- * Each enhancer has:
6
- * - selector: CSS selector to target elements
7
- * - description: Human-readable explanation
8
- * - demoHtml: Example usage markup
9
- * - run: Enhancement function (added at the end)
10
- */
11
-
12
- // ============================================================================
13
- // ENHANCEMENT METADATA DEFINITIONS
14
- // ============================================================================
15
-
16
- const enhancerDefinitions = [
17
- {
18
- selector: ".accordion",
19
- description:
20
- "Ensures only one <details> element can be open at a time within the accordion.",
21
- demoHtml: `
22
- <div class="accordion">
23
- <details>
24
- <summary>Section 1</summary>
25
- <p>Content for section 1</p>
26
- </details>
27
- <details>
28
- <summary>Section 2</summary>
29
- <p>Content for section 2</p>
30
- </details>
31
- <details>
32
- <summary>Section 3</summary>
33
- <p>Content for section 3</p>
34
- </details>
35
- </div>
36
- `.trim(),
37
- },
38
- {
39
- selector: "nav[data-dropdown]",
40
- description:
41
- "Enhances a nav element with data-dropdown to function as a dropdown menu.",
42
- demoHtml: `
43
- <nav data-dropdown>
44
- <button class="btn-primary">Menu</button>
45
- <menu>
46
- <li><a href="#">Item 1</a></li>
47
- <li><a href="#">Item 2</a></li>
48
- </menu>
49
- </nav>
50
- `.trim(),
51
- },
52
- {
53
- selector: "label[data-toggle]",
54
- description: "Creates a toggle switch element from a checkbox.",
55
- demoHtml: `
56
- <label data-toggle>
57
- <input type="checkbox">
58
- <span data-label>Enable notifications</span>
59
- </label>
60
- `.trim(),
61
- },
62
- {
63
- selector: 'input[type="range"]',
64
- description: "Enhances range inputs with an attached <output>.",
65
- demoHtml: `
66
- <label class="range-output">
67
- <span data-label>Volume</span>
68
- <input type="range" min="0" max="100" value="40">
69
- </label>
70
- `.trim(),
71
- },
72
- {
73
- selector: "form[data-required]",
74
- description:
75
- "Enhances required form fields using an asterisk in the label.",
76
- demoHtml: `
77
- <form data-required action="#" method="post">
78
- <label>
79
- <span>Field Label</span>
80
- <input type="text" required>
81
- </label>
82
- <nav class="form-actions">
83
- <button type="submit" class="btn-primary">Submit</button>
84
- </nav>
85
- </form>
86
- `.trim(),
87
- },
88
- {
89
- selector: "fieldset[role=group][data-open]",
90
- description:
91
- "Enhances a checkbox/radio group to be open (have a way to add and remove items).",
92
- demoHtml: `
93
- <fieldset role="group" data-open>
94
- <label>
95
- <span data-label>Test</span>
96
- <input value="lala" name="test1" type="radio" />
97
- </label>
98
- </fieldset>
99
- `.trim(),
100
- },
101
- {
102
- selector: "button, a[class*='btn-']",
103
- description:
104
- "Automatically manages spinner icon for buttons with .btn-working class",
105
- demoHtml: `
106
- <button class="btn-primary btn-working">
107
- <span>Saving</span>
108
- </button>
109
- `.trim(),
110
- },
111
- ];
112
-
113
- // ============================================================================
114
- // ENHANCEMENT RUNTIME FUNCTIONS
115
- // ============================================================================
116
-
117
- function enhanceAccordion(elem) {
118
- if (elem.dataset.enhancedAccordion) return;
119
- elem.dataset.enhancedAccordion = "true";
120
-
121
- elem.addEventListener("toggle", (event) => {
122
- // Only handle toggle events from direct child details elements
123
- // to avoid closing parent details when nested accordions are used
124
- if (event.target.open && event.target.parentElement === elem) {
125
- elem.querySelectorAll(":scope > details[open]").forEach((details) => {
126
- if (details !== event.target) {
127
- details.open = false;
128
- }
129
- });
130
- }
131
- }, true);
132
- }
133
-
134
- function enhanceDropdown(elem) {
135
- if (elem.dataset.enhancedDropdown) return;
136
- elem.dataset.enhancedDropdown = "true";
137
- const menu = elem.querySelector("menu");
138
- if (!menu) return;
139
-
140
- const trigger =
141
- elem.querySelector("[data-dropdown-toggle]") ||
142
- elem.querySelector("button");
143
-
144
- if (trigger && !trigger.hasAttribute("type")) {
145
- trigger.setAttribute("type", "button");
146
- }
147
-
148
- if (!menu.id) {
149
- menu.id = `dropdown-${Math.random().toString(36).slice(2, 9)}`;
150
- }
151
-
152
- menu.setAttribute("role", menu.getAttribute("role") || "menu");
153
- if (!menu.hasAttribute("aria-hidden")) {
154
- menu.setAttribute("aria-hidden", "true");
155
- }
156
-
157
- if (trigger) {
158
- trigger.setAttribute("aria-haspopup", "true");
159
- trigger.setAttribute("aria-controls", menu.id);
160
- trigger.setAttribute("aria-expanded", "false");
161
- }
162
-
163
- const resolveDirection = () => {
164
- const mode = (elem.getAttribute("data-mode") || "auto").toLowerCase();
165
- if (mode === "up" || mode === "down") return mode;
166
- const rect = elem.getBoundingClientRect();
167
- const spaceBelow = Math.max(0, window.innerHeight - rect.bottom);
168
- const spaceAbove = Math.max(0, rect.top);
169
- return spaceAbove > spaceBelow ? "up" : "down";
170
- };
171
-
172
- const openMenu = () => {
173
- elem.dataset.dropdownDirection = resolveDirection();
174
- menu.setAttribute("aria-hidden", "false");
175
- trigger?.setAttribute("aria-expanded", "true");
176
- };
177
-
178
- const closeMenu = () => {
179
- menu.setAttribute("aria-hidden", "true");
180
- trigger?.setAttribute("aria-expanded", "false");
181
- };
182
-
183
- const toggleMenu = () => {
184
- if (menu.getAttribute("aria-hidden") === "false") {
185
- closeMenu();
186
- } else {
187
- openMenu();
188
- }
189
- };
190
-
191
- trigger?.addEventListener("click", (event) => {
192
- event.preventDefault();
193
- event.stopPropagation();
194
- toggleMenu();
195
- });
196
-
197
- document.addEventListener("click", (event) => {
198
- if (!elem.contains(event.target)) {
199
- closeMenu();
200
- }
201
- });
202
-
203
- elem.addEventListener("keydown", (event) => {
204
- if (event.key === "Escape") {
205
- closeMenu();
206
- trigger?.focus();
207
- }
208
- });
209
-
210
- elem.addEventListener("focusout", (event) => {
211
- if (!event.relatedTarget || !elem.contains(event.relatedTarget)) {
212
- closeMenu();
213
- }
214
- });
215
- }
216
-
217
- function enhanceToggle(elem) {
218
- if (elem.dataset.enhancedToggle) return;
219
- elem.dataset.enhancedToggle = "true";
220
- const checkbox = elem.querySelector('input[type="checkbox"]');
221
- if (!checkbox) return;
222
-
223
- if (!elem.hasAttribute("tabindex")) {
224
- elem.setAttribute("tabindex", "0");
225
- }
226
-
227
- elem.setAttribute("role", "switch");
228
- elem.setAttribute("aria-checked", checkbox.checked ? "true" : "false");
229
-
230
- const toggleSwitch = document.createElement("span");
231
- toggleSwitch.className = "toggle-switch";
232
- toggleSwitch.setAttribute("role", "presentation");
233
- toggleSwitch.setAttribute("aria-hidden", "true");
234
- const knob = document.createElement("span");
235
- knob.className = "toggle-knob";
236
- toggleSwitch.appendChild(knob);
237
- elem.insertBefore(toggleSwitch, checkbox.nextSibling);
238
-
239
- const updateAria = () => {
240
- elem.setAttribute("aria-checked", checkbox.checked ? "true" : "false");
241
- };
242
-
243
- const toggle = () => {
244
- if (checkbox.disabled) return;
245
- checkbox.checked = !checkbox.checked;
246
- updateAria();
247
- checkbox.dispatchEvent(new Event("change", { bubbles: true }));
248
- };
249
-
250
- elem.addEventListener("click", (event) => {
251
- event.preventDefault();
252
- toggle();
253
- });
254
-
255
- elem.addEventListener("keydown", (event) => {
256
- if (event.key === " " || event.key === "Enter") {
257
- event.preventDefault();
258
- toggle();
259
- }
260
- });
261
-
262
- checkbox.addEventListener("change", updateAria);
263
- }
264
-
265
- function enhanceRange(elem) {
266
- if (elem.dataset.enhancedRange) return;
267
-
268
- const label = elem.closest("label");
269
- const hasRangeOutputClass = label?.classList.contains("range-output");
270
-
271
- const inputId =
272
- elem.id || `range-${Math.random().toString(36).substring(2, 11)}`;
273
- const outputId = `${inputId}-output`;
274
- elem.id = inputId;
275
-
276
- if (hasRangeOutputClass) {
277
- const labelSpan = label.querySelector("span");
278
- if (labelSpan && !labelSpan.classList.contains("range-output-wrapper")) {
279
- const wrapper = document.createElement("span");
280
- wrapper.className = "range-output-wrapper";
281
- wrapper.style.display = "flex";
282
- wrapper.style.justifyContent = "space-between";
283
- wrapper.style.alignItems = "center";
284
-
285
- const textSpan = document.createElement("span");
286
- textSpan.textContent = labelSpan.textContent;
287
- wrapper.appendChild(textSpan);
288
-
289
- const output = document.createElement("output");
290
- output.id = outputId;
291
- output.setAttribute("for", inputId);
292
- output.style.color =
293
- "var(--surface-text-secondary, var(--color-text-secondary))";
294
- output.style.fontSize = "0.875rem";
295
- output.textContent = elem.value;
296
- wrapper.appendChild(output);
297
-
298
- labelSpan.textContent = "";
299
- labelSpan.appendChild(wrapper);
300
-
301
- const updateOutput = () => {
302
- output.textContent = elem.value;
303
- };
304
- elem.addEventListener("input", updateOutput);
305
- }
306
- } else {
307
- let container = elem.closest(".range-container");
308
- if (!container) {
309
- container = document.createElement("div");
310
- container.className = "range-container";
311
- elem.parentNode?.insertBefore(container, elem);
312
- container.appendChild(elem);
313
- }
314
- container.style.position = "relative";
315
-
316
- const bubble = document.createElement("output");
317
- bubble.id = outputId;
318
- bubble.setAttribute("for", inputId);
319
- bubble.className = "range-bubble";
320
- bubble.setAttribute("aria-live", "polite");
321
- container.appendChild(bubble);
322
-
323
- const updateBubble = () => {
324
- const min = parseFloat(elem.min) || 0;
325
- const max = parseFloat(elem.max) || 100;
326
- const value = parseFloat(elem.value);
327
- const pct = (value - min) / (max - min);
328
- bubble.style.left = `calc(${pct * 100}% )`;
329
- bubble.textContent = String(value);
330
- };
331
- const show = () => bubble.classList.add("visible");
332
- const hide = () => bubble.classList.remove("visible");
333
- elem.addEventListener("input", updateBubble);
334
- elem.addEventListener("pointerdown", show);
335
- elem.addEventListener("pointerup", hide);
336
- elem.addEventListener("pointerleave", hide);
337
- elem.addEventListener("focus", show);
338
- elem.addEventListener("blur", hide);
339
- updateBubble();
340
- }
341
-
342
- elem.dataset.enhancedRange = "1";
343
- }
344
-
345
- function enhanceRequired(elem) {
346
-
347
- if (elem.dataset.enhancedRequired) return;
348
- elem.dataset.enhancedRequired = "true";
349
-
350
- const enhanceRequiredField = (input) => {
351
-
352
- const label = input.closest("label");
353
- if (!label) return;
354
- if (label.querySelector(".required-asterisk")) return;
355
-
356
- const asterisk = document.createElement("span");
357
- asterisk.classList.add("required-asterisk");
358
- asterisk.textContent = "*";
359
- asterisk.style.marginLeft = "4px";
360
- label.querySelector("span").appendChild(asterisk);
361
-
362
- const form = input.closest("form");
363
- if (form && !form.querySelector(".required-legend")) {
364
- const legend = document.createElement("small");
365
- legend.classList.add("required-legend");
366
- legend.textContent = "* Required fields";
367
- form.insertBefore(
368
- legend,
369
- form.querySelector(".form-actions") || form.lastElementChild
370
- );
371
- }
372
- }
373
-
374
- elem.querySelectorAll("[required]").forEach((input) => {
375
- enhanceRequiredField(input);
376
- });
377
-
378
- }
379
-
380
- function enhanceOpenGroup(elem) {
381
- if (elem.dataset.enhancedOpenGroup) return;
382
- elem.dataset.enhancedOpenGroup = "true";
383
-
384
- elem.classList.add("flex", "flex-wrap", "buttons");
385
-
386
- const addInput = document.createElement("input");
387
- addInput.type = "text";
388
- addInput.placeholder = "Add item...";
389
- addInput.classList.add("input-text", "input-sm");
390
- addInput.style.width = "auto";
391
- const firstInput = elem.querySelector(
392
- 'input[type="radio"], input[type="checkbox"]'
393
- );
394
-
395
- elem.appendChild(addInput);
396
- addInput.addEventListener("keydown", (event) => {
397
- if (event.key === "Enter" || event.key === "Tab") {
398
- const value = addInput.value.trim();
399
- if (value) {
400
- event.preventDefault();
401
-
402
- const type = firstInput.type === "radio" ? "radio" : "checkbox";
403
- const id = `open-group-${Math.random()
404
- .toString(36)
405
- .substring(2, 11)}`;
406
- const label = document.createElement("label");
407
-
408
- const span = document.createElement("span");
409
- span.setAttribute("data-label", "");
410
- span.textContent = value;
411
-
412
- const input = document.createElement("input");
413
- input.type = type;
414
- input.name =
415
- firstInput.name || elem.getAttribute("data-name") || "open-group";
416
- input.value = value;
417
- input.id = id;
418
-
419
- label.appendChild(span);
420
- label.appendChild(input);
421
-
422
- elem.insertBefore(label, addInput);
423
- addInput.value = "";
424
- }
425
- } else if (event.key === "Backspace" && addInput.value === "") {
426
- event.preventDefault();
427
- const labels = elem.querySelectorAll("label");
428
- if (labels.length > 0) {
429
- const lastLabel = labels[labels.length - 1];
430
- lastLabel.remove();
431
- }
432
- }
433
- });
434
- }
435
-
436
- function enhanceButtonWorking(elem) {
437
- if (elem.dataset.enhancedBtnWorking) return;
438
- elem.dataset.enhancedBtnWorking = "true";
439
-
440
- let originalIcon = null;
441
- let addedIcon = false;
442
-
443
- const observer = new MutationObserver((mutations) => {
444
- mutations.forEach((mutation) => {
445
- if (mutation.attributeName === "class") {
446
- const hasWorking = elem.classList.contains("btn-working");
447
- const icon = elem.querySelector("pds-icon");
448
-
449
- if (hasWorking) {
450
- if (icon) {
451
- if (!originalIcon) {
452
- originalIcon = icon.getAttribute("icon");
453
- }
454
- icon.setAttribute("icon", "circle-notch");
455
- } else {
456
- const newIcon = document.createElement("pds-icon");
457
- newIcon.setAttribute("icon", "circle-notch");
458
- newIcon.setAttribute("size", "sm");
459
- elem.insertBefore(newIcon, elem.firstChild);
460
- addedIcon = true;
461
- }
462
- } else if (mutation.oldValue?.includes("btn-working")) {
463
- if (icon) {
464
- if (addedIcon) {
465
- icon.remove();
466
- addedIcon = false;
467
- } else if (originalIcon) {
468
- icon.setAttribute("icon", originalIcon);
469
- originalIcon = null;
470
- }
471
- }
472
- }
473
- }
474
- });
475
- });
476
-
477
- observer.observe(elem, {
478
- attributes: true,
479
- attributeFilter: ["class"],
480
- attributeOldValue: true,
481
- });
482
- }
483
-
484
- // ============================================================================
485
- // EXPORTS
486
- // ============================================================================
487
-
488
- // Map selectors to their run functions
489
- const enhancerRunners = new Map([
490
- [".accordion", enhanceAccordion],
491
- ["nav[data-dropdown]", enhanceDropdown],
492
- ["label[data-toggle]", enhanceToggle],
493
- ['input[type="range"]', enhanceRange],
494
- ["form[data-required]", enhanceRequired],
495
- ["fieldset[role=group][data-open]", enhanceOpenGroup],
496
- ["button, a[class*='btn-']", enhanceButtonWorking],
497
- ]);
498
-
499
- /**
500
- * Complete enhancers with runtime functions.
501
- * Used by PDS.enhancer() and AutoDefiner at runtime.
502
- *
503
- * This is the canonical runtime array of enhancer objects.
504
- */
505
- export const defaultPDSEnhancers = enhancerDefinitions.map((meta) => ({
506
- ...meta,
507
- run: enhancerRunners.get(meta.selector) || (() => {}),
508
- }));
509
-
510
- /**
511
- * Metadata-only export for build tools and documentation.
512
- * This is semantically identical to enhancerDefinitions but exported
513
- * for tooling that wants to explicitly access metadata without run functions.
514
- *
515
- * Build tools can safely import defaultPDSEnhancers too - Node.js won't
516
- * execute browser-only DOM code in the run functions.
517
- */
518
- export const defaultPDSEnhancerMetadata = enhancerDefinitions;
1
+ /**
2
+ * PDS Enhancers - Single Source of Truth
3
+ *
4
+ * This file defines all progressive enhancements for the Pure Design System.
5
+ * Each enhancer has:
6
+ * - selector: CSS selector to target elements
7
+ * - description: Human-readable explanation
8
+ * - demoHtml: Example usage markup
9
+ * - run: Enhancement function (added at the end)
10
+ */
11
+
12
+ // ============================================================================
13
+ // ENHANCEMENT METADATA DEFINITIONS
14
+ // ============================================================================
15
+
16
+ const enhancerDefinitions = [
17
+ {
18
+ selector: ".accordion",
19
+ description:
20
+ "Ensures only one <details> element can be open at a time within the accordion.",
21
+ demoHtml: `
22
+ <div class="accordion">
23
+ <details>
24
+ <summary>Section 1</summary>
25
+ <p>Content for section 1</p>
26
+ </details>
27
+ <details>
28
+ <summary>Section 2</summary>
29
+ <p>Content for section 2</p>
30
+ </details>
31
+ <details>
32
+ <summary>Section 3</summary>
33
+ <p>Content for section 3</p>
34
+ </details>
35
+ </div>
36
+ `.trim(),
37
+ },
38
+ {
39
+ selector: "nav[data-dropdown]",
40
+ description:
41
+ "Enhances a nav element with data-dropdown to function as a dropdown menu.",
42
+ demoHtml: `
43
+ <nav data-dropdown>
44
+ <button class="btn-primary">Menu</button>
45
+ <menu>
46
+ <li><a href="#">Item 1</a></li>
47
+ <li><a href="#">Item 2</a></li>
48
+ </menu>
49
+ </nav>
50
+ `.trim(),
51
+ },
52
+ {
53
+ selector: "label[data-toggle]",
54
+ description: "Creates a toggle switch element from a checkbox.",
55
+ demoHtml: `
56
+ <label data-toggle>
57
+ <input type="checkbox">
58
+ <span data-label>Enable notifications</span>
59
+ </label>
60
+ `.trim(),
61
+ },
62
+ {
63
+ selector: 'input[type="range"]',
64
+ description: "Enhances range inputs with an attached <output>.",
65
+ demoHtml: `
66
+ <label class="range-output">
67
+ <span data-label>Volume</span>
68
+ <input type="range" min="0" max="100" value="40">
69
+ </label>
70
+ `.trim(),
71
+ },
72
+ {
73
+ selector: "form[data-required]",
74
+ description:
75
+ "Enhances required form fields using an asterisk in the label.",
76
+ demoHtml: `
77
+ <form data-required action="#" method="post">
78
+ <label>
79
+ <span>Field Label</span>
80
+ <input type="text" required>
81
+ </label>
82
+ <nav class="form-actions">
83
+ <button type="submit" class="btn-primary">Submit</button>
84
+ </nav>
85
+ </form>
86
+ `.trim(),
87
+ },
88
+ {
89
+ selector: "fieldset[role=group][data-open]",
90
+ description:
91
+ "Enhances a checkbox/radio group to be open (have a way to add and remove items).",
92
+ demoHtml: `
93
+ <fieldset role="group" data-open>
94
+ <label>
95
+ <span data-label>Test</span>
96
+ <input value="lala" name="test1" type="radio" />
97
+ </label>
98
+ </fieldset>
99
+ `.trim(),
100
+ },
101
+ {
102
+ selector: "button, a[class*='btn-']",
103
+ description:
104
+ "Automatically manages spinner icon for buttons with .btn-working class",
105
+ demoHtml: `
106
+ <button class="btn-primary btn-working">
107
+ <span>Saving</span>
108
+ </button>
109
+ `.trim(),
110
+ },
111
+ ];
112
+
113
+ // ============================================================================
114
+ // ENHANCEMENT RUNTIME FUNCTIONS
115
+ // ============================================================================
116
+
117
+ function enhanceAccordion(elem) {
118
+ if (elem.dataset.enhancedAccordion) return;
119
+ elem.dataset.enhancedAccordion = "true";
120
+
121
+ elem.addEventListener("toggle", (event) => {
122
+ // Only handle toggle events from direct child details elements
123
+ // to avoid closing parent details when nested accordions are used
124
+ if (event.target.open && event.target.parentElement === elem) {
125
+ elem.querySelectorAll(":scope > details[open]").forEach((details) => {
126
+ if (details !== event.target) {
127
+ details.open = false;
128
+ }
129
+ });
130
+ }
131
+ }, true);
132
+ }
133
+
134
+ function enhanceDropdown(elem) {
135
+ if (elem.dataset.enhancedDropdown) return;
136
+ elem.dataset.enhancedDropdown = "true";
137
+ const menu = elem.querySelector("menu");
138
+ if (!menu) return;
139
+
140
+ const trigger =
141
+ elem.querySelector("[data-dropdown-toggle]") ||
142
+ elem.querySelector("button");
143
+
144
+ if (trigger && !trigger.hasAttribute("type")) {
145
+ trigger.setAttribute("type", "button");
146
+ }
147
+
148
+ if (!menu.id) {
149
+ menu.id = `dropdown-${Math.random().toString(36).slice(2, 9)}`;
150
+ }
151
+
152
+ menu.setAttribute("role", menu.getAttribute("role") || "menu");
153
+ if (!menu.hasAttribute("aria-hidden")) {
154
+ menu.setAttribute("aria-hidden", "true");
155
+ }
156
+
157
+ if (trigger) {
158
+ trigger.setAttribute("aria-haspopup", "true");
159
+ trigger.setAttribute("aria-controls", menu.id);
160
+ trigger.setAttribute("aria-expanded", "false");
161
+ }
162
+
163
+ const resolveDirection = () => {
164
+ const mode = (elem.getAttribute("data-mode") || "auto").toLowerCase();
165
+ if (mode === "up" || mode === "down") return mode;
166
+ const rect = elem.getBoundingClientRect();
167
+ const spaceBelow = Math.max(0, window.innerHeight - rect.bottom);
168
+ const spaceAbove = Math.max(0, rect.top);
169
+ return spaceAbove > spaceBelow ? "up" : "down";
170
+ };
171
+
172
+ const openMenu = () => {
173
+ elem.dataset.dropdownDirection = resolveDirection();
174
+ menu.setAttribute("aria-hidden", "false");
175
+ trigger?.setAttribute("aria-expanded", "true");
176
+ };
177
+
178
+ const closeMenu = () => {
179
+ menu.setAttribute("aria-hidden", "true");
180
+ trigger?.setAttribute("aria-expanded", "false");
181
+ };
182
+
183
+ const toggleMenu = () => {
184
+ if (menu.getAttribute("aria-hidden") === "false") {
185
+ closeMenu();
186
+ } else {
187
+ openMenu();
188
+ }
189
+ };
190
+
191
+ trigger?.addEventListener("click", (event) => {
192
+ event.preventDefault();
193
+ event.stopPropagation();
194
+ toggleMenu();
195
+ });
196
+
197
+ document.addEventListener("click", (event) => {
198
+ if (!elem.contains(event.target)) {
199
+ closeMenu();
200
+ }
201
+ });
202
+
203
+ elem.addEventListener("keydown", (event) => {
204
+ if (event.key === "Escape") {
205
+ closeMenu();
206
+ trigger?.focus();
207
+ }
208
+ });
209
+
210
+ elem.addEventListener("focusout", (event) => {
211
+ if (!event.relatedTarget || !elem.contains(event.relatedTarget)) {
212
+ closeMenu();
213
+ }
214
+ });
215
+ }
216
+
217
+ function enhanceToggle(elem) {
218
+ if (elem.dataset.enhancedToggle) return;
219
+ elem.dataset.enhancedToggle = "true";
220
+ const checkbox = elem.querySelector('input[type="checkbox"]');
221
+ if (!checkbox) return;
222
+
223
+ if (!elem.hasAttribute("tabindex")) {
224
+ elem.setAttribute("tabindex", "0");
225
+ }
226
+
227
+ elem.setAttribute("role", "switch");
228
+ elem.setAttribute("aria-checked", checkbox.checked ? "true" : "false");
229
+
230
+ const toggleSwitch = document.createElement("span");
231
+ toggleSwitch.className = "toggle-switch";
232
+ toggleSwitch.setAttribute("role", "presentation");
233
+ toggleSwitch.setAttribute("aria-hidden", "true");
234
+ const knob = document.createElement("span");
235
+ knob.className = "toggle-knob";
236
+ toggleSwitch.appendChild(knob);
237
+ elem.insertBefore(toggleSwitch, checkbox.nextSibling);
238
+
239
+ const updateAria = () => {
240
+ elem.setAttribute("aria-checked", checkbox.checked ? "true" : "false");
241
+ };
242
+
243
+ const toggle = () => {
244
+ if (checkbox.disabled) return;
245
+ checkbox.checked = !checkbox.checked;
246
+ updateAria();
247
+ checkbox.dispatchEvent(new Event("change", { bubbles: true }));
248
+ };
249
+
250
+ elem.addEventListener("click", (event) => {
251
+ event.preventDefault();
252
+ toggle();
253
+ });
254
+
255
+ elem.addEventListener("keydown", (event) => {
256
+ if (event.key === " " || event.key === "Enter") {
257
+ event.preventDefault();
258
+ toggle();
259
+ }
260
+ });
261
+
262
+ checkbox.addEventListener("change", updateAria);
263
+ }
264
+
265
+ function enhanceRange(elem) {
266
+ if (elem.dataset.enhancedRange) return;
267
+
268
+ const label = elem.closest("label");
269
+ const hasRangeOutputClass = label?.classList.contains("range-output");
270
+
271
+ const inputId =
272
+ elem.id || `range-${Math.random().toString(36).substring(2, 11)}`;
273
+ const outputId = `${inputId}-output`;
274
+ elem.id = inputId;
275
+
276
+ if (hasRangeOutputClass) {
277
+ const labelSpan = label.querySelector("span");
278
+ if (labelSpan && !labelSpan.classList.contains("range-output-wrapper")) {
279
+ const wrapper = document.createElement("span");
280
+ wrapper.className = "range-output-wrapper";
281
+ wrapper.style.display = "flex";
282
+ wrapper.style.justifyContent = "space-between";
283
+ wrapper.style.alignItems = "center";
284
+
285
+ const textSpan = document.createElement("span");
286
+ textSpan.textContent = labelSpan.textContent;
287
+ wrapper.appendChild(textSpan);
288
+
289
+ const output = document.createElement("output");
290
+ output.id = outputId;
291
+ output.setAttribute("for", inputId);
292
+ output.style.color =
293
+ "var(--surface-text-secondary, var(--color-text-secondary))";
294
+ output.style.fontSize = "0.875rem";
295
+ output.textContent = elem.value;
296
+ wrapper.appendChild(output);
297
+
298
+ labelSpan.textContent = "";
299
+ labelSpan.appendChild(wrapper);
300
+
301
+ const updateOutput = () => {
302
+ output.textContent = elem.value;
303
+ };
304
+ elem.addEventListener("input", updateOutput);
305
+ }
306
+ } else {
307
+ let container = elem.closest(".range-container");
308
+ if (!container) {
309
+ container = document.createElement("div");
310
+ container.className = "range-container";
311
+ elem.parentNode?.insertBefore(container, elem);
312
+ container.appendChild(elem);
313
+ }
314
+ container.style.position = "relative";
315
+
316
+ const bubble = document.createElement("output");
317
+ bubble.id = outputId;
318
+ bubble.setAttribute("for", inputId);
319
+ bubble.className = "range-bubble";
320
+ bubble.setAttribute("aria-live", "polite");
321
+ container.appendChild(bubble);
322
+
323
+ const updateBubble = () => {
324
+ const min = parseFloat(elem.min) || 0;
325
+ const max = parseFloat(elem.max) || 100;
326
+ const value = parseFloat(elem.value);
327
+ const pct = (value - min) / (max - min);
328
+ bubble.style.left = `calc(${pct * 100}% )`;
329
+ bubble.textContent = String(value);
330
+ };
331
+ const show = () => bubble.classList.add("visible");
332
+ const hide = () => bubble.classList.remove("visible");
333
+ elem.addEventListener("input", updateBubble);
334
+ elem.addEventListener("pointerdown", show);
335
+ elem.addEventListener("pointerup", hide);
336
+ elem.addEventListener("pointerleave", hide);
337
+ elem.addEventListener("focus", show);
338
+ elem.addEventListener("blur", hide);
339
+ updateBubble();
340
+ }
341
+
342
+ elem.dataset.enhancedRange = "1";
343
+ }
344
+
345
+ function enhanceRequired(elem) {
346
+
347
+ if (elem.dataset.enhancedRequired) return;
348
+ elem.dataset.enhancedRequired = "true";
349
+
350
+ const enhanceRequiredField = (input) => {
351
+
352
+ const label = input.closest("label");
353
+ if (!label) return;
354
+ if (label.querySelector(".required-asterisk")) return;
355
+
356
+ const asterisk = document.createElement("span");
357
+ asterisk.classList.add("required-asterisk");
358
+ asterisk.textContent = "*";
359
+ asterisk.style.marginLeft = "4px";
360
+ label.querySelector("span").appendChild(asterisk);
361
+
362
+ const form = input.closest("form");
363
+ if (form && !form.querySelector(".required-legend")) {
364
+ const legend = document.createElement("small");
365
+ legend.classList.add("required-legend");
366
+ legend.textContent = "* Required fields";
367
+ form.insertBefore(
368
+ legend,
369
+ form.querySelector(".form-actions") || form.lastElementChild
370
+ );
371
+ }
372
+ }
373
+
374
+ elem.querySelectorAll("[required]").forEach((input) => {
375
+ enhanceRequiredField(input);
376
+ });
377
+
378
+ }
379
+
380
+ function enhanceOpenGroup(elem) {
381
+ if (elem.dataset.enhancedOpenGroup) return;
382
+ elem.dataset.enhancedOpenGroup = "true";
383
+
384
+ elem.classList.add("flex", "flex-wrap", "buttons");
385
+
386
+ const addInput = document.createElement("input");
387
+ addInput.type = "text";
388
+ addInput.placeholder = "Add item...";
389
+ addInput.classList.add("input-text", "input-sm");
390
+ addInput.style.width = "auto";
391
+ const firstInput = elem.querySelector(
392
+ 'input[type="radio"], input[type="checkbox"]'
393
+ );
394
+
395
+ elem.appendChild(addInput);
396
+ addInput.addEventListener("keydown", (event) => {
397
+ if (event.key === "Enter" || event.key === "Tab") {
398
+ const value = addInput.value.trim();
399
+ if (value) {
400
+ event.preventDefault();
401
+
402
+ const type = firstInput.type === "radio" ? "radio" : "checkbox";
403
+ const id = `open-group-${Math.random()
404
+ .toString(36)
405
+ .substring(2, 11)}`;
406
+ const label = document.createElement("label");
407
+
408
+ const span = document.createElement("span");
409
+ span.setAttribute("data-label", "");
410
+ span.textContent = value;
411
+
412
+ const input = document.createElement("input");
413
+ input.type = type;
414
+ input.name =
415
+ firstInput.name || elem.getAttribute("data-name") || "open-group";
416
+ input.value = value;
417
+ input.id = id;
418
+
419
+ label.appendChild(span);
420
+ label.appendChild(input);
421
+
422
+ elem.insertBefore(label, addInput);
423
+ addInput.value = "";
424
+ }
425
+ } else if (event.key === "Backspace" && addInput.value === "") {
426
+ event.preventDefault();
427
+ const labels = elem.querySelectorAll("label");
428
+ if (labels.length > 0) {
429
+ const lastLabel = labels[labels.length - 1];
430
+ lastLabel.remove();
431
+ }
432
+ }
433
+ });
434
+ }
435
+
436
+ function enhanceButtonWorking(elem) {
437
+ if (elem.dataset.enhancedBtnWorking) return;
438
+ elem.dataset.enhancedBtnWorking = "true";
439
+
440
+ let originalIcon = null;
441
+ let addedIcon = false;
442
+
443
+ const observer = new MutationObserver((mutations) => {
444
+ mutations.forEach((mutation) => {
445
+ if (mutation.attributeName === "class") {
446
+ const hasWorking = elem.classList.contains("btn-working");
447
+ const icon = elem.querySelector("pds-icon");
448
+
449
+ if (hasWorking) {
450
+ if (icon) {
451
+ if (!originalIcon) {
452
+ originalIcon = icon.getAttribute("icon");
453
+ }
454
+ icon.setAttribute("icon", "circle-notch");
455
+ } else {
456
+ const newIcon = document.createElement("pds-icon");
457
+ newIcon.setAttribute("icon", "circle-notch");
458
+ newIcon.setAttribute("size", "sm");
459
+ elem.insertBefore(newIcon, elem.firstChild);
460
+ addedIcon = true;
461
+ }
462
+ } else if (mutation.oldValue?.includes("btn-working")) {
463
+ if (icon) {
464
+ if (addedIcon) {
465
+ icon.remove();
466
+ addedIcon = false;
467
+ } else if (originalIcon) {
468
+ icon.setAttribute("icon", originalIcon);
469
+ originalIcon = null;
470
+ }
471
+ }
472
+ }
473
+ }
474
+ });
475
+ });
476
+
477
+ observer.observe(elem, {
478
+ attributes: true,
479
+ attributeFilter: ["class"],
480
+ attributeOldValue: true,
481
+ });
482
+ }
483
+
484
+ // ============================================================================
485
+ // EXPORTS
486
+ // ============================================================================
487
+
488
+ // Map selectors to their run functions
489
+ const enhancerRunners = new Map([
490
+ [".accordion", enhanceAccordion],
491
+ ["nav[data-dropdown]", enhanceDropdown],
492
+ ["label[data-toggle]", enhanceToggle],
493
+ ['input[type="range"]', enhanceRange],
494
+ ["form[data-required]", enhanceRequired],
495
+ ["fieldset[role=group][data-open]", enhanceOpenGroup],
496
+ ["button, a[class*='btn-']", enhanceButtonWorking],
497
+ ]);
498
+
499
+ /**
500
+ * Complete enhancers with runtime functions.
501
+ * Used by PDS.enhancer() and AutoDefiner at runtime.
502
+ *
503
+ * This is the canonical runtime array of enhancer objects.
504
+ */
505
+ export const defaultPDSEnhancers = enhancerDefinitions.map((meta) => ({
506
+ ...meta,
507
+ run: enhancerRunners.get(meta.selector) || (() => {}),
508
+ }));
509
+
510
+ /**
511
+ * Metadata-only export for build tools and documentation.
512
+ * This is semantically identical to enhancerDefinitions but exported
513
+ * for tooling that wants to explicitly access metadata without run functions.
514
+ *
515
+ * Build tools can safely import defaultPDSEnhancers too - Node.js won't
516
+ * execute browser-only DOM code in the run functions.
517
+ */
518
+ export const defaultPDSEnhancerMetadata = enhancerDefinitions;