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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -26
  3. package/dist/Alert.d.ts +72 -0
  4. package/dist/Alert.d.ts.map +1 -0
  5. package/dist/Alert.js +40 -0
  6. package/dist/Button.d.ts +100 -0
  7. package/dist/Button.d.ts.map +1 -0
  8. package/dist/Button.js +42 -0
  9. package/dist/Carousel.d.ts +346 -0
  10. package/dist/Carousel.d.ts.map +1 -0
  11. package/dist/Carousel.js +264 -0
  12. package/dist/Checkbox.d.ts +168 -0
  13. package/dist/Checkbox.d.ts.map +1 -0
  14. package/dist/Checkbox.js +146 -0
  15. package/dist/Collection.d.ts +254 -0
  16. package/dist/Collection.d.ts.map +1 -0
  17. package/dist/Collection.js +218 -0
  18. package/dist/Combobox.d.ts +367 -0
  19. package/dist/Combobox.d.ts.map +1 -0
  20. package/dist/Combobox.js +300 -0
  21. package/dist/Composite.d.ts +823 -0
  22. package/dist/Composite.d.ts.map +1 -0
  23. package/dist/Composite.js +615 -0
  24. package/dist/Dialog.d.ts +544 -0
  25. package/dist/Dialog.d.ts.map +1 -0
  26. package/dist/Dialog.js +357 -0
  27. package/dist/Disclosure.d.ts +219 -0
  28. package/dist/Disclosure.d.ts.map +1 -0
  29. package/dist/Disclosure.js +128 -0
  30. package/dist/Dom/Events.d.ts +122 -0
  31. package/dist/Dom/Events.d.ts.map +1 -0
  32. package/dist/Dom/Events.js +192 -0
  33. package/dist/Dom/Props.d.ts +161 -0
  34. package/dist/Dom/Props.d.ts.map +1 -0
  35. package/dist/Dom/Props.js +110 -0
  36. package/dist/Dom/Refs.d.ts +58 -0
  37. package/dist/Dom/Refs.d.ts.map +1 -0
  38. package/dist/Dom/Refs.js +61 -0
  39. package/dist/Dom/Render.d.ts +59 -0
  40. package/dist/Dom/Render.d.ts.map +1 -0
  41. package/dist/Dom/Render.js +71 -0
  42. package/dist/Dom/Types.d.ts +570 -0
  43. package/dist/Dom/Types.d.ts.map +1 -0
  44. package/dist/Dom/Types.js +1 -0
  45. package/dist/Dom/index.d.ts +20 -0
  46. package/dist/Dom/index.d.ts.map +1 -0
  47. package/dist/Dom/index.js +8 -0
  48. package/dist/Dom.d.ts +14 -0
  49. package/dist/Dom.d.ts.map +1 -0
  50. package/dist/Dom.js +13 -0
  51. package/dist/Focusable.d.ts +85 -0
  52. package/dist/Focusable.d.ts.map +1 -0
  53. package/dist/Focusable.js +35 -0
  54. package/dist/Form.d.ts +1727 -0
  55. package/dist/Form.d.ts.map +1 -0
  56. package/dist/Form.js +1142 -0
  57. package/dist/Grid.d.ts +388 -0
  58. package/dist/Grid.d.ts.map +1 -0
  59. package/dist/Grid.js +284 -0
  60. package/dist/Group.d.ts +128 -0
  61. package/dist/Group.d.ts.map +1 -0
  62. package/dist/Group.js +71 -0
  63. package/dist/Heading.d.ts +87 -0
  64. package/dist/Heading.d.ts.map +1 -0
  65. package/dist/Heading.js +58 -0
  66. package/dist/Hovercard.d.ts +297 -0
  67. package/dist/Hovercard.d.ts.map +1 -0
  68. package/dist/Hovercard.js +188 -0
  69. package/dist/HttpRouter.d.ts +129 -6
  70. package/dist/HttpRouter.d.ts.map +1 -1
  71. package/dist/HttpRouter.js +199 -56
  72. package/dist/Link.d.ts +63 -28
  73. package/dist/Link.d.ts.map +1 -1
  74. package/dist/Link.js +84 -37
  75. package/dist/Listbox.d.ts +305 -0
  76. package/dist/Listbox.d.ts.map +1 -0
  77. package/dist/Listbox.js +245 -0
  78. package/dist/Menu.d.ts +663 -0
  79. package/dist/Menu.d.ts.map +1 -0
  80. package/dist/Menu.js +569 -0
  81. package/dist/Menubar.d.ts +249 -0
  82. package/dist/Menubar.d.ts.map +1 -0
  83. package/dist/Menubar.js +207 -0
  84. package/dist/Meter.d.ts +157 -0
  85. package/dist/Meter.d.ts.map +1 -0
  86. package/dist/Meter.js +87 -0
  87. package/dist/NativeDetails.d.ts +41 -0
  88. package/dist/NativeDetails.d.ts.map +1 -0
  89. package/dist/NativeDetails.js +40 -0
  90. package/dist/NativeDialog.d.ts +66 -0
  91. package/dist/NativeDialog.d.ts.map +1 -0
  92. package/dist/NativeDialog.js +88 -0
  93. package/dist/NativePopover.d.ts +43 -0
  94. package/dist/NativePopover.d.ts.map +1 -0
  95. package/dist/NativePopover.js +84 -0
  96. package/dist/Popover.d.ts +240 -0
  97. package/dist/Popover.d.ts.map +1 -0
  98. package/dist/Popover.js +140 -0
  99. package/dist/RadioGroup.d.ts +330 -0
  100. package/dist/RadioGroup.d.ts.map +1 -0
  101. package/dist/RadioGroup.js +241 -0
  102. package/dist/Role.d.ts +64 -0
  103. package/dist/Role.d.ts.map +1 -0
  104. package/dist/Role.js +27 -0
  105. package/dist/Select.d.ts +418 -0
  106. package/dist/Select.d.ts.map +1 -0
  107. package/dist/Select.js +357 -0
  108. package/dist/Separator.d.ts +58 -0
  109. package/dist/Separator.d.ts.map +1 -0
  110. package/dist/Separator.js +32 -0
  111. package/dist/Slider.d.ts +141 -0
  112. package/dist/Slider.d.ts.map +1 -0
  113. package/dist/Slider.js +101 -0
  114. package/dist/SpinButton.d.ts +141 -0
  115. package/dist/SpinButton.d.ts.map +1 -0
  116. package/dist/SpinButton.js +101 -0
  117. package/dist/Storybook.d.ts +76 -0
  118. package/dist/Storybook.d.ts.map +1 -0
  119. package/dist/Storybook.js +102 -0
  120. package/dist/Switch.d.ts +148 -0
  121. package/dist/Switch.d.ts.map +1 -0
  122. package/dist/Switch.js +110 -0
  123. package/dist/Tab.d.ts +26 -0
  124. package/dist/Tab.d.ts.map +1 -0
  125. package/dist/Tab.js +25 -0
  126. package/dist/Tabs.d.ts +411 -0
  127. package/dist/Tabs.d.ts.map +1 -0
  128. package/dist/Tabs.js +262 -0
  129. package/dist/Toolbar.d.ts +248 -0
  130. package/dist/Toolbar.d.ts.map +1 -0
  131. package/dist/Toolbar.js +187 -0
  132. package/dist/Tooltip.d.ts +296 -0
  133. package/dist/Tooltip.d.ts.map +1 -0
  134. package/dist/Tooltip.js +172 -0
  135. package/dist/Tree.d.ts +405 -0
  136. package/dist/Tree.d.ts.map +1 -0
  137. package/dist/Tree.js +333 -0
  138. package/dist/TreeGrid.d.ts +426 -0
  139. package/dist/TreeGrid.d.ts.map +1 -0
  140. package/dist/TreeGrid.js +308 -0
  141. package/dist/VisuallyHidden.d.ts +68 -0
  142. package/dist/VisuallyHidden.d.ts.map +1 -0
  143. package/dist/VisuallyHidden.js +44 -0
  144. package/dist/WindowSplitter.d.ts +336 -0
  145. package/dist/WindowSplitter.d.ts.map +1 -0
  146. package/dist/WindowSplitter.js +305 -0
  147. package/dist/index.d.ts +48 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +48 -0
  150. package/package.json +47 -14
  151. package/src/HttpRouter.test.ts +0 -294
  152. package/src/HttpRouter.ts +0 -168
  153. package/src/Link.test.ts +0 -85
  154. package/src/Link.ts +0 -107
  155. package/src/index.ts +0 -2
  156. package/tsconfig.json +0 -6
@@ -0,0 +1,240 @@
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 popover visibility.
10
+ * @remarks
11
+ * ## Why
12
+ * Open state can be controlled and tested without rendering the top layer.
13
+ * ## Ownership and lifetime
14
+ * Plain data retains no resources; RefSubject lifetime is Scope-owned.
15
+ * @since 1.0.0
16
+ * @category Open state
17
+ */
18
+ export interface State {
19
+ /** Whether native popover content should be open.
20
+ * @remarks
21
+ * ## Why
22
+ * The value coordinates trigger ARIA state and native Popover API state.
23
+ * ## Ownership and lifetime
24
+ * Plain state acquires no resources.
25
+ * @since 1.0.0
26
+ * @category Open state
27
+ */
28
+ readonly open: boolean;
29
+ }
30
+ /** Optional initial popover visibility.
31
+ * @remarks
32
+ * ## Why
33
+ * A false default gives SSR and hydration a deterministic closed 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 serializable 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 serialized popover state.
52
+ * @remarks
53
+ * ## Why
54
+ * A shared boolean shape keeps server and browser hydration 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 popover state.
64
+ * @remarks
65
+ * ## Why
66
+ * Applications can own and test visibility independently of a component.
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 Popover from "@typed/ui/Popover"
73
+ *
74
+ * const program = Effect.gen(function* () {
75
+ * return yield* Popover.makeState()
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 popover visibility atomically.
85
+ * @remarks
86
+ * ## Why
87
+ * Explicit state transitions coordinate trigger and content without hidden
88
+ * component-local state.
89
+ * ## Ownership and lifetime
90
+ * The Effect uses the existing RefSubject lifetime and acquires no resource.
91
+ * @example
92
+ * ```ts
93
+ * import { Effect } from "effect"
94
+ * import * as Popover from "@typed/ui/Popover"
95
+ *
96
+ * const program = Effect.gen(function* () {
97
+ * const state = yield* Popover.makeState()
98
+ * yield* Popover.setOpen(state, true)
99
+ * })
100
+ * ```
101
+ * @since 1.0.0
102
+ * @category Open state
103
+ */
104
+ export declare function setOpen<E, R>(state: RefSubject.RefSubject<State, E, R>, open: boolean): Effect.Effect<State, E, R>;
105
+ /** Options for the native popover trigger.
106
+ * @remarks
107
+ * ## Why
108
+ * The trigger exposes native target attributes when an id is supplied and a
109
+ * state-driven fallback otherwise.
110
+ * ## Ownership and lifetime
111
+ * Options are inert; rendering owns listeners/subscriptions by Scope.
112
+ * @since 1.0.0
113
+ * @category Opening controls
114
+ */
115
+ export interface TriggerOptions extends Dom.HostOptions<HTMLButtonElement> {
116
+ /** Hydrated state shared with popover content.
117
+ * @remarks
118
+ * ## Why
119
+ * It drives `aria-expanded` and fallback activation from one source.
120
+ * ## Ownership and lifetime
121
+ * The trigger borrows state; its original Scope owns it.
122
+ * @since 1.0.0
123
+ * @category State connection
124
+ */
125
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
126
+ /** Id of content targeted through native `popovertarget`.
127
+ * @remarks
128
+ * ## Why
129
+ * Native targeting lets the browser run the Popover API lifecycle directly.
130
+ * ## Ownership and lifetime
131
+ * The string is reflected and retains no resources.
132
+ * @since 1.0.0
133
+ * @category Identity and relationships
134
+ */
135
+ readonly controls?: string;
136
+ /** Visible trigger content and accessible name.
137
+ * @remarks
138
+ * ## Why
139
+ * A trigger needs a discoverable accessible name.
140
+ * ## Ownership and lifetime
141
+ * Dynamic content follows the trigger Scope.
142
+ * @since 1.0.0
143
+ * @category Rendered content
144
+ */
145
+ readonly content: Renderable.Any;
146
+ }
147
+ declare function triggerInternalProps<const Options extends TriggerOptions>(options: Options): ({ property }: Dom.InternalPropsHelpers<Options>) => {
148
+ readonly type: "button";
149
+ readonly "aria-expanded": RefSubject.Computed<boolean, Schema.SchemaError, never>;
150
+ readonly popovertarget: Exclude<Dom.Property<Options, "controls">, Dom.Nullish> | undefined;
151
+ readonly popovertargetaction: "show" | undefined;
152
+ readonly onclick: Effect.Effect<State, Schema.SchemaError, never> | undefined;
153
+ readonly onkeydown: EventHandler.EventHandler<KeyboardEvent, Schema.SchemaError, never>;
154
+ };
155
+ type TriggerInternalProps<Options extends TriggerOptions> = ReturnType<ReturnType<typeof triggerInternalProps<Options>>>;
156
+ /** Renders a native button that opens popover content.
157
+ * @remarks
158
+ * ## Why
159
+ * It preserves button keyboard behavior, native target commands, real DOM
160
+ * events, and an explicit state fallback when no target id is supplied.
161
+ * ## Ownership and lifetime
162
+ * Running the Fx owns reactive ARIA state and listeners in its Scope. A custom
163
+ * host must preserve button type, target props, `aria-expanded`, and handlers.
164
+ * @example
165
+ * ```ts
166
+ * import { Effect } from "effect"
167
+ * import * as Popover from "@typed/ui/Popover"
168
+ *
169
+ * const program = Effect.gen(function* () {
170
+ * const state = yield* Popover.makeState()
171
+ * return Popover.Trigger({ state, controls: "account", content: "Account" })
172
+ * })
173
+ * ```
174
+ * @since 1.0.0
175
+ * @category Opening controls
176
+ */
177
+ export declare function Trigger<const Options extends TriggerOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, TriggerInternalProps<Options>>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
178
+ /** Options for manually controlled native popover content.
179
+ * @remarks
180
+ * ## Why
181
+ * Content and state remain explicit while the browser owns top-layer behavior.
182
+ * ## Ownership and lifetime
183
+ * Options are inert; rendering owns the native ref and subscriptions by Scope.
184
+ * @since 1.0.0
185
+ * @category Native content host
186
+ */
187
+ export interface ContentOptions extends Dom.HostOptions<HTMLDivElement> {
188
+ /** Hydrated state synchronized with native popover visibility.
189
+ * @remarks
190
+ * ## Why
191
+ * Native toggle events and application updates converge on one source.
192
+ * ## Ownership and lifetime
193
+ * The content borrows the state; its original Scope owns it.
194
+ * @since 1.0.0
195
+ * @category State connection
196
+ */
197
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
198
+ /** Content rendered in the top layer.
199
+ * @remarks
200
+ * ## Why
201
+ * Renderable output retains Typed error and service channels.
202
+ * ## Ownership and lifetime
203
+ * Dynamic content follows the content Scope.
204
+ * @since 1.0.0
205
+ * @category Rendered content
206
+ */
207
+ readonly content: Renderable.Any;
208
+ }
209
+ declare function contentInternalProps<const Options extends ContentOptions>(options: Options): () => {
210
+ readonly popover: "manual";
211
+ readonly onkeydown: EventHandler.EventHandler<KeyboardEvent, Schema.SchemaError, never>;
212
+ readonly ontoggle: EventHandler.EventHandler<Event, Schema.SchemaError, never>;
213
+ readonly ref: import("./Dom/Refs.js").ComposedRef<RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>, (element: HTMLElement) => Effect.Effect<void, Schema.SchemaError, Scope.Scope>> | undefined;
214
+ };
215
+ type ContentInternalProps<Options extends ContentOptions> = ReturnType<ReturnType<typeof contentInternalProps<Options>>>;
216
+ /** Renders manually controlled native popover content.
217
+ * @remarks
218
+ * ## Why
219
+ * The component delegates top-layer lifecycle to the Popover API, synchronizes
220
+ * completed `toggle` events, and handles Escape through native events.
221
+ * ## Ownership and lifetime
222
+ * Running the Fx owns listeners and the NativePopover observer in its Effect
223
+ * Scope. A custom host must preserve `popover="manual"`, the toggle handler, and
224
+ * the single composed hydration ref.
225
+ * @example
226
+ * ```ts
227
+ * import { Effect } from "effect"
228
+ * import * as Popover from "@typed/ui/Popover"
229
+ *
230
+ * const program = Effect.gen(function* () {
231
+ * const state = yield* Popover.makeState()
232
+ * return Popover.Content({ state, content: "Preferences" })
233
+ * })
234
+ * ```
235
+ * @since 1.0.0
236
+ * @category Native content host
237
+ */
238
+ 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>;
239
+ export {};
240
+ //# sourceMappingURL=Popover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../src/Popover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Schema from "effect/Schema";
3
+ import { RefSubject } from "@typed/fx";
4
+ import { EventHandler, html, } from "@typed/template";
5
+ import * as Dom from "./Dom.js";
6
+ import * as NativePopover from "./NativePopover.js";
7
+ /** Schema for serialized popover state.
8
+ * @remarks
9
+ * ## Why
10
+ * A shared boolean shape keeps server and browser hydration compatible.
11
+ * ## Ownership and lifetime
12
+ * The immutable schema acquires no resources.
13
+ * @since 1.0.0
14
+ * @category Open state
15
+ */
16
+ export const StateSchema = Schema.Struct({ open: Schema.Boolean });
17
+ /** Creates hydrated popover state.
18
+ * @remarks
19
+ * ## Why
20
+ * Applications can own and test visibility independently of a component.
21
+ * ## Ownership and lifetime
22
+ * The calling Effect Scope owns the returned RefSubject.
23
+ * @example
24
+ * ```ts
25
+ * import { Effect } from "effect"
26
+ * import * as Popover from "@typed/ui/Popover"
27
+ *
28
+ * const program = Effect.gen(function* () {
29
+ * return yield* Popover.makeState()
30
+ * })
31
+ * ```
32
+ * @since 1.0.0
33
+ * @category Open state
34
+ */
35
+ export function makeState(initial = {}) {
36
+ return RefSubject.hydrate(StateSchema, { open: initial.open ?? false });
37
+ }
38
+ /** Sets popover visibility atomically.
39
+ * @remarks
40
+ * ## Why
41
+ * Explicit state transitions coordinate trigger and content without hidden
42
+ * component-local state.
43
+ * ## Ownership and lifetime
44
+ * The Effect uses the existing RefSubject lifetime and acquires no resource.
45
+ * @example
46
+ * ```ts
47
+ * import { Effect } from "effect"
48
+ * import * as Popover from "@typed/ui/Popover"
49
+ *
50
+ * const program = Effect.gen(function* () {
51
+ * const state = yield* Popover.makeState()
52
+ * yield* Popover.setOpen(state, true)
53
+ * })
54
+ * ```
55
+ * @since 1.0.0
56
+ * @category Open state
57
+ */
58
+ export function setOpen(state, open) {
59
+ return RefSubject.update(state, (current) => ({ ...current, open }));
60
+ }
61
+ function triggerInternalProps(options) {
62
+ const open = RefSubject.map(options.state, (state) => state.open);
63
+ return ({ property }) => ({
64
+ type: "button",
65
+ "aria-expanded": open,
66
+ popovertarget: property("controls", undefined),
67
+ popovertargetaction: options.controls === undefined ? undefined : "show",
68
+ onclick: options.controls === undefined ? setOpen(options.state, true) : undefined,
69
+ onkeydown: EventHandler.make(Effect.fn(function* (event) {
70
+ if (event.key !== "Escape")
71
+ return;
72
+ event.preventDefault();
73
+ yield* setOpen(options.state, false);
74
+ })),
75
+ });
76
+ }
77
+ /** Renders a native button that opens popover content.
78
+ * @remarks
79
+ * ## Why
80
+ * It preserves button keyboard behavior, native target commands, real DOM
81
+ * events, and an explicit state fallback when no target id is supplied.
82
+ * ## Ownership and lifetime
83
+ * Running the Fx owns reactive ARIA state and listeners in its Scope. A custom
84
+ * host must preserve button type, target props, `aria-expanded`, and handlers.
85
+ * @example
86
+ * ```ts
87
+ * import { Effect } from "effect"
88
+ * import * as Popover from "@typed/ui/Popover"
89
+ *
90
+ * const program = Effect.gen(function* () {
91
+ * const state = yield* Popover.makeState()
92
+ * return Popover.Trigger({ state, controls: "account", content: "Account" })
93
+ * })
94
+ * ```
95
+ * @since 1.0.0
96
+ * @category Opening controls
97
+ */
98
+ export function Trigger(options, host) {
99
+ return Dom.renderHost()(options, host, triggerInternalProps(options), options.content, (props, content) => html `<button ...${props}>${content}</button>`);
100
+ }
101
+ function contentInternalProps(options) {
102
+ return () => ({
103
+ popover: "manual",
104
+ onkeydown: EventHandler.make(Effect.fn(function* (event) {
105
+ if (event.key !== "Escape")
106
+ return;
107
+ event.preventDefault();
108
+ yield* setOpen(options.state, false);
109
+ })),
110
+ ontoggle: EventHandler.make(Effect.fn((event) => setOpen(options.state, Dom.toggleState(event) === "open"))),
111
+ ref: Dom.composeRefs(options.state, NativePopover.ref(options.state)),
112
+ });
113
+ }
114
+ /** Renders manually controlled native popover content.
115
+ * @remarks
116
+ * ## Why
117
+ * The component delegates top-layer lifecycle to the Popover API, synchronizes
118
+ * completed `toggle` events, and handles Escape through native events.
119
+ * ## Ownership and lifetime
120
+ * Running the Fx owns listeners and the NativePopover observer in its Effect
121
+ * Scope. A custom host must preserve `popover="manual"`, the toggle handler, and
122
+ * the single composed hydration ref.
123
+ * @example
124
+ * ```ts
125
+ * import { Effect } from "effect"
126
+ * import * as Popover from "@typed/ui/Popover"
127
+ *
128
+ * const program = Effect.gen(function* () {
129
+ * const state = yield* Popover.makeState()
130
+ * return Popover.Content({ state, content: "Preferences" })
131
+ * })
132
+ * ```
133
+ * @since 1.0.0
134
+ * @category Native content host
135
+ */
136
+ export function Content(options, host) {
137
+ return Dom.renderHost()(options, host, contentInternalProps(options), options.content, (props, content) => {
138
+ return html `<div ...${props}>${content}</div>`;
139
+ });
140
+ }