@typed/ui 1.0.0-beta.3 → 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.
- package/README.md +53 -9
- package/dist/Alert.d.ts +78 -0
- package/dist/Alert.d.ts.map +1 -0
- package/dist/Alert.js +37 -0
- package/dist/Button.d.ts +144 -0
- package/dist/Button.d.ts.map +1 -0
- package/dist/Button.js +38 -0
- package/dist/Carousel.d.ts +547 -0
- package/dist/Carousel.d.ts.map +1 -0
- package/dist/Carousel.js +387 -0
- package/dist/Checkbox.d.ts +203 -0
- package/dist/Checkbox.d.ts.map +1 -0
- package/dist/Checkbox.js +140 -0
- package/dist/Collection.d.ts +254 -0
- package/dist/Collection.d.ts.map +1 -0
- package/dist/Collection.js +218 -0
- package/dist/Combobox.d.ts +516 -0
- package/dist/Combobox.d.ts.map +1 -0
- package/dist/Combobox.js +371 -0
- package/dist/Component.d.ts +127 -0
- package/dist/Component.d.ts.map +1 -0
- package/dist/Component.js +60 -0
- package/dist/Composite.d.ts +823 -0
- package/dist/Composite.d.ts.map +1 -0
- package/dist/Composite.js +615 -0
- package/dist/Dialog.d.ts +544 -0
- package/dist/Dialog.d.ts.map +1 -0
- package/dist/Dialog.js +355 -0
- package/dist/Disclosure.d.ts +219 -0
- package/dist/Disclosure.d.ts.map +1 -0
- package/dist/Disclosure.js +128 -0
- package/dist/Dom/Events.d.ts +122 -0
- package/dist/Dom/Events.d.ts.map +1 -0
- package/dist/Dom/Events.js +192 -0
- package/dist/Dom/Props.d.ts +161 -0
- package/dist/Dom/Props.d.ts.map +1 -0
- package/dist/Dom/Props.js +110 -0
- package/dist/Dom/Refs.d.ts +58 -0
- package/dist/Dom/Refs.d.ts.map +1 -0
- package/dist/Dom/Refs.js +61 -0
- package/dist/Dom/Render.d.ts +59 -0
- package/dist/Dom/Render.d.ts.map +1 -0
- package/dist/Dom/Render.js +71 -0
- package/dist/Dom/Types.d.ts +570 -0
- package/dist/Dom/Types.d.ts.map +1 -0
- package/dist/Dom/Types.js +1 -0
- package/dist/Dom/index.d.ts +20 -0
- package/dist/Dom/index.d.ts.map +1 -0
- package/dist/Dom/index.js +8 -0
- package/dist/Dom.d.ts +14 -0
- package/dist/Dom.d.ts.map +1 -0
- package/dist/Dom.js +13 -0
- package/dist/Focusable.d.ts +85 -0
- package/dist/Focusable.d.ts.map +1 -0
- package/dist/Focusable.js +35 -0
- package/dist/Form.d.ts +1695 -0
- package/dist/Form.d.ts.map +1 -0
- package/dist/Form.js +987 -0
- package/dist/Grid.d.ts +569 -0
- package/dist/Grid.d.ts.map +1 -0
- package/dist/Grid.js +379 -0
- package/dist/Group.d.ts +147 -0
- package/dist/Group.d.ts.map +1 -0
- package/dist/Group.js +63 -0
- package/dist/Heading.d.ts +86 -0
- package/dist/Heading.d.ts.map +1 -0
- package/dist/Heading.js +48 -0
- package/dist/Hovercard.d.ts +297 -0
- package/dist/Hovercard.d.ts.map +1 -0
- package/dist/Hovercard.js +188 -0
- package/dist/HttpRouter.d.ts +129 -6
- package/dist/HttpRouter.d.ts.map +1 -1
- package/dist/HttpRouter.js +197 -54
- package/dist/Link.d.ts +67 -28
- package/dist/Link.d.ts.map +1 -1
- package/dist/Link.js +91 -37
- package/dist/Listbox.d.ts +437 -0
- package/dist/Listbox.d.ts.map +1 -0
- package/dist/Listbox.js +316 -0
- package/dist/Menu.d.ts +972 -0
- package/dist/Menu.d.ts.map +1 -0
- package/dist/Menu.js +731 -0
- package/dist/Menubar.d.ts +367 -0
- package/dist/Menubar.d.ts.map +1 -0
- package/dist/Menubar.js +265 -0
- package/dist/Meter.d.ts +217 -0
- package/dist/Meter.d.ts.map +1 -0
- package/dist/Meter.js +94 -0
- package/dist/NativeDetails.d.ts +41 -0
- package/dist/NativeDetails.d.ts.map +1 -0
- package/dist/NativeDetails.js +40 -0
- package/dist/NativeDialog.d.ts +64 -0
- package/dist/NativeDialog.d.ts.map +1 -0
- package/dist/NativeDialog.js +50 -0
- package/dist/NativePopover.d.ts +41 -0
- package/dist/NativePopover.d.ts.map +1 -0
- package/dist/NativePopover.js +46 -0
- package/dist/Popover.d.ts +241 -0
- package/dist/Popover.d.ts.map +1 -0
- package/dist/Popover.js +141 -0
- package/dist/RadioGroup.d.ts +432 -0
- package/dist/RadioGroup.d.ts.map +1 -0
- package/dist/RadioGroup.js +291 -0
- package/dist/Role.d.ts +64 -0
- package/dist/Role.d.ts.map +1 -0
- package/dist/Role.js +27 -0
- package/dist/Select.d.ts +529 -0
- package/dist/Select.d.ts.map +1 -0
- package/dist/Select.js +407 -0
- package/dist/Separator.d.ts +54 -0
- package/dist/Separator.d.ts.map +1 -0
- package/dist/Separator.js +26 -0
- package/dist/Slider.d.ts +188 -0
- package/dist/Slider.d.ts.map +1 -0
- package/dist/Slider.js +96 -0
- package/dist/SpinButton.d.ts +188 -0
- package/dist/SpinButton.d.ts.map +1 -0
- package/dist/SpinButton.js +96 -0
- package/dist/Storybook.d.ts +76 -0
- package/dist/Storybook.d.ts.map +1 -0
- package/dist/Storybook.js +102 -0
- package/dist/Switch.d.ts +187 -0
- package/dist/Switch.d.ts.map +1 -0
- package/dist/Switch.js +116 -0
- package/dist/Tab.d.ts +26 -0
- package/dist/Tab.d.ts.map +1 -0
- package/dist/Tab.js +25 -0
- package/dist/Tabs.d.ts +591 -0
- package/dist/Tabs.d.ts.map +1 -0
- package/dist/Tabs.js +346 -0
- package/dist/Toolbar.d.ts +366 -0
- package/dist/Toolbar.d.ts.map +1 -0
- package/dist/Toolbar.js +245 -0
- package/dist/Tooltip.d.ts +296 -0
- package/dist/Tooltip.d.ts.map +1 -0
- package/dist/Tooltip.js +172 -0
- package/dist/Tree.d.ts +591 -0
- package/dist/Tree.d.ts.map +1 -0
- package/dist/Tree.js +434 -0
- package/dist/TreeGrid.d.ts +645 -0
- package/dist/TreeGrid.d.ts.map +1 -0
- package/dist/TreeGrid.js +420 -0
- package/dist/VisuallyHidden.d.ts +54 -0
- package/dist/VisuallyHidden.d.ts.map +1 -0
- package/dist/VisuallyHidden.js +27 -0
- package/dist/WindowSplitter.d.ts +376 -0
- package/dist/WindowSplitter.d.ts.map +1 -0
- package/dist/WindowSplitter.js +222 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -0
- package/package.json +42 -15
- package/src/HttpRouter.test.ts +0 -294
- package/src/HttpRouter.ts +0 -168
- package/src/Link.test.ts +0 -84
- package/src/Link.ts +0 -107
- 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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"}
|
package/dist/Tooltip.js
ADDED
|
@@ -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;
|