@typed/ui 1.0.0-beta.4 → 1.0.0-beta.6

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 (157) hide show
  1. package/README.md +53 -9
  2. package/dist/Alert.d.ts +78 -0
  3. package/dist/Alert.d.ts.map +1 -0
  4. package/dist/Alert.js +37 -0
  5. package/dist/Button.d.ts +144 -0
  6. package/dist/Button.d.ts.map +1 -0
  7. package/dist/Button.js +38 -0
  8. package/dist/Carousel.d.ts +547 -0
  9. package/dist/Carousel.d.ts.map +1 -0
  10. package/dist/Carousel.js +387 -0
  11. package/dist/Checkbox.d.ts +203 -0
  12. package/dist/Checkbox.d.ts.map +1 -0
  13. package/dist/Checkbox.js +140 -0
  14. package/dist/Collection.d.ts +254 -0
  15. package/dist/Collection.d.ts.map +1 -0
  16. package/dist/Collection.js +218 -0
  17. package/dist/Combobox.d.ts +516 -0
  18. package/dist/Combobox.d.ts.map +1 -0
  19. package/dist/Combobox.js +371 -0
  20. package/dist/Component.d.ts +127 -0
  21. package/dist/Component.d.ts.map +1 -0
  22. package/dist/Component.js +60 -0
  23. package/dist/Composite.d.ts +823 -0
  24. package/dist/Composite.d.ts.map +1 -0
  25. package/dist/Composite.js +615 -0
  26. package/dist/Dialog.d.ts +544 -0
  27. package/dist/Dialog.d.ts.map +1 -0
  28. package/dist/Dialog.js +355 -0
  29. package/dist/Disclosure.d.ts +219 -0
  30. package/dist/Disclosure.d.ts.map +1 -0
  31. package/dist/Disclosure.js +128 -0
  32. package/dist/Dom/Events.d.ts +122 -0
  33. package/dist/Dom/Events.d.ts.map +1 -0
  34. package/dist/Dom/Events.js +192 -0
  35. package/dist/Dom/Props.d.ts +161 -0
  36. package/dist/Dom/Props.d.ts.map +1 -0
  37. package/dist/Dom/Props.js +110 -0
  38. package/dist/Dom/Refs.d.ts +58 -0
  39. package/dist/Dom/Refs.d.ts.map +1 -0
  40. package/dist/Dom/Refs.js +61 -0
  41. package/dist/Dom/Render.d.ts +59 -0
  42. package/dist/Dom/Render.d.ts.map +1 -0
  43. package/dist/Dom/Render.js +71 -0
  44. package/dist/Dom/Types.d.ts +570 -0
  45. package/dist/Dom/Types.d.ts.map +1 -0
  46. package/dist/Dom/Types.js +1 -0
  47. package/dist/Dom/index.d.ts +20 -0
  48. package/dist/Dom/index.d.ts.map +1 -0
  49. package/dist/Dom/index.js +8 -0
  50. package/dist/Dom.d.ts +14 -0
  51. package/dist/Dom.d.ts.map +1 -0
  52. package/dist/Dom.js +13 -0
  53. package/dist/Focusable.d.ts +85 -0
  54. package/dist/Focusable.d.ts.map +1 -0
  55. package/dist/Focusable.js +35 -0
  56. package/dist/Form.d.ts +1695 -0
  57. package/dist/Form.d.ts.map +1 -0
  58. package/dist/Form.js +987 -0
  59. package/dist/Grid.d.ts +569 -0
  60. package/dist/Grid.d.ts.map +1 -0
  61. package/dist/Grid.js +379 -0
  62. package/dist/Group.d.ts +147 -0
  63. package/dist/Group.d.ts.map +1 -0
  64. package/dist/Group.js +63 -0
  65. package/dist/Heading.d.ts +86 -0
  66. package/dist/Heading.d.ts.map +1 -0
  67. package/dist/Heading.js +48 -0
  68. package/dist/Hovercard.d.ts +297 -0
  69. package/dist/Hovercard.d.ts.map +1 -0
  70. package/dist/Hovercard.js +188 -0
  71. package/dist/HttpRouter.d.ts +129 -6
  72. package/dist/HttpRouter.d.ts.map +1 -1
  73. package/dist/HttpRouter.js +196 -53
  74. package/dist/Link.d.ts +67 -28
  75. package/dist/Link.d.ts.map +1 -1
  76. package/dist/Link.js +91 -37
  77. package/dist/Listbox.d.ts +437 -0
  78. package/dist/Listbox.d.ts.map +1 -0
  79. package/dist/Listbox.js +316 -0
  80. package/dist/Menu.d.ts +972 -0
  81. package/dist/Menu.d.ts.map +1 -0
  82. package/dist/Menu.js +731 -0
  83. package/dist/Menubar.d.ts +367 -0
  84. package/dist/Menubar.d.ts.map +1 -0
  85. package/dist/Menubar.js +265 -0
  86. package/dist/Meter.d.ts +217 -0
  87. package/dist/Meter.d.ts.map +1 -0
  88. package/dist/Meter.js +94 -0
  89. package/dist/NativeDetails.d.ts +41 -0
  90. package/dist/NativeDetails.d.ts.map +1 -0
  91. package/dist/NativeDetails.js +40 -0
  92. package/dist/NativeDialog.d.ts +64 -0
  93. package/dist/NativeDialog.d.ts.map +1 -0
  94. package/dist/NativeDialog.js +50 -0
  95. package/dist/NativePopover.d.ts +41 -0
  96. package/dist/NativePopover.d.ts.map +1 -0
  97. package/dist/NativePopover.js +46 -0
  98. package/dist/Popover.d.ts +241 -0
  99. package/dist/Popover.d.ts.map +1 -0
  100. package/dist/Popover.js +141 -0
  101. package/dist/RadioGroup.d.ts +432 -0
  102. package/dist/RadioGroup.d.ts.map +1 -0
  103. package/dist/RadioGroup.js +291 -0
  104. package/dist/Role.d.ts +64 -0
  105. package/dist/Role.d.ts.map +1 -0
  106. package/dist/Role.js +27 -0
  107. package/dist/Select.d.ts +529 -0
  108. package/dist/Select.d.ts.map +1 -0
  109. package/dist/Select.js +407 -0
  110. package/dist/Separator.d.ts +54 -0
  111. package/dist/Separator.d.ts.map +1 -0
  112. package/dist/Separator.js +26 -0
  113. package/dist/Slider.d.ts +188 -0
  114. package/dist/Slider.d.ts.map +1 -0
  115. package/dist/Slider.js +96 -0
  116. package/dist/SpinButton.d.ts +188 -0
  117. package/dist/SpinButton.d.ts.map +1 -0
  118. package/dist/SpinButton.js +96 -0
  119. package/dist/Storybook.d.ts +76 -0
  120. package/dist/Storybook.d.ts.map +1 -0
  121. package/dist/Storybook.js +102 -0
  122. package/dist/Switch.d.ts +187 -0
  123. package/dist/Switch.d.ts.map +1 -0
  124. package/dist/Switch.js +116 -0
  125. package/dist/Tab.d.ts +26 -0
  126. package/dist/Tab.d.ts.map +1 -0
  127. package/dist/Tab.js +25 -0
  128. package/dist/Tabs.d.ts +591 -0
  129. package/dist/Tabs.d.ts.map +1 -0
  130. package/dist/Tabs.js +346 -0
  131. package/dist/Toolbar.d.ts +366 -0
  132. package/dist/Toolbar.d.ts.map +1 -0
  133. package/dist/Toolbar.js +245 -0
  134. package/dist/Tooltip.d.ts +296 -0
  135. package/dist/Tooltip.d.ts.map +1 -0
  136. package/dist/Tooltip.js +172 -0
  137. package/dist/Tree.d.ts +591 -0
  138. package/dist/Tree.d.ts.map +1 -0
  139. package/dist/Tree.js +434 -0
  140. package/dist/TreeGrid.d.ts +645 -0
  141. package/dist/TreeGrid.d.ts.map +1 -0
  142. package/dist/TreeGrid.js +420 -0
  143. package/dist/VisuallyHidden.d.ts +54 -0
  144. package/dist/VisuallyHidden.d.ts.map +1 -0
  145. package/dist/VisuallyHidden.js +27 -0
  146. package/dist/WindowSplitter.d.ts +376 -0
  147. package/dist/WindowSplitter.d.ts.map +1 -0
  148. package/dist/WindowSplitter.js +222 -0
  149. package/dist/index.d.ts +49 -0
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +49 -0
  152. package/package.json +45 -18
  153. package/src/HttpRouter.test.ts +0 -256
  154. package/src/HttpRouter.ts +0 -165
  155. package/src/Link.test.ts +0 -84
  156. package/src/Link.ts +0 -107
  157. package/src/index.ts +0 -2
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Carousel state keeps the active slide and rotation pause policy outside the renderer. Root
3
+ * pauses rotation on focus and pointer entry; slides retain DOM identity while hidden; controls
4
+ * use native button clicks.
5
+ *
6
+ * @remarks
7
+ * The module keeps policy, state transitions, and DOM rendering separable so applications can use
8
+ * the state and pure operations without mounting UI, or supply custom hosts without replacing native
9
+ * events and browser-owned focus.
10
+ *
11
+ * @since 1.0.0
12
+ * @category modules
13
+ * @packageDocumentation
14
+ */
15
+ import * as Effect from "effect/Effect";
16
+ import * as Schema from "effect/Schema";
17
+ import { RefSubject } from "@typed/fx";
18
+ import { html } from "@typed/template";
19
+ import * as Collection from "./Collection.js";
20
+ import * as Composite from "./Composite.js";
21
+ import * as Dom from "./Dom.js";
22
+ /**
23
+ * Effect Schema used by makeState to encode, decode, and hydrate Carousel state.
24
+ *
25
+ * @remarks
26
+ * ## Why
27
+ *
28
+ * A public schema makes hydration and serialized state use the same runtime validation as direct
29
+ * construction.
30
+ *
31
+ * ## Ownership and lifetime
32
+ *
33
+ * This declaration is data or schema metadata and acquires no resources.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import * as Schema from "effect/Schema";
38
+ * import * as Carousel from "@typed/ui/Carousel";
39
+ *
40
+ * const decodeState = Schema.decodeUnknownEffect(Carousel.StateSchema);
41
+ * ```
42
+ * @since 1.0.0
43
+ * @category schemas
44
+ */
45
+ export const StateSchema = Schema.Struct({ activeId: Schema.String, paused: Schema.Boolean });
46
+ /**
47
+ * Creates hydrated Carousel state. activeId is required and paused defaults to true.
48
+ *
49
+ * @remarks
50
+ * ## Why
51
+ *
52
+ * State and collection ownership can be composed and tested independently from any renderer.
53
+ *
54
+ * ## Ownership and lifetime
55
+ *
56
+ * The returned Effect creates the RefSubject when run. That state is renderer-independent;
57
+ * collection registrations belong to the separate Scope that runs register or ref, not to state
58
+ * creation.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * import * as Effect from "effect/Effect";
63
+ * import * as Carousel from "@typed/ui/Carousel";
64
+ *
65
+ * const program = Effect.scoped(
66
+ * Effect.gen(function* () {
67
+ * const state = yield* Carousel.makeState({ activeId: "slide-1" });
68
+ * const collection = yield* Carousel.makeCollection();
69
+ * return { state: yield* state, collection: yield* collection };
70
+ * }),
71
+ * );
72
+ * ```
73
+ * @since 1.0.0
74
+ * @category constructors
75
+ */
76
+ export function makeState(initial) {
77
+ return RefSubject.hydrate(StateSchema, {
78
+ activeId: initial.activeId,
79
+ paused: initial.paused ?? true,
80
+ });
81
+ }
82
+ /**
83
+ * Creates a scoped Collection for Carousel items.
84
+ *
85
+ * @remarks
86
+ * ## Why
87
+ *
88
+ * State and collection ownership can be composed and tested independently from any renderer.
89
+ *
90
+ * ## Ownership and lifetime
91
+ *
92
+ * The returned Effect allocates the RefSubject in the caller's Scope. Each later registration is
93
+ * owned by the Scope that runs register, independently of this construction Effect.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * import * as Effect from "effect/Effect";
98
+ * import * as Carousel from "@typed/ui/Carousel";
99
+ *
100
+ * const program = Effect.scoped(
101
+ * Effect.gen(function* () {
102
+ * const collection = yield* Carousel.makeCollection();
103
+ * return yield* collection;
104
+ * }),
105
+ * );
106
+ * ```
107
+ * @since 1.0.0
108
+ * @category constructors
109
+ */
110
+ export const makeCollection = Collection.makeState;
111
+ /**
112
+ * Sets activeId without changing paused state.
113
+ *
114
+ * @remarks
115
+ * ## Why
116
+ *
117
+ * The operation exposes Carousel's transition directly so callers can compose it in Effect
118
+ * programs and native event handlers.
119
+ *
120
+ * ## Ownership and lifetime
121
+ *
122
+ * The returned Effect performs the update or DOM side effect only when run, preserves the declared
123
+ * error and service channels, and retains no resources after completion.
124
+ *
125
+ * ## Example
126
+ *
127
+ * Import with `import { select } from "@typed/ui/Carousel";` Extend the [Carousel.makeState
128
+ * runnable setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Inside the linked Effect
129
+ * program invoke `yield* select(state, "slide-2")`, then read state to observe `activeId` become
130
+ * `"slide-2"` without changing `paused`.
131
+ * @since 1.0.0
132
+ * @category combinators
133
+ */
134
+ export function select(state, activeId) {
135
+ return RefSubject.update(state, (current) => ({ ...current, activeId }));
136
+ }
137
+ /**
138
+ * Selects the previous or next enabled registered slide; movement wraps and leaves state unchanged
139
+ * for an empty collection.
140
+ *
141
+ * @remarks
142
+ * ## Why
143
+ *
144
+ * The operation exposes Carousel's transition directly so callers can compose it in Effect
145
+ * programs and native event handlers.
146
+ *
147
+ * ## Ownership and lifetime
148
+ *
149
+ * The returned Effect performs the update or DOM side effect only when run, preserves the declared
150
+ * error and service channels, and retains no resources after completion.
151
+ *
152
+ * ## Example
153
+ *
154
+ * Import with `import { move } from "@typed/ui/Carousel";` Extend the [Carousel.makeState runnable
155
+ * setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Inside the linked Effect program invoke
156
+ * `yield* move(state, collection, "next")`, then read the state snapshot to observe the transition
157
+ * described above.
158
+ * @since 1.0.0
159
+ * @category combinators
160
+ */
161
+ export function move(state, collection, direction) {
162
+ return Effect.gen(function* () {
163
+ const next = Composite.moveActiveId(yield* collection, { activeId: (yield* state).activeId, loop: true }, direction);
164
+ return next === null ? yield* state : yield* select(state, next);
165
+ });
166
+ }
167
+ /**
168
+ * Flips paused and preserves activeId.
169
+ *
170
+ * @remarks
171
+ * ## Why
172
+ *
173
+ * The operation exposes Carousel's transition directly so callers can compose it in Effect
174
+ * programs and native event handlers.
175
+ *
176
+ * ## Ownership and lifetime
177
+ *
178
+ * The returned Effect performs the update or DOM side effect only when run, preserves the declared
179
+ * error and service channels, and retains no resources after completion.
180
+ *
181
+ * ## Example
182
+ *
183
+ * Import with `import { toggleRotation } from "@typed/ui/Carousel";` Extend the [Carousel.makeState
184
+ * runnable setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Inside the linked Effect
185
+ * program invoke `yield* toggleRotation(state)`, then read state to observe `paused` invert without
186
+ * changing `activeId`.
187
+ * @since 1.0.0
188
+ * @category combinators
189
+ */
190
+ export function toggleRotation(state) {
191
+ return RefSubject.update(state, (current) => ({ ...current, paused: !current.paused }));
192
+ }
193
+ function rootInternalProps(options) {
194
+ const pause = RefSubject.update(options.state, (current) => current.paused ? current : { ...current, paused: true });
195
+ let resumeOnPointerLeave = false;
196
+ const pauseForFocus = Effect.andThen(Effect.sync(() => {
197
+ resumeOnPointerLeave = false;
198
+ }), pause);
199
+ const pauseForPointer = Effect.flatMap(options.state, (current) => {
200
+ resumeOnPointerLeave = !current.paused;
201
+ return pause;
202
+ });
203
+ const resumeAfterPointer = Effect.suspend(() => {
204
+ if (!resumeOnPointerLeave)
205
+ return Effect.void;
206
+ resumeOnPointerLeave = false;
207
+ return RefSubject.update(options.state, (current) => current.paused ? { ...current, paused: false } : current);
208
+ });
209
+ return ({ property }) => ({
210
+ role: "region",
211
+ "aria-roledescription": "carousel",
212
+ "aria-label": property("label", undefined),
213
+ onfocusin: pauseForFocus,
214
+ onmouseenter: pauseForPointer,
215
+ onmouseleave: resumeAfterPointer,
216
+ ref: options.state,
217
+ });
218
+ }
219
+ /**
220
+ * Renders the carousel region and pauses automatic rotation on focus or pointer entry without
221
+ * replacing its child nodes.
222
+ *
223
+ * @remarks
224
+ * ## Why
225
+ *
226
+ * The component applies the family behavior while leaving callers free to supply a custom host
227
+ * through the shared DOM boundary.
228
+ *
229
+ * ## Ownership and lifetime
230
+ *
231
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
232
+ * collection registrations only when rendered. The rendering Scope removes those resources;
233
+ * unrelated nodes and attributes remain caller-owned.
234
+ *
235
+ * ## Example
236
+ *
237
+ * Import with `import { Root } from "@typed/ui/Carousel";` Extend the [Carousel.makeState runnable
238
+ * setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Replace the linked program's final
239
+ * snapshot read with `Root({ state, content: "Slides", label: "Featured" })`; render that Fx before
240
+ * the same Scope closes.
241
+ * @since 1.0.0
242
+ * @category components
243
+ */
244
+ export function Root(options, host) {
245
+ return Dom.renderHost()(options, host, rootInternalProps(options), options.content, (props, content) => {
246
+ return html `<div ...${props}>${content}</div>`;
247
+ });
248
+ }
249
+ function slideInternalProps(options) {
250
+ const register = options.collection === undefined
251
+ ? undefined
252
+ : Collection.ref(options.collection, {
253
+ id: options.id,
254
+ value: options.id,
255
+ textValue: options.id,
256
+ });
257
+ return ({ property }) => ({
258
+ id: options.id,
259
+ role: "group",
260
+ "aria-roledescription": "slide",
261
+ "aria-label": property("label", undefined),
262
+ "?hidden": RefSubject.map(options.state, (state) => state.activeId !== options.id),
263
+ ref: register,
264
+ });
265
+ }
266
+ /**
267
+ * Renders one labelled slide, registers it when a collection is supplied, and toggles hidden from
268
+ * activeId.
269
+ *
270
+ * @remarks
271
+ * ## Why
272
+ *
273
+ * The component applies the family behavior while leaving callers free to supply a custom host
274
+ * through the shared DOM boundary.
275
+ *
276
+ * ## Ownership and lifetime
277
+ *
278
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
279
+ * collection registrations only when rendered. The rendering Scope removes those resources;
280
+ * unrelated nodes and attributes remain caller-owned.
281
+ *
282
+ * ## Example
283
+ *
284
+ * Import with `import { Slide } from "@typed/ui/Carousel";` Extend the [Carousel.makeState runnable
285
+ * setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Replace the linked program's final
286
+ * snapshot read with `Slide({ state, id: "slide-1", label: "1 of 3", content: "First slide" })`;
287
+ * render that Fx before the same Scope closes.
288
+ * @since 1.0.0
289
+ * @category components
290
+ */
291
+ export function Slide(options, host) {
292
+ return Dom.renderHost()(options, host, slideInternalProps(options), options.content, (props, content) => {
293
+ return html `<div ...${props}>${content}</div>`;
294
+ });
295
+ }
296
+ function controlInternalProps(options, action) {
297
+ return () => ({ type: "button", onclick: action });
298
+ }
299
+ function control(options, host, action) {
300
+ const internal = controlInternalProps(options, action);
301
+ return Dom.renderHost()(options, host, internal, options.content, (props, content) => html `<button ...${props}>${content}</button>`);
302
+ }
303
+ /**
304
+ * Renders a button whose native click selects the previous registered slide with wrapping enabled.
305
+ *
306
+ * @remarks
307
+ * ## Why
308
+ *
309
+ * The component applies the family behavior while leaving callers free to supply a custom host
310
+ * through the shared DOM boundary.
311
+ *
312
+ * ## Ownership and lifetime
313
+ *
314
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
315
+ * collection registrations only when rendered. The rendering Scope removes those resources;
316
+ * unrelated nodes and attributes remain caller-owned.
317
+ *
318
+ * ## Example
319
+ *
320
+ * Import with `import { Previous } from "@typed/ui/Carousel";` Extend the [Carousel.makeState
321
+ * runnable setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Replace the linked program's
322
+ * final snapshot read with `Previous({ state, content: "Previous" })`; render that Fx before the
323
+ * same Scope closes.
324
+ * @since 1.0.0
325
+ * @category components
326
+ */
327
+ export function Previous(options, host) {
328
+ return control(options, host, options.collection === undefined
329
+ ? Effect.void
330
+ : move(options.state, options.collection, "previous"));
331
+ }
332
+ /**
333
+ * Renders a button whose native click selects the next registered slide with wrapping enabled.
334
+ *
335
+ * @remarks
336
+ * ## Why
337
+ *
338
+ * The component applies the family behavior while leaving callers free to supply a custom host
339
+ * through the shared DOM boundary.
340
+ *
341
+ * ## Ownership and lifetime
342
+ *
343
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
344
+ * collection registrations only when rendered. The rendering Scope removes those resources;
345
+ * unrelated nodes and attributes remain caller-owned.
346
+ *
347
+ * ## Example
348
+ *
349
+ * Import with `import { Next } from "@typed/ui/Carousel";` Extend the [Carousel.makeState runnable
350
+ * setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Replace the linked program's final
351
+ * snapshot read with `Next({ state, content: "Next" })`; render that Fx before the same Scope
352
+ * closes.
353
+ * @since 1.0.0
354
+ * @category components
355
+ */
356
+ export function Next(options, host) {
357
+ return control(options, host, options.collection === undefined
358
+ ? Effect.void
359
+ : move(options.state, options.collection, "next"));
360
+ }
361
+ /**
362
+ * Renders a button whose native click toggles the explicit paused state.
363
+ *
364
+ * @remarks
365
+ * ## Why
366
+ *
367
+ * The component applies the family behavior while leaving callers free to supply a custom host
368
+ * through the shared DOM boundary.
369
+ *
370
+ * ## Ownership and lifetime
371
+ *
372
+ * The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
373
+ * collection registrations only when rendered. The rendering Scope removes those resources;
374
+ * unrelated nodes and attributes remain caller-owned.
375
+ *
376
+ * ## Example
377
+ *
378
+ * Import with `import { RotationControl } from "@typed/ui/Carousel";` Extend the
379
+ * [Carousel.makeState runnable setup](/reference/%40typed%2Fui%2FCarousel%23makeState). Replace the
380
+ * linked program's final snapshot read with `RotationControl({ state, content: "Pause rotation"
381
+ * })`; render that Fx before the same Scope closes.
382
+ * @since 1.0.0
383
+ * @category components
384
+ */
385
+ export function RotationControl(options, host) {
386
+ return control(options, host, toggleRotation(options.state));
387
+ }
@@ -0,0 +1,203 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import { RefSubject } from "@typed/fx";
4
+ import { EventHandler } from "@typed/template";
5
+ import * as Dom from "./Dom.js";
6
+ import type { HostResult } from "./Dom/Types.js";
7
+ /** Three-state value accepted by a checkbox.
8
+ * @remarks
9
+ * ## Why
10
+ * `"mixed"` models the platform's indeterminate presentation without
11
+ * conflating it with either submitted boolean state.
12
+ * ## Ownership and lifetime
13
+ * This pure type acquires no resources.
14
+ * @since 1.0.0
15
+ * @category models
16
+ */
17
+ export type Checked = boolean | "mixed";
18
+ /** Current renderer-independent checkbox state.
19
+ * @remarks
20
+ * ## Why
21
+ * State can be updated and tested through RefSubject before any input mounts.
22
+ * ## Ownership and lifetime
23
+ * The value is plain data; the hydrated RefSubject returned by `makeState`
24
+ * owns observation for its Effect Scope.
25
+ * @since 1.0.0
26
+ * @category state
27
+ */
28
+ export interface State {
29
+ /** Current checked or mixed value.
30
+ * @remarks
31
+ * ## Why
32
+ * One field drives `checked`, `indeterminate`, and `aria-checked` together.
33
+ * ## Ownership and lifetime
34
+ * Plain state retains no resources.
35
+ * @since 1.0.0
36
+ * @category state
37
+ */
38
+ readonly checked: Checked;
39
+ }
40
+ /** Initial checkbox state accepted by `makeState`.
41
+ * @remarks
42
+ * ## Why
43
+ * The optional value gives uncontrolled creation a deterministic false default.
44
+ * ## Ownership and lifetime
45
+ * This configuration is inert and retains no resources.
46
+ * @since 1.0.0
47
+ * @category state
48
+ */
49
+ export interface InitialState {
50
+ /** Initial checked value; defaults to `false`.
51
+ * @remarks
52
+ * ## Why
53
+ * Explicit mixed state can survive SSR hydration.
54
+ * ## Ownership and lifetime
55
+ * The value is copied into hydrated state and retains no resource.
56
+ * @since 1.0.0
57
+ * @category state
58
+ */
59
+ readonly checked?: Checked;
60
+ }
61
+ /** Schema used to encode and hydrate checkbox state.
62
+ * @remarks
63
+ * ## Why
64
+ * A shared schema keeps server and client state shape identical.
65
+ * ## Ownership and lifetime
66
+ * The schema is immutable and acquires no resources.
67
+ * @since 1.0.0
68
+ * @category schemas
69
+ */
70
+ export declare const StateSchema: Schema.Struct<{
71
+ readonly checked: Schema.Literals<readonly [true, false, "mixed"]>;
72
+ }>;
73
+ /** Creates hydrated, renderer-independent checkbox state.
74
+ * @remarks
75
+ * ## Why
76
+ * State transitions remain testable without rendering and can be consumed by
77
+ * any UI producer.
78
+ * ## Ownership and lifetime
79
+ * The caller's Effect Scope owns the hydrated RefSubject and its subscriptions.
80
+ * @example
81
+ * ```ts
82
+ * import { Effect } from "effect"
83
+ * import * as Checkbox from "@typed/ui/Checkbox"
84
+ *
85
+ * const program = Effect.gen(function* () {
86
+ * return yield* Checkbox.makeState({ checked: "mixed" })
87
+ * })
88
+ * ```
89
+ * @since 1.0.0
90
+ * @category constructors
91
+ */
92
+ export declare function makeState(initial?: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
93
+ readonly checked: "mixed" | boolean;
94
+ }, Schema.SchemaError, never, never>, never, import("effect/Scope").Scope>;
95
+ /** Sets the checkbox state to a boolean or mixed value.
96
+ * @remarks
97
+ * ## Why
98
+ * A single atomic RefSubject update keeps DOM properties and ARIA state aligned.
99
+ * ## Ownership and lifetime
100
+ * The Effect uses the existing subject lifetime and acquires no new resource.
101
+ * @example
102
+ * ```ts
103
+ * import { Effect } from "effect"
104
+ * import * as Checkbox from "@typed/ui/Checkbox"
105
+ *
106
+ * const program = Effect.gen(function* () {
107
+ * const state = yield* Checkbox.makeState()
108
+ * yield* Checkbox.setChecked(state, true)
109
+ * })
110
+ * ```
111
+ * @since 1.0.0
112
+ * @category state
113
+ */
114
+ export declare function setChecked<E, R>(state: RefSubject.RefSubject<State, E, R>, checked: Checked): Effect.Effect<State, E, R>;
115
+ /** Toggles checked to false and false or mixed to true.
116
+ * @remarks
117
+ * ## Why
118
+ * User activation of an indeterminate checkbox resolves to a concrete checked
119
+ * state, matching native input behavior.
120
+ * ## Ownership and lifetime
121
+ * The Effect updates the existing RefSubject and acquires no resource.
122
+ * @example
123
+ * ```ts
124
+ * import { Effect } from "effect"
125
+ * import * as Checkbox from "@typed/ui/Checkbox"
126
+ *
127
+ * const program = Effect.gen(function* () {
128
+ * const state = yield* Checkbox.makeState({ checked: "mixed" })
129
+ * yield* Checkbox.toggle(state)
130
+ * })
131
+ * ```
132
+ * @since 1.0.0
133
+ * @category state
134
+ */
135
+ export declare function toggle<E, R>(state: RefSubject.RefSubject<State, E, R>): Effect.Effect<State, E, R>;
136
+ /** Options for the native checkbox input.
137
+ * @remarks
138
+ * ## Why
139
+ * The state reference is the single source for native and ARIA checked values.
140
+ * ## Ownership and lifetime
141
+ * Options are inert; the component Scope owns DOM subscriptions and refs.
142
+ * @since 1.0.0
143
+ * @category models
144
+ */
145
+ export interface InputOptions extends Dom.HostOptions<HTMLInputElement> {
146
+ /** Hydrated state synchronized with the input.
147
+ * @remarks
148
+ * ## Why
149
+ * Hydration preserves checked identity and enables state-only tests.
150
+ * ## Ownership and lifetime
151
+ * The input borrows the RefSubject; its original Scope remains the owner.
152
+ * @since 1.0.0
153
+ * @category state
154
+ */
155
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
156
+ }
157
+ declare function internalProps<const Options extends InputOptions>(options: Options): ({ property }: Dom.InternalPropsHelpers<Options>) => {
158
+ type: string;
159
+ "aria-checked": RefSubject.Computed<Checked, Schema.SchemaError, never>;
160
+ "?checked": RefSubject.Computed<boolean, Schema.SchemaError, never>;
161
+ ".checked": RefSubject.Computed<boolean, Schema.SchemaError, never>;
162
+ "?disabled": false | Exclude<Dom.Property<Options, "disabled">, Dom.Nullish>;
163
+ "?required": false | Exclude<Dom.Property<Options, "required">, Dom.Nullish>;
164
+ ".indeterminate": RefSubject.Computed<boolean, Schema.SchemaError, never>;
165
+ onchange: EventHandler.EventHandler<Event, Schema.SchemaError, never>;
166
+ ref: RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>;
167
+ };
168
+ type InputInternalProps<Options extends InputOptions> = ReturnType<ReturnType<typeof internalProps<Options>>>;
169
+ /** Renders a native checkbox input synchronized with hydrated state.
170
+ * @remarks
171
+ * ## Why
172
+ * Native keyboard, form, disabled, required, and accessibility behavior remain
173
+ * available while state is exposed through RefSubject.
174
+ * ## Ownership and lifetime
175
+ * Running the Fx installs native listeners and state subscriptions in its
176
+ * Scope. A custom host must apply `type`, checked, indeterminate, ARIA, and ref
177
+ * props; only one hydration owner may be composed for the element.
178
+ * @example
179
+ * ```ts
180
+ * import { Effect } from "effect"
181
+ * import * as Checkbox from "@typed/ui/Checkbox"
182
+ *
183
+ * const program = Effect.gen(function* () {
184
+ * const state = yield* Checkbox.makeState()
185
+ * return Checkbox.Input({ state })
186
+ * })
187
+ * ```
188
+ * @since 1.0.0
189
+ * @category components
190
+ */
191
+ export declare function Input<const Options extends InputOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, InputInternalProps<Options>>, "", Host>): import("./Dom/Types.js").HostComponent<Host | Options>;
192
+ /** Canonical component alias for `Input`.
193
+ * @remarks
194
+ * ## Why
195
+ * The alias provides the widget name while retaining the explicit input API.
196
+ * ## Ownership and lifetime
197
+ * It has exactly the same Scope and native-element ownership as `Input`.
198
+ * @since 1.0.0
199
+ * @category aliases
200
+ */
201
+ export declare const Checkbox: typeof Input;
202
+ export {};
203
+ //# sourceMappingURL=Checkbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../src/Checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAQ,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAExC;;;;;;;;;GASG;AACH,MAAM,WAAW,KAAK;IACpB;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW;;EAEtB,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,YAAiB;;2EAEnD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAC7B,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,EACzC,OAAO,EAAE,OAAO,GACf,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAE5B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EACzB,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,GACxC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAK5B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAa,SAAQ,GAAG,CAAC,WAAW,CAAC,gBAAgB,CAAC;IACrE;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;CAC1E;AAED,iBAAS,aAAa,CAAC,KAAK,CAAC,OAAO,SAAS,YAAY,EAAE,OAAO,EAAE,OAAO,kBASnD,GAAG,CAAC,oBAAoB,CAAC,OAAO,CAAC;IACrD,IAAI;IACJ,cAAc;IACd,UAAU;IACV,UAAU;IACV,WAAW;IACX,WAAW;IACX,gBAAgB;IAChB,QAAQ;IACR,GAAG;EAEN;AAED,KAAK,kBAAkB,CAAC,OAAO,SAAS,YAAY,IAAI,UAAU,CAChE,UAAU,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC,CAC1C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,KAAK,CAAC,OAAO,SAAS,YAAY,EAAE,KAAK,CAAC,IAAI,SAAS,UAAU,GAAG,KAAK,EAC7F,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,0DAW7F;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,cAAQ,CAAC"}