@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.
- package/custom-elements.json +445 -276
- package/dist/components/hx-accordion/hx-accordion-item.d.ts +35 -0
- package/dist/components/hx-accordion/hx-accordion-item.d.ts.map +1 -1
- package/dist/components/hx-checkbox/hx-checkbox.d.ts +153 -1
- package/dist/components/hx-checkbox/hx-checkbox.d.ts.map +1 -1
- package/dist/components/hx-checkbox/hx-checkbox.styles.d.ts.map +1 -1
- package/dist/components/hx-checkbox/index.js +1 -1
- package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts +151 -2
- package/dist/components/hx-checkbox-group/hx-checkbox-group.d.ts.map +1 -1
- package/dist/components/hx-checkbox-group/index.js +1 -1
- package/dist/components/hx-color-picker/hx-color-picker.d.ts +163 -1
- package/dist/components/hx-color-picker/hx-color-picker.d.ts.map +1 -1
- package/dist/components/hx-color-picker/hx-color-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-color-picker/index.js +1 -1
- package/dist/components/hx-combobox/hx-combobox.d.ts +311 -2
- package/dist/components/hx-combobox/hx-combobox.d.ts.map +1 -1
- package/dist/components/hx-combobox/index.js +1 -1
- package/dist/components/hx-date-picker/hx-date-picker.d.ts +182 -56
- package/dist/components/hx-date-picker/hx-date-picker.d.ts.map +1 -1
- package/dist/components/hx-date-picker/hx-date-picker.styles.d.ts.map +1 -1
- package/dist/components/hx-date-picker/index.js +1 -1
- package/dist/components/hx-dialog/hx-dialog.d.ts +240 -0
- package/dist/components/hx-dialog/hx-dialog.d.ts.map +1 -1
- package/dist/components/hx-dialog/index.js +1 -1
- package/dist/components/hx-dropdown/hx-dropdown.d.ts +80 -0
- package/dist/components/hx-dropdown/hx-dropdown.d.ts.map +1 -1
- package/dist/components/hx-dropdown/index.js +1 -1
- package/dist/components/hx-field/hx-field.d.ts +109 -0
- package/dist/components/hx-field/hx-field.d.ts.map +1 -1
- package/dist/components/hx-field/index.js +1 -1
- package/dist/components/hx-popover/hx-popover.d.ts +91 -0
- package/dist/components/hx-popover/hx-popover.d.ts.map +1 -1
- package/dist/components/hx-popover/index.js +1 -1
- package/dist/components/hx-radio-group/hx-radio-group.d.ts +152 -1
- package/dist/components/hx-radio-group/hx-radio-group.d.ts.map +1 -1
- package/dist/components/hx-radio-group/hx-radio.d.ts +14 -0
- package/dist/components/hx-radio-group/hx-radio.d.ts.map +1 -1
- package/dist/components/hx-radio-group/index.js +1 -1
- package/dist/components/hx-select/hx-select.d.ts +303 -2
- package/dist/components/hx-select/hx-select.d.ts.map +1 -1
- package/dist/components/hx-select/hx-select.styles.d.ts.map +1 -1
- package/dist/components/hx-select/index.js +1 -1
- package/dist/components/hx-side-nav/hx-nav-item.styles.d.ts.map +1 -1
- package/dist/components/hx-side-nav/index.js +1 -1
- package/dist/components/hx-switch/hx-switch.d.ts +78 -1
- package/dist/components/hx-switch/hx-switch.d.ts.map +1 -1
- package/dist/components/hx-switch/hx-switch.styles.d.ts.map +1 -1
- package/dist/components/hx-switch/index.js +1 -1
- package/dist/components/hx-toggle-button/hx-toggle-button.d.ts +110 -0
- package/dist/components/hx-toggle-button/hx-toggle-button.d.ts.map +1 -1
- package/dist/components/hx-toggle-button/hx-toggle-button.styles.d.ts.map +1 -1
- package/dist/components/hx-toggle-button/index.js +1 -1
- package/dist/components/hx-tooltip/hx-tooltip.d.ts +52 -0
- package/dist/components/hx-tooltip/hx-tooltip.d.ts.map +1 -1
- package/dist/components/hx-tooltip/index.js +1 -1
- package/dist/css/helix-all.css +98 -1
- package/dist/css/helix-forms.css +98 -1
- package/dist/css/hx-checkbox.css +18 -0
- package/dist/css/hx-color-picker.css +25 -0
- package/dist/css/hx-date-picker.css +2 -1
- package/dist/css/hx-select.css +19 -0
- package/dist/css/hx-switch.css +17 -0
- package/dist/css/hx-toggle-button.css +17 -0
- package/dist/css/index.css +1 -1
- package/dist/css/manifest.json +2 -1
- package/dist/index.js +15 -15
- package/dist/shared/aria-flatten-DY6v2vah.js +22 -0
- package/dist/shared/aria-flatten-DY6v2vah.js.map +1 -0
- package/dist/shared/aria-idref-Q0yiSR3p.js +104 -0
- package/dist/shared/aria-idref-Q0yiSR3p.js.map +1 -0
- package/dist/shared/hx-accordion-ZVzgDzTG.js.map +1 -1
- package/dist/shared/hx-checkbox-BdgoUeWi.js +696 -0
- package/dist/shared/hx-checkbox-BdgoUeWi.js.map +1 -0
- package/dist/shared/hx-checkbox-group-LWezHrvS.js +496 -0
- package/dist/shared/hx-checkbox-group-LWezHrvS.js.map +1 -0
- package/dist/shared/hx-color-picker-DVhZl88b.js +1221 -0
- package/dist/shared/hx-color-picker-DVhZl88b.js.map +1 -0
- package/dist/shared/hx-combobox-DvlezcDV.js +1359 -0
- package/dist/shared/hx-combobox-DvlezcDV.js.map +1 -0
- package/dist/shared/{hx-date-picker-2iRG1p74.js → hx-date-picker-N-0aG5XL.js} +542 -206
- package/dist/shared/hx-date-picker-N-0aG5XL.js.map +1 -0
- package/dist/shared/hx-dialog-DzB7VytW.js +717 -0
- package/dist/shared/hx-dialog-DzB7VytW.js.map +1 -0
- package/dist/shared/{hx-dropdown-LyaRc8Rf.js → hx-dropdown-DJWlF94E.js} +130 -77
- package/dist/shared/hx-dropdown-DJWlF94E.js.map +1 -0
- package/dist/shared/{hx-field-B3Qo8OLS.js → hx-field-zw0U1KVi.js} +99 -38
- package/dist/shared/hx-field-zw0U1KVi.js.map +1 -0
- package/dist/shared/{hx-nav-item-xqRPOCWX.js → hx-nav-item-CODtUlew.js} +13 -9
- package/dist/shared/{hx-nav-item-xqRPOCWX.js.map → hx-nav-item-CODtUlew.js.map} +1 -1
- package/dist/shared/{hx-popover-B-FP3-wW.js → hx-popover-CHxWY_cd.js} +123 -66
- package/dist/shared/hx-popover-CHxWY_cd.js.map +1 -0
- package/dist/shared/hx-radio-CeGzARNk.js +822 -0
- package/dist/shared/hx-radio-CeGzARNk.js.map +1 -0
- package/dist/shared/hx-select-DrcS-YRJ.js +1089 -0
- package/dist/shared/hx-select-DrcS-YRJ.js.map +1 -0
- package/dist/shared/hx-switch-BX_8uNUs.js +540 -0
- package/dist/shared/hx-switch-BX_8uNUs.js.map +1 -0
- package/dist/shared/{hx-toggle-button-iLiYrMbD.js → hx-toggle-button-Dcz9IlUm.js} +226 -65
- package/dist/shared/hx-toggle-button-Dcz9IlUm.js.map +1 -0
- package/dist/shared/{hx-tooltip-nYOv9OLu.js → hx-tooltip-DVqtKPCD.js} +68 -46
- package/dist/shared/hx-tooltip-DVqtKPCD.js.map +1 -0
- package/dist/utils/aria-flatten.d.ts +56 -0
- package/dist/utils/aria-flatten.d.ts.map +1 -0
- package/dist/utils/aria-idref.d.ts +127 -0
- package/dist/utils/aria-idref.d.ts.map +1 -0
- package/figma-inventory.json +64 -1
- package/package.json +2 -2
- package/dist/shared/hx-checkbox-D7xma9YH.js +0 -524
- package/dist/shared/hx-checkbox-D7xma9YH.js.map +0 -1
- package/dist/shared/hx-checkbox-group-C9n315Ju.js +0 -323
- package/dist/shared/hx-checkbox-group-C9n315Ju.js.map +0 -1
- package/dist/shared/hx-color-picker-uRc865FJ.js +0 -882
- package/dist/shared/hx-color-picker-uRc865FJ.js.map +0 -1
- package/dist/shared/hx-combobox-DDzqNKEW.js +0 -924
- package/dist/shared/hx-combobox-DDzqNKEW.js.map +0 -1
- package/dist/shared/hx-date-picker-2iRG1p74.js.map +0 -1
- package/dist/shared/hx-dialog-DRN_1-Y-.js +0 -514
- package/dist/shared/hx-dialog-DRN_1-Y-.js.map +0 -1
- package/dist/shared/hx-dropdown-LyaRc8Rf.js.map +0 -1
- package/dist/shared/hx-field-B3Qo8OLS.js.map +0 -1
- package/dist/shared/hx-popover-B-FP3-wW.js.map +0 -1
- package/dist/shared/hx-radio-CJvNU2yP.js +0 -621
- package/dist/shared/hx-radio-CJvNU2yP.js.map +0 -1
- package/dist/shared/hx-select-C8fEHQhC.js +0 -807
- package/dist/shared/hx-select-C8fEHQhC.js.map +0 -1
- package/dist/shared/hx-switch-BrZFaRue.js +0 -420
- package/dist/shared/hx-switch-BrZFaRue.js.map +0 -1
- package/dist/shared/hx-toggle-button-iLiYrMbD.js.map +0 -1
- 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
|