@rak200/ui 0.2.12 → 0.2.13
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/README.md +3 -2
- package/dist/field.d.ts +6 -1
- package/dist/field.d.ts.map +1 -1
- package/dist/field.js +51 -5
- package/dist/field.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/radio.d.ts +107 -0
- package/dist/radio.d.ts.map +1 -0
- package/dist/radio.js +291 -0
- package/dist/radio.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -86,8 +86,9 @@ components; `docs/` is what describes them.
|
|
|
86
86
|
|
|
87
87
|
## Status
|
|
88
88
|
|
|
89
|
-
**v0.**
|
|
90
|
-
`<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select
|
|
89
|
+
**v0.** Eleven components — `<ui-button>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
|
|
90
|
+
`<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>`, `<ui-radio-group>`, `<ui-radio>`
|
|
91
|
+
and `<ui-icon>` — the token layer
|
|
91
92
|
under them, and an adopted glyph set, built to the ecosystem's full quality bar rather than
|
|
92
93
|
sketched: type-checked at the strictest available setting, formatted, tested in a real browser and
|
|
93
94
|
**asserted against axe** for WCAG A/AA, 100% coverage and **100% mutation score**, scanned, and
|
package/dist/field.d.ts
CHANGED
|
@@ -15,7 +15,12 @@ import { LitElement, type TemplateResult } from 'lit';
|
|
|
15
15
|
*
|
|
16
16
|
* **The control may sit below a wrapper**, and the same constraint is why: `<ui-input>` is
|
|
17
17
|
* a box around a control that had to stay in the light DOM, so this element looks through
|
|
18
|
-
* it. See {@link UiField.#control}
|
|
18
|
+
* it. See {@link UiField.#control} — and the one wrapper it does *not* look through, which
|
|
19
|
+
* is a wrapper carrying a `role`, because a group is the control rather than a box for one.
|
|
20
|
+
*
|
|
21
|
+
* **Not every control can be labelled the platform's way**, and `<ui-radio-group>` is the
|
|
22
|
+
* first that cannot: `<label for>` reaches a labelable element and nothing else, so the
|
|
23
|
+
* name arrives as `aria-labelledby` there. See {@link UiField.#name}.
|
|
19
24
|
*
|
|
20
25
|
* @example
|
|
21
26
|
* ```html
|
package/dist/field.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAMjE
|
|
1
|
+
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAMjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,OAAQ,SAAQ,UAAU;;IACnC,gBAAyB,MAAM,0BAuB7B;IAeO,iBAAiB,IAAI,IAAI;IASzB,oBAAoB,IAAI,IAAI;IAK5B,YAAY,IAAI,IAAI;IAIpB,MAAM,IAAI,cAAc;CA0JpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;KACvB;CACJ"}
|
package/dist/field.js
CHANGED
|
@@ -18,7 +18,12 @@ let sequence = 0;
|
|
|
18
18
|
*
|
|
19
19
|
* **The control may sit below a wrapper**, and the same constraint is why: `<ui-input>` is
|
|
20
20
|
* a box around a control that had to stay in the light DOM, so this element looks through
|
|
21
|
-
* it. See {@link UiField.#control}
|
|
21
|
+
* it. See {@link UiField.#control} — and the one wrapper it does *not* look through, which
|
|
22
|
+
* is a wrapper carrying a `role`, because a group is the control rather than a box for one.
|
|
23
|
+
*
|
|
24
|
+
* **Not every control can be labelled the platform's way**, and `<ui-radio-group>` is the
|
|
25
|
+
* first that cannot: `<label for>` reaches a labelable element and nothing else, so the
|
|
26
|
+
* name arrives as `aria-labelledby` there. See {@link UiField.#name}.
|
|
22
27
|
*
|
|
23
28
|
* @example
|
|
24
29
|
* ```html
|
|
@@ -110,6 +115,14 @@ export class UiField extends LitElement {
|
|
|
110
115
|
*/
|
|
111
116
|
#control() {
|
|
112
117
|
const slotted = this.#slotted(null);
|
|
118
|
+
// A wrapper carrying a `role` is claiming to *be* the widget rather than to box
|
|
119
|
+
// one, and the descent stops there. `<ui-radio-group>` is the first: it sets
|
|
120
|
+
// `role="radiogroup"` on itself, and the thing this field names is that group —
|
|
121
|
+
// looking through it would reach the first radio, which names one option and
|
|
122
|
+
// leaves the group anonymous.
|
|
123
|
+
if (slotted?.hasAttribute('role') === true) {
|
|
124
|
+
return slotted;
|
|
125
|
+
}
|
|
113
126
|
return slotted?.querySelector('input, textarea, select') ?? slotted;
|
|
114
127
|
}
|
|
115
128
|
/**
|
|
@@ -131,10 +144,7 @@ export class UiField extends LitElement {
|
|
|
131
144
|
if (control.id === '') {
|
|
132
145
|
control.id = `${uid}-control`;
|
|
133
146
|
}
|
|
134
|
-
|
|
135
|
-
if (label instanceof HTMLLabelElement) {
|
|
136
|
-
label.htmlFor = control.id;
|
|
137
|
-
}
|
|
147
|
+
this.#name(control, this.#slotted('label'), `${uid}-label`);
|
|
138
148
|
const help = this.#describer(this.#slotted('help'), `${uid}-help`);
|
|
139
149
|
const error = this.#describer(this.#slotted('error'), `${uid}-error`);
|
|
140
150
|
// The error comes first: a screen reader announces descriptions in order, and the
|
|
@@ -158,6 +168,42 @@ export class UiField extends LitElement {
|
|
|
158
168
|
control.setAttribute('aria-invalid', 'true');
|
|
159
169
|
}
|
|
160
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Gives the control its accessible name from whatever fills the label slot.
|
|
173
|
+
*
|
|
174
|
+
* **`<label for>` is preferred wherever it reaches**, because it is the platform's own
|
|
175
|
+
* association and it does two things `aria-labelledby` does not: it names the control
|
|
176
|
+
* and it makes the label a click target for it. It only reaches a **labelable**
|
|
177
|
+
* element, though, and `<ui-radio-group>` is not one — a `<label for>` aimed at a
|
|
178
|
+
* custom element labels nothing, which axe reports at critical impact.
|
|
179
|
+
*
|
|
180
|
+
* Which elements are labelable is asked of the platform rather than listed here: those
|
|
181
|
+
* are exactly the ones it gives a `labels` collection to. A list kept in this file
|
|
182
|
+
* would be one the next labelable element ages out of, silently.
|
|
183
|
+
*
|
|
184
|
+
* The name is dropped when the slot empties, the way `aria-describedby` is: a group
|
|
185
|
+
* left pointing at a label that is no longer there has a dangling IDREF, which is the
|
|
186
|
+
* one failure mode nobody sees — the group simply stops having a name.
|
|
187
|
+
*
|
|
188
|
+
* @param fallbackId - Given to the label only on the path that needs one to point at.
|
|
189
|
+
* A host-supplied id survives, the same way the control's and the describers' do.
|
|
190
|
+
*/
|
|
191
|
+
#name(control, label, fallbackId) {
|
|
192
|
+
if ('labels' in control) {
|
|
193
|
+
if (label instanceof HTMLLabelElement) {
|
|
194
|
+
label.htmlFor = control.id;
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (label === undefined) {
|
|
199
|
+
control.removeAttribute('aria-labelledby');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (label.id === '') {
|
|
203
|
+
label.id = fallbackId;
|
|
204
|
+
}
|
|
205
|
+
control.setAttribute('aria-labelledby', label.id);
|
|
206
|
+
}
|
|
161
207
|
/**
|
|
162
208
|
* The id to describe the control by, or `undefined` when the element is absent or has
|
|
163
209
|
* nothing to say. An element rendered empty — the shape a framework produces for
|
package/dist/field.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,8DAA8D;AAC9D,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB
|
|
1
|
+
{"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,8DAA8D;AAC9D,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,OAAO,OAAQ,SAAQ,UAAU;aACV,WAAM,GAAG,GAAG,CAAA;;;2BAGd,SAAS,CAAC,WAAW,CAAC;;;;;;wBAMzB,SAAS,CAAC,YAAY,CAAC;;;;qBAI1B,SAAS,CAAC,iBAAiB,CAAC;;;;;;;qBAO5B,SAAS,CAAC,mBAAmB,CAAC;;;KAG9C,CAAC;IAEF,oFAAoF;IACpF,IAAI,CAAU;IAEd;;;;;OAKG;IACM,SAAS,GAAG,IAAI,gBAAgB,CAAC,GAAG,EAAE;QAC3C,IAAI,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEM,iBAAiB;QACtB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAE1B,mFAAmF;QACnF,8EAA8E;QAC9E,kDAAkD;QAClD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1F,CAAC;IAEQ,oBAAoB;QACzB,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QAC5B,KAAK,CAAC,oBAAoB,EAAE,CAAC;IACjC,CAAC;IAEQ,YAAY;QACjB,IAAI,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC;IAEQ,MAAM;QACX,OAAO,IAAI,CAAA;;;;;;;SAOV,CAAC;IACN,CAAC;IAED,4EAA4E;IAC5E,QAAQ,CAAC,IAAmB;QACxB,MAAM,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,mBAAmB,IAAI,IAAI,CAAC;QACvF,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAE3C,OAAO,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,CAAC;IAED;;;;;;;;;;;OAWG;IACH,QAAQ;QACJ,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEpC,gFAAgF;QAChF,6EAA6E;QAC7E,gFAAgF;QAChF,6EAA6E;QAC7E,8BAA8B;QAC9B,IAAI,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;YACzC,OAAO,OAAO,CAAC;QACnB,CAAC;QAED,OAAO,OAAO,EAAE,aAAa,CAAC,yBAAyB,CAAC,IAAI,OAAO,CAAC;IACxE,CAAC;IAED;;;;;OAKG;IACH,UAAU;QACN,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAEhC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO;QACX,CAAC;QAED,mFAAmF;QACnF,iFAAiF;QACjF,+EAA+E;QAC/E,kFAAkF;QAClF,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,YAAY,MAAM,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAE7D,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACpB,OAAO,CAAC,EAAE,GAAG,GAAG,GAAG,UAAU,CAAC;QAClC,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC;QAE5D,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,QAAQ,CAAC,CAAC;QAEtE,kFAAkF;QAClF,6EAA6E;QAC7E,EAAE;QACF,oFAAoF;QACpF,oFAAoF;QACpF,+EAA+E;QAC/E,uBAAuB;QACvB,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QAEjE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,YAAY,CAAC,kBAAkB,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAC;QAChD,CAAC;QAED,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QACjD,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,KAAK,CAAC,OAAoB,EAAE,KAA8B,EAAE,UAAkB;QAC1E,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;YACtB,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;gBACpC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC;YAC/B,CAAC;YAED,OAAO;QACX,CAAC;QAED,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACtB,OAAO,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;YAE3C,OAAO;QACX,CAAC;QAED,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YAClB,KAAK,CAAC,EAAE,GAAG,UAAU,CAAC;QAC1B,CAAC;QAED,OAAO,CAAC,YAAY,CAAC,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,OAAgC,EAAE,UAAkB;QAC3D,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC7D,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACpB,OAAO,CAAC,EAAE,GAAG,UAAU,CAAC;QAC5B,CAAC;QAED,OAAO,OAAO,CAAC,EAAE,CAAC;IACtB,CAAC;;AAGL,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AACzF,oEAAoE;AACpE,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { UiDialog } from './dialog.js';
|
|
|
4
4
|
export { UiField } from './field.js';
|
|
5
5
|
export { UiIcon, register } from './icon.js';
|
|
6
6
|
export { UiInput, UiTextarea } from './input.js';
|
|
7
|
+
export { UiRadio, UiRadioGroup, type RadioOrientation } from './radio.js';
|
|
7
8
|
export { UiSelect } from './select.js';
|
|
8
9
|
export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, type Token, type DerivedToken, } from './tokens.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { UiDialog } from './dialog.js';
|
|
|
4
4
|
export { UiField } from './field.js';
|
|
5
5
|
export { UiIcon, register } from './icon.js';
|
|
6
6
|
export { UiInput, UiTextarea } from './input.js';
|
|
7
|
+
export { UiRadio, UiRadioGroup } from './radio.js';
|
|
7
8
|
export { UiSelect } from './select.js';
|
|
8
9
|
export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, } from './tokens.js';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAyB,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
|
package/dist/radio.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { LitElement, type TemplateResult } from 'lit';
|
|
2
|
+
/** Which way a group lays its options out, and announces that it did. */
|
|
3
|
+
export type RadioOrientation = 'vertical' | 'horizontal';
|
|
4
|
+
/**
|
|
5
|
+
* One option's control, drawn from the token layer rather than replaced.
|
|
6
|
+
*
|
|
7
|
+
* **The `<input type="radio">` is yours.** You write it, you set its `name`, `value`,
|
|
8
|
+
* `checked` and `disabled`, and it stays in the light DOM — the shape every control in
|
|
9
|
+
* this kit has, and forced by the same constraint: an ARIA relationship by IDREF does not
|
|
10
|
+
* cross a shadow boundary, so a control rendered in here could not be labelled by the
|
|
11
|
+
* `<label>` around it.
|
|
12
|
+
*
|
|
13
|
+
* **This element carries no state and no name.** It is a drawing, and everything that
|
|
14
|
+
* makes a set of radios *a group* — one tab stop, arrow keys that move and select, one
|
|
15
|
+
* value out of the set — is the platform's, because they are native radios sharing a
|
|
16
|
+
* `name`. Wrapping each one changes none of that: measured in this suite, a group whose
|
|
17
|
+
* radios sit inside custom elements is still one group.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```html
|
|
21
|
+
* <label>
|
|
22
|
+
* <ui-radio><input type="radio" name="plan" value="free" /></ui-radio>
|
|
23
|
+
* Free
|
|
24
|
+
* </label>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class UiRadio extends LitElement {
|
|
28
|
+
static readonly styles: import("lit").CSSResult;
|
|
29
|
+
render(): TemplateResult;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A set of radios, laid out and named as one thing.
|
|
33
|
+
*
|
|
34
|
+
* **It hand-rolls no roving tabindex and it brings no state machine**, and that is this
|
|
35
|
+
* element's whole claim rather than a gap in it. Issue #15 asked which of the two it would
|
|
36
|
+
* be, because the APG **Radio Group** pattern — one tab stop, arrow keys that move *and*
|
|
37
|
+
* select, wrapping at the ends — is the first behaviour in the v0 cut that could have
|
|
38
|
+
* justified Zag. Neither was needed: native radios sharing a `name` **are** that pattern,
|
|
39
|
+
* and this suite measures it here rather than trusting it, wrappers and all.
|
|
40
|
+
*
|
|
41
|
+
* That is the test `ROADMAP.md` states, applied: Zag arrives where the platform has **no
|
|
42
|
+
* element** for the pattern, not merely where the pattern has state. `ui-menu` is where
|
|
43
|
+
* that question is open; a radio group is not.
|
|
44
|
+
*
|
|
45
|
+
* **What is left for this element is what the platform leaves out**: the layout, the
|
|
46
|
+
* `role` that makes a set of radios a group to a screen reader, and the group-level name,
|
|
47
|
+
* description and error state — which {@link UiField} wires onto *this* element rather
|
|
48
|
+
* than onto an option, because a `<label for>` naming one radio names one option and
|
|
49
|
+
* leaves the group anonymous.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```html
|
|
53
|
+
* <ui-field>
|
|
54
|
+
* <label slot="label">Plan</label>
|
|
55
|
+
* <ui-radio-group>
|
|
56
|
+
* <label><ui-radio><input type="radio" name="plan" value="free" /></ui-radio> Free</label>
|
|
57
|
+
* <label><ui-radio><input type="radio" name="plan" value="pro" /></ui-radio> Pro</label>
|
|
58
|
+
* </ui-radio-group>
|
|
59
|
+
* </ui-field>
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare class UiRadioGroup extends LitElement {
|
|
63
|
+
static readonly styles: import("lit").CSSResult;
|
|
64
|
+
static readonly properties: {
|
|
65
|
+
orientation: {
|
|
66
|
+
type: StringConstructor;
|
|
67
|
+
reflect: boolean;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Which way the options are laid out.
|
|
72
|
+
*
|
|
73
|
+
* A plain field rather than the `accessor` keyword, for the reason `src/button.ts`
|
|
74
|
+
* gives beside its own: the browser this suite runs in does not implement
|
|
75
|
+
* auto-accessors, and the module would fail to parse.
|
|
76
|
+
*/
|
|
77
|
+
orientation: RadioOrientation;
|
|
78
|
+
/**
|
|
79
|
+
* Marks the element a group before anything asks what it is.
|
|
80
|
+
*
|
|
81
|
+
* In `connectedCallback` rather than at first render, and the timing is the point:
|
|
82
|
+
* {@link UiField} reads this attribute to decide that the group — and not the first
|
|
83
|
+
* radio inside it — is the thing to name, and it does so on its own first update,
|
|
84
|
+
* which lands before this element's. Set a microtask later, the role would arrive
|
|
85
|
+
* after the only reader that needs it.
|
|
86
|
+
*
|
|
87
|
+
* A `role` the host wrote is never overwritten, the same way `<ui-switch>` never
|
|
88
|
+
* overwrites one on the control it announces.
|
|
89
|
+
*/
|
|
90
|
+
connectedCallback(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Keeps the announced orientation on the drawn one.
|
|
93
|
+
*
|
|
94
|
+
* Written unconditionally rather than only for the horizontal case: the default a
|
|
95
|
+
* screen reader assumes for a `radiogroup` is not something a component should have an
|
|
96
|
+
* opinion about, and the attribute costs nothing to state.
|
|
97
|
+
*/
|
|
98
|
+
willUpdate(): void;
|
|
99
|
+
render(): TemplateResult;
|
|
100
|
+
}
|
|
101
|
+
declare global {
|
|
102
|
+
interface HTMLElementTagNameMap {
|
|
103
|
+
'ui-radio': UiRadio;
|
|
104
|
+
'ui-radio-group': UiRadioGroup;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=radio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../src/radio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGjE,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,YAAY,CAAC;AAmCzD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,OAAQ,SAAQ,UAAU;IACnC,gBAAyB,MAAM,0BA8G7B;IAEO,MAAM,IAAI,cAAc;CAGpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAa,SAAQ,UAAU;IACxC,gBAAyB,MAAM,0BAiC7B;IAEF,gBAAyB,UAAU;;;;;MAEjC;IAEF;;;;;;OAMG;IACH,WAAW,EAAE,gBAAgB,CAAc;IAE3C;;;;;;;;;;;OAWG;IACM,iBAAiB,IAAI,IAAI;IAQlC;;;;;;OAMG;IACM,UAAU,IAAI,IAAI;IAIlB,MAAM,IAAI,cAAc;CAGpC;AAWD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;QACpB,gBAAgB,EAAE,YAAY,CAAC;KAClC;CACJ"}
|
package/dist/radio.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { LitElement, css, html } from 'lit';
|
|
2
|
+
import { reference } from './reference.js';
|
|
3
|
+
/**
|
|
4
|
+
* The size the control is drawn at, and a floor rather than a preference.
|
|
5
|
+
*
|
|
6
|
+
* The same measurement `src/checkbox.ts` carries, taken again on this element: a native
|
|
7
|
+
* radio is 13x13 in this engine, which is under WCAG 2.2's **2.5.8 Target Size (Minimum)**
|
|
8
|
+
* of 24x24 and escapes it only through that criterion's *user agent control* exception.
|
|
9
|
+
* Drawing it ourselves gives the exception up, so the floor becomes ours to hold, and
|
|
10
|
+
* `max()` is what holds it: a host who shrinks `--ui-space` cannot take this control below
|
|
11
|
+
* the floor without saying so in their own CSS.
|
|
12
|
+
*
|
|
13
|
+
* Written out rather than imported from the checkbox, for the reason `src/select.ts` gives
|
|
14
|
+
* about the box it shares with `src/input.ts` — and answered the same way, on the same
|
|
15
|
+
* terms: `tests/radio.test.ts` mounts both controls and asserts they agree on the size,
|
|
16
|
+
* the boundary, the fill and the focus ring, so something compares them and it fails when
|
|
17
|
+
* they drift.
|
|
18
|
+
*/
|
|
19
|
+
const size = css `max(24px, calc(${reference('--ui-space')} * 3))`;
|
|
20
|
+
/** A mask layer covering the whole control, which the mark is then subtracted from. */
|
|
21
|
+
const whole = css `linear-gradient(#000, #000)`;
|
|
22
|
+
/**
|
|
23
|
+
* The mark, as a hole rather than a dot — the same decision `src/checkbox.ts` explains at
|
|
24
|
+
* length, reached here without a picture at all.
|
|
25
|
+
*
|
|
26
|
+
* A checkbox's tick needs a path and therefore a `data:` URI; a radio's mark is a circle,
|
|
27
|
+
* which is a gradient with a size. What both share is that the mark is **absent** rather
|
|
28
|
+
* than coloured: `mask-composite: exclude` punches it out of the accent fill, so what
|
|
29
|
+
* shows through is whatever the control sits on, and no colour is frozen anywhere a host
|
|
30
|
+
* could not override it.
|
|
31
|
+
*/
|
|
32
|
+
const mark = css `radial-gradient(circle closest-side, #000 100%, transparent 100%)`;
|
|
33
|
+
/**
|
|
34
|
+
* One option's control, drawn from the token layer rather than replaced.
|
|
35
|
+
*
|
|
36
|
+
* **The `<input type="radio">` is yours.** You write it, you set its `name`, `value`,
|
|
37
|
+
* `checked` and `disabled`, and it stays in the light DOM — the shape every control in
|
|
38
|
+
* this kit has, and forced by the same constraint: an ARIA relationship by IDREF does not
|
|
39
|
+
* cross a shadow boundary, so a control rendered in here could not be labelled by the
|
|
40
|
+
* `<label>` around it.
|
|
41
|
+
*
|
|
42
|
+
* **This element carries no state and no name.** It is a drawing, and everything that
|
|
43
|
+
* makes a set of radios *a group* — one tab stop, arrow keys that move and select, one
|
|
44
|
+
* value out of the set — is the platform's, because they are native radios sharing a
|
|
45
|
+
* `name`. Wrapping each one changes none of that: measured in this suite, a group whose
|
|
46
|
+
* radios sit inside custom elements is still one group.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```html
|
|
50
|
+
* <label>
|
|
51
|
+
* <ui-radio><input type="radio" name="plan" value="free" /></ui-radio>
|
|
52
|
+
* Free
|
|
53
|
+
* </label>
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export class UiRadio extends LitElement {
|
|
57
|
+
static { this.styles = css `
|
|
58
|
+
:host {
|
|
59
|
+
display: inline-flex;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Everything below reaches a control the *host* wrote, through ::slotted — the
|
|
63
|
+
whole shape of this component, and the same one ui-checkbox has.
|
|
64
|
+
|
|
65
|
+
appearance: none is its whole cost, and it is taken for the reason
|
|
66
|
+
ARCHITECTURE.md states as a debt rather than a free hand: it removes the
|
|
67
|
+
platform's dot, its target size and its behaviour under forced colors in one
|
|
68
|
+
declaration, and each of those is answered below. What it buys is a radio that
|
|
69
|
+
belongs beside the checkbox this kit already draws — a native 13px radio next to
|
|
70
|
+
a drawn 24px checkbox would argue cosmetically for a difference that is
|
|
71
|
+
semantic. */
|
|
72
|
+
::slotted(input) {
|
|
73
|
+
appearance: none;
|
|
74
|
+
box-sizing: border-box;
|
|
75
|
+
/* The user agent puts 3px around a radio. Left in, it would sit inside the
|
|
76
|
+
host box and put the control off centre in any layout that aligns to it. */
|
|
77
|
+
margin: 0;
|
|
78
|
+
inline-size: ${size};
|
|
79
|
+
block-size: ${size};
|
|
80
|
+
/* A circle, and the one measurement here that is not a token: the shape is
|
|
81
|
+
what tells a radio from a checkbox before either is read, so it cannot
|
|
82
|
+
follow --ui-radius without a square radio becoming possible. */
|
|
83
|
+
border-radius: 50%;
|
|
84
|
+
background-color: ${reference('--ui-color-surface')};
|
|
85
|
+
background-repeat: no-repeat;
|
|
86
|
+
border: 1px solid ${reference('--ui-color-border')};
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
/* The focus ring is deliberately not in this list, for the reason
|
|
89
|
+
src/button.ts gives beside its own: delaying the affordance that says *this
|
|
90
|
+
is where you are* is the opposite of what it exists to do. */
|
|
91
|
+
transition:
|
|
92
|
+
background-color ${reference('--ui-duration-state')}
|
|
93
|
+
${reference('--ui-easing-state')},
|
|
94
|
+
border-color ${reference('--ui-duration-state')} ${reference('--ui-easing-state')};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* The mark is a hole: the whole control, minus the circle, so what shows through
|
|
98
|
+
is whatever the control sits on. The docblock on the mask constant says why a
|
|
99
|
+
hole rather than a colour. */
|
|
100
|
+
::slotted(input:checked) {
|
|
101
|
+
background-color: ${reference('--ui-color-accent')};
|
|
102
|
+
border-color: ${reference('--ui-color-accent')};
|
|
103
|
+
mask-image: ${whole}, ${mark};
|
|
104
|
+
mask-composite: exclude;
|
|
105
|
+
mask-size:
|
|
106
|
+
100% 100%,
|
|
107
|
+
calc(${size} / 2);
|
|
108
|
+
mask-position: center;
|
|
109
|
+
mask-repeat: no-repeat;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Unselected, the boundary finishes the mix it already started — the same hover
|
|
113
|
+
ui-input and ui-checkbox have, because it is the same boundary. Selected, the
|
|
114
|
+
boundary is not what the eye is on, so the fill moves instead, the way
|
|
115
|
+
ui-button's does.
|
|
116
|
+
|
|
117
|
+
The disabled guard is measured rather than assumed: a disabled control still
|
|
118
|
+
matches :hover. */
|
|
119
|
+
::slotted(input:hover:not(:disabled)) {
|
|
120
|
+
border-color: ${reference('--ui-color-text')};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
::slotted(input:checked:hover:not(:disabled)) {
|
|
124
|
+
background-color: ${reference('--ui-color-accent-hover')};
|
|
125
|
+
border-color: ${reference('--ui-color-accent-hover')};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* A visible focus ring is not decoration: removing it is the single most common
|
|
129
|
+
way a component stops being usable by keyboard — and on a radio group it is the
|
|
130
|
+
only thing that says which option an arrow key just moved to. */
|
|
131
|
+
::slotted(input:focus-visible) {
|
|
132
|
+
outline: 2px solid ${reference('--ui-color-focus')};
|
|
133
|
+
outline-offset: 2px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
::slotted(input:disabled) {
|
|
137
|
+
cursor: not-allowed;
|
|
138
|
+
opacity: 0.5;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* There is no error rule here, and its absence is the decision: what a radio group
|
|
142
|
+
gets wrong is the *choice*, not one option, so ui-radio-group paints the state
|
|
143
|
+
for the whole set. It does it by retargeting the boundary token over its own
|
|
144
|
+
subtree, which this rule reads without knowing anything about it. */
|
|
145
|
+
|
|
146
|
+
/* Forced colors replaces every author colour with one from the user's palette, so
|
|
147
|
+
the accent that says *selected* becomes the same Canvas as the surface that says
|
|
148
|
+
*not* — the state disappears, silently, for the people who turned the mode on to
|
|
149
|
+
see states more clearly. System colours are the ones forced colors keeps, so the
|
|
150
|
+
selected state names Highlight and the difference survives; the mark keeps
|
|
151
|
+
working untouched, because a mask has no colour to force.
|
|
152
|
+
|
|
153
|
+
Opacity is not a colour and is not forced, so the disabled control would still
|
|
154
|
+
be half-transparent against a palette chosen for contrast. GrayText is what that
|
|
155
|
+
mode has for *unavailable*, and it is a colour rather than a veil. */
|
|
156
|
+
@media (forced-colors: active) {
|
|
157
|
+
::slotted(input:checked) {
|
|
158
|
+
background-color: Highlight;
|
|
159
|
+
border-color: Highlight;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
::slotted(input:disabled) {
|
|
163
|
+
border-color: GrayText;
|
|
164
|
+
opacity: 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
`; }
|
|
168
|
+
render() {
|
|
169
|
+
return html `<slot></slot>`;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* A set of radios, laid out and named as one thing.
|
|
174
|
+
*
|
|
175
|
+
* **It hand-rolls no roving tabindex and it brings no state machine**, and that is this
|
|
176
|
+
* element's whole claim rather than a gap in it. Issue #15 asked which of the two it would
|
|
177
|
+
* be, because the APG **Radio Group** pattern — one tab stop, arrow keys that move *and*
|
|
178
|
+
* select, wrapping at the ends — is the first behaviour in the v0 cut that could have
|
|
179
|
+
* justified Zag. Neither was needed: native radios sharing a `name` **are** that pattern,
|
|
180
|
+
* and this suite measures it here rather than trusting it, wrappers and all.
|
|
181
|
+
*
|
|
182
|
+
* That is the test `ROADMAP.md` states, applied: Zag arrives where the platform has **no
|
|
183
|
+
* element** for the pattern, not merely where the pattern has state. `ui-menu` is where
|
|
184
|
+
* that question is open; a radio group is not.
|
|
185
|
+
*
|
|
186
|
+
* **What is left for this element is what the platform leaves out**: the layout, the
|
|
187
|
+
* `role` that makes a set of radios a group to a screen reader, and the group-level name,
|
|
188
|
+
* description and error state — which {@link UiField} wires onto *this* element rather
|
|
189
|
+
* than onto an option, because a `<label for>` naming one radio names one option and
|
|
190
|
+
* leaves the group anonymous.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```html
|
|
194
|
+
* <ui-field>
|
|
195
|
+
* <label slot="label">Plan</label>
|
|
196
|
+
* <ui-radio-group>
|
|
197
|
+
* <label><ui-radio><input type="radio" name="plan" value="free" /></ui-radio> Free</label>
|
|
198
|
+
* <label><ui-radio><input type="radio" name="plan" value="pro" /></ui-radio> Pro</label>
|
|
199
|
+
* </ui-radio-group>
|
|
200
|
+
* </ui-field>
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export class UiRadioGroup extends LitElement {
|
|
204
|
+
constructor() {
|
|
205
|
+
super(...arguments);
|
|
206
|
+
/**
|
|
207
|
+
* Which way the options are laid out.
|
|
208
|
+
*
|
|
209
|
+
* A plain field rather than the `accessor` keyword, for the reason `src/button.ts`
|
|
210
|
+
* gives beside its own: the browser this suite runs in does not implement
|
|
211
|
+
* auto-accessors, and the module would fail to parse.
|
|
212
|
+
*/
|
|
213
|
+
this.orientation = 'vertical';
|
|
214
|
+
}
|
|
215
|
+
static { this.styles = css `
|
|
216
|
+
:host {
|
|
217
|
+
display: flex;
|
|
218
|
+
flex-direction: column;
|
|
219
|
+
gap: calc(${reference('--ui-space')} / 2);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Laid out by the attribute rather than by the host's own CSS, so the drawing and
|
|
223
|
+
the announcement cannot disagree: orientation sets both this rule and
|
|
224
|
+
aria-orientation, and a host who reached for flex-direction instead would
|
|
225
|
+
change one of the two. */
|
|
226
|
+
:host([orientation='horizontal']) {
|
|
227
|
+
flex-direction: row;
|
|
228
|
+
flex-wrap: wrap;
|
|
229
|
+
column-gap: calc(${reference('--ui-space')} * 2);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* The error, painted for the whole set by retargeting the boundary token over this
|
|
233
|
+
subtree — which is the override surface the token layer already publishes,
|
|
234
|
+
pointed at the group's own descendants instead of at a page.
|
|
235
|
+
|
|
236
|
+
A custom property inherits through a shadow boundary and a selector does not, so
|
|
237
|
+
this is what reaches a control two elements down that this sheet cannot name:
|
|
238
|
+
::slotted() stops at the group's own children, :host-context() is not
|
|
239
|
+
cross-engine, and :host(:has(...)) is invalid in this engine. The value still
|
|
240
|
+
arrives as a token reference, so a host who retunes danger retunes this too.
|
|
241
|
+
|
|
242
|
+
aria-invalid is not written here: ui-field sets it as part of the wiring it
|
|
243
|
+
already owns, so the message under the group and the boundary around its options
|
|
244
|
+
cannot disagree about whether there is an error. */
|
|
245
|
+
:host([aria-invalid='true']) {
|
|
246
|
+
--ui-color-border: ${reference('--ui-color-danger')};
|
|
247
|
+
}
|
|
248
|
+
`; }
|
|
249
|
+
static { this.properties = {
|
|
250
|
+
orientation: { type: String, reflect: true },
|
|
251
|
+
}; }
|
|
252
|
+
/**
|
|
253
|
+
* Marks the element a group before anything asks what it is.
|
|
254
|
+
*
|
|
255
|
+
* In `connectedCallback` rather than at first render, and the timing is the point:
|
|
256
|
+
* {@link UiField} reads this attribute to decide that the group — and not the first
|
|
257
|
+
* radio inside it — is the thing to name, and it does so on its own first update,
|
|
258
|
+
* which lands before this element's. Set a microtask later, the role would arrive
|
|
259
|
+
* after the only reader that needs it.
|
|
260
|
+
*
|
|
261
|
+
* A `role` the host wrote is never overwritten, the same way `<ui-switch>` never
|
|
262
|
+
* overwrites one on the control it announces.
|
|
263
|
+
*/
|
|
264
|
+
connectedCallback() {
|
|
265
|
+
super.connectedCallback();
|
|
266
|
+
if (!this.hasAttribute('role')) {
|
|
267
|
+
this.setAttribute('role', 'radiogroup');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Keeps the announced orientation on the drawn one.
|
|
272
|
+
*
|
|
273
|
+
* Written unconditionally rather than only for the horizontal case: the default a
|
|
274
|
+
* screen reader assumes for a `radiogroup` is not something a component should have an
|
|
275
|
+
* opinion about, and the attribute costs nothing to state.
|
|
276
|
+
*/
|
|
277
|
+
willUpdate() {
|
|
278
|
+
this.setAttribute('aria-orientation', this.orientation);
|
|
279
|
+
}
|
|
280
|
+
render() {
|
|
281
|
+
return html `<slot></slot>`;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Stryker disable next-line StringLiteral: the registration runs once, at import, inside
|
|
285
|
+
// the warm process Stryker switches mutants in — so by the time a mutant on this line is
|
|
286
|
+
// active the element is already defined under the original name, and no test can observe
|
|
287
|
+
// the change. Outside the runner's reach, not an equivalent mutant.
|
|
288
|
+
customElements.define('ui-radio', UiRadio);
|
|
289
|
+
// Stryker disable next-line StringLiteral: the same, for the same reason.
|
|
290
|
+
customElements.define('ui-radio-group', UiRadioGroup);
|
|
291
|
+
//# sourceMappingURL=radio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radio.js","sourceRoot":"","sources":["../src/radio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAK3C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,IAAI,GAAG,GAAG,CAAA,kBAAkB,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC;AAElE,uFAAuF;AACvF,MAAM,KAAK,GAAG,GAAG,CAAA,6BAA6B,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,IAAI,GAAG,GAAG,CAAA,mEAAmE,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,OAAQ,SAAQ,UAAU;aACV,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;2BAqBd,IAAI;0BACL,IAAI;;;;;gCAKE,SAAS,CAAC,oBAAoB,CAAC;;gCAE/B,SAAS,CAAC,mBAAmB,CAAC;;;;;;mCAM3B,SAAS,CAAC,qBAAqB,CAAC;sBAC7C,SAAS,CAAC,mBAAmB,CAAC;+BACrB,SAAS,CAAC,qBAAqB,CAAC,IAAI,SAAS,CAAC,mBAAmB,CAAC;;;;;;;gCAOjE,SAAS,CAAC,mBAAmB,CAAC;4BAClC,SAAS,CAAC,mBAAmB,CAAC;0BAChC,KAAK,KAAK,IAAI;;;;uBAIjB,IAAI;;;;;;;;;;;;;4BAaC,SAAS,CAAC,iBAAiB,CAAC;;;;gCAIxB,SAAS,CAAC,yBAAyB,CAAC;4BACxC,SAAS,CAAC,yBAAyB,CAAC;;;;;;;iCAO/B,SAAS,CAAC,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmCzD,CAAC;IAEO,MAAM;QACX,OAAO,IAAI,CAAA,eAAe,CAAC;IAC/B,CAAC;;AAGL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,OAAO,YAAa,SAAQ,UAAU;IAA5C;;QAwCI;;;;;;WAMG;QACH,gBAAW,GAAqB,UAAU,CAAC;IAoC/C,CAAC;aAlF4B,WAAM,GAAG,GAAG,CAAA;;;;wBAIjB,SAAS,CAAC,YAAY,CAAC;;;;;;;;;;+BAUhB,SAAS,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;iCAiBrB,SAAS,CAAC,mBAAmB,CAAC;;KAE1D,AAjC8B,CAiC7B;aAEuB,eAAU,GAAG;QAClC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;KAC/C,AAFkC,CAEjC;IAWF;;;;;;;;;;;OAWG;IACM,iBAAiB;QACtB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAE1B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC5C,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACM,UAAU;QACf,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5D,CAAC;IAEQ,MAAM;QACX,OAAO,IAAI,CAAA,eAAe,CAAC;IAC/B,CAAC;;AAGL,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AACzF,oEAAoE;AACpE,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAE3C,0EAA0E;AAC1E,cAAc,CAAC,MAAM,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rak200/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "Host-agnostic custom-element UI components for the rak200 ecosystem",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "rak200 <rak.ricardo@windowslive.com>",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"test": "vitest run",
|
|
46
46
|
"coverage": "coverage-floor",
|
|
47
47
|
"scan": "rak200-scan",
|
|
48
|
-
"mutation": "
|
|
48
|
+
"mutation": "node tests/tooling/mutate-changed.mjs",
|
|
49
49
|
"build": "tsc -p tsconfig.icons.json && tsc -p tsconfig.build.json",
|
|
50
50
|
"storybook": "storybook dev -p 6006",
|
|
51
51
|
"build-storybook": "storybook build"
|