@typed/ui 1.0.0-beta.0 → 1.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -26
  3. package/dist/Alert.d.ts +72 -0
  4. package/dist/Alert.d.ts.map +1 -0
  5. package/dist/Alert.js +40 -0
  6. package/dist/Button.d.ts +100 -0
  7. package/dist/Button.d.ts.map +1 -0
  8. package/dist/Button.js +42 -0
  9. package/dist/Carousel.d.ts +346 -0
  10. package/dist/Carousel.d.ts.map +1 -0
  11. package/dist/Carousel.js +264 -0
  12. package/dist/Checkbox.d.ts +168 -0
  13. package/dist/Checkbox.d.ts.map +1 -0
  14. package/dist/Checkbox.js +146 -0
  15. package/dist/Collection.d.ts +254 -0
  16. package/dist/Collection.d.ts.map +1 -0
  17. package/dist/Collection.js +218 -0
  18. package/dist/Combobox.d.ts +367 -0
  19. package/dist/Combobox.d.ts.map +1 -0
  20. package/dist/Combobox.js +300 -0
  21. package/dist/Composite.d.ts +823 -0
  22. package/dist/Composite.d.ts.map +1 -0
  23. package/dist/Composite.js +615 -0
  24. package/dist/Dialog.d.ts +544 -0
  25. package/dist/Dialog.d.ts.map +1 -0
  26. package/dist/Dialog.js +357 -0
  27. package/dist/Disclosure.d.ts +219 -0
  28. package/dist/Disclosure.d.ts.map +1 -0
  29. package/dist/Disclosure.js +128 -0
  30. package/dist/Dom/Events.d.ts +122 -0
  31. package/dist/Dom/Events.d.ts.map +1 -0
  32. package/dist/Dom/Events.js +192 -0
  33. package/dist/Dom/Props.d.ts +161 -0
  34. package/dist/Dom/Props.d.ts.map +1 -0
  35. package/dist/Dom/Props.js +110 -0
  36. package/dist/Dom/Refs.d.ts +58 -0
  37. package/dist/Dom/Refs.d.ts.map +1 -0
  38. package/dist/Dom/Refs.js +61 -0
  39. package/dist/Dom/Render.d.ts +59 -0
  40. package/dist/Dom/Render.d.ts.map +1 -0
  41. package/dist/Dom/Render.js +71 -0
  42. package/dist/Dom/Types.d.ts +570 -0
  43. package/dist/Dom/Types.d.ts.map +1 -0
  44. package/dist/Dom/Types.js +1 -0
  45. package/dist/Dom/index.d.ts +20 -0
  46. package/dist/Dom/index.d.ts.map +1 -0
  47. package/dist/Dom/index.js +8 -0
  48. package/dist/Dom.d.ts +14 -0
  49. package/dist/Dom.d.ts.map +1 -0
  50. package/dist/Dom.js +13 -0
  51. package/dist/Focusable.d.ts +85 -0
  52. package/dist/Focusable.d.ts.map +1 -0
  53. package/dist/Focusable.js +35 -0
  54. package/dist/Form.d.ts +1727 -0
  55. package/dist/Form.d.ts.map +1 -0
  56. package/dist/Form.js +1142 -0
  57. package/dist/Grid.d.ts +388 -0
  58. package/dist/Grid.d.ts.map +1 -0
  59. package/dist/Grid.js +284 -0
  60. package/dist/Group.d.ts +128 -0
  61. package/dist/Group.d.ts.map +1 -0
  62. package/dist/Group.js +71 -0
  63. package/dist/Heading.d.ts +87 -0
  64. package/dist/Heading.d.ts.map +1 -0
  65. package/dist/Heading.js +58 -0
  66. package/dist/Hovercard.d.ts +297 -0
  67. package/dist/Hovercard.d.ts.map +1 -0
  68. package/dist/Hovercard.js +188 -0
  69. package/dist/HttpRouter.d.ts +129 -6
  70. package/dist/HttpRouter.d.ts.map +1 -1
  71. package/dist/HttpRouter.js +199 -56
  72. package/dist/Link.d.ts +63 -28
  73. package/dist/Link.d.ts.map +1 -1
  74. package/dist/Link.js +84 -37
  75. package/dist/Listbox.d.ts +305 -0
  76. package/dist/Listbox.d.ts.map +1 -0
  77. package/dist/Listbox.js +245 -0
  78. package/dist/Menu.d.ts +663 -0
  79. package/dist/Menu.d.ts.map +1 -0
  80. package/dist/Menu.js +569 -0
  81. package/dist/Menubar.d.ts +249 -0
  82. package/dist/Menubar.d.ts.map +1 -0
  83. package/dist/Menubar.js +207 -0
  84. package/dist/Meter.d.ts +157 -0
  85. package/dist/Meter.d.ts.map +1 -0
  86. package/dist/Meter.js +87 -0
  87. package/dist/NativeDetails.d.ts +41 -0
  88. package/dist/NativeDetails.d.ts.map +1 -0
  89. package/dist/NativeDetails.js +40 -0
  90. package/dist/NativeDialog.d.ts +66 -0
  91. package/dist/NativeDialog.d.ts.map +1 -0
  92. package/dist/NativeDialog.js +88 -0
  93. package/dist/NativePopover.d.ts +43 -0
  94. package/dist/NativePopover.d.ts.map +1 -0
  95. package/dist/NativePopover.js +84 -0
  96. package/dist/Popover.d.ts +240 -0
  97. package/dist/Popover.d.ts.map +1 -0
  98. package/dist/Popover.js +140 -0
  99. package/dist/RadioGroup.d.ts +330 -0
  100. package/dist/RadioGroup.d.ts.map +1 -0
  101. package/dist/RadioGroup.js +241 -0
  102. package/dist/Role.d.ts +64 -0
  103. package/dist/Role.d.ts.map +1 -0
  104. package/dist/Role.js +27 -0
  105. package/dist/Select.d.ts +418 -0
  106. package/dist/Select.d.ts.map +1 -0
  107. package/dist/Select.js +357 -0
  108. package/dist/Separator.d.ts +58 -0
  109. package/dist/Separator.d.ts.map +1 -0
  110. package/dist/Separator.js +32 -0
  111. package/dist/Slider.d.ts +141 -0
  112. package/dist/Slider.d.ts.map +1 -0
  113. package/dist/Slider.js +101 -0
  114. package/dist/SpinButton.d.ts +141 -0
  115. package/dist/SpinButton.d.ts.map +1 -0
  116. package/dist/SpinButton.js +101 -0
  117. package/dist/Storybook.d.ts +76 -0
  118. package/dist/Storybook.d.ts.map +1 -0
  119. package/dist/Storybook.js +102 -0
  120. package/dist/Switch.d.ts +148 -0
  121. package/dist/Switch.d.ts.map +1 -0
  122. package/dist/Switch.js +110 -0
  123. package/dist/Tab.d.ts +26 -0
  124. package/dist/Tab.d.ts.map +1 -0
  125. package/dist/Tab.js +25 -0
  126. package/dist/Tabs.d.ts +411 -0
  127. package/dist/Tabs.d.ts.map +1 -0
  128. package/dist/Tabs.js +262 -0
  129. package/dist/Toolbar.d.ts +248 -0
  130. package/dist/Toolbar.d.ts.map +1 -0
  131. package/dist/Toolbar.js +187 -0
  132. package/dist/Tooltip.d.ts +296 -0
  133. package/dist/Tooltip.d.ts.map +1 -0
  134. package/dist/Tooltip.js +172 -0
  135. package/dist/Tree.d.ts +405 -0
  136. package/dist/Tree.d.ts.map +1 -0
  137. package/dist/Tree.js +333 -0
  138. package/dist/TreeGrid.d.ts +426 -0
  139. package/dist/TreeGrid.d.ts.map +1 -0
  140. package/dist/TreeGrid.js +308 -0
  141. package/dist/VisuallyHidden.d.ts +68 -0
  142. package/dist/VisuallyHidden.d.ts.map +1 -0
  143. package/dist/VisuallyHidden.js +44 -0
  144. package/dist/WindowSplitter.d.ts +336 -0
  145. package/dist/WindowSplitter.d.ts.map +1 -0
  146. package/dist/WindowSplitter.js +305 -0
  147. package/dist/index.d.ts +48 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +48 -0
  150. package/package.json +47 -14
  151. package/src/HttpRouter.test.ts +0 -294
  152. package/src/HttpRouter.ts +0 -168
  153. package/src/Link.test.ts +0 -85
  154. package/src/Link.ts +0 -107
  155. package/src/index.ts +0 -2
  156. package/tsconfig.json +0 -6
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Native radio selection with optional registered keyboard movement.
3
+ * Choice values, element IDs, and shared native names have distinct responsibilities.
4
+ *
5
+ * Read the [RadioGroup guide](/explore/ui-radio-group) for a complete example.
6
+ *
7
+ * [APG interaction reference](https://www.w3.org/WAI/ARIA/apg/patterns/radio/).
8
+ * @since 1.0.0
9
+ * @category Overview
10
+ * @packageDocumentation
11
+ */
12
+ import * as Effect from "effect/Effect";
13
+ import type * as Scope from "effect/Scope";
14
+ import * as Schema from "effect/Schema";
15
+ import type { Fx } from "@typed/fx/Fx";
16
+ import { RefSubject } from "@typed/fx";
17
+ import { EventHandler, type Renderable, type RenderEvent, type RenderTemplate } from "@typed/template";
18
+ import * as Collection from "./Collection.js";
19
+ import * as Composite from "./Composite.js";
20
+ import * as Dom from "./Dom.js";
21
+ import type { HostResult } from "./Dom/Types.js";
22
+ /**
23
+ * Selected choice plus the active element used for keyboard movement.
24
+ *
25
+ * @remarks
26
+ * value need not equal activeId. Orientation is vertical, virtualFocus is false, and the native
27
+ * inputs retain their own browser grouping by name.
28
+ * @since 1.0.0
29
+ * @category State models
30
+ */
31
+ export interface State extends Omit<Composite.State, "orientation"> {
32
+ /**
33
+ * Axis used to interpret Arrow-key movement.
34
+ * @since 1.0.0
35
+ * @category Keyboard navigation
36
+ */
37
+ readonly orientation: "vertical";
38
+ /**
39
+ * Current semantic value selected or edited by the widget.
40
+ * @since 1.0.0
41
+ * @category Value state
42
+ */
43
+ readonly value: string;
44
+ }
45
+ /**
46
+ * Required selected value with optional initial active ID and wrap policy.
47
+ *
48
+ * @remarks
49
+ * activeId defaults to null; loop defaults to true. Supply an element ID only when the initial
50
+ * active choice is known.
51
+ * @since 1.0.0
52
+ * @category State models
53
+ */
54
+ export interface InitialState {
55
+ /**
56
+ * Current semantic value selected or edited by the widget.
57
+ * @since 1.0.0
58
+ * @category Value state
59
+ */
60
+ readonly value: string;
61
+ /**
62
+ * Id currently active for keyboard navigation; null means no active item.
63
+ * @since 1.0.0
64
+ * @category Keyboard focus
65
+ */
66
+ readonly activeId?: string | null;
67
+ /**
68
+ * Whether movement wraps between the first and last enabled items.
69
+ * @since 1.0.0
70
+ * @category Keyboard navigation
71
+ */
72
+ readonly loop?: boolean;
73
+ }
74
+ /**
75
+ * Effect Schema used by makeState to encode, decode, and hydrate RadioGroup state.
76
+ *
77
+ * @remarks
78
+ * A public schema makes hydration and serialized state use the same runtime validation as direct
79
+ * construction.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * import * as Schema from "effect/Schema";
84
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
85
+ *
86
+ * const decodeState = Schema.decodeUnknownEffect(RadioGroup.StateSchema);
87
+ * ```
88
+ * @since 1.0.0
89
+ * @category Hydration schemas
90
+ */
91
+ export declare const StateSchema: Schema.Struct<{
92
+ readonly value: Schema.String;
93
+ readonly activeId: Schema.NullOr<Schema.String>;
94
+ readonly orientation: Schema.Literals<readonly ["vertical"]>;
95
+ readonly loop: Schema.Boolean;
96
+ readonly rtl: Schema.Boolean;
97
+ readonly virtualFocus: Schema.Boolean;
98
+ }>;
99
+ /**
100
+ * Creates hydrated radio selection with a required value, null active ID, and looping enabled by
101
+ * default.
102
+ *
103
+ * @remarks
104
+ * The selected value and element ID are separate. The first keyboard movement can resolve the
105
+ * selected value against the mounted collection. Creation requires Scope; renderers borrow the
106
+ * returned subject.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * import * as Effect from "effect/Effect";
111
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
112
+ *
113
+ * const program = Effect.scoped(
114
+ * Effect.gen(function* () {
115
+ * const state = yield* RadioGroup.makeState({ value: "email" });
116
+ * const collection = yield* RadioGroup.makeCollection();
117
+ * return { state: yield* state, collection: yield* collection };
118
+ * }),
119
+ * );
120
+ * ```
121
+ * @since 1.0.0
122
+ * @category State construction
123
+ */
124
+ export declare function makeState(initial: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
125
+ readonly value: string;
126
+ readonly activeId: string | null;
127
+ readonly orientation: "vertical";
128
+ readonly loop: boolean;
129
+ readonly rtl: boolean;
130
+ readonly virtualFocus: boolean;
131
+ }, Schema.SchemaError, never, never>, never, Scope.Scope>;
132
+ /**
133
+ * Creates a scoped Collection for RadioGroup items.
134
+ *
135
+ * @remarks
136
+ * State and collection ownership can be composed and tested independently from any renderer.
137
+ *
138
+ * The returned Effect allocates the RefSubject in the caller's Scope. Each later registration is
139
+ * owned by the Scope that runs register, independently of this construction Effect.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * import * as Effect from "effect/Effect";
144
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
145
+ *
146
+ * const program = Effect.scoped(
147
+ * Effect.gen(function* () {
148
+ * const collection = yield* RadioGroup.makeCollection();
149
+ * return yield* collection;
150
+ * }),
151
+ * );
152
+ * ```
153
+ * @since 1.0.0
154
+ * @category State construction
155
+ */
156
+ export declare const makeCollection: (initial?: Collection.State<string, Element>) => Effect.Effect<RefSubject.RefSubject<Collection.State<string, Element>, never, never>, never, Scope.Scope>;
157
+ /**
158
+ * Sets the selected value and optionally the active element ID.
159
+ *
160
+ * @remarks
161
+ * Omitting activeId preserves the previous active ID. This state update does not look up an
162
+ * item, move DOM focus, or verify that the value belongs to the group.
163
+ * @since 1.0.0
164
+ * @category State transitions
165
+ */
166
+ export declare function setValue<E, R>(state: RefSubject.RefSubject<State, E, R>, value: string, activeId?: string): Effect.Effect<State, E, R>;
167
+ /**
168
+ * State, optional navigation collection, child content, and group name.
169
+ *
170
+ * @remarks
171
+ * Share the same collection with Item to enable registered keyboard movement. label names the
172
+ * group; each item still needs its own label.
173
+ * @since 1.0.0
174
+ * @category Component options
175
+ */
176
+ export interface RootOptions extends Dom.HostOptions<HTMLDivElement> {
177
+ /**
178
+ * Renderer-independent RefSubject state consumed by this component or operation.
179
+ * @since 1.0.0
180
+ * @category State connection
181
+ */
182
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
183
+ /**
184
+ * Item registry used for collection-driven keyboard behavior and mounted ordering.
185
+ * @since 1.0.0
186
+ * @category Item registration
187
+ */
188
+ readonly collection?: RefSubject.RefSubject<Collection.State<string>>;
189
+ /**
190
+ * Renderable child content for the component host.
191
+ * @since 1.0.0
192
+ * @category Rendered content
193
+ */
194
+ readonly content: Renderable.Any;
195
+ /**
196
+ * Accessible label rendered through aria-label.
197
+ * @since 1.0.0
198
+ * @category Accessible naming
199
+ */
200
+ readonly label?: Renderable.Any<string | null | undefined>;
201
+ }
202
+ declare function rootInternalProps<const Options extends RootOptions>(options: Options): ({ property }: Dom.InternalPropsHelpers<Options>) => {
203
+ readonly role: "radiogroup";
204
+ readonly "aria-label": Exclude<Dom.Property<Options, "label">, Dom.Nullish> | undefined;
205
+ readonly onkeydown: EventHandler.EventHandler<KeyboardEvent, Schema.SchemaError, never> | undefined;
206
+ readonly ref: RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>;
207
+ };
208
+ type RootInternalProps<Options extends RootOptions> = ReturnType<ReturnType<typeof rootInternalProps<Options>>>;
209
+ /**
210
+ * Renders a named radiogroup around native radio items.
211
+ *
212
+ * @remarks
213
+ * Pass the same state to every item and the same native name to items in one group. With a
214
+ * collection, the root handles vertical arrow movement and Home/End, skips disabled
215
+ * registrations, updates selection, and moves real focus. Without a collection, native input
216
+ * behavior remains the keyboard boundary.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * import { html } from "@typed/template";
221
+ * import { component } from "@typed/template";
222
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
223
+ *
224
+ * export const DeliveryChoice = component(function* () {
225
+ * const state = yield* RadioGroup.makeState({ value: "standard" });
226
+ * const collection = yield* RadioGroup.makeCollection();
227
+ * return RadioGroup.Root({
228
+ * state,
229
+ * collection,
230
+ * label: "Delivery speed",
231
+ * content: html`
232
+ * <label>${RadioGroup.Item({
233
+ * state, collection, id: "delivery-standard", name: "delivery", value: "standard",
234
+ * })} Standard delivery</label>
235
+ * <label>${RadioGroup.Item({
236
+ * state, collection, id: "delivery-express", name: "delivery", value: "express",
237
+ * })} Express delivery</label>
238
+ * `,
239
+ * });
240
+ * });
241
+ * ```
242
+ * @since 1.0.0
243
+ * @category Native controls
244
+ */
245
+ export declare function Root<const Options extends RootOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, RootInternalProps<Options>>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
246
+ /**
247
+ * One native choice with stable element ID, application value, and native group name.
248
+ *
249
+ * @remarks
250
+ * Use the same state and name across one radio group, unique IDs across the document, and distinct
251
+ * values across choices. disabled is a boolean used by both rendering and registration.
252
+ * @since 1.0.0
253
+ * @category Component options
254
+ */
255
+ export interface ItemOptions extends Dom.HostOptions<HTMLInputElement> {
256
+ /**
257
+ * Renderer-independent RefSubject state consumed by this component or operation.
258
+ * @since 1.0.0
259
+ * @category State connection
260
+ */
261
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
262
+ /**
263
+ * Item registry used for collection-driven keyboard behavior and mounted ordering.
264
+ * @since 1.0.0
265
+ * @category Item registration
266
+ */
267
+ readonly collection?: RefSubject.RefSubject<Collection.State<string>>;
268
+ /**
269
+ * Stable id used for collection identity and ARIA relationships.
270
+ * @since 1.0.0
271
+ * @category Identity and relationships
272
+ */
273
+ readonly id: string;
274
+ /**
275
+ * Current semantic value selected or edited by the widget.
276
+ * @since 1.0.0
277
+ * @category Value state
278
+ */
279
+ readonly value: string;
280
+ /**
281
+ * Native radio-group form name shared by related input items.
282
+ * @since 1.0.0
283
+ * @category Native form data
284
+ */
285
+ readonly name?: string;
286
+ /**
287
+ * Flag used by collection movement and widget handlers to skip activation by default.
288
+ * @since 1.0.0
289
+ * @category Availability
290
+ */
291
+ readonly disabled?: boolean;
292
+ }
293
+ declare function itemInternalProps<const Options extends ItemOptions>(options: Options): () => {
294
+ readonly id: string;
295
+ readonly type: "radio";
296
+ readonly role: "radio";
297
+ readonly name: string | undefined;
298
+ readonly value: string;
299
+ readonly "?disabled": boolean;
300
+ readonly "aria-checked": RefSubject.Computed<boolean, Schema.SchemaError, never>;
301
+ readonly "aria-disabled": boolean;
302
+ readonly "?checked": RefSubject.Computed<boolean, Schema.SchemaError, never>;
303
+ readonly ".checked": RefSubject.Computed<boolean, Schema.SchemaError, never>;
304
+ readonly onchange: Effect.Effect<void, never, never> | Effect.Effect<State, Schema.SchemaError, never>;
305
+ readonly ref: ((element: Element & HTMLInputElement & RefSubject.HydrationElement) => Effect.Effect<void, any, any>) | undefined;
306
+ };
307
+ type ItemInternalProps<Options extends ItemOptions> = ReturnType<ReturnType<typeof itemInternalProps<Options>>>;
308
+ /**
309
+ * Renders one native radio input and optionally registers it for group navigation.
310
+ *
311
+ * @remarks
312
+ * id identifies the element; value identifies the choice; name establishes the native browser
313
+ * group. A native change updates the selected value and active ID. Supply a wrapping label or an
314
+ * external label linked to id. Registration ends with the rendered Scope.
315
+ * @since 1.0.0
316
+ * @category Native controls
317
+ */
318
+ export declare function Item<const Options extends ItemOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, ItemInternalProps<Options>>, "", Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
319
+ /**
320
+ * Alias of Root.
321
+ *
322
+ * @remarks
323
+ * The alias exposes the same native-input grouping and optional collection behavior; it introduces
324
+ * no additional state or focus policy.
325
+ * @since 1.0.0
326
+ * @category Native controls
327
+ */
328
+ export declare const RadioGroup: typeof Root;
329
+ export {};
330
+ //# sourceMappingURL=RadioGroup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RadioGroup.d.ts","sourceRoot":"","sources":["../src/RadioGroup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Native radio selection with optional registered keyboard movement.
3
+ * Choice values, element IDs, and shared native names have distinct responsibilities.
4
+ *
5
+ * Read the [RadioGroup guide](/explore/ui-radio-group) for a complete example.
6
+ *
7
+ * [APG interaction reference](https://www.w3.org/WAI/ARIA/apg/patterns/radio/).
8
+ * @since 1.0.0
9
+ * @category Overview
10
+ * @packageDocumentation
11
+ */
12
+ import * as Effect from "effect/Effect";
13
+ import * as Schema from "effect/Schema";
14
+ import { RefSubject } from "@typed/fx";
15
+ import { EventHandler, html, } from "@typed/template";
16
+ import * as Collection from "./Collection.js";
17
+ import * as Composite from "./Composite.js";
18
+ import * as Dom from "./Dom.js";
19
+ /**
20
+ * Effect Schema used by makeState to encode, decode, and hydrate RadioGroup state.
21
+ *
22
+ * @remarks
23
+ * A public schema makes hydration and serialized state use the same runtime validation as direct
24
+ * construction.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import * as Schema from "effect/Schema";
29
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
30
+ *
31
+ * const decodeState = Schema.decodeUnknownEffect(RadioGroup.StateSchema);
32
+ * ```
33
+ * @since 1.0.0
34
+ * @category Hydration schemas
35
+ */
36
+ export const StateSchema = Schema.Struct({
37
+ value: Schema.String,
38
+ activeId: Schema.NullOr(Schema.String),
39
+ orientation: Schema.Literals(["vertical"]),
40
+ loop: Schema.Boolean,
41
+ rtl: Schema.Boolean,
42
+ virtualFocus: Schema.Boolean,
43
+ });
44
+ /**
45
+ * Creates hydrated radio selection with a required value, null active ID, and looping enabled by
46
+ * default.
47
+ *
48
+ * @remarks
49
+ * The selected value and element ID are separate. The first keyboard movement can resolve the
50
+ * selected value against the mounted collection. Creation requires Scope; renderers borrow the
51
+ * returned subject.
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * import * as Effect from "effect/Effect";
56
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
57
+ *
58
+ * const program = Effect.scoped(
59
+ * Effect.gen(function* () {
60
+ * const state = yield* RadioGroup.makeState({ value: "email" });
61
+ * const collection = yield* RadioGroup.makeCollection();
62
+ * return { state: yield* state, collection: yield* collection };
63
+ * }),
64
+ * );
65
+ * ```
66
+ * @since 1.0.0
67
+ * @category State construction
68
+ */
69
+ export function makeState(initial) {
70
+ return RefSubject.hydrate(StateSchema, {
71
+ value: initial.value,
72
+ activeId: initial.activeId ?? null,
73
+ orientation: "vertical",
74
+ loop: initial.loop ?? true,
75
+ rtl: false,
76
+ virtualFocus: false,
77
+ });
78
+ }
79
+ /**
80
+ * Creates a scoped Collection for RadioGroup items.
81
+ *
82
+ * @remarks
83
+ * State and collection ownership can be composed and tested independently from any renderer.
84
+ *
85
+ * The returned Effect allocates the RefSubject in the caller's Scope. Each later registration is
86
+ * owned by the Scope that runs register, independently of this construction Effect.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * import * as Effect from "effect/Effect";
91
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
92
+ *
93
+ * const program = Effect.scoped(
94
+ * Effect.gen(function* () {
95
+ * const collection = yield* RadioGroup.makeCollection();
96
+ * return yield* collection;
97
+ * }),
98
+ * );
99
+ * ```
100
+ * @since 1.0.0
101
+ * @category State construction
102
+ */
103
+ export const makeCollection = Collection.makeState;
104
+ /**
105
+ * Sets the selected value and optionally the active element ID.
106
+ *
107
+ * @remarks
108
+ * Omitting activeId preserves the previous active ID. This state update does not look up an
109
+ * item, move DOM focus, or verify that the value belongs to the group.
110
+ * @since 1.0.0
111
+ * @category State transitions
112
+ */
113
+ export function setValue(state, value, activeId) {
114
+ return RefSubject.update(state, (current) => ({
115
+ ...current,
116
+ value,
117
+ activeId: activeId ?? current.activeId,
118
+ }));
119
+ }
120
+ function move(state, collection, direction) {
121
+ return Effect.gen(function* () {
122
+ const current = yield* state;
123
+ const items = yield* collection;
124
+ const activeId = current.activeId ?? items.find((item) => item.value === current.value)?.id ?? null;
125
+ const nextId = Composite.moveActiveId(items, { ...current, activeId }, direction);
126
+ const item = nextId === null ? undefined : items.find((item) => item.id === nextId);
127
+ if (item?.value === undefined)
128
+ return current;
129
+ const next = yield* setValue(state, item.value, item.id);
130
+ yield* Composite.focusActive({ state, collection });
131
+ return next;
132
+ });
133
+ }
134
+ function rootInternalProps(options) {
135
+ const onkeydown = options.collection === undefined
136
+ ? undefined
137
+ : EventHandler.make(Effect.fn(function* (event) {
138
+ const direction = Composite.keyMove(event, { orientation: "vertical" });
139
+ if (direction === undefined)
140
+ return;
141
+ event.preventDefault();
142
+ yield* move(options.state, options.collection, direction);
143
+ }));
144
+ return ({ property }) => ({
145
+ role: "radiogroup",
146
+ "aria-label": property("label", undefined),
147
+ onkeydown,
148
+ ref: options.state,
149
+ });
150
+ }
151
+ /**
152
+ * Renders a named radiogroup around native radio items.
153
+ *
154
+ * @remarks
155
+ * Pass the same state to every item and the same native name to items in one group. With a
156
+ * collection, the root handles vertical arrow movement and Home/End, skips disabled
157
+ * registrations, updates selection, and moves real focus. Without a collection, native input
158
+ * behavior remains the keyboard boundary.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * import { html } from "@typed/template";
163
+ * import { component } from "@typed/template";
164
+ * import * as RadioGroup from "@typed/ui/RadioGroup";
165
+ *
166
+ * export const DeliveryChoice = component(function* () {
167
+ * const state = yield* RadioGroup.makeState({ value: "standard" });
168
+ * const collection = yield* RadioGroup.makeCollection();
169
+ * return RadioGroup.Root({
170
+ * state,
171
+ * collection,
172
+ * label: "Delivery speed",
173
+ * content: html`
174
+ * <label>${RadioGroup.Item({
175
+ * state, collection, id: "delivery-standard", name: "delivery", value: "standard",
176
+ * })} Standard delivery</label>
177
+ * <label>${RadioGroup.Item({
178
+ * state, collection, id: "delivery-express", name: "delivery", value: "express",
179
+ * })} Express delivery</label>
180
+ * `,
181
+ * });
182
+ * });
183
+ * ```
184
+ * @since 1.0.0
185
+ * @category Native controls
186
+ */
187
+ export function Root(options, host) {
188
+ return Dom.renderHost()(options, host, rootInternalProps(options), options.content, (props, content) => {
189
+ return html `<div ...${props}>${content}</div>`;
190
+ });
191
+ }
192
+ function itemInternalProps(options) {
193
+ const checked = RefSubject.map(options.state, (state) => state.value === options.value);
194
+ const register = options.collection === undefined
195
+ ? undefined
196
+ : Collection.ref(options.collection, {
197
+ id: options.id,
198
+ value: options.value,
199
+ textValue: options.value,
200
+ disabled: options.disabled,
201
+ });
202
+ return () => ({
203
+ id: options.id,
204
+ type: "radio",
205
+ role: "radio",
206
+ name: options.name,
207
+ value: options.value,
208
+ "?disabled": options.disabled ?? false,
209
+ "aria-checked": checked,
210
+ "aria-disabled": options.disabled ?? false,
211
+ "?checked": checked,
212
+ ".checked": checked,
213
+ onchange: options.disabled === true
214
+ ? Effect.void
215
+ : setValue(options.state, options.value, options.id),
216
+ ref: Dom.composeRefs(register, options.ref),
217
+ });
218
+ }
219
+ /**
220
+ * Renders one native radio input and optionally registers it for group navigation.
221
+ *
222
+ * @remarks
223
+ * id identifies the element; value identifies the choice; name establishes the native browser
224
+ * group. A native change updates the selected value and active ID. Supply a wrapping label or an
225
+ * external label linked to id. Registration ends with the rendered Scope.
226
+ * @since 1.0.0
227
+ * @category Native controls
228
+ */
229
+ export function Item(options, host) {
230
+ return Dom.renderHost()(options, host, itemInternalProps(options), "", (props) => html `<input ...${props} />`);
231
+ }
232
+ /**
233
+ * Alias of Root.
234
+ *
235
+ * @remarks
236
+ * The alias exposes the same native-input grouping and optional collection behavior; it introduces
237
+ * no additional state or focus policy.
238
+ * @since 1.0.0
239
+ * @category Native controls
240
+ */
241
+ export const RadioGroup = Root;
package/dist/Role.d.ts ADDED
@@ -0,0 +1,64 @@
1
+ import type * as Scope from "effect/Scope";
2
+ import type { Fx } from "@typed/fx/Fx";
3
+ import { type Renderable, type RenderEvent, type RenderTemplate } from "@typed/template";
4
+ import * as Dom from "./Dom.js";
5
+ import type { HostResult } from "./Dom/Types.js";
6
+ /**
7
+ * Options for assigning explicit ARIA semantics to a generic host.
8
+ * @remarks
9
+ * ## Why
10
+ * This primitive keeps role selection visible when no dedicated component
11
+ * matches the required semantic.
12
+ * ## Ownership and lifetime
13
+ * The model acquires no resources; dynamic values follow the rendered Scope.
14
+ * @since 1.0.0
15
+ * @category Semantic hosts
16
+ */
17
+ export interface RoleOptions extends Dom.HostOptions<HTMLDivElement> {
18
+ /** Content rendered inside the semantic host.
19
+ * @remarks
20
+ * ## Why
21
+ * Native content remains available to the accessibility tree.
22
+ * ## Ownership and lifetime
23
+ * Dynamic content follows the component Scope.
24
+ * @since 1.0.0
25
+ * @category Rendered content
26
+ */
27
+ readonly content: Renderable.Any;
28
+ /** The explicit ARIA role.
29
+ * @remarks
30
+ * ## Why
31
+ * Callers must choose semantics appropriate to their interaction model.
32
+ * ## Ownership and lifetime
33
+ * The reflected value retains no resources.
34
+ * @since 1.0.0
35
+ * @category Semantic roles
36
+ */
37
+ readonly role: Renderable.Any<string | null | undefined>;
38
+ }
39
+ declare function internalProps<const Options extends RoleOptions>({ property, }: Dom.InternalPropsHelpers<Options>): {
40
+ role: Exclude<Dom.Property<Options, "role">, Dom.Nullish> | undefined;
41
+ };
42
+ type RoleInternalProps<Options extends RoleOptions> = ReturnType<typeof internalProps<Options>>;
43
+ /**
44
+ * Renders a generic element with caller-selected ARIA semantics.
45
+ * @remarks
46
+ * ## Why
47
+ * It provides cooperative host composition without pretending every ARIA role
48
+ * has identical keyboard behavior; the caller remains responsible for the
49
+ * chosen role's complete accessibility contract.
50
+ * ## Ownership and lifetime
51
+ * Running the returned Fx owns dynamic attributes and content until its Effect
52
+ * Scope closes. A custom host must preserve the supplied role.
53
+ * @example
54
+ * ```ts
55
+ * import { Role } from "@typed/ui/Role"
56
+ *
57
+ * const status = Role({ role: "status", content: "Ready" })
58
+ * ```
59
+ * @since 1.0.0
60
+ * @category Semantic hosts
61
+ */
62
+ export declare function Role<const Options extends RoleOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, RoleInternalProps<Options>>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
63
+ export {};
64
+ //# sourceMappingURL=Role.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Role.d.ts","sourceRoot":"","sources":["../src/Role.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAQ,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/F,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAY,SAAQ,GAAG,CAAC,WAAW,CAAC,cAAc,CAAC;IAClE;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CAC1D;AAED,iBAAS,aAAa,CAAC,KAAK,CAAC,OAAO,SAAS,WAAW,EAAE,EACxD,QAAQ,GACT,EAAE,GAAG,CAAC,oBAAoB,CAAC,OAAO,CAAC;IACzB,IAAI;EACd;AAED,KAAK,iBAAiB,CAAC,OAAO,SAAS,WAAW,IAAI,UAAU,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;AAEhG;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,IAAI,CAAC,KAAK,CAAC,OAAO,SAAS,WAAW,EAAE,KAAK,CAAC,IAAI,SAAS,UAAU,GAAG,KAAK,EAC3F,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,GAAG,CAAC,YAAY,CACrB,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,EACxD,OAAO,CAAC,SAAS,CAAC,EAClB,IAAI,CACL,GACA,EAAE,CACH,WAAW,EACX,UAAU,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,EAChC,UAAU,CAAC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,GAAG,cAAc,CACnE,CAcA"}
package/dist/Role.js ADDED
@@ -0,0 +1,27 @@
1
+ import { html } from "@typed/template";
2
+ import * as Dom from "./Dom.js";
3
+ function internalProps({ property, }) {
4
+ return { role: property("role", undefined) };
5
+ }
6
+ /**
7
+ * Renders a generic element with caller-selected ARIA semantics.
8
+ * @remarks
9
+ * ## Why
10
+ * It provides cooperative host composition without pretending every ARIA role
11
+ * has identical keyboard behavior; the caller remains responsible for the
12
+ * chosen role's complete accessibility contract.
13
+ * ## Ownership and lifetime
14
+ * Running the returned Fx owns dynamic attributes and content until its Effect
15
+ * Scope closes. A custom host must preserve the supplied role.
16
+ * @example
17
+ * ```ts
18
+ * import { Role } from "@typed/ui/Role"
19
+ *
20
+ * const status = Role({ role: "status", content: "Ready" })
21
+ * ```
22
+ * @since 1.0.0
23
+ * @category Semantic hosts
24
+ */
25
+ export function Role(options, host) {
26
+ return Dom.renderHost()(options, host, internalProps, options.content, (props, content) => html `<div ...${props}>${content}</div>`);
27
+ }