@rak200/ui 0.2.19 → 0.4.0

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 CHANGED
@@ -29,26 +29,14 @@ npm install @rak200/ui
29
29
  import '@rak200/ui';
30
30
  </script>
31
31
 
32
- <ui-field>
33
- <label slot="label">Amount</label>
34
- <ui-input><input type="number" name="amount" /></ui-input>
35
- <span slot="help">In BRL, two decimals.</span>
36
- </ui-field>
37
-
38
- <ui-field>
39
- <label slot="label">Currency</label>
40
- <ui-select>
41
- <select name="currency">
42
- <option value="brl">Real</option>
43
- <option value="usd">Dollar</option>
44
- </select>
45
- </ui-select>
46
- </ui-field>
47
-
48
- <ui-field>
49
- <label slot="label">Email notifications</label>
50
- <ui-switch><input type="checkbox" name="notify" checked /></ui-switch>
51
- </ui-field>
32
+ <ui-input label="Amount" help="In BRL, two decimals." type="number" name="amount"></ui-input>
33
+
34
+ <ui-select label="Currency" name="currency">
35
+ <ui-option value="brl">Real</ui-option>
36
+ <ui-option value="usd">Dollar</ui-option>
37
+ </ui-select>
38
+
39
+ <ui-switch label="Email notifications" name="notify" checked></ui-switch>
52
40
 
53
41
  <ui-button><ui-icon name="check"></ui-icon> Save</ui-button>
54
42
  <ui-button variant="secondary">Cancel</ui-button>
@@ -89,9 +77,10 @@ schemes rather than one.
89
77
 
90
78
  ## Status
91
79
 
92
- **v0.** Seventeen components — `<ui-button>`, `<ui-card>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
93
- `<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>`, `<ui-radio-group>`, `<ui-radio>`,
94
- `<ui-tooltip>`, `<ui-toaster>`, `<ui-toast>`, `<ui-menu>`, `<ui-table>` and `<ui-icon>` — the token layer
80
+ **v0.** Nineteen components — `<ui-button>`, `<ui-card>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
81
+ `<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>`, `<ui-option>`, `<ui-optgroup>`,
82
+ `<ui-radio-group>`, `<ui-radio>`, `<ui-tooltip>`, `<ui-toaster>`, `<ui-toast>`, `<ui-menu>`,
83
+ `<ui-table>` and `<ui-icon>` — the token layer
95
84
  under them, and an adopted glyph set, built to the ecosystem's full quality bar rather than
96
85
  sketched: type-checked at the strictest available setting, formatted, tested in a real browser and
97
86
  **asserted against axe** for WCAG A/AA, 100% coverage and **100% mutation score**, scanned, and
@@ -2,20 +2,202 @@ import { LitElement, type CSSResult, type TemplateResult } from 'lit';
2
2
  /**
3
3
  * A boolean control, drawn from the token layer rather than replaced.
4
4
  *
5
- * The `<input type="checkbox">` is the host's own and stays in the light DOM; this element
6
- * is the box around it. {@link UiCheckbox} and {@link UiSwitch} differ in what they draw
7
- * and in what they announce, never in what they are made of.
5
+ * {@link UiCheckbox} and {@link UiSwitch} differ in what they draw and in what they
6
+ * announce, never in what they are made of. Both render a native `<input type="checkbox">`
7
+ * inside a `<label>` in this element's shadow root, so every behaviour a boolean control
8
+ * has stays the platform's — see the docblock on the shared styles above.
9
+ *
10
+ * **The element is the form control**, through `ElementInternals`. An `<input>` inside a
11
+ * shadow root has no form owner, so the value reaches a submit because this element passes
12
+ * it on and never because the input did.
13
+ *
14
+ * That is the entire price, and it is worth listing because it is smaller than it looks:
15
+ * `setFormValue`, `setValidity`, the three form lifecycle callbacks a form can no longer
16
+ * perform directly, and a re-dispatch of `change`, which is non-composed. **None of it is
17
+ * accessibility** — the role, the checked state, the tab stop and what `disabled` does to
18
+ * it are all still the platform's, because the control really is one.
19
+ *
20
+ * The same object pays the other half, which is a cost of the control being *in here*
21
+ * rather than of the form: two states a host stylesheet could no longer see. See
22
+ * {@link UiToggle.expose}.
8
23
  */
9
24
  declare class UiToggle extends LitElement {
25
+ #private;
26
+ static readonly formAssociated = true;
27
+ /**
28
+ * Focus is delegated, so `focus()` and `reportValidity()` reach the control.
29
+ *
30
+ * Neither is optional: the host is not a focusable element, so without this a call to
31
+ * either lands on something that cannot take focus and does nothing — and the second
32
+ * one is what the browser does on its own when a form with an invalid control is
33
+ * submitted.
34
+ */
35
+ static readonly shadowRootOptions: {
36
+ delegatesFocus: boolean;
37
+ clonable?: boolean;
38
+ customElementRegistry?: CustomElementRegistry | null;
39
+ mode: ShadowRootMode;
40
+ serializable?: boolean;
41
+ slotAssignment?: SlotAssignmentMode;
42
+ };
43
+ static readonly properties: {
44
+ label: {
45
+ type: StringConstructor;
46
+ reflect: boolean;
47
+ };
48
+ name: {
49
+ type: StringConstructor;
50
+ reflect: boolean;
51
+ };
52
+ value: {
53
+ type: StringConstructor;
54
+ reflect: boolean;
55
+ };
56
+ checked: {
57
+ type: BooleanConstructor;
58
+ };
59
+ disabled: {
60
+ type: BooleanConstructor;
61
+ reflect: boolean;
62
+ };
63
+ required: {
64
+ type: BooleanConstructor;
65
+ reflect: boolean;
66
+ };
67
+ error: {
68
+ type: StringConstructor;
69
+ reflect: boolean;
70
+ };
71
+ };
72
+ /**
73
+ * The short road to a label. `slot="label"` overrides it where markup is needed.
74
+ *
75
+ * A plain field rather than the `accessor` keyword, for the reason `src/button.ts`
76
+ * gives beside its own — and so for every property below.
77
+ */
78
+ label: string;
79
+ /**
80
+ * The name the value is submitted under.
81
+ *
82
+ * **Reflected, and that is a requirement rather than a convenience.** A form reads a
83
+ * form-associated custom element's name from the content attribute, so a host who sets
84
+ * only the property would submit nothing at all — silently, with a value published and
85
+ * no entry to carry it.
86
+ */
87
+ name: string;
88
+ /** What a checked control submits, which the platform spells `on` by default. */
89
+ value: string;
90
+ /**
91
+ * Whether the control is on.
92
+ *
93
+ * **Not reflected, which is the platform's own answer rather than an omission.** A
94
+ * native checkbox's `checked` IDL attribute does not reflect either: the content
95
+ * attribute is the *default*, which is what a form reset returns to, and an attribute
96
+ * that followed every click would make the default whatever the user last did.
97
+ *
98
+ * A host stylesheet reaches the live state through `:state(checked)` — see
99
+ * {@link UiToggle.expose}, and the measurement there for why it is not `::part()`.
100
+ */
101
+ checked: boolean;
102
+ /** Whether the control rejects interaction. Reflected, the way the platform's is. */
103
+ disabled: boolean;
104
+ /** Whether a form is invalid while this control is off. Reflected, likewise. */
105
+ required: boolean;
106
+ /** The error message, which paints the boundary and is announced with the control. */
107
+ error: string;
108
+ /**
109
+ * Published after every render, and after the first one too — Lit calls this on the
110
+ * initial update as well, which is why there is no `firstUpdated` beside it. A second
111
+ * call from there would be a statement no test could distinguish from its own absence.
112
+ */
113
+ updated(): void;
114
+ /**
115
+ * Puts one internal state where a host stylesheet can select on it.
116
+ *
117
+ * **This is what replaces the attribute `checked` deliberately does not reflect**, and
118
+ * the alternative was measured rather than assumed: `::part(box):checked` does not
119
+ * match. A `::part()` may be followed by user-action pseudo-classes and not by state
120
+ * ones, so a host had no way at all to reach a state this element keeps in its shadow
121
+ * root — which would have made *not reflecting* a decision that costs the host
122
+ * something, rather than one that costs nothing.
123
+ *
124
+ * Only the two states with no other route are exposed. `:valid`, `:invalid` and
125
+ * `:disabled` already match on this element, measured, because a form-associated
126
+ * custom element takes part in them; `required` reflects, so `[required]` reaches it.
127
+ *
128
+ * @param state - The name, as `:state(name)` spells it.
129
+ */
130
+ protected expose(state: string, on: boolean): void;
131
+ /**
132
+ * What the inner control announces itself as, where the platform needs telling.
133
+ *
134
+ * Empty here, and the attribute is then omitted rather than written blank: an
135
+ * `<input type="checkbox">` already announces `checkbox`, and restating it would be
136
+ * this component overriding the platform with the platform. {@link UiSwitch} is the
137
+ * one that has something to add.
138
+ */
139
+ protected readonly controlRole: string;
140
+ /**
141
+ * The listener, as a field rather than a method, which is this repository's shape for
142
+ * one — `src/toast.ts` carries the same. What it delegates to is a method, so a
143
+ * subclass can extend the mirroring without restating the binding.
144
+ *
145
+ * **The `change` is re-dispatched rather than left to bubble**, because it does not:
146
+ * `change` is one of the events the platform marks non-composed, so the one the inner
147
+ * control fires stops at the shadow boundary and a host listening on the tag would
148
+ * hear nothing. `input` needs no such help — it is composed, and arrives retargeted to
149
+ * this element on its own.
150
+ */
151
+ protected readonly changed: (event: Event) => void;
152
+ /**
153
+ * Mirrors the platform's own state back into the properties it came from.
154
+ *
155
+ * Read off the control rather than inverted from what was there: the platform is what
156
+ * just changed it, and asking is the only way to stay right about a state this element
157
+ * did not decide.
158
+ */
159
+ protected sync(control: HTMLInputElement): void;
160
+ /**
161
+ * A form reset, which the platform calls and this element cannot see any other way.
162
+ *
163
+ * The content attribute is what it returns to, which is exactly `defaultChecked` on a
164
+ * native control — and it stays a usable default only because `checked` is not
165
+ * reflected onto it.
166
+ */
167
+ formResetCallback(): void;
168
+ /** A `<fieldset disabled>` above this element, which reaches it and nothing below. */
169
+ formDisabledCallback(disabled: boolean): void;
170
+ /** Restoring after a back-navigation, where the browser hands the value back. */
171
+ formStateRestoreCallback(state: string | null): void;
172
+ /** The form this element participates in, for a host that needs it. */
173
+ get form(): HTMLFormElement | null;
174
+ /** Whether the control currently satisfies its constraints. */
175
+ get validity(): ValidityState;
176
+ /** The message a form would report for it, empty while the control is valid. */
177
+ get validationMessage(): string;
10
178
  render(): TemplateResult;
179
+ /**
180
+ * The whole drawing, with the one thing the two elements disagree on passed in.
181
+ *
182
+ * **A parameter rather than an overridable hook**, and the difference is one the
183
+ * mutation floor found: a hook returning `false` is indistinguishable from a hook
184
+ * returning nothing, because `input.indeterminate` coerces `undefined` to `false` — so
185
+ * no test could tell the base implementation from its own absence. Passed at the call
186
+ * site it is a value, and `<ui-switch>` reading back an unmixed control is what checks
187
+ * it.
188
+ *
189
+ * The mixed state is bound rather than written onto the control after render, which is
190
+ * what keeps this element free of a query whose null branch nothing could reach.
191
+ */
192
+ protected template(mixed: boolean): TemplateResult;
11
193
  }
12
194
  /**
13
195
  * A checkbox, styled by the token layer rather than replaced.
14
196
  *
15
- * **The `<input>` is yours.** You write it, you set its attributes, and it stays in the
16
- * light DOM — so `name`, `checked`, `required` and `disabled` are the platform's business,
17
- * and it reaches a form submit because it is a native control inside a `<form>`. The same
18
- * shape `<ui-input>` has, for the same reason.
197
+ * **The control is this element's**, which RFC 0005 decided and reversed an earlier rule to
198
+ * do: the `<input>` and its `<label>` are rendered together in one shadow root, so `name`,
199
+ * `checked`, `required` and `disabled` are properties here rather than attributes a host
200
+ * writes on a control it supplies.
19
201
  *
20
202
  * **The indeterminate state is drawn, and that is not a feature being added.**
21
203
  * `appearance: none` takes the platform's dash away with the rest of the drawing, so a
@@ -23,19 +205,68 @@ declare class UiToggle extends LitElement {
23
205
  * rather than a missing one. The dash below is what stops that, and nothing here invites
24
206
  * a tri-state that APG says is rare.
25
207
  *
26
- * Composes with {@link UiField}, which finds the control through this wrapper and wires
27
- * the label, the help, the error and `aria-invalid` to it.
28
- *
29
208
  * @example
30
209
  * ```html
31
- * <ui-field>
32
- * <label slot="label">Send a receipt</label>
33
- * <ui-checkbox><input type="checkbox" name="receipt" /></ui-checkbox>
34
- * </ui-field>
210
+ * <ui-checkbox label="Send a receipt" name="receipt"></ui-checkbox>
211
+ * ```
212
+ *
213
+ * @example A label an attribute cannot hold.
214
+ * ```html
215
+ * <ui-checkbox name="terms">
216
+ * <span slot="label">I accept the <a href="/terms">terms</a></span>
217
+ * </ui-checkbox>
35
218
  * ```
36
219
  */
37
220
  export declare class UiCheckbox extends UiToggle {
221
+ static readonly properties: {
222
+ indeterminate: {
223
+ type: BooleanConstructor;
224
+ };
225
+ label: {
226
+ type: StringConstructor;
227
+ reflect: boolean;
228
+ };
229
+ name: {
230
+ type: StringConstructor;
231
+ reflect: boolean;
232
+ };
233
+ value: {
234
+ type: StringConstructor;
235
+ reflect: boolean;
236
+ };
237
+ checked: {
238
+ type: BooleanConstructor;
239
+ };
240
+ disabled: {
241
+ type: BooleanConstructor;
242
+ reflect: boolean;
243
+ };
244
+ required: {
245
+ type: BooleanConstructor;
246
+ reflect: boolean;
247
+ };
248
+ error: {
249
+ type: StringConstructor;
250
+ reflect: boolean;
251
+ };
252
+ };
253
+ /**
254
+ * The mixed state, which the platform stopped drawing once `appearance` was removed.
255
+ *
256
+ * It takes an attribute where the platform offers none, which is this element having
257
+ * become the control rather than a box around one — and it is not reflected, for the
258
+ * reason `checked` is not.
259
+ */
260
+ indeterminate: boolean;
38
261
  static readonly styles: CSSResult[];
262
+ render(): TemplateResult;
263
+ updated(): void;
264
+ /**
265
+ * A toggle answers the question the mixed state was asking, and the platform has
266
+ * already cleared it on the control — so this reads it back rather than assuming.
267
+ * Without it the next render would put the mixed state straight back.
268
+ */
269
+ protected sync(control: HTMLInputElement): void;
39
270
  }
40
271
  /**
41
272
  * A switch, which is a checkbox that says *on* and *off* rather than *checked*.
@@ -43,28 +274,22 @@ export declare class UiCheckbox extends UiToggle {
43
274
  * **The difference is semantic and the drawing follows it**, which is the order that
44
275
  * matters: a switch takes effect immediately and a checkbox is a value you submit, so the
45
276
  * two are not one component with two skins. `role="switch"` is what carries that to a
46
- * screen reader, and this element sets it on the slotted control rather than asking the
47
- * host to remember — forgetting it would leave a control that looks like a switch and
48
- * announces as a checkbox, with nothing anywhere to read. A role the host wrote is never
49
- * overwritten, the same way {@link UiField} never overwrites an `id` it did not generate.
277
+ * screen reader, and this element writes it on the control it renders — so a host has
278
+ * nothing to remember and nothing to forget.
50
279
  *
51
280
  * There is no native switch to delegate to: `<input type="checkbox" switch>` is
52
281
  * unsupported in the engine this suite measures, so the element is a checkbox with a role
53
282
  * and a drawing. A host who wants the mixed state wants {@link UiCheckbox} — `switch` has
54
- * no third value, so this element does not draw one.
283
+ * no third value, so this element does not draw one and offers no property for one.
55
284
  *
56
285
  * @example
57
286
  * ```html
58
- * <ui-field>
59
- * <label slot="label">Email notifications</label>
60
- * <ui-switch><input type="checkbox" name="notify" checked /></ui-switch>
61
- * </ui-field>
287
+ * <ui-switch label="Email notifications" name="notify" checked></ui-switch>
62
288
  * ```
63
289
  */
64
290
  export declare class UiSwitch extends UiToggle {
65
- #private;
66
291
  static readonly styles: CSSResult[];
67
- render(): TemplateResult;
292
+ protected readonly controlRole: string;
68
293
  }
69
294
  declare global {
70
295
  interface HTMLElementTagNameMap {
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../src/checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AA6IjF;;;;;;GAMG;AACH,cAAM,QAAS,SAAQ,UAAU;IACpB,MAAM,IAAI,cAAc;CAGpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,UAAW,SAAQ,QAAQ;IACpC,gBAAyB,MAAM,EAAE,SAAS,EAAE,CA+B1C;CACL;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,QAAS,SAAQ,QAAQ;;IAClC,gBAAyB,MAAM,EAAE,SAAS,EAAE,CAuC1C;IAEO,MAAM,IAAI,cAAc;CAmBpC;AAWD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,aAAa,EAAE,UAAU,CAAC;QAC1B,WAAW,EAAE,QAAQ,CAAC;KACzB;CACJ"}
1
+ {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../src/checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAqL1F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,cAAM,QAAS,SAAQ,UAAU;;IAC7B,MAAM,CAAC,QAAQ,CAAC,cAAc,QAAQ;IAEtC;;;;;;;OAOG;IACH,gBAAyB,iBAAiB;;;;;;;MAGxC;IAEF,gBAAyB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAQjC;IAEF;;;;;OAKG;IACH,KAAK,SAAM;IAEX;;;;;;;OAOG;IACH,IAAI,SAAM;IAEV,iFAAiF;IACjF,KAAK,SAAQ;IAEb;;;;;;;;;;OAUG;IACH,OAAO,UAAS;IAEhB,qFAAqF;IACrF,QAAQ,UAAS;IAEjB,gFAAgF;IAChF,QAAQ,UAAS;IAEjB,sFAAsF;IACtF,KAAK,SAAM;IAoBX;;;;OAIG;IACM,OAAO,IAAI,IAAI;IAKxB;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI;IAQlD;;;;;;;OAOG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAM;IAE5C;;;;;;;;;;OAUG;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,GAAI,OAAO,KAAK,KAAG,IAAI,CAG/C;IAEF;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAI/C;;;;;;OAMG;IACH,iBAAiB,IAAI,IAAI;IAIzB,sFAAsF;IACtF,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAI7C,iFAAiF;IACjF,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIpD,uEAAuE;IACvE,IAAI,IAAI,IAAI,eAAe,GAAG,IAAI,CAEjC;IAED,+DAA+D;IAC/D,IAAI,QAAQ,IAAI,aAAa,CAE5B;IAED,gFAAgF;IAChF,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAEQ,MAAM,IAAI,cAAc;IAIjC;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc;CAwBrD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,UAAW,SAAQ,QAAQ;IACpC,gBAAyB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAGjC;IAEF;;;;;;OAMG;IACH,aAAa,UAAS;IAEtB,gBAAyB,MAAM,EAAE,SAAS,EAAE,CA6C1C;IAEO,MAAM,IAAI,cAAc;IAIxB,OAAO,IAAI,IAAI;IAKxB;;;;OAIG;cACgB,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;CAI3D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,QAAS,SAAQ,QAAQ;IAClC,gBAAyB,MAAM,EAAE,SAAS,EAAE,CAuC1C;IAEF,mBAA4B,WAAW,EAAE,MAAM,CAAY;CAC9D;AAWD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,aAAa,EAAE,UAAU,CAAC;QAC1B,WAAW,EAAE,QAAQ,CAAC;KACzB;CACJ"}