@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
package/dist/Dialog.js ADDED
@@ -0,0 +1,357 @@
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 Open state
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 Open state
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 Open 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 Direct dismissal
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 Cancelable dismissal
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 Opening controls
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
+ // Native close events are queued. The same dialog may already have reopened
173
+ // by the time an earlier close event is delivered.
174
+ onclose: EventHandler.make(Effect.fn((event) => setOpen(options.state, Dom.currentTarget(event).open))),
175
+ ontoggle: EventHandler.make(Effect.fn((event) => setOpen(options.state, Dom.currentTarget(event).open))),
176
+ ref: Dom.composeRefs(options.state, Dom.composeRefs(synchronize, dialogRef(options.state))),
177
+ });
178
+ }
179
+ function dialogRef(state) {
180
+ return Effect.fn(function* (dialog) {
181
+ dialogs.set(state, dialog);
182
+ const scope = yield* Effect.scope;
183
+ yield* Scope.addFinalizer(scope, Effect.sync(() => {
184
+ if (dialogs.get(state) === dialog)
185
+ dialogs.delete(state);
186
+ }));
187
+ });
188
+ }
189
+ /**
190
+ * Renders native dialog content synchronized with hydrated state.
191
+ * @remarks
192
+ * ## Why
193
+ * `Content` uses a real `<dialog>` and its `show()`, `showModal()`, `close()`,
194
+ * `cancel`, `close`, and `toggle` lifecycle. The browser therefore owns focus,
195
+ * top-layer placement, modal inertness, and default Escape behavior.
196
+ * ## Ownership and lifetime
197
+ * Running the Fx owns native listeners, dialog registration, and the
198
+ * NativeDialog observer in its Effect Scope. Finalization removes registration
199
+ * only when it still points at this element. A custom host must preserve native
200
+ * dialog identity, naming props, lifecycle handlers, and exactly one hydration
201
+ * ref owner.
202
+ * @example
203
+ * ```ts
204
+ * import { Effect } from "effect"
205
+ * import * as Dialog from "@typed/ui/Dialog"
206
+ *
207
+ * const program = Effect.gen(function* () {
208
+ * const state = yield* Dialog.makeState()
209
+ * return Dialog.Content({
210
+ * state,
211
+ * id: "confirm",
212
+ * label: "Confirm",
213
+ * content: "Continue?"
214
+ * })
215
+ * })
216
+ * ```
217
+ * @since 1.0.0
218
+ * @category Native content host
219
+ */
220
+ export function Content(options, host) {
221
+ return Dom.renderHost()(options, host, contentInternalProps(options), options.content, (props, content) => {
222
+ return html `<dialog ...${props}>${content}</dialog>`;
223
+ });
224
+ }
225
+ /** Canonical widget alias for `Content`.
226
+ * @remarks
227
+ * ## Why
228
+ * The alias provides the widget name while `Content` identifies compound use.
229
+ * ## Ownership and lifetime
230
+ * It has exactly the same native element and Scope ownership as `Content`.
231
+ * @since 1.0.0
232
+ * @category Native content host
233
+ */
234
+ export const Dialog = Content;
235
+ function closeInternalProps(options, requestCloseLifecycle) {
236
+ return ({ property }) => ({
237
+ type: "button",
238
+ commandfor: property("controls", undefined),
239
+ command: options.controls === undefined
240
+ ? undefined
241
+ : requestCloseLifecycle
242
+ ? "request-close"
243
+ : "close",
244
+ onclick: options.controls !== undefined
245
+ ? undefined
246
+ : requestCloseLifecycle
247
+ ? requestClose(options.state)
248
+ : close(options.state),
249
+ });
250
+ }
251
+ /** Renders a button that closes a dialog without a cancel request.
252
+ * @remarks
253
+ * ## Why
254
+ * Accepted actions need a direct close path distinct from cancelable dismissal.
255
+ * `controls` selects native `command="close"` and disables the Fx click
256
+ * fallback; omit it when native dialog commands are unavailable.
257
+ * ## Ownership and lifetime
258
+ * Running the Fx owns handlers/dynamic content by Scope. Custom hosts must
259
+ * preserve type, command or fallback click behavior, and content.
260
+ * @example
261
+ * ```ts
262
+ * import { Effect } from "effect"
263
+ * import * as Dialog from "@typed/ui/Dialog"
264
+ *
265
+ * const program = Effect.gen(function* () {
266
+ * const state = yield* Dialog.makeState({ open: true })
267
+ * return Dialog.Close({ state, content: "Done" })
268
+ * })
269
+ * ```
270
+ * @since 1.0.0
271
+ * @category Direct dismissal
272
+ */
273
+ export function Close(options, host) {
274
+ return Dom.renderHost()(options, host, closeInternalProps(options, false), options.content, (props, content) => html `<button ...${props}>${content}</button>`);
275
+ }
276
+ /** Alias for direct `Close` behavior.
277
+ * @remarks
278
+ * ## Why
279
+ * The alias supports dismissal vocabulary without introducing another lifecycle.
280
+ * ## Ownership and lifetime
281
+ * It has exactly the same Scope and native button ownership as `Close`.
282
+ * @since 1.0.0
283
+ * @category Direct dismissal
284
+ */
285
+ export const Dismiss = Close;
286
+ /** Renders a button that starts the cancelable native close-request lifecycle.
287
+ * @remarks
288
+ * ## Why
289
+ * Consumers can intercept a real `cancel` event before the dialog closes.
290
+ * `controls` selects native `command="request-close"` and disables the Fx click
291
+ * fallback; omit it when native dialog commands are unavailable.
292
+ * ## Ownership and lifetime
293
+ * Running the Fx owns handlers by Scope; dialog registration remains owned by
294
+ * the corresponding `Content` Scope.
295
+ * @example
296
+ * ```ts
297
+ * import { Effect } from "effect"
298
+ * import * as Dialog from "@typed/ui/Dialog"
299
+ *
300
+ * const program = Effect.gen(function* () {
301
+ * const state = yield* Dialog.makeState({ open: true })
302
+ * return Dialog.RequestClose({ state, content: "Cancel" })
303
+ * })
304
+ * ```
305
+ * @since 1.0.0
306
+ * @category Cancelable dismissal
307
+ */
308
+ export function RequestClose(options, host) {
309
+ return Dom.renderHost()(options, host, closeInternalProps(options, true), options.content, (props, content) => html `<button ...${props}>${content}</button>`);
310
+ }
311
+ function headingInternalProps() {
312
+ return ({ property }) => ({
313
+ id: property("id", undefined),
314
+ role: "heading",
315
+ "aria-level": property("level", 2),
316
+ });
317
+ }
318
+ /** Renders a dialog heading with explicit ARIA level semantics.
319
+ * @remarks
320
+ * ## Why
321
+ * The helper creates a reliable naming target and preserves heading hierarchy.
322
+ * ## Ownership and lifetime
323
+ * Running the Fx owns dynamic content by Scope. A custom host must preserve id,
324
+ * role, and `aria-level`.
325
+ * @example
326
+ * ```ts
327
+ * import { Heading } from "@typed/ui/Dialog"
328
+ *
329
+ * const title = Heading({ id: "confirm-title", content: "Confirm" })
330
+ * ```
331
+ * @since 1.0.0
332
+ * @category Dialog naming and description
333
+ */
334
+ export function Heading(options, host) {
335
+ return Dom.renderHost()(options, host, headingInternalProps(), options.content, (props, content) => html `<h2 ...${props}>${content}</h2>`);
336
+ }
337
+ function descriptionInternalProps() {
338
+ return ({ property }) => ({ id: property("id", undefined) });
339
+ }
340
+ /** Renders descriptive dialog text with an optional relationship id.
341
+ * @remarks
342
+ * ## Why
343
+ * The helper keeps accessible description wiring explicit and platform-native.
344
+ * ## Ownership and lifetime
345
+ * Running the Fx owns dynamic content by Scope. A custom host must preserve id.
346
+ * @example
347
+ * ```ts
348
+ * import { Description } from "@typed/ui/Dialog"
349
+ *
350
+ * const detail = Description({ id: "confirm-detail", content: "This cannot be undone." })
351
+ * ```
352
+ * @since 1.0.0
353
+ * @category Dialog naming and description
354
+ */
355
+ export function Description(options, host) {
356
+ return Dom.renderHost()(options, host, descriptionInternalProps(), options.content, (props, content) => html `<p ...${props}>${content}</p>`);
357
+ }
@@ -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 Open 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 Open 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 Open 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 Open 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 Open state
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 Open state
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 Open 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 Summary activation
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 Rendered 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 Summary activation
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 Native content host
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 connection
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 Rendered 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 Native content host
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 Native content host
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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"}