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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +53 -9
  2. package/dist/Alert.d.ts +78 -0
  3. package/dist/Alert.d.ts.map +1 -0
  4. package/dist/Alert.js +37 -0
  5. package/dist/Button.d.ts +144 -0
  6. package/dist/Button.d.ts.map +1 -0
  7. package/dist/Button.js +38 -0
  8. package/dist/Carousel.d.ts +547 -0
  9. package/dist/Carousel.d.ts.map +1 -0
  10. package/dist/Carousel.js +387 -0
  11. package/dist/Checkbox.d.ts +203 -0
  12. package/dist/Checkbox.d.ts.map +1 -0
  13. package/dist/Checkbox.js +140 -0
  14. package/dist/Collection.d.ts +254 -0
  15. package/dist/Collection.d.ts.map +1 -0
  16. package/dist/Collection.js +218 -0
  17. package/dist/Combobox.d.ts +516 -0
  18. package/dist/Combobox.d.ts.map +1 -0
  19. package/dist/Combobox.js +371 -0
  20. package/dist/Component.d.ts +127 -0
  21. package/dist/Component.d.ts.map +1 -0
  22. package/dist/Component.js +60 -0
  23. package/dist/Composite.d.ts +823 -0
  24. package/dist/Composite.d.ts.map +1 -0
  25. package/dist/Composite.js +615 -0
  26. package/dist/Dialog.d.ts +544 -0
  27. package/dist/Dialog.d.ts.map +1 -0
  28. package/dist/Dialog.js +355 -0
  29. package/dist/Disclosure.d.ts +219 -0
  30. package/dist/Disclosure.d.ts.map +1 -0
  31. package/dist/Disclosure.js +128 -0
  32. package/dist/Dom/Events.d.ts +122 -0
  33. package/dist/Dom/Events.d.ts.map +1 -0
  34. package/dist/Dom/Events.js +192 -0
  35. package/dist/Dom/Props.d.ts +161 -0
  36. package/dist/Dom/Props.d.ts.map +1 -0
  37. package/dist/Dom/Props.js +110 -0
  38. package/dist/Dom/Refs.d.ts +58 -0
  39. package/dist/Dom/Refs.d.ts.map +1 -0
  40. package/dist/Dom/Refs.js +61 -0
  41. package/dist/Dom/Render.d.ts +59 -0
  42. package/dist/Dom/Render.d.ts.map +1 -0
  43. package/dist/Dom/Render.js +71 -0
  44. package/dist/Dom/Types.d.ts +570 -0
  45. package/dist/Dom/Types.d.ts.map +1 -0
  46. package/dist/Dom/Types.js +1 -0
  47. package/dist/Dom/index.d.ts +20 -0
  48. package/dist/Dom/index.d.ts.map +1 -0
  49. package/dist/Dom/index.js +8 -0
  50. package/dist/Dom.d.ts +14 -0
  51. package/dist/Dom.d.ts.map +1 -0
  52. package/dist/Dom.js +13 -0
  53. package/dist/Focusable.d.ts +85 -0
  54. package/dist/Focusable.d.ts.map +1 -0
  55. package/dist/Focusable.js +35 -0
  56. package/dist/Form.d.ts +1695 -0
  57. package/dist/Form.d.ts.map +1 -0
  58. package/dist/Form.js +987 -0
  59. package/dist/Grid.d.ts +569 -0
  60. package/dist/Grid.d.ts.map +1 -0
  61. package/dist/Grid.js +379 -0
  62. package/dist/Group.d.ts +147 -0
  63. package/dist/Group.d.ts.map +1 -0
  64. package/dist/Group.js +63 -0
  65. package/dist/Heading.d.ts +86 -0
  66. package/dist/Heading.d.ts.map +1 -0
  67. package/dist/Heading.js +48 -0
  68. package/dist/Hovercard.d.ts +297 -0
  69. package/dist/Hovercard.d.ts.map +1 -0
  70. package/dist/Hovercard.js +188 -0
  71. package/dist/HttpRouter.d.ts +129 -6
  72. package/dist/HttpRouter.d.ts.map +1 -1
  73. package/dist/HttpRouter.js +196 -53
  74. package/dist/Link.d.ts +67 -28
  75. package/dist/Link.d.ts.map +1 -1
  76. package/dist/Link.js +91 -37
  77. package/dist/Listbox.d.ts +437 -0
  78. package/dist/Listbox.d.ts.map +1 -0
  79. package/dist/Listbox.js +316 -0
  80. package/dist/Menu.d.ts +972 -0
  81. package/dist/Menu.d.ts.map +1 -0
  82. package/dist/Menu.js +731 -0
  83. package/dist/Menubar.d.ts +367 -0
  84. package/dist/Menubar.d.ts.map +1 -0
  85. package/dist/Menubar.js +265 -0
  86. package/dist/Meter.d.ts +217 -0
  87. package/dist/Meter.d.ts.map +1 -0
  88. package/dist/Meter.js +94 -0
  89. package/dist/NativeDetails.d.ts +41 -0
  90. package/dist/NativeDetails.d.ts.map +1 -0
  91. package/dist/NativeDetails.js +40 -0
  92. package/dist/NativeDialog.d.ts +64 -0
  93. package/dist/NativeDialog.d.ts.map +1 -0
  94. package/dist/NativeDialog.js +50 -0
  95. package/dist/NativePopover.d.ts +41 -0
  96. package/dist/NativePopover.d.ts.map +1 -0
  97. package/dist/NativePopover.js +46 -0
  98. package/dist/Popover.d.ts +241 -0
  99. package/dist/Popover.d.ts.map +1 -0
  100. package/dist/Popover.js +141 -0
  101. package/dist/RadioGroup.d.ts +432 -0
  102. package/dist/RadioGroup.d.ts.map +1 -0
  103. package/dist/RadioGroup.js +291 -0
  104. package/dist/Role.d.ts +64 -0
  105. package/dist/Role.d.ts.map +1 -0
  106. package/dist/Role.js +27 -0
  107. package/dist/Select.d.ts +529 -0
  108. package/dist/Select.d.ts.map +1 -0
  109. package/dist/Select.js +407 -0
  110. package/dist/Separator.d.ts +54 -0
  111. package/dist/Separator.d.ts.map +1 -0
  112. package/dist/Separator.js +26 -0
  113. package/dist/Slider.d.ts +188 -0
  114. package/dist/Slider.d.ts.map +1 -0
  115. package/dist/Slider.js +96 -0
  116. package/dist/SpinButton.d.ts +188 -0
  117. package/dist/SpinButton.d.ts.map +1 -0
  118. package/dist/SpinButton.js +96 -0
  119. package/dist/Storybook.d.ts +76 -0
  120. package/dist/Storybook.d.ts.map +1 -0
  121. package/dist/Storybook.js +102 -0
  122. package/dist/Switch.d.ts +187 -0
  123. package/dist/Switch.d.ts.map +1 -0
  124. package/dist/Switch.js +116 -0
  125. package/dist/Tab.d.ts +26 -0
  126. package/dist/Tab.d.ts.map +1 -0
  127. package/dist/Tab.js +25 -0
  128. package/dist/Tabs.d.ts +591 -0
  129. package/dist/Tabs.d.ts.map +1 -0
  130. package/dist/Tabs.js +346 -0
  131. package/dist/Toolbar.d.ts +366 -0
  132. package/dist/Toolbar.d.ts.map +1 -0
  133. package/dist/Toolbar.js +245 -0
  134. package/dist/Tooltip.d.ts +296 -0
  135. package/dist/Tooltip.d.ts.map +1 -0
  136. package/dist/Tooltip.js +172 -0
  137. package/dist/Tree.d.ts +591 -0
  138. package/dist/Tree.d.ts.map +1 -0
  139. package/dist/Tree.js +434 -0
  140. package/dist/TreeGrid.d.ts +645 -0
  141. package/dist/TreeGrid.d.ts.map +1 -0
  142. package/dist/TreeGrid.js +420 -0
  143. package/dist/VisuallyHidden.d.ts +54 -0
  144. package/dist/VisuallyHidden.d.ts.map +1 -0
  145. package/dist/VisuallyHidden.js +27 -0
  146. package/dist/WindowSplitter.d.ts +376 -0
  147. package/dist/WindowSplitter.d.ts.map +1 -0
  148. package/dist/WindowSplitter.js +222 -0
  149. package/dist/index.d.ts +49 -0
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +49 -0
  152. package/package.json +45 -18
  153. package/src/HttpRouter.test.ts +0 -256
  154. package/src/HttpRouter.ts +0 -165
  155. package/src/Link.test.ts +0 -84
  156. package/src/Link.ts +0 -107
  157. package/src/index.ts +0 -2
@@ -0,0 +1,296 @@
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 tooltip identity and visibility.
10
+ * @remarks
11
+ * ## Why
12
+ * A stable id connects the anchor's description relationship to native popover
13
+ * content while open state stays renderer-independent.
14
+ * ## Ownership and lifetime
15
+ * Plain state retains no resources; RefSubject observation is Scope-owned.
16
+ * @since 1.0.0
17
+ * @category state
18
+ */
19
+ export interface State {
20
+ /** Stable id used by `aria-describedby` and tooltip content.
21
+ * @remarks
22
+ * ## Why
23
+ * Server and client must agree on the relationship target during hydration.
24
+ * ## Ownership and lifetime
25
+ * Plain data acquires no resources.
26
+ * @since 1.0.0
27
+ * @category relationships
28
+ */
29
+ readonly id: string;
30
+ /** Whether tooltip content is open.
31
+ * @remarks
32
+ * ## Why
33
+ * The value coordinates delayed anchor interactions and native popover state.
34
+ * ## Ownership and lifetime
35
+ * Plain data acquires no resources.
36
+ * @since 1.0.0
37
+ * @category state
38
+ */
39
+ readonly open: boolean;
40
+ }
41
+ /** Initial tooltip identity and visibility.
42
+ * @remarks
43
+ * ## Why
44
+ * Requiring an id makes the accessibility relationship explicit.
45
+ * ## Ownership and lifetime
46
+ * Configuration is inert.
47
+ * @since 1.0.0
48
+ * @category state
49
+ */
50
+ export interface InitialState {
51
+ /** Stable tooltip id.
52
+ * @remarks
53
+ * ## Why
54
+ * Deterministic ids prevent SSR/client relationship drift.
55
+ * ## Ownership and lifetime
56
+ * Plain data retains no resources.
57
+ * @since 1.0.0
58
+ * @category relationships
59
+ */
60
+ readonly id: string;
61
+ /** Initial visibility, defaulting to false.
62
+ * @remarks
63
+ * ## Why
64
+ * Closed is the deterministic default before user focus or hover.
65
+ * ## Ownership and lifetime
66
+ * Plain data retains no resources.
67
+ * @since 1.0.0
68
+ * @category state
69
+ */
70
+ readonly open?: boolean;
71
+ }
72
+ /** Schema for tooltip hydration state.
73
+ * @remarks
74
+ * ## Why
75
+ * Shared identity/open encoding keeps SSR and browser relationships compatible.
76
+ * ## Ownership and lifetime
77
+ * The immutable schema acquires no resources.
78
+ * @since 1.0.0
79
+ * @category schemas
80
+ */
81
+ export declare const StateSchema: Schema.Struct<{
82
+ readonly id: Schema.String;
83
+ readonly open: Schema.Boolean;
84
+ }>;
85
+ /** Creates hydrated tooltip state.
86
+ * @remarks
87
+ * ## Why
88
+ * Visibility logic can be tested without mounting an anchor or popover.
89
+ * ## Ownership and lifetime
90
+ * The calling Effect Scope owns the returned RefSubject.
91
+ * @example
92
+ * ```ts
93
+ * import { Effect } from "effect"
94
+ * import * as Tooltip from "@typed/ui/Tooltip"
95
+ *
96
+ * const program = Effect.gen(function* () {
97
+ * return yield* Tooltip.makeState({ id: "save-help" })
98
+ * })
99
+ * ```
100
+ * @since 1.0.0
101
+ * @category constructors
102
+ */
103
+ export declare function makeState(initial: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
104
+ readonly id: string;
105
+ readonly open: boolean;
106
+ }, Schema.SchemaError, never, never>, never, Scope.Scope>;
107
+ /** Sets tooltip visibility.
108
+ * @remarks
109
+ * ## Why
110
+ * Delayed focus/hover logic converges on one explicit state transition.
111
+ * ## Ownership and lifetime
112
+ * The Effect uses the existing state Scope and acquires no resource.
113
+ * @example
114
+ * ```ts
115
+ * import { Effect } from "effect"
116
+ * import * as Tooltip from "@typed/ui/Tooltip"
117
+ *
118
+ * const program = Effect.gen(function* () {
119
+ * const state = yield* Tooltip.makeState({ id: "save-help" })
120
+ * yield* Tooltip.setOpen(state, true)
121
+ * })
122
+ * ```
123
+ * @since 1.0.0
124
+ * @category state
125
+ */
126
+ export declare function setOpen<E, R>(state: RefSubject.RefSubject<State, E, R>, open: boolean): Effect.Effect<State, E, R>;
127
+ /** Options for a tooltip anchor host.
128
+ * @remarks
129
+ * ## Why
130
+ * Pointer entry works with the default span. Keyboard focus behavior requires
131
+ * the host itself to be focusable, for example `props: { tabindex: 0 }` or a
132
+ * focusable custom host; focus and blur on descendants do not bubble to it.
133
+ * ## Ownership and lifetime
134
+ * Options are inert; rendering owns listeners/timers by Scope.
135
+ * @since 1.0.0
136
+ * @category models
137
+ */
138
+ export interface AnchorOptions extends Dom.HostOptions<HTMLSpanElement> {
139
+ /** Hydrated state shared with tooltip content.
140
+ * @remarks
141
+ * ## Why
142
+ * Its id drives `aria-describedby` and its open value drives the popover.
143
+ * ## Ownership and lifetime
144
+ * The anchor borrows state; its original Scope owns it.
145
+ * @since 1.0.0
146
+ * @category state
147
+ */
148
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
149
+ /** Anchor content.
150
+ * @remarks
151
+ * ## Why
152
+ * The anchor wraps descriptive content. A focusable descendant does not make
153
+ * the default span receive its non-bubbling focus and blur handlers.
154
+ * ## Ownership and lifetime
155
+ * Dynamic content follows the anchor Scope.
156
+ * @since 1.0.0
157
+ * @category content
158
+ */
159
+ readonly content: Renderable.Any;
160
+ /** Delay in milliseconds before opening.
161
+ * @remarks
162
+ * ## Why
163
+ * A delay avoids accidental tooltip activation during pointer transit.
164
+ * ## Ownership and lifetime
165
+ * Delayed Effects run inside the component interaction lifetime; newer
166
+ * schedules invalidate older versions.
167
+ * @since 1.0.0
168
+ * @category timing
169
+ */
170
+ readonly showDelay?: number;
171
+ /** Delay in milliseconds before closing.
172
+ * @remarks
173
+ * ## Why
174
+ * A delay prevents flicker during brief pointer movement.
175
+ * ## Ownership and lifetime
176
+ * Newer schedules invalidate older delayed updates.
177
+ * @since 1.0.0
178
+ * @category timing
179
+ */
180
+ readonly hideDelay?: number;
181
+ }
182
+ declare function anchorInternalProps<const Options extends AnchorOptions>(options: Options): () => {
183
+ readonly "aria-describedby": RefSubject.Computed<string, Schema.SchemaError, never>;
184
+ readonly onfocus: Effect.Effect<void, Schema.SchemaError, never>;
185
+ readonly onblur: Effect.Effect<void, Schema.SchemaError, never>;
186
+ readonly onkeydown: EventHandler.EventHandler<KeyboardEvent, Schema.SchemaError, never>;
187
+ readonly onmouseenter: Effect.Effect<void, Schema.SchemaError, never>;
188
+ readonly onmouseleave: Effect.Effect<void, Schema.SchemaError, never>;
189
+ };
190
+ type AnchorInternalProps<Options extends AnchorOptions> = ReturnType<ReturnType<typeof anchorInternalProps<Options>>>;
191
+ /** Renders a tooltip anchor with pointer and optional host-focus behavior.
192
+ * @remarks
193
+ * ## Why
194
+ * The default host is a non-focusable `<span>`, so it provides pointer behavior
195
+ * only. Give that host `tabindex`, or supply a natively focusable custom host,
196
+ * to activate its direct focus, blur, and keydown handlers. Those events are
197
+ * attached to the host itself and do not observe focus on nested content.
198
+ * Tooltip content remains descriptive rather than an interactive dialog.
199
+ * ## Ownership and lifetime
200
+ * Running the Fx owns event listeners and dynamic relationship state in its
201
+ * Scope. Custom hosts must preserve `aria-describedby` and all handlers.
202
+ * @example
203
+ * ```ts
204
+ * import { Effect } from "effect"
205
+ * import * as Tooltip from "@typed/ui/Tooltip"
206
+ *
207
+ * const program = Effect.gen(function* () {
208
+ * const state = yield* Tooltip.makeState({ id: "save-help" })
209
+ * return Tooltip.Anchor({
210
+ * state,
211
+ * content: "Save",
212
+ * props: { tabindex: 0 }
213
+ * })
214
+ * })
215
+ * ```
216
+ * @since 1.0.0
217
+ * @category components
218
+ */
219
+ export declare function Anchor<const Options extends AnchorOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, AnchorInternalProps<Options>>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
220
+ /** Options for native tooltip popover content.
221
+ * @remarks
222
+ * ## Why
223
+ * The role and native top-layer lifecycle remain explicit.
224
+ * ## Ownership and lifetime
225
+ * Options are inert; rendering owns listeners/ref observation by Scope.
226
+ * @since 1.0.0
227
+ * @category models
228
+ */
229
+ export interface ContentOptions extends Dom.HostOptions<HTMLDivElement> {
230
+ /** Hydrated state supplying content id and visibility.
231
+ * @remarks
232
+ * ## Why
233
+ * One source keeps ARIA relationship and native popover state aligned.
234
+ * ## Ownership and lifetime
235
+ * Content borrows state; its original Scope owns it.
236
+ * @since 1.0.0
237
+ * @category state
238
+ */
239
+ readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
240
+ /** Non-interactive explanatory tooltip content.
241
+ * @remarks
242
+ * ## Why
243
+ * Tooltip content describes its anchor and must not contain required controls.
244
+ * ## Ownership and lifetime
245
+ * Dynamic content follows the content Scope.
246
+ * @since 1.0.0
247
+ * @category content
248
+ */
249
+ readonly content: Renderable.Any;
250
+ }
251
+ declare function contentInternalProps<const Options extends ContentOptions>(options: Options): () => {
252
+ readonly id: RefSubject.Computed<string, Schema.SchemaError, never>;
253
+ readonly role: "tooltip";
254
+ readonly popover: "manual";
255
+ readonly onmouseenter: Effect.Effect<void, Schema.SchemaError, never>;
256
+ readonly onmouseleave: EventHandler.EventHandler<MouseEvent, Schema.SchemaError, never>;
257
+ readonly onkeydown: EventHandler.EventHandler<KeyboardEvent, Schema.SchemaError, never>;
258
+ readonly ontoggle: EventHandler.EventHandler<Event, Schema.SchemaError, never>;
259
+ readonly ref: import("./Dom/Refs.js").ComposedRef<RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>, (element: HTMLElement) => Effect.Effect<void, Schema.SchemaError, Scope.Scope>> | undefined;
260
+ };
261
+ type ContentInternalProps<Options extends ContentOptions> = ReturnType<ReturnType<typeof contentInternalProps<Options>>>;
262
+ /** Renders `role="tooltip"` content through the native Popover API.
263
+ * @remarks
264
+ * ## Why
265
+ * Native top-layer output avoids reparenting into a private portal while real
266
+ * toggle/mouse/keyboard events synchronize renderer-independent state.
267
+ * ## Ownership and lifetime
268
+ * Running the Fx owns listeners and NativePopover observation in its Scope. A
269
+ * custom host must preserve id, role, manual popover, handlers, and one
270
+ * hydration ref owner.
271
+ * @example
272
+ * ```ts
273
+ * import { Effect } from "effect"
274
+ * import * as Tooltip from "@typed/ui/Tooltip"
275
+ *
276
+ * const program = Effect.gen(function* () {
277
+ * const state = yield* Tooltip.makeState({ id: "save-help" })
278
+ * return Tooltip.Content({ state, content: "Stores changes" })
279
+ * })
280
+ * ```
281
+ * @since 1.0.0
282
+ * @category components
283
+ */
284
+ 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>;
285
+ /** Canonical widget alias for `Content`.
286
+ * @remarks
287
+ * ## Why
288
+ * The alias provides the widget name while `Content` names compound use.
289
+ * ## Ownership and lifetime
290
+ * It has exactly the same Scope and popover ownership as `Content`.
291
+ * @since 1.0.0
292
+ * @category aliases
293
+ */
294
+ export declare const Tooltip: typeof Content;
295
+ export {};
296
+ //# sourceMappingURL=Tooltip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../src/Tooltip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
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 tooltip hydration state.
8
+ * @remarks
9
+ * ## Why
10
+ * Shared identity/open encoding keeps SSR and browser relationships 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({ id: Schema.String, open: Schema.Boolean });
17
+ /** Creates hydrated tooltip state.
18
+ * @remarks
19
+ * ## Why
20
+ * Visibility logic can be tested without mounting an anchor or popover.
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 Tooltip from "@typed/ui/Tooltip"
27
+ *
28
+ * const program = Effect.gen(function* () {
29
+ * return yield* Tooltip.makeState({ id: "save-help" })
30
+ * })
31
+ * ```
32
+ * @since 1.0.0
33
+ * @category constructors
34
+ */
35
+ export function makeState(initial) {
36
+ return RefSubject.hydrate(StateSchema, { id: initial.id, open: initial.open ?? false });
37
+ }
38
+ /** Sets tooltip visibility.
39
+ * @remarks
40
+ * ## Why
41
+ * Delayed focus/hover logic converges on one explicit state transition.
42
+ * ## Ownership and lifetime
43
+ * The Effect uses the existing state Scope and acquires no resource.
44
+ * @example
45
+ * ```ts
46
+ * import { Effect } from "effect"
47
+ * import * as Tooltip from "@typed/ui/Tooltip"
48
+ *
49
+ * const program = Effect.gen(function* () {
50
+ * const state = yield* Tooltip.makeState({ id: "save-help" })
51
+ * yield* Tooltip.setOpen(state, true)
52
+ * })
53
+ * ```
54
+ * @since 1.0.0
55
+ * @category state
56
+ */
57
+ export function setOpen(state, open) {
58
+ return RefSubject.update(state, (current) => ({ ...current, open }));
59
+ }
60
+ const scheduleVersions = new WeakMap();
61
+ const scheduleOpen = Effect.fn(function* (state, open, delay) {
62
+ const version = (scheduleVersions.get(state) ?? 0) + 1;
63
+ scheduleVersions.set(state, version);
64
+ if (delay > 0)
65
+ yield* Effect.sleep(delay);
66
+ if (scheduleVersions.get(state) === version)
67
+ yield* setOpen(state, open);
68
+ });
69
+ function anchorInternalProps(options) {
70
+ const id = RefSubject.map(options.state, (state) => state.id);
71
+ return () => ({
72
+ "aria-describedby": id,
73
+ onfocus: scheduleOpen(options.state, true, options.showDelay ?? 0),
74
+ onblur: scheduleOpen(options.state, false, options.hideDelay ?? 0),
75
+ onkeydown: EventHandler.make(Effect.fn(function* (event) {
76
+ if (event.key === "Escape")
77
+ yield* scheduleOpen(options.state, false, 0);
78
+ })),
79
+ onmouseenter: scheduleOpen(options.state, true, options.showDelay ?? 0),
80
+ onmouseleave: scheduleOpen(options.state, false, options.hideDelay ?? 0),
81
+ });
82
+ }
83
+ /** Renders a tooltip anchor with pointer and optional host-focus behavior.
84
+ * @remarks
85
+ * ## Why
86
+ * The default host is a non-focusable `<span>`, so it provides pointer behavior
87
+ * only. Give that host `tabindex`, or supply a natively focusable custom host,
88
+ * to activate its direct focus, blur, and keydown handlers. Those events are
89
+ * attached to the host itself and do not observe focus on nested content.
90
+ * Tooltip content remains descriptive rather than an interactive dialog.
91
+ * ## Ownership and lifetime
92
+ * Running the Fx owns event listeners and dynamic relationship state in its
93
+ * Scope. Custom hosts must preserve `aria-describedby` and all handlers.
94
+ * @example
95
+ * ```ts
96
+ * import { Effect } from "effect"
97
+ * import * as Tooltip from "@typed/ui/Tooltip"
98
+ *
99
+ * const program = Effect.gen(function* () {
100
+ * const state = yield* Tooltip.makeState({ id: "save-help" })
101
+ * return Tooltip.Anchor({
102
+ * state,
103
+ * content: "Save",
104
+ * props: { tabindex: 0 }
105
+ * })
106
+ * })
107
+ * ```
108
+ * @since 1.0.0
109
+ * @category components
110
+ */
111
+ export function Anchor(options, host) {
112
+ return Dom.renderHost()(options, host, anchorInternalProps(options), options.content, (props, content) => html `<span ...${props}>${content}</span>`);
113
+ }
114
+ function contentInternalProps(options) {
115
+ const id = RefSubject.map(options.state, (state) => state.id);
116
+ return () => ({
117
+ id,
118
+ role: "tooltip",
119
+ popover: "manual",
120
+ onmouseenter: scheduleOpen(options.state, true, 0),
121
+ onmouseleave: EventHandler.make(Effect.fn(function* (event) {
122
+ const contentId = (yield* options.state).id;
123
+ if (event.relatedTarget instanceof Element &&
124
+ event.relatedTarget.getAttribute("aria-describedby") === contentId)
125
+ return;
126
+ yield* scheduleOpen(options.state, false, 0);
127
+ })),
128
+ onkeydown: EventHandler.make(Effect.fn(function* (event) {
129
+ if (event.key === "Escape")
130
+ yield* scheduleOpen(options.state, false, 0);
131
+ })),
132
+ ontoggle: EventHandler.make(Effect.fn((event) => setOpen(options.state, Dom.toggleState(event) === "open"))),
133
+ ref: Dom.composeRefs(options.state, NativePopover.ref(options.state)),
134
+ });
135
+ }
136
+ /** Renders `role="tooltip"` content through the native Popover API.
137
+ * @remarks
138
+ * ## Why
139
+ * Native top-layer output avoids reparenting into a private portal while real
140
+ * toggle/mouse/keyboard events synchronize renderer-independent state.
141
+ * ## Ownership and lifetime
142
+ * Running the Fx owns listeners and NativePopover observation in its Scope. A
143
+ * custom host must preserve id, role, manual popover, handlers, and one
144
+ * hydration ref owner.
145
+ * @example
146
+ * ```ts
147
+ * import { Effect } from "effect"
148
+ * import * as Tooltip from "@typed/ui/Tooltip"
149
+ *
150
+ * const program = Effect.gen(function* () {
151
+ * const state = yield* Tooltip.makeState({ id: "save-help" })
152
+ * return Tooltip.Content({ state, content: "Stores changes" })
153
+ * })
154
+ * ```
155
+ * @since 1.0.0
156
+ * @category components
157
+ */
158
+ export function Content(options, host) {
159
+ return Dom.renderHost()(options, host, contentInternalProps(options), options.content, (props, content) => {
160
+ return html `<div ...${props}>${content}</div>`;
161
+ });
162
+ }
163
+ /** Canonical widget alias for `Content`.
164
+ * @remarks
165
+ * ## Why
166
+ * The alias provides the widget name while `Content` names compound use.
167
+ * ## Ownership and lifetime
168
+ * It has exactly the same Scope and popover ownership as `Content`.
169
+ * @since 1.0.0
170
+ * @category aliases
171
+ */
172
+ export const Tooltip = Content;