@helixui/library 3.3.1-next.115 → 3.3.1-next.118

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 (129) hide show
  1. package/custom-elements.json +445 -276
  2. package/dist/components/hx-accordion/hx-accordion-item.d.ts +35 -0
  3. package/dist/components/hx-accordion/hx-accordion-item.d.ts.map +1 -1
  4. package/dist/components/hx-checkbox/hx-checkbox.d.ts +153 -1
  5. package/dist/components/hx-checkbox/hx-checkbox.d.ts.map +1 -1
  6. package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
  7. package/dist/components/hx-checkbox/index.js +1 -1
  8. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts +151 -2
  9. package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts.map +1 -1
  10. package/dist/components/hx-checkbox-group/index.js +1 -1
  11. package/dist/components/hx-color-picker/hx-color-picker.d.ts +163 -1
  12. package/dist/components/hx-color-picker/hx-color-picker.d.ts.map +1 -1
  13. package/dist/components/hx-color-picker/hx-color-picker.styles.d.ts.map +1 -1
  14. package/dist/components/hx-color-picker/index.js +1 -1
  15. package/dist/components/hx-combobox/hx-combobox.d.ts +311 -2
  16. package/dist/components/hx-combobox/hx-combobox.d.ts.map +1 -1
  17. package/dist/components/hx-combobox/index.js +1 -1
  18. package/dist/components/hx-date-picker/hx-date-picker.d.ts +182 -56
  19. package/dist/components/hx-date-picker/hx-date-picker.d.ts.map +1 -1
  20. package/dist/components/hx-date-picker/hx-date-picker.styles.d.ts.map +1 -1
  21. package/dist/components/hx-date-picker/index.js +1 -1
  22. package/dist/components/hx-dialog/hx-dialog.d.ts +240 -0
  23. package/dist/components/hx-dialog/hx-dialog.d.ts.map +1 -1
  24. package/dist/components/hx-dialog/index.js +1 -1
  25. package/dist/components/hx-dropdown/hx-dropdown.d.ts +80 -0
  26. package/dist/components/hx-dropdown/hx-dropdown.d.ts.map +1 -1
  27. package/dist/components/hx-dropdown/index.js +1 -1
  28. package/dist/components/hx-field/hx-field.d.ts +109 -0
  29. package/dist/components/hx-field/hx-field.d.ts.map +1 -1
  30. package/dist/components/hx-field/index.js +1 -1
  31. package/dist/components/hx-popover/hx-popover.d.ts +91 -0
  32. package/dist/components/hx-popover/hx-popover.d.ts.map +1 -1
  33. package/dist/components/hx-popover/index.js +1 -1
  34. package/dist/components/hx-radio-group/hx-radio-group.d.ts +152 -1
  35. package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
  36. package/dist/components/hx-radio-group/hx-radio.d.ts +14 -0
  37. package/dist/components/hx-radio-group/hx-radio.d.ts.map +1 -1
  38. package/dist/components/hx-radio-group/index.js +1 -1
  39. package/dist/components/hx-select/hx-select.d.ts +303 -2
  40. package/dist/components/hx-select/hx-select.d.ts.map +1 -1
  41. package/dist/components/hx-select/hx-select.styles.d.ts.map +1 -1
  42. package/dist/components/hx-select/index.js +1 -1
  43. package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
  44. package/dist/components/hx-side-nav/index.js +1 -1
  45. package/dist/components/hx-switch/hx-switch.d.ts +78 -1
  46. package/dist/components/hx-switch/hx-switch.d.ts.map +1 -1
  47. package/dist/components/hx-switch/hx-switch.styles.d.ts.map +1 -1
  48. package/dist/components/hx-switch/index.js +1 -1
  49. package/dist/components/hx-toggle-button/hx-toggle-button.d.ts +110 -0
  50. package/dist/components/hx-toggle-button/hx-toggle-button.d.ts.map +1 -1
  51. package/dist/components/hx-toggle-button/hx-toggle-button.styles.d.ts.map +1 -1
  52. package/dist/components/hx-toggle-button/index.js +1 -1
  53. package/dist/components/hx-tooltip/hx-tooltip.d.ts +52 -0
  54. package/dist/components/hx-tooltip/hx-tooltip.d.ts.map +1 -1
  55. package/dist/components/hx-tooltip/index.js +1 -1
  56. package/dist/css/helix-all.css +98 -1
  57. package/dist/css/helix-forms.css +98 -1
  58. package/dist/css/hx-checkbox.css +18 -0
  59. package/dist/css/hx-color-picker.css +25 -0
  60. package/dist/css/hx-date-picker.css +2 -1
  61. package/dist/css/hx-select.css +19 -0
  62. package/dist/css/hx-switch.css +17 -0
  63. package/dist/css/hx-toggle-button.css +17 -0
  64. package/dist/css/index.css +1 -1
  65. package/dist/css/manifest.json +2 -1
  66. package/dist/index.js +15 -15
  67. package/dist/shared/aria-flatten-DY6v2vah.js +22 -0
  68. package/dist/shared/aria-flatten-DY6v2vah.js.map +1 -0
  69. package/dist/shared/aria-idref-Q0yiSR3p.js +104 -0
  70. package/dist/shared/aria-idref-Q0yiSR3p.js.map +1 -0
  71. package/dist/shared/hx-accordion-ZVzgDzTG.js.map +1 -1
  72. package/dist/shared/hx-checkbox-BdgoUeWi.js +696 -0
  73. package/dist/shared/hx-checkbox-BdgoUeWi.js.map +1 -0
  74. package/dist/shared/hx-checkbox-group-LWezHrvS.js +496 -0
  75. package/dist/shared/hx-checkbox-group-LWezHrvS.js.map +1 -0
  76. package/dist/shared/hx-color-picker-DVhZl88b.js +1221 -0
  77. package/dist/shared/hx-color-picker-DVhZl88b.js.map +1 -0
  78. package/dist/shared/hx-combobox-DvlezcDV.js +1359 -0
  79. package/dist/shared/hx-combobox-DvlezcDV.js.map +1 -0
  80. package/dist/shared/{hx-date-picker-2iRG1p74.js → hx-date-picker-N-0aG5XL.js} +542 -206
  81. package/dist/shared/hx-date-picker-N-0aG5XL.js.map +1 -0
  82. package/dist/shared/hx-dialog-DzB7VytW.js +717 -0
  83. package/dist/shared/hx-dialog-DzB7VytW.js.map +1 -0
  84. package/dist/shared/{hx-dropdown-LyaRc8Rf.js → hx-dropdown-DJWlF94E.js} +130 -77
  85. package/dist/shared/hx-dropdown-DJWlF94E.js.map +1 -0
  86. package/dist/shared/{hx-field-B3Qo8OLS.js → hx-field-zw0U1KVi.js} +99 -38
  87. package/dist/shared/hx-field-zw0U1KVi.js.map +1 -0
  88. package/dist/shared/{hx-nav-item-xqRPOCWX.js → hx-nav-item-CODtUlew.js} +13 -9
  89. package/dist/shared/{hx-nav-item-xqRPOCWX.js.map → hx-nav-item-CODtUlew.js.map} +1 -1
  90. package/dist/shared/{hx-popover-B-FP3-wW.js → hx-popover-CHxWY_cd.js} +123 -66
  91. package/dist/shared/hx-popover-CHxWY_cd.js.map +1 -0
  92. package/dist/shared/hx-radio-CeGzARNk.js +822 -0
  93. package/dist/shared/hx-radio-CeGzARNk.js.map +1 -0
  94. package/dist/shared/hx-select-DrcS-YRJ.js +1089 -0
  95. package/dist/shared/hx-select-DrcS-YRJ.js.map +1 -0
  96. package/dist/shared/hx-switch-BX_8uNUs.js +540 -0
  97. package/dist/shared/hx-switch-BX_8uNUs.js.map +1 -0
  98. package/dist/shared/{hx-toggle-button-iLiYrMbD.js → hx-toggle-button-Dcz9IlUm.js} +226 -65
  99. package/dist/shared/hx-toggle-button-Dcz9IlUm.js.map +1 -0
  100. package/dist/shared/{hx-tooltip-nYOv9OLu.js → hx-tooltip-DVqtKPCD.js} +68 -46
  101. package/dist/shared/hx-tooltip-DVqtKPCD.js.map +1 -0
  102. package/dist/utils/aria-flatten.d.ts +56 -0
  103. package/dist/utils/aria-flatten.d.ts.map +1 -0
  104. package/dist/utils/aria-idref.d.ts +127 -0
  105. package/dist/utils/aria-idref.d.ts.map +1 -0
  106. package/figma-inventory.json +64 -1
  107. package/package.json +2 -2
  108. package/dist/shared/hx-checkbox-D7xma9YH.js +0 -524
  109. package/dist/shared/hx-checkbox-D7xma9YH.js.map +0 -1
  110. package/dist/shared/hx-checkbox-group-C9n315Ju.js +0 -323
  111. package/dist/shared/hx-checkbox-group-C9n315Ju.js.map +0 -1
  112. package/dist/shared/hx-color-picker-uRc865FJ.js +0 -882
  113. package/dist/shared/hx-color-picker-uRc865FJ.js.map +0 -1
  114. package/dist/shared/hx-combobox-DDzqNKEW.js +0 -924
  115. package/dist/shared/hx-combobox-DDzqNKEW.js.map +0 -1
  116. package/dist/shared/hx-date-picker-2iRG1p74.js.map +0 -1
  117. package/dist/shared/hx-dialog-DRN_1-Y-.js +0 -514
  118. package/dist/shared/hx-dialog-DRN_1-Y-.js.map +0 -1
  119. package/dist/shared/hx-dropdown-LyaRc8Rf.js.map +0 -1
  120. package/dist/shared/hx-field-B3Qo8OLS.js.map +0 -1
  121. package/dist/shared/hx-popover-B-FP3-wW.js.map +0 -1
  122. package/dist/shared/hx-radio-CJvNU2yP.js +0 -621
  123. package/dist/shared/hx-radio-CJvNU2yP.js.map +0 -1
  124. package/dist/shared/hx-select-C8fEHQhC.js +0 -807
  125. package/dist/shared/hx-select-C8fEHQhC.js.map +0 -1
  126. package/dist/shared/hx-switch-BrZFaRue.js +0 -420
  127. package/dist/shared/hx-switch-BrZFaRue.js.map +0 -1
  128. package/dist/shared/hx-toggle-button-iLiYrMbD.js.map +0 -1
  129. package/dist/shared/hx-tooltip-nYOv9OLu.js.map +0 -1
@@ -0,0 +1,822 @@
1
+ import { css as O, nothing as R, html as y } from "lit";
2
+ import { property as d, state as S, customElement as A } from "lit/decorators.js";
3
+ import { classMap as B } from "lit/directives/class-map.js";
4
+ import { F as G } from "./FormMixin-B8PXk5RQ.js";
5
+ import { b as L } from "./forced-colors-CTEDFRGa.js";
6
+ import { s as V, i as j, r as T } from "./aria-idref-Q0yiSR3p.js";
7
+ import { H as $ } from "./helix-element-BNEYeiys.js";
8
+ import { c as z } from "./id-counter-DuX8vsui.js";
9
+ const N = O`
10
+ :host {
11
+ display: block;
12
+ }
13
+
14
+ :host([disabled]) {
15
+ opacity: var(--hx-opacity-disabled, 0.5);
16
+ pointer-events: none;
17
+ }
18
+
19
+ * {
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ /* ─── Fieldset ─── */
24
+
25
+ .fieldset {
26
+ border: none;
27
+ margin: 0;
28
+ padding: 0;
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: var(--hx-space-2, 0.5rem);
32
+ font-family: var(--hx-radio-group-font-family, var(--hx-font-family-sans, sans-serif));
33
+ }
34
+
35
+ /* ─── Legend ─── */
36
+
37
+ .fieldset__legend {
38
+ display: flex;
39
+ align-items: baseline;
40
+ gap: var(--hx-space-1, 0.25rem);
41
+ font-size: var(--hx-font-size-sm, 0.875rem);
42
+ font-weight: var(--hx-font-weight-medium, 500);
43
+ color: var(--hx-radio-group-label-color, var(--hx-color-text-strong, #202b39));
44
+ line-height: var(--hx-line-height-normal, 1.5);
45
+ padding: 0;
46
+ margin-bottom: var(--hx-space-1, 0.25rem);
47
+ }
48
+
49
+ .fieldset__required-marker {
50
+ color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));
51
+ font-weight: var(--hx-font-weight-bold, 700);
52
+ }
53
+
54
+ /* ─── Group Container ─── */
55
+
56
+ .fieldset__group {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: var(--hx-radio-group-gap, var(--hx-space-3, 0.75rem));
60
+ }
61
+
62
+ :host([orientation='horizontal']) .fieldset__group {
63
+ flex-direction: row;
64
+ flex-wrap: wrap;
65
+ }
66
+
67
+ /* ─── Error State ─── */
68
+
69
+ .fieldset--error .fieldset__legend {
70
+ color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));
71
+ }
72
+
73
+ /* ─── Help Text & Error Messages ─── */
74
+
75
+ .fieldset__help-text {
76
+ font-size: var(--hx-font-size-xs, 0.75rem);
77
+ color: var(--hx-radio-group-help-text-color, var(--hx-color-text-muted, #66787b));
78
+ line-height: var(--hx-line-height-normal, 1.5);
79
+ }
80
+
81
+ .fieldset__error {
82
+ font-size: var(--hx-font-size-xs, 0.75rem);
83
+ color: var(--hx-radio-group-error-color, var(--hx-color-error-text, #c92a2a));
84
+ line-height: var(--hx-line-height-normal, 1.5);
85
+ }
86
+
87
+ /* ─── High Contrast Mode (forced-colors) ─── */
88
+
89
+ @media (forced-colors: active) {
90
+ .fieldset {
91
+ border: none;
92
+ }
93
+
94
+ .fieldset__legend {
95
+ color: CanvasText;
96
+ }
97
+
98
+ .fieldset--error .fieldset__legend {
99
+ color: LinkText;
100
+ }
101
+
102
+ :host([disabled]) {
103
+ opacity: 1;
104
+ }
105
+
106
+ :host([disabled]) .fieldset__legend {
107
+ color: GrayText;
108
+ }
109
+
110
+ .fieldset__help-text {
111
+ color: GrayText;
112
+ }
113
+
114
+ .fieldset__error {
115
+ color: LinkText;
116
+ }
117
+ }
118
+ `;
119
+ var K = Object.defineProperty, P = Object.getOwnPropertyDescriptor, n = (e, t, i, r) => {
120
+ for (var s = r > 1 ? void 0 : r ? P(t, i) : t, o = e.length - 1, a; o >= 0; o--)
121
+ (a = e[o]) && (s = (r ? a(t, i, s) : a(s)) || s);
122
+ return r && s && K(t, i, s), s;
123
+ };
124
+ const U = z("hx-radio-group");
125
+ function H(e) {
126
+ const t = e.querySelector("slot");
127
+ if (t) {
128
+ const i = t.assignedNodes({ flatten: !0 });
129
+ if (i.length > 0)
130
+ return i.map((r) => r.textContent ?? "").join("").trim();
131
+ }
132
+ return (e.textContent ?? "").trim();
133
+ }
134
+ let l = class extends G($) {
135
+ constructor() {
136
+ super(...arguments), this.value = "", this.name = "", this.label = "", this.required = !1, this.disabled = !1, this.error = "", this.helpText = "", this.orientation = "vertical", this._hasErrorSlot = !1, this._hasHelpSlot = !1, this._supportsIdrefRefs = !0, this._groupId = U(), this._helpTextId = `${this._groupId}-help`, this._errorId = `${this._groupId}-error`, this._helpSlotTextObserver = null, this._errorSlotTextObserver = null, this._ariaMirror = null, this._announcedError = "", this._suppressedChildren = /* @__PURE__ */ new WeakSet(), this._previousRadios = [], this._lastWrittenLabelledBy = null, this._lastWrittenDescribedBy = null, this._consumerLabelledBy = null, this._consumerDescribedBy = null, this._cachedRadios = null, this._individualDisabledStates = /* @__PURE__ */ new WeakMap(), this._handleRadioSelect = (e) => {
137
+ if (!(e instanceof CustomEvent)) return;
138
+ e.stopPropagation();
139
+ const t = e.detail.value;
140
+ t !== this.value && (this.value = t, this._handleInteractionInput(), this.dispatchEvent(
141
+ new CustomEvent("hx-change", {
142
+ bubbles: !0,
143
+ composed: !0,
144
+ detail: { value: this.value, checked: !0 }
145
+ })
146
+ ));
147
+ }, this._handleKeydown = (e) => {
148
+ var u, p, c, b;
149
+ const t = this._getEnabledRadios();
150
+ if (t.length === 0 || ![
151
+ "ArrowUp",
152
+ "ArrowDown",
153
+ "ArrowLeft",
154
+ "ArrowRight",
155
+ " ",
156
+ "Home",
157
+ "End"
158
+ ].includes(e.key))
159
+ return;
160
+ if (e.preventDefault(), e.key === " ") {
161
+ const h = (p = (u = e.target) == null ? void 0 : u.closest) == null ? void 0 : p.call(u, "hx-radio");
162
+ h && !h.disabled && h.dispatchEvent(
163
+ new CustomEvent("hx-radio-select", {
164
+ bubbles: !0,
165
+ composed: !0,
166
+ detail: { value: h.value }
167
+ })
168
+ );
169
+ return;
170
+ }
171
+ const r = (b = (c = e.target) == null ? void 0 : c.closest) == null ? void 0 : b.call(c, "hx-radio"), s = r ? t.indexOf(r) : t.findIndex((h) => h.checked);
172
+ let o;
173
+ e.key === "Home" ? o = 0 : e.key === "End" ? o = t.length - 1 : e.key === "ArrowDown" || e.key === "ArrowRight" ? o = s === -1 ? 0 : (s + 1) % t.length : o = s <= 0 ? t.length - 1 : s - 1;
174
+ const a = t[o];
175
+ a && (a.focus(), a.dispatchEvent(
176
+ new CustomEvent("hx-radio-select", {
177
+ bubbles: !0,
178
+ composed: !0,
179
+ detail: { value: a.value }
180
+ })
181
+ ));
182
+ }, this._childDisabledObserver = null;
183
+ }
184
+ /**
185
+ * Queries the rendered group container element within the shadow root.
186
+ * @internal
187
+ */
188
+ get _groupEl() {
189
+ var e;
190
+ return ((e = this.renderRoot) == null ? void 0 : e.querySelector(".fieldset__group")) ?? null;
191
+ }
192
+ // ─── Slot Handlers ───
193
+ /**
194
+ * Handles slotchange events on the error slot to detect assigned content.
195
+ * @internal
196
+ */
197
+ _handleErrorSlotChange(e) {
198
+ e.target instanceof HTMLSlotElement && (this._hasErrorSlot = e.target.assignedNodes({ flatten: !0 }).length > 0, this._installErrorSlotTextObserver(e.target), this._syncHostAriaSemantics());
199
+ }
200
+ /**
201
+ * Handles slotchange events on the help-text slot to detect assigned content.
202
+ * Codex aria-group-2 finding: slot-only help text was not contributing to
203
+ * `aria-describedby` because the wrapper was conditionally rendered on the
204
+ * `helpText` property alone.
205
+ * @internal
206
+ */
207
+ _handleHelpSlotChange(e) {
208
+ e.target instanceof HTMLSlotElement && (this._hasHelpSlot = e.target.assignedNodes({ flatten: !0 }).length > 0, this._installHelpSlotTextObserver(e.target), this._syncHostAriaSemantics());
209
+ }
210
+ /**
211
+ * (Re-)installs the mutation observer over the current set of assigned
212
+ * help-text-slot nodes. Codex round-23 P2 (Finding C).
213
+ * @internal
214
+ */
215
+ _installHelpSlotTextObserver(e) {
216
+ var i;
217
+ if ((i = this._helpSlotTextObserver) == null || i.disconnect(), !e) {
218
+ this._helpSlotTextObserver = null;
219
+ return;
220
+ }
221
+ const t = new MutationObserver(() => {
222
+ this._syncHostAriaSemantics();
223
+ });
224
+ e.assignedNodes().forEach((r) => {
225
+ t.observe(r, {
226
+ characterData: !0,
227
+ childList: !0,
228
+ subtree: !0
229
+ });
230
+ }), this._helpSlotTextObserver = t;
231
+ }
232
+ /**
233
+ * (Re-)installs the mutation observer over the current set of assigned
234
+ * error-slot nodes. Codex round-23 P2 (Finding C).
235
+ * @internal
236
+ */
237
+ _installErrorSlotTextObserver(e) {
238
+ var i;
239
+ if ((i = this._errorSlotTextObserver) == null || i.disconnect(), !e) {
240
+ this._errorSlotTextObserver = null;
241
+ return;
242
+ }
243
+ const t = new MutationObserver(() => {
244
+ this._syncHostAriaSemantics();
245
+ });
246
+ e.assignedNodes().forEach((r) => {
247
+ t.observe(r, {
248
+ characterData: !0,
249
+ childList: !0,
250
+ subtree: !0
251
+ });
252
+ }), this._errorSlotTextObserver = t;
253
+ }
254
+ // ─── Lifecycle ───
255
+ connectedCallback() {
256
+ super.connectedCallback(), this._supportsIdrefRefs = V(this._internals), this.addEventListener("hx-radio-select", this._handleRadioSelect), this.addEventListener("keydown", this._handleKeydown), this._syncHostAriaSemantics(), this._ariaMirror = j(this, () => {
257
+ this._syncHostAriaSemantics();
258
+ });
259
+ const e = this._getRadios();
260
+ e.length > 0 && (e.forEach((t) => {
261
+ t._groupedSuppress = !0, this._suppressedChildren.add(t);
262
+ }), this._previousRadios = e);
263
+ }
264
+ disconnectedCallback() {
265
+ var e, t, i, r;
266
+ super.disconnectedCallback(), this.removeEventListener("hx-radio-select", this._handleRadioSelect), this.removeEventListener("keydown", this._handleKeydown), (e = this._ariaMirror) == null || e.disconnect(), this._ariaMirror = null, (t = this._childDisabledObserver) == null || t.disconnect(), this._childDisabledObserver = null, (i = this._helpSlotTextObserver) == null || i.disconnect(), this._helpSlotTextObserver = null, (r = this._errorSlotTextObserver) == null || r.disconnect(), this._errorSlotTextObserver = null, this._previousRadios.forEach((s) => {
267
+ this._suppressedChildren.has(s) && (s._groupedSuppress = !1, this._suppressedChildren.delete(s));
268
+ }), this._previousRadios = [];
269
+ }
270
+ updated(e) {
271
+ super.updated(e), e.has("value") && (this._internals.setFormValue(this.value || null), this._syncRadios()), e.has("disabled") && this._syncRadios(), this._syncHostAriaSemantics(), e.has("error") && (e.get("error") && this.error ? (this._announcedError = "", requestAnimationFrame(() => {
272
+ this._announcedError = this.error;
273
+ })) : this._announcedError = this.error);
274
+ }
275
+ /**
276
+ * Mirrors radiogroup semantics onto the host via ElementInternals so that
277
+ * consumer-supplied `aria-label`, `aria-labelledby`, and `aria-describedby`
278
+ * on `<hx-radio-group>` reach the announced control. The codex aria-group-2
279
+ * finding identified that the inner `<fieldset>` was the announced node and
280
+ * the host's external IDREF tokens could not cross the shadow boundary.
281
+ * @internal
282
+ */
283
+ _syncHostAriaSemantics() {
284
+ var E, w, k, C;
285
+ const e = this._internals;
286
+ e.role = "radiogroup", e.ariaRequired = this.required ? "true" : "false", e.ariaInvalid = e.validity.valid ? "false" : "true", e.ariaDisabled = this.disabled ? "true" : "false", e.ariaOrientation = this.orientation === "horizontal" ? "horizontal" : "vertical";
287
+ const t = ((E = this.getAttribute("aria-label")) == null ? void 0 : E.trim()) || "", i = (w = this.shadowRoot) == null ? void 0 : w.getElementById(`${this._groupId}-legend`), r = (k = this.shadowRoot) == null ? void 0 : k.getElementById(this._helpTextId), s = (C = this.shadowRoot) == null ? void 0 : C.getElementById(this._errorId), o = this.getAttribute("aria-labelledby");
288
+ o !== this._lastWrittenLabelledBy && (this._consumerLabelledBy = o);
289
+ const a = this.getAttribute("aria-describedby");
290
+ a !== this._lastWrittenDescribedBy && (this._consumerDescribedBy = a);
291
+ const u = this._consumerLabelledBy, p = this._consumerDescribedBy, c = T(this, u), b = c.length > 0;
292
+ t ? e.ariaLabel = t : b ? e.ariaLabel = null : e.ariaLabel = this.label || null, c.length === 0 && !t && this.label && i && c.push(i);
293
+ const h = T(this, p), v = !!(this.error || this._hasErrorSlot);
294
+ if (r && !v && (this.helpText || this._hasHelpSlot) && h.push(r), s && v && h.push(s), this._supportsIdrefRefs) {
295
+ const g = e;
296
+ g.ariaLabelledByElements = c.length > 0 ? c : null, g.ariaDescribedByElements = h.length > 0 ? h : null, e.ariaDescription = null;
297
+ } else {
298
+ const g = new Set(((u == null ? void 0 : u.split(/\s+/)) ?? []).filter(Boolean)), M = new Set(((p == null ? void 0 : p.split(/\s+/)) ?? []).filter(Boolean)), x = b ? [...g].filter(Boolean).join(" ") : "", I = this.getAttribute("aria-labelledby");
299
+ x ? (I !== x && this.setAttribute("aria-labelledby", x), this._lastWrittenLabelledBy = x) : I !== null && (this.removeAttribute("aria-labelledby"), this._lastWrittenLabelledBy = null);
300
+ const m = [...M].filter(Boolean).join(" ") || "", D = this.getAttribute("aria-describedby");
301
+ m ? (D !== m && this.setAttribute("aria-describedby", m), this._lastWrittenDescribedBy = m) : D !== null && this._lastWrittenDescribedBy !== null && (this.removeAttribute("aria-describedby"), this._lastWrittenDescribedBy = null);
302
+ const F = r && !v && (this.helpText || this._hasHelpSlot) ? H(r) : "", q = s && v ? H(s) : "", W = [F, q].filter(Boolean).join(" ");
303
+ e.ariaDescription = W || null;
304
+ }
305
+ }
306
+ firstUpdated(e) {
307
+ super.firstUpdated(e), this._syncRadios(), this._previousRadios = this._getRadios(), this._installChildDisabledObservers(), !this.label && this.getAttribute("aria-label");
308
+ }
309
+ /**
310
+ * Returns all child hx-radio elements, using the cache when available.
311
+ * @internal
312
+ */
313
+ _getRadios() {
314
+ return this._cachedRadios || (this._cachedRadios = Array.from(this.querySelectorAll("hx-radio"))), this._cachedRadios;
315
+ }
316
+ /**
317
+ * Returns only the child hx-radio elements that are not disabled.
318
+ * @internal
319
+ */
320
+ _getEnabledRadios() {
321
+ return this._getRadios().filter((e) => !e.disabled && !this.disabled);
322
+ }
323
+ /**
324
+ * Synchronizes checked state, disabled state, and roving tabindex across all child radios.
325
+ * @internal
326
+ */
327
+ _syncRadios() {
328
+ const e = this._getRadios(), t = this._getEnabledRadios();
329
+ e.forEach((r) => {
330
+ r._groupedSuppress = !0, this._suppressedChildren.add(r);
331
+ const s = r.value === this.value && this.value !== "";
332
+ if (r.checked = s, this.disabled)
333
+ this._individualDisabledStates.has(r) || this._individualDisabledStates.set(r, r.disabled), r.disabled = !0;
334
+ else {
335
+ const o = this._individualDisabledStates.get(r);
336
+ o !== void 0 && (r.disabled = o, this._individualDisabledStates.delete(r));
337
+ }
338
+ });
339
+ const i = t.find((r) => r.checked);
340
+ if (e.forEach((r) => {
341
+ r.tabIndex = -1;
342
+ }), i)
343
+ i.tabIndex = 0;
344
+ else if (t.length > 0) {
345
+ const r = t[0];
346
+ r && (r.tabIndex = 0);
347
+ }
348
+ }
349
+ /**
350
+ * Handles slotchange events on the default slot. Refreshes the cached
351
+ * radio list, reconciles `value`/`setFormValue`/validity against the new
352
+ * children, then re-tunes the per-child disabled observer so in-place
353
+ * `radio.disabled = true` mutations trigger the same reconcile pass.
354
+ *
355
+ * Codex round-2 finding #3: previously this handler only invalidated the
356
+ * cache and re-synced child state, so removing the currently-selected
357
+ * radio (or disabling it, or adding a new `checked` radio) left
358
+ * `this.value` and the submitted form value pointing at stale state and
359
+ * `_updateValidity` was never re-run.
360
+ *
361
+ * Codex round-7 finding #6: an in-place `selectedRadio.disabled = true`
362
+ * never re-entered this reconciler (the radio remained slotted), so the
363
+ * group could keep submitting a disabled value and stay valid until some
364
+ * unrelated slot mutation kicked things over. The per-child observer
365
+ * installed at the end of this method re-runs the same reconcile logic on
366
+ * every `disabled` attribute mutation.
367
+ * @internal
368
+ */
369
+ _handleSlotChange() {
370
+ this._cachedRadios = null, this._reconcileChildren(), this._installChildDisabledObservers();
371
+ }
372
+ /**
373
+ * Re-runs the slotchange reconcile pass without touching the disabled
374
+ * observer wiring. Called from the slotchange handler and from the
375
+ * per-child disabled observer; factored out so both entry points share
376
+ * the same value/formValue/validity reconciliation path. Round-7 #6.
377
+ * @internal
378
+ */
379
+ _reconcileChildren() {
380
+ const e = this._previousRadios, t = this._getRadios().filter((s) => s.checked && !s.disabled), i = t.find((s) => s.value !== this.value) ?? t[0];
381
+ if (i && i.value !== this.value && (this.value = i.value), this._syncRadios(), this.disabled) {
382
+ const s = this._getRadios().map((o) => o.value);
383
+ this.value && !s.includes(this.value) && (this.value = ""), this._updateValidity();
384
+ } else {
385
+ const s = this._getRadios().find((a) => a.checked && !a.disabled), o = (s == null ? void 0 : s.value) ?? "";
386
+ o !== this.value && (this.value = o), this._internals.setFormValue(this.value || null), this._updateValidity();
387
+ }
388
+ const r = new Set(this._getRadios());
389
+ e.forEach((s) => {
390
+ !r.has(s) && this._suppressedChildren.has(s) && (s._groupedSuppress = !1, this._suppressedChildren.delete(s));
391
+ }), this._previousRadios = this._getRadios();
392
+ }
393
+ /**
394
+ * Installs (or re-installs) a single MutationObserver across the current
395
+ * set of slotted `<hx-radio>` children, listening for `disabled` attribute
396
+ * mutations. Round-7 finding #6: in-place `disabled = true` never reaches
397
+ * the slotchange-driven reconciler otherwise.
398
+ * @internal
399
+ */
400
+ _installChildDisabledObservers() {
401
+ var i;
402
+ (i = this._childDisabledObserver) == null || i.disconnect();
403
+ const e = this._getRadios();
404
+ if (e.length === 0) {
405
+ this._childDisabledObserver = null;
406
+ return;
407
+ }
408
+ const t = new MutationObserver((r) => {
409
+ r.some((s) => s.attributeName === "disabled") && this._reconcileChildren();
410
+ });
411
+ e.forEach((r) => {
412
+ t.observe(r, {
413
+ attributes: !0,
414
+ attributeFilter: ["disabled"]
415
+ });
416
+ }), this._childDisabledObserver = t;
417
+ }
418
+ // ─── Form Integration ───
419
+ /**
420
+ * Updates the ElementInternals validity state based on the required constraint and current value.
421
+ * @internal
422
+ */
423
+ _updateValidity() {
424
+ if (this.required && !this.value) {
425
+ const e = this._getRadios().find((t) => t.checked && !t.disabled) ?? this._getEnabledRadios()[0] ?? this._groupEl ?? void 0;
426
+ this._internals.setValidity(
427
+ { valueMissing: !0 },
428
+ this.error || "Please select an option.",
429
+ e
430
+ );
431
+ } else
432
+ this._internals.setValidity({});
433
+ this._syncHostAriaSemantics();
434
+ }
435
+ /** @internal */
436
+ _onFormReset() {
437
+ this.value = "", this._internals.setFormValue(null), this._syncRadios(), this._resetInteractionState();
438
+ }
439
+ /** @internal */
440
+ _onFormStateRestore(e, t) {
441
+ typeof e == "string" && (this.value = e);
442
+ }
443
+ /** @internal */
444
+ _onFormDisabled(e) {
445
+ this.disabled = e;
446
+ }
447
+ // ─── Render ───
448
+ render() {
449
+ const e = !!this.error || this._hasErrorSlot, t = !!this.helpText || this._hasHelpSlot, i = `${this._groupId}-legend`, r = {
450
+ fieldset: !0,
451
+ "fieldset--error": e,
452
+ "fieldset--disabled": this.disabled,
453
+ "fieldset--required": this.required
454
+ };
455
+ return y`
456
+ <fieldset
457
+ part="fieldset"
458
+ class=${B(r)}
459
+ role="presentation"
460
+ aria-orientation=${this.orientation === "horizontal" ? "horizontal" : R}
461
+ >
462
+ ${this.label ? y`
463
+ <legend part="legend" class="fieldset__legend" id=${i}>
464
+ ${this.label}
465
+ ${this.required ? y`<span class="fieldset__required-marker" aria-hidden="true">*</span>` : R}
466
+ </legend>
467
+ ` : R}
468
+
469
+ <div part="group" class="fieldset__group" role="none">
470
+ <slot @slotchange=${this._handleSlotChange}></slot>
471
+ </div>
472
+
473
+ <!--
474
+ Persistent error live region. role="alert" is set from first paint
475
+ so the WAI-ARIA contract for live updates is honoured: content
476
+ changes in place rather than the container being toggled.
477
+ -->
478
+ <div
479
+ part="error"
480
+ class="fieldset__error"
481
+ id=${this._errorId}
482
+ role="alert"
483
+ ?hidden=${!e}
484
+ >
485
+ <slot name="error" @slotchange=${this._handleErrorSlotChange}
486
+ >${this._announcedError}</slot
487
+ >
488
+ </div>
489
+
490
+ <!--
491
+ Persistent help-text container. Rendered whenever the property OR
492
+ the slot has content; hidden when an error is present so guidance
493
+ does not compete with validation feedback. Always in the shadow
494
+ tree so the host's aria-describedby chain is stable.
495
+ -->
496
+ <div
497
+ part="help-text"
498
+ class="fieldset__help-text"
499
+ id=${this._helpTextId}
500
+ ?hidden=${!t || e}
501
+ >
502
+ <slot name="help-text" @slotchange=${this._handleHelpSlotChange}>${this.helpText}</slot>
503
+ </div>
504
+ </fieldset>
505
+ `;
506
+ }
507
+ };
508
+ l.styles = [N, L];
509
+ l.formAssociated = !0;
510
+ n([
511
+ d({ type: String, reflect: !0 })
512
+ ], l.prototype, "value", 2);
513
+ n([
514
+ d({ type: String, reflect: !0 })
515
+ ], l.prototype, "name", 2);
516
+ n([
517
+ d({ type: String })
518
+ ], l.prototype, "label", 2);
519
+ n([
520
+ d({ type: Boolean, reflect: !0 })
521
+ ], l.prototype, "required", 2);
522
+ n([
523
+ d({ type: Boolean, reflect: !0 })
524
+ ], l.prototype, "disabled", 2);
525
+ n([
526
+ d({ type: String })
527
+ ], l.prototype, "error", 2);
528
+ n([
529
+ d({ type: String, attribute: "help-text" })
530
+ ], l.prototype, "helpText", 2);
531
+ n([
532
+ d({ type: String, reflect: !0 })
533
+ ], l.prototype, "orientation", 2);
534
+ n([
535
+ S()
536
+ ], l.prototype, "_hasErrorSlot", 2);
537
+ n([
538
+ S()
539
+ ], l.prototype, "_hasHelpSlot", 2);
540
+ n([
541
+ S()
542
+ ], l.prototype, "_supportsIdrefRefs", 2);
543
+ n([
544
+ S()
545
+ ], l.prototype, "_announcedError", 2);
546
+ l = n([
547
+ A("hx-radio-group")
548
+ ], l);
549
+ const J = O`
550
+ :host {
551
+ display: block;
552
+ }
553
+
554
+ :host([disabled]) {
555
+ opacity: var(--hx-opacity-disabled, 0.5);
556
+ pointer-events: none;
557
+ }
558
+
559
+ * {
560
+ box-sizing: border-box;
561
+ }
562
+
563
+ .radio {
564
+ display: inline-flex;
565
+ align-items: center;
566
+ gap: var(--hx-space-2, 0.5rem);
567
+ /* WCAG 2.5.5 (healthcare mandate): minimum 44px touch target height */
568
+ min-height: var(--hx-touch-target-min, 2.75rem);
569
+ cursor: pointer;
570
+ position: relative;
571
+ font-family: var(--hx-radio-font-family, var(--hx-font-family-sans, sans-serif));
572
+ }
573
+
574
+ .radio--disabled {
575
+ cursor: not-allowed;
576
+ }
577
+
578
+ /* ─── Hidden Native Input ─── */
579
+
580
+ .radio__input {
581
+ position: absolute;
582
+ width: var(--hx-space-px);
583
+ height: var(--hx-space-px);
584
+ padding: 0;
585
+ margin: calc(var(--hx-space-px) * -1);
586
+ overflow: hidden;
587
+ clip: rect(0, 0, 0, 0);
588
+ white-space: nowrap;
589
+ border: 0;
590
+ }
591
+
592
+ /* ─── Visual Radio Circle ─── */
593
+
594
+ .radio__control {
595
+ display: inline-flex;
596
+ align-items: center;
597
+ justify-content: center;
598
+ width: var(--hx-radio-size, var(--hx-size-5, 1.25rem));
599
+ height: var(--hx-radio-size, var(--hx-size-5, 1.25rem));
600
+ border: var(--hx-border-width-medium, 2px) solid
601
+ var(--hx-radio-border-color, var(--hx-color-border-strong, #66787b));
602
+ border-radius: var(--hx-border-radius-full, 9999px);
603
+ background-color: var(--hx-radio-bg, var(--hx-color-surface-default, #ffffff));
604
+ transition:
605
+ border-color var(--hx-transition-fast, 150ms ease),
606
+ background-color var(--hx-transition-fast, 150ms ease),
607
+ box-shadow var(--hx-transition-fast, 150ms ease);
608
+ flex-shrink: 0;
609
+ }
610
+
611
+ /* ─── Inner Dot ─── */
612
+
613
+ .radio__dot {
614
+ width: calc(var(--hx-radio-size, var(--hx-size-5, 1.25rem)) * 0.4);
615
+ height: calc(var(--hx-radio-size, var(--hx-size-5, 1.25rem)) * 0.4);
616
+ border-radius: var(--hx-border-radius-full, 9999px);
617
+ background-color: var(--hx-radio-dot-color, var(--hx-color-text-on-primary, #ffffff));
618
+ transform: scale(0);
619
+ transition: transform var(--hx-transition-fast, 150ms ease);
620
+ }
621
+
622
+ /* ─── Checked State ─── */
623
+
624
+ .radio--checked .radio__control {
625
+ border-color: var(--hx-radio-checked-border-color, var(--hx-color-primary-500, #429797));
626
+ background-color: var(--hx-radio-checked-bg, var(--hx-color-primary-500, #429797));
627
+ }
628
+
629
+ .radio--checked .radio__dot {
630
+ transform: scale(1);
631
+ }
632
+
633
+ /* ─── Focus State ─── */
634
+
635
+ :host(:focus-visible) .radio__control {
636
+ outline: var(--hx-focus-ring-width, 2px) solid
637
+ var(--hx-radio-focus-ring-color, var(--hx-focus-ring-color, #0f7078));
638
+ outline-offset: var(--hx-focus-ring-offset, 2px);
639
+ }
640
+
641
+ /* ─── Hover State ─── */
642
+
643
+ .radio:not(.radio--disabled):not(.radio--checked):hover .radio__control {
644
+ border-color: var(--hx-radio-hover-border-color, var(--hx-color-border-strong, #66787b));
645
+ }
646
+
647
+ /* ─── Label ─── */
648
+
649
+ .radio__label {
650
+ font-size: var(--hx-font-size-md, 1rem);
651
+ color: var(--hx-radio-label-color, var(--hx-color-text-strong, #202b39));
652
+ line-height: var(--hx-line-height-normal, 1.5);
653
+ user-select: none;
654
+ -webkit-user-select: none;
655
+ }
656
+
657
+ /* ─── Reduced Motion ─── */
658
+
659
+ @media (prefers-reduced-motion: reduce) {
660
+ .radio__control,
661
+ .radio__dot {
662
+ transition: none;
663
+ }
664
+ }
665
+
666
+ /* ─── High Contrast Mode (forced-colors) ─── */
667
+
668
+ @media (forced-colors: active) {
669
+ .radio__control {
670
+ forced-color-adjust: none;
671
+ background-color: ButtonFace;
672
+ border: 2px solid ButtonText;
673
+ }
674
+
675
+ :host(:focus-visible) .radio__control {
676
+ outline: 3px solid Highlight;
677
+ outline-offset: 2px;
678
+ }
679
+
680
+ .radio--checked .radio__control {
681
+ background-color: Highlight;
682
+ border-color: Highlight;
683
+ }
684
+
685
+ .radio--checked .radio__dot {
686
+ background-color: HighlightText;
687
+ }
688
+
689
+ :host([disabled]) {
690
+ opacity: 1;
691
+ }
692
+
693
+ :host([disabled]) .radio__control {
694
+ border-color: GrayText;
695
+ background-color: ButtonFace;
696
+ }
697
+
698
+ :host([disabled]) .radio--checked .radio__control {
699
+ background-color: GrayText;
700
+ border-color: GrayText;
701
+ }
702
+
703
+ :host([disabled]) .radio__label {
704
+ color: GrayText;
705
+ }
706
+
707
+ .radio__label {
708
+ color: CanvasText;
709
+ }
710
+ }
711
+ `;
712
+ var Q = Object.defineProperty, X = Object.getOwnPropertyDescriptor, f = (e, t, i, r) => {
713
+ for (var s = r > 1 ? void 0 : r ? X(t, i) : t, o = e.length - 1, a; o >= 0; o--)
714
+ (a = e[o]) && (s = (r ? a(t, i, s) : a(s)) || s);
715
+ return r && s && Q(t, i, s), s;
716
+ };
717
+ const Y = z("hx-radio");
718
+ let _ = class extends $ {
719
+ constructor() {
720
+ super(...arguments), this.value = "", this.label = "", this.disabled = !1, this.checked = !1, this.__groupedSuppress = !1, this._inputId = Y();
721
+ }
722
+ /**
723
+ * Set by `hx-radio-group` to mark this child as group-managed. `hx-radio` is
724
+ * not form-associated (the group is the sole form participant), so this
725
+ * flag is currently inert on this element. It exists for symmetry with
726
+ * `hx-checkbox._groupedSuppress` so the group/child contract is identical
727
+ * across both selection-control families and any future form-association
728
+ * change on `hx-radio` is automatically suppressed inside a group. Codex
729
+ * round-3 finding #1 (defense-in-depth).
730
+ * @internal
731
+ */
732
+ set _groupedSuppress(e) {
733
+ this.__groupedSuppress = e;
734
+ }
735
+ get _groupedSuppress() {
736
+ return this.__groupedSuppress;
737
+ }
738
+ // ─── Lifecycle ───
739
+ connectedCallback() {
740
+ super.connectedCallback(), this._internals.role = "radio", this._syncAriaState();
741
+ }
742
+ updated(e) {
743
+ super.updated(e), (e.has("checked") || e.has("label") || e.has("disabled")) && this._syncAriaState();
744
+ }
745
+ /**
746
+ * Mirror reactive ARIA state onto ElementInternals. Setting a value to `null`
747
+ * removes it from the accessibility tree (matching the previous
748
+ * removeAttribute behavior).
749
+ *
750
+ * WCAG 4.1.2: expose the label text as ariaLabel on the host so assistive
751
+ * technology can associate the visible label with the radio role. The label
752
+ * span lives inside Shadow DOM and aria-labelledby cannot cross shadow
753
+ * boundaries, so ariaLabel on the host is the correct pattern here.
754
+ *
755
+ * WCAG 4.1.2: omit ariaDisabled entirely when not disabled. Setting
756
+ * aria-disabled="false" is verbose and unnecessary — omission is preferred.
757
+ *
758
+ * @internal
759
+ */
760
+ _syncAriaState() {
761
+ this._internals.ariaChecked = String(this.checked), this._internals.ariaLabel = this.label || null, this._internals.ariaDisabled = this.disabled ? "true" : null;
762
+ }
763
+ // ─── Event Handling ───
764
+ /** @internal */
765
+ _handleClick() {
766
+ this.disabled || this.dispatchEvent(
767
+ new CustomEvent("hx-radio-select", {
768
+ bubbles: !0,
769
+ composed: !0,
770
+ detail: { value: this.value }
771
+ })
772
+ );
773
+ }
774
+ // ─── Render ───
775
+ render() {
776
+ const e = {
777
+ radio: !0,
778
+ "radio--checked": this.checked,
779
+ "radio--disabled": this.disabled
780
+ };
781
+ return y`
782
+ <div class=${B(e)} @click=${this._handleClick}>
783
+ <input
784
+ class="radio__input"
785
+ type="radio"
786
+ id=${this._inputId}
787
+ .checked=${this.checked}
788
+ ?disabled=${this.disabled}
789
+ tabindex="-1"
790
+ aria-hidden="true"
791
+ />
792
+ <span part="radio" class="radio__control" aria-hidden="true">
793
+ <span class="radio__dot"></span>
794
+ </span>
795
+ <span part="label" class="radio__label">
796
+ <slot>${this.label}</slot>
797
+ </span>
798
+ </div>
799
+ `;
800
+ }
801
+ };
802
+ _.styles = [J, L];
803
+ f([
804
+ d({ type: String })
805
+ ], _.prototype, "value", 2);
806
+ f([
807
+ d({ type: String })
808
+ ], _.prototype, "label", 2);
809
+ f([
810
+ d({ type: Boolean, reflect: !0 })
811
+ ], _.prototype, "disabled", 2);
812
+ f([
813
+ d({ type: Boolean, reflect: !0 })
814
+ ], _.prototype, "checked", 2);
815
+ _ = f([
816
+ A("hx-radio")
817
+ ], _);
818
+ export {
819
+ _ as H,
820
+ l as a
821
+ };
822
+ //# sourceMappingURL=hx-radio-CeGzARNk.js.map