@rak200/ui 0.3.0 → 0.5.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/dist/radio.d.ts CHANGED
@@ -1,101 +1,184 @@
1
- import { LitElement, type TemplateResult } from 'lit';
1
+ import { LitElement, type CSSResult, type TemplateResult } from 'lit';
2
2
  /** Which way a group lays its options out, and announces that it did. */
3
3
  export type RadioOrientation = 'vertical' | 'horizontal';
4
4
  /**
5
- * One option's control, drawn from the token layer rather than replaced.
5
+ * One choice in a {@link UiRadioGroup}: a value, and the text beside it.
6
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.
7
+ * **It draws nothing.** The group renders the real `<input type="radio">` and the
8
+ * `<label>` around it, because a `role="radiogroup"`, the name pointing at it and the
9
+ * controls it contains have to share one tree scope — `ARCHITECTURE.md`, *A relationship
10
+ * needs one tree scope*. So this element exists to be read rather than to be seen, the way
11
+ * `<ui-option>` does for `<ui-select>`.
18
12
  *
19
13
  * @example
20
14
  * ```html
21
- * <label>
22
- * <ui-radio><input type="radio" name="plan" value="free" /></ui-radio>
23
- * Free
24
- * </label>
15
+ * <ui-radio value="free">Free</ui-radio>
25
16
  * ```
26
17
  */
27
18
  export declare class UiRadio extends LitElement {
28
- static readonly styles: import("lit").CSSResult;
19
+ #private;
20
+ static readonly styles: CSSResult;
21
+ static readonly properties: {
22
+ value: {
23
+ type: StringConstructor;
24
+ reflect: boolean;
25
+ };
26
+ checked: {
27
+ type: BooleanConstructor;
28
+ reflect: boolean;
29
+ };
30
+ disabled: {
31
+ type: BooleanConstructor;
32
+ reflect: boolean;
33
+ };
34
+ };
35
+ /** What the group submits while this choice is the one in force. */
36
+ value: string;
37
+ /** Whether this is the choice a group with no `value` starts on, and resets to. */
38
+ checked: boolean;
39
+ /** Whether this one choice refuses interaction, the rest of the set staying live. */
40
+ disabled: boolean;
41
+ /**
42
+ * Tells the group something changed, on every update and on every slot change.
43
+ *
44
+ * A property write is not something a `MutationObserver` reports, which is what makes a
45
+ * declaration this package owns different from an element a host wrote: the property is
46
+ * reactive here. It bubbles because the choices are the group's own light-DOM children,
47
+ * and it is dispatched from `updated()` rather than from `disconnectedCallback`, which
48
+ * has no path left to bubble along.
49
+ */
50
+ updated(): void;
29
51
  render(): TemplateResult;
30
52
  }
31
53
  /**
32
- * A set of radios, laid out and named as one thing.
54
+ * A set of choices, laid out and named as one thing.
33
55
  *
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.
56
+ * **It hand-rolls no roving tabindex and it brings no state machine.** Native radios
57
+ * sharing a `name` **are** the APG **Radio Group** pattern — one tab stop, arrow keys that
58
+ * move *and* select, wrapping at the ends, and left/right swapped under `dir="rtl"` — and
59
+ * that holds inside a shadow root, where the group is scoped by its tree because the
60
+ * controls have no form owner. `tests/radio.test.ts` measures it rather than trusting it.
61
+ * `ARCHITECTURE.md`, *Behaviour is delegated*, carries why no machine arrived.
40
62
  *
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.
63
+ * **What it owns is what the platform leaves out**: the drawing, the layout, the group's
64
+ * own name, help and message, and form participation through `ElementInternals` — a
65
+ * control in a shadow root has no form owner, so the element answers for the value, the
66
+ * validity and the three form lifecycle callbacks.
50
67
  *
51
68
  * @example
52
69
  * ```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>
70
+ * <ui-radio-group label="Plan" name="plan" help="Change it any time.">
71
+ * <ui-radio value="free">Free</ui-radio>
72
+ * <ui-radio value="pro" checked>Pro</ui-radio>
73
+ * </ui-radio-group>
60
74
  * ```
61
75
  */
62
76
  export declare class UiRadioGroup extends LitElement {
63
- static readonly styles: import("lit").CSSResult;
77
+ #private;
78
+ static readonly styles: CSSResult;
79
+ static readonly formAssociated = true;
80
+ /**
81
+ * Focus is delegated, so `focus()` and `reportValidity()` reach a control — the host is
82
+ * not a focusable element. Measured with the group: the delegation lands on the first
83
+ * option rather than making all of them tab stops, because the roving one is still the
84
+ * platform's.
85
+ */
86
+ static readonly shadowRootOptions: {
87
+ delegatesFocus: boolean;
88
+ clonable?: boolean;
89
+ customElementRegistry?: CustomElementRegistry | null;
90
+ mode: ShadowRootMode;
91
+ serializable?: boolean;
92
+ slotAssignment?: SlotAssignmentMode;
93
+ };
64
94
  static readonly properties: {
95
+ label: {
96
+ type: StringConstructor;
97
+ reflect: boolean;
98
+ };
99
+ help: {
100
+ type: StringConstructor;
101
+ reflect: boolean;
102
+ };
103
+ error: {
104
+ type: StringConstructor;
105
+ reflect: boolean;
106
+ };
107
+ name: {
108
+ type: StringConstructor;
109
+ reflect: boolean;
110
+ };
111
+ value: {
112
+ type: StringConstructor;
113
+ };
65
114
  orientation: {
66
115
  type: StringConstructor;
67
116
  reflect: boolean;
68
117
  };
118
+ required: {
119
+ type: BooleanConstructor;
120
+ reflect: boolean;
121
+ };
122
+ disabled: {
123
+ type: BooleanConstructor;
124
+ reflect: boolean;
125
+ };
69
126
  };
70
127
  /**
71
- * Which way the options are laid out.
128
+ * The accessible name, and the text above the set.
72
129
  *
73
130
  * 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.
131
+ * gives beside its own — and so for every property below.
76
132
  */
77
- orientation: RadioOrientation;
133
+ label: string;
134
+ /** Supporting text under the set, which survives an error rather than yielding. */
135
+ help: string;
136
+ /** The message, which paints the boundaries and is announced with the group. */
137
+ error: string;
78
138
  /**
79
- * Marks the element a group before anything asks what it is.
139
+ * The name the value is submitted under.
80
140
  *
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.
141
+ * **Reflected, and that is a requirement rather than a convenience.** A form names a
142
+ * form-associated custom element's entry from the content attribute, so a host who set
143
+ * only the property would submit nothing at all — silently.
144
+ */
145
+ name: string;
146
+ /**
147
+ * The choice currently made.
86
148
  *
87
- * A `role` the host wrote is never overwritten, the same courtesy {@link UiField}
88
- * extends to an `id` it finds already there.
149
+ * **Empty means _the declared default_ rather than _nothing_**, which is what lets a
150
+ * `<ui-radio checked>` mean what `<input checked>` means and a reset return to it.
151
+ * Not reflected, the way a native control's `checked` IDL attribute is not.
89
152
  */
153
+ value: string;
154
+ /** Which way the options are laid out, and what the group announces it did. */
155
+ orientation: RadioOrientation;
156
+ /** Whether a form is invalid while no choice is made. */
157
+ required: boolean;
158
+ /** Whether the whole set refuses interaction, and submits nothing. */
159
+ disabled: boolean;
90
160
  connectedCallback(): void;
91
161
  /**
92
- * Keeps the announced orientation on the drawn one.
162
+ * The value and the validity, which move together here or disagree anywhere.
93
163
  *
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.
164
+ * The first control is the one asked, and it is the one the browser would focus when a
165
+ * form reports an invalid group. Iterated rather than asserted, for the reason
166
+ * `src/input.ts` gives beside the same shape: a null branch here is unreachable, which
167
+ * is a coverage hole and an immortal mutant at once.
97
168
  */
98
- willUpdate(): void;
169
+ updated(): void;
170
+ /** A form reset, which returns to the choice the declarations name. */
171
+ formResetCallback(): void;
172
+ /** A `<fieldset disabled>` above this element, which reaches it and nothing below. */
173
+ formDisabledCallback(disabled: boolean): void;
174
+ /** Restoring after a back-navigation, where the browser hands the value back. */
175
+ formStateRestoreCallback(state: string | null): void;
176
+ /** The form this element participates in, for a host that needs it. */
177
+ get form(): HTMLFormElement | null;
178
+ /** Whether the group currently satisfies its constraints. */
179
+ get validity(): ValidityState;
180
+ /** The message a form would report for it, empty while the group is valid. */
181
+ get validationMessage(): string;
99
182
  render(): TemplateResult;
100
183
  }
101
184
  declare global {
@@ -1 +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"}
1
+ {"version":3,"file":"radio.d.ts","sourceRoot":"","sources":["../src/radio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAG1F,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,YAAY,CAAC;AAmDzD;;;;;;;;;;;;;GAaG;AACH,qBAAa,OAAQ,SAAQ,UAAU;;IACnC,gBAAyB,MAAM,EAAE,SAAS,CAIxC;IAEF,gBAAyB,UAAU;;;;;;;;;;;;;MAIjC;IAEF,oEAAoE;IACpE,KAAK,SAAM;IAEX,mFAAmF;IACnF,OAAO,UAAS;IAEhB,qFAAqF;IACrF,QAAQ,UAAS;IAEjB;;;;;;;;OAQG;IACM,OAAO,IAAI,IAAI;IAQf,MAAM,IAAI,cAAc;CAGpC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAa,SAAQ,UAAU;;IACxC,gBAAyB,MAAM,EAAE,SAAS,CAiLxC;IAEF,MAAM,CAAC,QAAQ,CAAC,cAAc,QAAQ;IAEtC;;;;;OAKG;IACH,gBAAyB,iBAAiB;;;;;;;MAGxC;IAEF,gBAAyB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAajC;IAEF;;;;;OAKG;IACH,KAAK,SAAM;IAEX,mFAAmF;IACnF,IAAI,SAAM;IAEV,gFAAgF;IAChF,KAAK,SAAM;IAEX;;;;;;OAMG;IACH,IAAI,SAAM;IAEV;;;;;;OAMG;IACH,KAAK,SAAM;IAEX,+EAA+E;IAC/E,WAAW,EAAE,gBAAgB,CAAc;IAE3C,yDAAyD;IACzD,QAAQ,UAAS;IAEjB,sEAAsE;IACtE,QAAQ,UAAS;IAQR,iBAAiB,IAAI,IAAI;IA+ClC;;;;;;;OAOG;IACM,OAAO,IAAI,IAAI;IAoBxB,uEAAuE;IACvE,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,6DAA6D;IAC7D,IAAI,QAAQ,IAAI,aAAa,CAE5B;IAED,8EAA8E;IAC9E,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAoCQ,MAAM,IAAI,cAAc;CAiCpC;AAWD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;QACpB,gBAAgB,EAAE,YAAY,CAAC;KAClC;CACJ"}