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

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,241 @@
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 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 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 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 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 schemas
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 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 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 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 models
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
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 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 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 components
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 models
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
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 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 onbeforetoggle: EventHandler.EventHandler<Event, Schema.SchemaError, never>;
213
+ readonly ontoggle: EventHandler.EventHandler<Event, Schema.SchemaError, never>;
214
+ readonly ref: import("./Dom/Refs.js").ComposedRef<RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>, (element: HTMLElement) => Effect.Effect<void, Schema.SchemaError, Scope.Scope>> | undefined;
215
+ };
216
+ type ContentInternalProps<Options extends ContentOptions> = ReturnType<ReturnType<typeof contentInternalProps<Options>>>;
217
+ /** Renders manually controlled native popover content.
218
+ * @remarks
219
+ * ## Why
220
+ * The component delegates top-layer lifecycle to the Popover API, synchronizes
221
+ * real `beforetoggle`/`toggle` events, and handles Escape through native events.
222
+ * ## Ownership and lifetime
223
+ * Running the Fx owns listeners and the NativePopover observer in its Effect
224
+ * Scope. A custom host must preserve `popover="manual"`, toggle handlers, and
225
+ * the single composed hydration ref.
226
+ * @example
227
+ * ```ts
228
+ * import { Effect } from "effect"
229
+ * import * as Popover from "@typed/ui/Popover"
230
+ *
231
+ * const program = Effect.gen(function* () {
232
+ * const state = yield* Popover.makeState()
233
+ * return Popover.Content({ state, content: "Preferences" })
234
+ * })
235
+ * ```
236
+ * @since 1.0.0
237
+ * @category components
238
+ */
239
+ 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>;
240
+ export {};
241
+ //# 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,141 @@
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 schemas
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 constructors
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 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 components
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
+ onbeforetoggle: EventHandler.make(Effect.fn((event) => setOpen(options.state, Dom.toggleState(event) === "open"))),
111
+ ontoggle: EventHandler.make(Effect.fn((event) => setOpen(options.state, Dom.toggleState(event) === "open"))),
112
+ ref: Dom.composeRefs(options.state, NativePopover.ref(options.state)),
113
+ });
114
+ }
115
+ /** Renders manually controlled native popover content.
116
+ * @remarks
117
+ * ## Why
118
+ * The component delegates top-layer lifecycle to the Popover API, synchronizes
119
+ * real `beforetoggle`/`toggle` events, and handles Escape through native events.
120
+ * ## Ownership and lifetime
121
+ * Running the Fx owns listeners and the NativePopover observer in its Effect
122
+ * Scope. A custom host must preserve `popover="manual"`, toggle handlers, and
123
+ * the single composed hydration ref.
124
+ * @example
125
+ * ```ts
126
+ * import { Effect } from "effect"
127
+ * import * as Popover from "@typed/ui/Popover"
128
+ *
129
+ * const program = Effect.gen(function* () {
130
+ * const state = yield* Popover.makeState()
131
+ * return Popover.Content({ state, content: "Preferences" })
132
+ * })
133
+ * ```
134
+ * @since 1.0.0
135
+ * @category components
136
+ */
137
+ export function Content(options, host) {
138
+ return Dom.renderHost()(options, host, contentInternalProps(options), options.content, (props, content) => {
139
+ return html `<div ...${props}>${content}</div>`;
140
+ });
141
+ }