@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
package/dist/Dialog.js ADDED
@@ -0,0 +1,355 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Scope from "effect/Scope";
3
+ import * as Schema from "effect/Schema";
4
+ import { RefSubject } from "@typed/fx";
5
+ import { EventHandler, html, } from "@typed/template";
6
+ import * as Dom from "./Dom.js";
7
+ import * as NativeDialog from "./NativeDialog.js";
8
+ const dialogs = new WeakMap();
9
+ /** Schema for dialog hydration state.
10
+ * @remarks
11
+ * ## Why
12
+ * Shared encoding keeps server and browser state compatible.
13
+ * ## Ownership and lifetime
14
+ * The immutable schema acquires no resources.
15
+ * @since 1.0.0
16
+ * @category schemas
17
+ */
18
+ export const StateSchema = Schema.Struct({ open: Schema.Boolean });
19
+ /** Creates hydrated dialog state independent of rendering.
20
+ * @remarks
21
+ * ## Why
22
+ * Application transitions can be composed and tested before a native dialog
23
+ * element exists.
24
+ * ## Ownership and lifetime
25
+ * The calling Effect Scope owns the returned RefSubject.
26
+ * @example
27
+ * ```ts
28
+ * import { Effect } from "effect"
29
+ * import * as Dialog from "@typed/ui/Dialog"
30
+ *
31
+ * const program = Effect.gen(function* () {
32
+ * return yield* Dialog.makeState()
33
+ * })
34
+ * ```
35
+ * @since 1.0.0
36
+ * @category constructors
37
+ */
38
+ export function makeState(initial = {}) {
39
+ return RefSubject.hydrate(StateSchema, { open: initial.open ?? false });
40
+ }
41
+ /** Sets desired dialog visibility.
42
+ * @remarks
43
+ * ## Why
44
+ * Explicit state transitions stay outside renderers and retain Effect typing.
45
+ * ## Ownership and lifetime
46
+ * The Effect uses the existing RefSubject lifetime and acquires no resource.
47
+ * @example
48
+ * ```ts
49
+ * import { Effect } from "effect"
50
+ * import * as Dialog from "@typed/ui/Dialog"
51
+ *
52
+ * const program = Effect.gen(function* () {
53
+ * const state = yield* Dialog.makeState()
54
+ * yield* Dialog.setOpen(state, true)
55
+ * })
56
+ * ```
57
+ * @since 1.0.0
58
+ * @category state
59
+ */
60
+ export function setOpen(state, open) {
61
+ return RefSubject.update(state, (current) => ({ ...current, open }));
62
+ }
63
+ /** Marks dialog state closed without requesting native cancellation.
64
+ * @remarks
65
+ * ## Why
66
+ * Programmatic close is distinct from `requestClose`, which runs the browser's
67
+ * cancelable close-request lifecycle.
68
+ * ## Ownership and lifetime
69
+ * The Effect updates existing state and acquires no resource.
70
+ * @example
71
+ * ```ts
72
+ * import { Effect } from "effect"
73
+ * import * as Dialog from "@typed/ui/Dialog"
74
+ *
75
+ * const program = Effect.gen(function* () {
76
+ * const state = yield* Dialog.makeState({ open: true })
77
+ * yield* Dialog.close(state)
78
+ * })
79
+ * ```
80
+ * @since 1.0.0
81
+ * @category state
82
+ */
83
+ export function close(state) {
84
+ return setOpen(state, false);
85
+ }
86
+ /**
87
+ * Requests the native dialog close lifecycle, including its cancel event.
88
+ * @remarks
89
+ * ## Why
90
+ * The browser's `requestClose()` is cancelable. The fallback dispatches a real
91
+ * cancelable `cancel` event and calls `close()` only when it is accepted, so
92
+ * user handlers retain the same veto point.
93
+ * ## Ownership and lifetime
94
+ * The Effect consults the dialog currently registered for the hydrated state;
95
+ * registration is removed when `Content`'s Scope closes. No dialog is retained
96
+ * after that finalizer.
97
+ * @example
98
+ * ```ts
99
+ * import { Effect } from "effect"
100
+ * import * as Dialog from "@typed/ui/Dialog"
101
+ *
102
+ * const program = Effect.gen(function* () {
103
+ * const state = yield* Dialog.makeState({ open: true })
104
+ * yield* Dialog.requestClose(state)
105
+ * })
106
+ * ```
107
+ * @since 1.0.0
108
+ * @category lifecycle
109
+ */
110
+ export function requestClose(state, returnValue) {
111
+ return Effect.sync(() => {
112
+ const dialog = dialogs.get(state);
113
+ const requestClose = dialog === undefined ? undefined : Reflect.get(dialog, "requestClose");
114
+ if (typeof requestClose === "function") {
115
+ requestClose.call(dialog, returnValue);
116
+ }
117
+ else if (dialog !== undefined) {
118
+ const accepted = dialog.dispatchEvent(new Event("cancel", { cancelable: true }));
119
+ if (accepted)
120
+ dialog.close(returnValue);
121
+ }
122
+ });
123
+ }
124
+ function triggerInternalProps(options) {
125
+ const open = RefSubject.map(options.state, (state) => state.open);
126
+ const show = setOpen(options.state, true);
127
+ return ({ property }) => ({
128
+ type: "button",
129
+ "aria-haspopup": "dialog",
130
+ "aria-expanded": open,
131
+ "aria-controls": property("controls", undefined),
132
+ commandfor: property("controls", undefined),
133
+ command: options.controls === undefined ? undefined : "show-modal",
134
+ onclick: options.controls === undefined ? show : undefined,
135
+ });
136
+ }
137
+ /** Renders a native button that opens dialog content.
138
+ * @remarks
139
+ * ## Why
140
+ * It preserves button semantics, real events, and ARIA relationship state.
141
+ * Supplying `controls` selects native `commandfor` / `show-modal` behavior and
142
+ * disables the Fx click fallback, so that form requires browser command support.
143
+ * Omitting `controls` installs the portable state-driven click handler.
144
+ * ## Ownership and lifetime
145
+ * Running the Fx owns reactive props/listeners in its Scope. A custom host must
146
+ * preserve button type, ARIA, command, click props, and content.
147
+ * @example
148
+ * ```ts
149
+ * import { Effect } from "effect"
150
+ * import * as Dialog from "@typed/ui/Dialog"
151
+ *
152
+ * const program = Effect.gen(function* () {
153
+ * const state = yield* Dialog.makeState()
154
+ * // Omitting controls selects the Fx state fallback.
155
+ * return Dialog.Trigger({ state, content: "Open" })
156
+ * })
157
+ * ```
158
+ * @since 1.0.0
159
+ * @category components
160
+ */
161
+ export function Trigger(options, host) {
162
+ return Dom.renderHost()(options, host, triggerInternalProps(options), options.content, (props, content) => html `<button ...${props}>${content}</button>`);
163
+ }
164
+ function contentInternalProps(options) {
165
+ const synchronize = NativeDialog.ref(options.state, { modal: options.modal });
166
+ return ({ property }) => ({
167
+ id: property("id", undefined),
168
+ "aria-labelledby": property("labelledBy", undefined),
169
+ "aria-describedby": property("describedBy", undefined),
170
+ "aria-label": property("label", undefined),
171
+ oncancel: close(options.state),
172
+ onclose: close(options.state),
173
+ ontoggle: EventHandler.make(Effect.fn((event) => setOpen(options.state, Dom.currentTarget(event).open))),
174
+ ref: Dom.composeRefs(options.state, Dom.composeRefs(synchronize, dialogRef(options.state))),
175
+ });
176
+ }
177
+ function dialogRef(state) {
178
+ return Effect.fn(function* (dialog) {
179
+ dialogs.set(state, dialog);
180
+ const scope = yield* Effect.scope;
181
+ yield* Scope.addFinalizer(scope, Effect.sync(() => {
182
+ if (dialogs.get(state) === dialog)
183
+ dialogs.delete(state);
184
+ }));
185
+ });
186
+ }
187
+ /**
188
+ * Renders native dialog content synchronized with hydrated state.
189
+ * @remarks
190
+ * ## Why
191
+ * `Content` uses a real `<dialog>` and its `show()`, `showModal()`, `close()`,
192
+ * `cancel`, `close`, and `toggle` lifecycle. The browser therefore owns focus,
193
+ * top-layer placement, modal inertness, and default Escape behavior.
194
+ * ## Ownership and lifetime
195
+ * Running the Fx owns native listeners, dialog registration, and the
196
+ * NativeDialog observer in its Effect Scope. Finalization removes registration
197
+ * only when it still points at this element. A custom host must preserve native
198
+ * dialog identity, naming props, lifecycle handlers, and exactly one hydration
199
+ * ref owner.
200
+ * @example
201
+ * ```ts
202
+ * import { Effect } from "effect"
203
+ * import * as Dialog from "@typed/ui/Dialog"
204
+ *
205
+ * const program = Effect.gen(function* () {
206
+ * const state = yield* Dialog.makeState()
207
+ * return Dialog.Content({
208
+ * state,
209
+ * id: "confirm",
210
+ * label: "Confirm",
211
+ * content: "Continue?"
212
+ * })
213
+ * })
214
+ * ```
215
+ * @since 1.0.0
216
+ * @category components
217
+ */
218
+ export function Content(options, host) {
219
+ return Dom.renderHost()(options, host, contentInternalProps(options), options.content, (props, content) => {
220
+ return html `<dialog ...${props}>${content}</dialog>`;
221
+ });
222
+ }
223
+ /** Canonical widget alias for `Content`.
224
+ * @remarks
225
+ * ## Why
226
+ * The alias provides the widget name while `Content` identifies compound use.
227
+ * ## Ownership and lifetime
228
+ * It has exactly the same native element and Scope ownership as `Content`.
229
+ * @since 1.0.0
230
+ * @category aliases
231
+ */
232
+ export const Dialog = Content;
233
+ function closeInternalProps(options, requestCloseLifecycle) {
234
+ return ({ property }) => ({
235
+ type: "button",
236
+ commandfor: property("controls", undefined),
237
+ command: options.controls === undefined
238
+ ? undefined
239
+ : requestCloseLifecycle
240
+ ? "request-close"
241
+ : "close",
242
+ onclick: options.controls !== undefined
243
+ ? undefined
244
+ : requestCloseLifecycle
245
+ ? requestClose(options.state)
246
+ : close(options.state),
247
+ });
248
+ }
249
+ /** Renders a button that closes a dialog without a cancel request.
250
+ * @remarks
251
+ * ## Why
252
+ * Accepted actions need a direct close path distinct from cancelable dismissal.
253
+ * `controls` selects native `command="close"` and disables the Fx click
254
+ * fallback; omit it when native dialog commands are unavailable.
255
+ * ## Ownership and lifetime
256
+ * Running the Fx owns handlers/dynamic content by Scope. Custom hosts must
257
+ * preserve type, command or fallback click behavior, and content.
258
+ * @example
259
+ * ```ts
260
+ * import { Effect } from "effect"
261
+ * import * as Dialog from "@typed/ui/Dialog"
262
+ *
263
+ * const program = Effect.gen(function* () {
264
+ * const state = yield* Dialog.makeState({ open: true })
265
+ * return Dialog.Close({ state, content: "Done" })
266
+ * })
267
+ * ```
268
+ * @since 1.0.0
269
+ * @category components
270
+ */
271
+ export function Close(options, host) {
272
+ return Dom.renderHost()(options, host, closeInternalProps(options, false), options.content, (props, content) => html `<button ...${props}>${content}</button>`);
273
+ }
274
+ /** Alias for direct `Close` behavior.
275
+ * @remarks
276
+ * ## Why
277
+ * The alias supports dismissal vocabulary without introducing another lifecycle.
278
+ * ## Ownership and lifetime
279
+ * It has exactly the same Scope and native button ownership as `Close`.
280
+ * @since 1.0.0
281
+ * @category aliases
282
+ */
283
+ export const Dismiss = Close;
284
+ /** Renders a button that starts the cancelable native close-request lifecycle.
285
+ * @remarks
286
+ * ## Why
287
+ * Consumers can intercept a real `cancel` event before the dialog closes.
288
+ * `controls` selects native `command="request-close"` and disables the Fx click
289
+ * fallback; omit it when native dialog commands are unavailable.
290
+ * ## Ownership and lifetime
291
+ * Running the Fx owns handlers by Scope; dialog registration remains owned by
292
+ * the corresponding `Content` Scope.
293
+ * @example
294
+ * ```ts
295
+ * import { Effect } from "effect"
296
+ * import * as Dialog from "@typed/ui/Dialog"
297
+ *
298
+ * const program = Effect.gen(function* () {
299
+ * const state = yield* Dialog.makeState({ open: true })
300
+ * return Dialog.RequestClose({ state, content: "Cancel" })
301
+ * })
302
+ * ```
303
+ * @since 1.0.0
304
+ * @category components
305
+ */
306
+ export function RequestClose(options, host) {
307
+ return Dom.renderHost()(options, host, closeInternalProps(options, true), options.content, (props, content) => html `<button ...${props}>${content}</button>`);
308
+ }
309
+ function headingInternalProps() {
310
+ return ({ property }) => ({
311
+ id: property("id", undefined),
312
+ role: "heading",
313
+ "aria-level": property("level", 2),
314
+ });
315
+ }
316
+ /** Renders a dialog heading with explicit ARIA level semantics.
317
+ * @remarks
318
+ * ## Why
319
+ * The helper creates a reliable naming target and preserves heading hierarchy.
320
+ * ## Ownership and lifetime
321
+ * Running the Fx owns dynamic content by Scope. A custom host must preserve id,
322
+ * role, and `aria-level`.
323
+ * @example
324
+ * ```ts
325
+ * import { Heading } from "@typed/ui/Dialog"
326
+ *
327
+ * const title = Heading({ id: "confirm-title", content: "Confirm" })
328
+ * ```
329
+ * @since 1.0.0
330
+ * @category components
331
+ */
332
+ export function Heading(options, host) {
333
+ return Dom.renderHost()(options, host, headingInternalProps(), options.content, (props, content) => html `<h2 ...${props}>${content}</h2>`);
334
+ }
335
+ function descriptionInternalProps() {
336
+ return ({ property }) => ({ id: property("id", undefined) });
337
+ }
338
+ /** Renders descriptive dialog text with an optional relationship id.
339
+ * @remarks
340
+ * ## Why
341
+ * The helper keeps accessible description wiring explicit and platform-native.
342
+ * ## Ownership and lifetime
343
+ * Running the Fx owns dynamic content by Scope. A custom host must preserve id.
344
+ * @example
345
+ * ```ts
346
+ * import { Description } from "@typed/ui/Dialog"
347
+ *
348
+ * const detail = Description({ id: "confirm-detail", content: "This cannot be undone." })
349
+ * ```
350
+ * @since 1.0.0
351
+ * @category components
352
+ */
353
+ export function Description(options, host) {
354
+ return Dom.renderHost()(options, host, descriptionInternalProps(), options.content, (props, content) => html `<p ...${props}>${content}</p>`);
355
+ }
@@ -0,0 +1,219 @@
1
+ import * as Effect from "effect/Effect";
2
+ import type * as Scope from "effect/Scope";
3
+ import * as Schema from "effect/Schema";
4
+ import type { Fx } from "@typed/fx/Fx";
5
+ import { RefSubject } from "@typed/fx";
6
+ import { EventHandler, type Renderable, type RenderEvent, type RenderTemplate } from "@typed/template";
7
+ import * as Dom from "./Dom.js";
8
+ import type { HostResult } from "./Dom/Types.js";
9
+ /** Current renderer-independent disclosure state.
10
+ * @remarks
11
+ * ## Why
12
+ * Open state remains testable without mounting native details content.
13
+ * ## Ownership and lifetime
14
+ * Plain state retains no resources; RefSubject lifetime is Scope-owned.
15
+ * @since 1.0.0
16
+ * @category state
17
+ */
18
+ export interface State {
19
+ /** Whether the details element is open.
20
+ * @remarks
21
+ * ## Why
22
+ * One field coordinates application state with native element state.
23
+ * ## Ownership and lifetime
24
+ * Plain data acquires no resources.
25
+ * @since 1.0.0
26
+ * @category state
27
+ */
28
+ readonly open: boolean;
29
+ }
30
+ /** Optional initial disclosure state.
31
+ * @remarks
32
+ * ## Why
33
+ * Omission produces a deterministic closed SSR snapshot.
34
+ * ## Ownership and lifetime
35
+ * Configuration is inert.
36
+ * @since 1.0.0
37
+ * @category state
38
+ */
39
+ export interface InitialState {
40
+ /** Initial open state, defaulting to false.
41
+ * @remarks
42
+ * ## Why
43
+ * The value seeds the hydration contract.
44
+ * ## Ownership and lifetime
45
+ * Plain data retains no resources.
46
+ * @since 1.0.0
47
+ * @category state
48
+ */
49
+ readonly open?: boolean;
50
+ }
51
+ /** Schema for disclosure hydration state.
52
+ * @remarks
53
+ * ## Why
54
+ * Shared encoding keeps server and browser state compatible.
55
+ * ## Ownership and lifetime
56
+ * The immutable schema acquires no resources.
57
+ * @since 1.0.0
58
+ * @category schemas
59
+ */
60
+ export declare const StateSchema: Schema.Struct<{
61
+ readonly open: Schema.Boolean;
62
+ }>;
63
+ /** Creates hydrated disclosure state.
64
+ * @remarks
65
+ * ## Why
66
+ * Applications can own and test open-state transitions independently of UI.
67
+ * ## Ownership and lifetime
68
+ * The calling Effect Scope owns the returned RefSubject.
69
+ * @example
70
+ * ```ts
71
+ * import { Effect } from "effect"
72
+ * import * as Disclosure from "@typed/ui/Disclosure"
73
+ *
74
+ * const program = Effect.gen(function* () {
75
+ * return yield* Disclosure.makeState({ open: true })
76
+ * })
77
+ * ```
78
+ * @since 1.0.0
79
+ * @category constructors
80
+ */
81
+ export declare function makeState(initial?: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
82
+ readonly open: boolean;
83
+ }, Schema.SchemaError, never, never>, never, Scope.Scope>;
84
+ /** Sets disclosure visibility.
85
+ * @remarks
86
+ * ## Why
87
+ * Explicit state transitions remain composable with Effect and outside renderers.
88
+ * ## Ownership and lifetime
89
+ * The Effect reuses the existing state lifetime and acquires no resource.
90
+ * @example
91
+ * ```ts
92
+ * import { Effect } from "effect"
93
+ * import * as Disclosure from "@typed/ui/Disclosure"
94
+ *
95
+ * const program = Effect.gen(function* () {
96
+ * const state = yield* Disclosure.makeState()
97
+ * yield* Disclosure.setOpen(state, true)
98
+ * })
99
+ * ```
100
+ * @since 1.0.0
101
+ * @category state
102
+ */
103
+ export declare function setOpen<E, R>(state: RefSubject.RefSubject<State, E, R>, open: boolean): Effect.Effect<State, E, R>;
104
+ /** Options for the native `<summary>` trigger.
105
+ * @remarks
106
+ * ## Why
107
+ * Summary preserves built-in pointer, keyboard, and disclosure behavior.
108
+ * ## Ownership and lifetime
109
+ * Options are inert; dynamic content follows the rendered Scope.
110
+ * @since 1.0.0
111
+ * @category models
112
+ */
113
+ export interface ButtonOptions extends Dom.HostOptions<HTMLElement> {
114
+ /** Visible summary content and accessible name.
115
+ * @remarks
116
+ * ## Why
117
+ * Native summary content labels its containing details element.
118
+ * ## Ownership and lifetime
119
+ * Dynamic content follows the trigger Scope.
120
+ * @since 1.0.0
121
+ * @category content
122
+ */
123
+ readonly content: Renderable.Any;
124
+ }
125
+ declare function buttonInternalProps(): {};
126
+ type ButtonInternalProps = ReturnType<typeof buttonInternalProps>;
127
+ /** Renders a native `<summary>` disclosure trigger.
128
+ * @remarks
129
+ * ## Why
130
+ * The browser owns activation and toggling; Typed does not emulate them with
131
+ * click handlers or synthetic events.
132
+ * ## Ownership and lifetime
133
+ * Running the Fx owns dynamic content in its Scope. A custom host must remain a
134
+ * valid summary participant inside the associated details element.
135
+ * @example
136
+ * ```ts
137
+ * import { Button } from "@typed/ui/Disclosure"
138
+ *
139
+ * const summary = Button({ content: "Advanced settings" })
140
+ * ```
141
+ * @since 1.0.0
142
+ * @category components
143
+ */
144
+ export declare function Button<const Options extends ButtonOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, ButtonInternalProps>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
145
+ /** Options for native `<details>` content.
146
+ * @remarks
147
+ * ## Why
148
+ * A shared state lets application code observe and control the browser-owned
149
+ * disclosure lifecycle.
150
+ * ## Ownership and lifetime
151
+ * Options are inert; rendering owns listeners and the NativeDetails observer.
152
+ * @since 1.0.0
153
+ * @category models
154
+ */
155
+ export interface ContentOptions extends Dom.HostOptions<HTMLDetailsElement> {
156
+ /** Hydrated state synchronized with the details element.
157
+ * @remarks
158
+ * ## Why
159
+ * Native toggle events and application updates converge on one source.
160
+ * ## Ownership and lifetime
161
+ * The content borrows state; its originating Scope owns it.
162
+ * @since 1.0.0
163
+ * @category state
164
+ */
165
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
166
+ /** Summary and disclosed body content.
167
+ * @remarks
168
+ * ## Why
169
+ * Keeping the subtree renderable preserves Typed error/service composition.
170
+ * ## Ownership and lifetime
171
+ * Dynamic content follows the details Scope.
172
+ * @since 1.0.0
173
+ * @category content
174
+ */
175
+ readonly content: Renderable.Any;
176
+ }
177
+ declare function contentInternalProps<const Options extends ContentOptions>(options: Options): () => {
178
+ readonly ontoggle: EventHandler.EventHandler<Event, Schema.SchemaError, never>;
179
+ readonly ref: import("./Dom/Refs.js").ComposedRef<RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>, (element: HTMLDetailsElement) => Effect.Effect<void, Schema.SchemaError, Scope.Scope>> | undefined;
180
+ };
181
+ type ContentInternalProps<Options extends ContentOptions> = ReturnType<ReturnType<typeof contentInternalProps<Options>>>;
182
+ /** Renders native details content synchronized with hydrated state.
183
+ * @remarks
184
+ * ## Why
185
+ * Native disclosure behavior, semantics, and `toggle` events stay intact while
186
+ * Effect state can control or observe visibility.
187
+ * ## Ownership and lifetime
188
+ * Running the Fx owns the native listener and scoped observer. A custom host
189
+ * must preserve the toggle handler and exactly one composed hydration ref.
190
+ * @example
191
+ * ```ts
192
+ * import { Effect } from "effect"
193
+ * import * as Disclosure from "@typed/ui/Disclosure"
194
+ *
195
+ * const program = Effect.gen(function* () {
196
+ * const state = yield* Disclosure.makeState()
197
+ * return Disclosure.Content({
198
+ * state,
199
+ * content: Disclosure.Button({ content: "More" })
200
+ * })
201
+ * })
202
+ * ```
203
+ * @since 1.0.0
204
+ * @category components
205
+ */
206
+ export declare function Content<const Options extends ContentOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, ContentInternalProps<Options>>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
207
+ /** Canonical alias for `Content`.
208
+ * @remarks
209
+ * ## Why
210
+ * The widget name remains convenient while `Content` identifies its role in
211
+ * compound composition.
212
+ * ## Ownership and lifetime
213
+ * It has exactly the same Scope and details-element ownership as `Content`.
214
+ * @since 1.0.0
215
+ * @category aliases
216
+ */
217
+ export declare const Disclosure: typeof Content;
218
+ export {};
219
+ //# sourceMappingURL=Disclosure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Disclosure.d.ts","sourceRoot":"","sources":["../src/Disclosure.ts"],"names":[],"mappings":"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"}