@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.
- 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 +196 -53
- 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 +45 -18
- package/src/HttpRouter.test.ts +0 -256
- package/src/HttpRouter.ts +0 -165
- package/src/Link.test.ts +0 -84
- package/src/Link.ts +0 -107
- package/src/index.ts +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Composite.d.ts","sourceRoot":"","sources":["../src/Composite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite contains the reusable policy behind roving tabindex and virtual focus. Its pure
|
|
3
|
+
* helpers define orientation, RTL, wrapping, disabled-item, typeahead, and DOM-order rules; Effect
|
|
4
|
+
* helpers perform focus and scrolling only when run.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* The module keeps policy, state transitions, and DOM rendering separable so applications can use
|
|
8
|
+
* the state and pure operations without mounting UI, or supply custom hosts without replacing native
|
|
9
|
+
* events and browser-owned focus.
|
|
10
|
+
*
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
* @category modules
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
import * as Effect from "effect/Effect";
|
|
16
|
+
import * as Schema from "effect/Schema";
|
|
17
|
+
import { RefSubject } from "@typed/fx";
|
|
18
|
+
import * as Collection from "./Collection.js";
|
|
19
|
+
/**
|
|
20
|
+
* Effect Schema used by makeState to encode, decode, and hydrate Composite state.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* ## Why
|
|
24
|
+
*
|
|
25
|
+
* A public schema makes hydration and serialized state use the same runtime validation as direct
|
|
26
|
+
* construction.
|
|
27
|
+
*
|
|
28
|
+
* ## Ownership and lifetime
|
|
29
|
+
*
|
|
30
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import * as Schema from "effect/Schema";
|
|
35
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
36
|
+
*
|
|
37
|
+
* const decodeState = Schema.decodeUnknownEffect(Composite.StateSchema);
|
|
38
|
+
* ```
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
* @category schemas
|
|
41
|
+
*/
|
|
42
|
+
export const StateSchema = Schema.Struct({
|
|
43
|
+
activeId: Schema.NullOr(Schema.String),
|
|
44
|
+
orientation: Schema.Literals(["horizontal", "vertical", "both"]),
|
|
45
|
+
loop: Schema.Boolean,
|
|
46
|
+
rtl: Schema.Boolean,
|
|
47
|
+
virtualFocus: Schema.Boolean,
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Creates hydrated Composite state. activeId defaults to null, orientation to horizontal, loop to
|
|
51
|
+
* true, RTL and virtual focus to false.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* ## Why
|
|
55
|
+
*
|
|
56
|
+
* State and collection ownership can be composed and tested independently from any renderer.
|
|
57
|
+
*
|
|
58
|
+
* ## Ownership and lifetime
|
|
59
|
+
*
|
|
60
|
+
* The returned Effect creates the RefSubject when run. That state is renderer-independent;
|
|
61
|
+
* collection registrations belong to the separate Scope that runs register or ref, not to state
|
|
62
|
+
* creation.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* import * as Effect from "effect/Effect";
|
|
67
|
+
* import * as Collection from "@typed/ui/Collection";
|
|
68
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
69
|
+
*
|
|
70
|
+
* const program = Effect.scoped(
|
|
71
|
+
* Effect.gen(function* () {
|
|
72
|
+
* const state = yield* Composite.makeState({});
|
|
73
|
+
* const collection = yield* Collection.makeState<string>();
|
|
74
|
+
* return { state: yield* state, collection: yield* collection };
|
|
75
|
+
* }),
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
* @since 1.0.0
|
|
79
|
+
* @category constructors
|
|
80
|
+
*/
|
|
81
|
+
export function makeState(initial = {}) {
|
|
82
|
+
return RefSubject.hydrate(StateSchema, {
|
|
83
|
+
activeId: initial.activeId ?? null,
|
|
84
|
+
orientation: initial.orientation ?? "horizontal",
|
|
85
|
+
loop: initial.loop ?? true,
|
|
86
|
+
rtl: initial.rtl ?? false,
|
|
87
|
+
virtualFocus: initial.virtualFocus ?? false,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Computes the next active id and updates state; collection and state failures/services are
|
|
92
|
+
* preserved in the returned Effect.
|
|
93
|
+
*
|
|
94
|
+
* @remarks
|
|
95
|
+
* ## Why
|
|
96
|
+
*
|
|
97
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
98
|
+
* programs and native event handlers.
|
|
99
|
+
*
|
|
100
|
+
* ## Ownership and lifetime
|
|
101
|
+
*
|
|
102
|
+
* The returned Effect performs the update or DOM side effect only when run, preserves the declared
|
|
103
|
+
* error and service channels, and retains no resources after completion.
|
|
104
|
+
*
|
|
105
|
+
* ## Example
|
|
106
|
+
*
|
|
107
|
+
* Import with `import { move } from "@typed/ui/Composite";` Extend the [Composite.makeState
|
|
108
|
+
* runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside the linked Effect
|
|
109
|
+
* program invoke `yield* move({ state, collection }, "next")`, then read state to observe the next
|
|
110
|
+
* enabled registered id selected under the current loop policy.
|
|
111
|
+
* @since 1.0.0
|
|
112
|
+
* @category combinators
|
|
113
|
+
*/
|
|
114
|
+
export function move(options, direction) {
|
|
115
|
+
return Effect.gen(function* () {
|
|
116
|
+
const current = yield* options.state;
|
|
117
|
+
const activeId = moveActiveId(yield* options.collection, current, direction, options.includeDisabled);
|
|
118
|
+
return yield* RefSubject.update(options.state, (state) => ({ ...state, activeId }));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Derives 0 only for the active item under roving focus; virtual-focus items always receive -1.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* ## Why
|
|
126
|
+
*
|
|
127
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
128
|
+
* programs and native event handlers.
|
|
129
|
+
*
|
|
130
|
+
* ## Ownership and lifetime
|
|
131
|
+
*
|
|
132
|
+
* Creating the Computed value acquires nothing eagerly. The Scope that subscribes to it owns
|
|
133
|
+
* observation of the source RefSubject.
|
|
134
|
+
*
|
|
135
|
+
* ## Example
|
|
136
|
+
*
|
|
137
|
+
* Import with `import { tabIndex } from "@typed/ui/Composite";` Extend the [Composite.makeState
|
|
138
|
+
* runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside the linked Effect
|
|
139
|
+
* program read `const value = yield* tabIndex(state, "item-2")`; it is `0` only while that item
|
|
140
|
+
* owns roving focus and is `-1` under virtual focus.
|
|
141
|
+
* @since 1.0.0
|
|
142
|
+
* @category combinators
|
|
143
|
+
*/
|
|
144
|
+
export function tabIndex(state, id) {
|
|
145
|
+
return RefSubject.map(state, (current) => current.virtualFocus ? -1 : current.activeId === id ? 0 : -1);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Derives activeId only under virtual focus, otherwise undefined.
|
|
149
|
+
*
|
|
150
|
+
* @remarks
|
|
151
|
+
* ## Why
|
|
152
|
+
*
|
|
153
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
154
|
+
* programs and native event handlers.
|
|
155
|
+
*
|
|
156
|
+
* ## Ownership and lifetime
|
|
157
|
+
*
|
|
158
|
+
* Creating the Computed value acquires nothing eagerly. The Scope that subscribes to it owns
|
|
159
|
+
* observation of the source RefSubject.
|
|
160
|
+
*
|
|
161
|
+
* ## Example
|
|
162
|
+
*
|
|
163
|
+
* Import with `import { activeDescendant } from "@typed/ui/Composite";` Extend the
|
|
164
|
+
* [Composite.makeState runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside
|
|
165
|
+
* the linked Effect program read `const id = yield* activeDescendant(state)`; it is the active id
|
|
166
|
+
* only under virtual focus and otherwise `undefined`.
|
|
167
|
+
* @since 1.0.0
|
|
168
|
+
* @category combinators
|
|
169
|
+
*/
|
|
170
|
+
export function activeDescendant(state) {
|
|
171
|
+
return RefSubject.map(state, (current) => current.virtualFocus && current.activeId ? current.activeId : undefined);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Derives 0 when the root owns focus and -1 once a roving-focus item is active.
|
|
175
|
+
*
|
|
176
|
+
* @remarks
|
|
177
|
+
* ## Why
|
|
178
|
+
*
|
|
179
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
180
|
+
* programs and native event handlers.
|
|
181
|
+
*
|
|
182
|
+
* ## Ownership and lifetime
|
|
183
|
+
*
|
|
184
|
+
* Creating the Computed value acquires nothing eagerly. The Scope that subscribes to it owns
|
|
185
|
+
* observation of the source RefSubject.
|
|
186
|
+
*
|
|
187
|
+
* ## Example
|
|
188
|
+
*
|
|
189
|
+
* Import with `import { rootTabIndex } from "@typed/ui/Composite";` Extend the [Composite.makeState
|
|
190
|
+
* runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside the linked Effect
|
|
191
|
+
* program read `const value = yield* rootTabIndex(state)`; it is `0` while the root owns focus and
|
|
192
|
+
* `-1` after a roving-focus item becomes active.
|
|
193
|
+
* @since 1.0.0
|
|
194
|
+
* @category combinators
|
|
195
|
+
*/
|
|
196
|
+
export function rootTabIndex(state) {
|
|
197
|
+
return RefSubject.map(state, (current) => current.virtualFocus || current.activeId === null ? 0 : -1);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Maps Home/End and orientation-aware Arrow keys to movement, reversing horizontal arrows for RTL.
|
|
201
|
+
*
|
|
202
|
+
* @remarks
|
|
203
|
+
* ## Why
|
|
204
|
+
*
|
|
205
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
206
|
+
* reuse it in custom composites.
|
|
207
|
+
*
|
|
208
|
+
* ## Ownership and lifetime
|
|
209
|
+
*
|
|
210
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
211
|
+
* state, event, or DOM.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
216
|
+
*
|
|
217
|
+
* const direction = Composite.keyMove({ key: "ArrowLeft" }, { orientation: "horizontal", rtl: true });
|
|
218
|
+
* ```
|
|
219
|
+
* @since 1.0.0
|
|
220
|
+
* @category combinators
|
|
221
|
+
*/
|
|
222
|
+
export function keyMove(event, options) {
|
|
223
|
+
if (event.key === "Home")
|
|
224
|
+
return "first";
|
|
225
|
+
if (event.key === "End")
|
|
226
|
+
return "last";
|
|
227
|
+
const orientation = options.orientation ?? "horizontal";
|
|
228
|
+
const rtl = options.rtl ?? false;
|
|
229
|
+
if (orientation !== "vertical" && event.key === "ArrowRight")
|
|
230
|
+
return rtl ? "previous" : "next";
|
|
231
|
+
if (orientation !== "vertical" && event.key === "ArrowLeft")
|
|
232
|
+
return rtl ? "next" : "previous";
|
|
233
|
+
if (orientation !== "horizontal" && event.key === "ArrowDown")
|
|
234
|
+
return "next";
|
|
235
|
+
if (orientation !== "horizontal" && event.key === "ArrowUp")
|
|
236
|
+
return "previous";
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Prevents the native key default only when keyMove recognizes the key, then updates active state.
|
|
241
|
+
*
|
|
242
|
+
* @remarks
|
|
243
|
+
* ## Why
|
|
244
|
+
*
|
|
245
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
246
|
+
* programs and native event handlers.
|
|
247
|
+
*
|
|
248
|
+
* ## Ownership and lifetime
|
|
249
|
+
*
|
|
250
|
+
* The returned Effect performs the update or DOM side effect only when run, preserves the declared
|
|
251
|
+
* error and service channels, and retains no resources after completion.
|
|
252
|
+
*
|
|
253
|
+
* ## Example
|
|
254
|
+
*
|
|
255
|
+
* Import with `import { moveByKey } from "@typed/ui/Composite";` Extend the [Composite.makeState
|
|
256
|
+
* runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside the linked Effect
|
|
257
|
+
* program run `yield* moveByKey({ key: "ArrowDown" }, { state, collection })`, then read state to
|
|
258
|
+
* observe the active id selected by the current orientation and RTL policy.
|
|
259
|
+
* @since 1.0.0
|
|
260
|
+
* @category combinators
|
|
261
|
+
*/
|
|
262
|
+
export function moveByKey(event, options) {
|
|
263
|
+
return Effect.gen(function* () {
|
|
264
|
+
const direction = keyMove(event, yield* options.state);
|
|
265
|
+
if (direction === undefined)
|
|
266
|
+
return false;
|
|
267
|
+
event.preventDefault?.();
|
|
268
|
+
yield* move(options, direction);
|
|
269
|
+
return true;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Moves active state, then focuses and scrolls the mounted active item when virtual focus is
|
|
274
|
+
* disabled.
|
|
275
|
+
*
|
|
276
|
+
* @remarks
|
|
277
|
+
* ## Why
|
|
278
|
+
*
|
|
279
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
280
|
+
* programs and native event handlers.
|
|
281
|
+
*
|
|
282
|
+
* ## Ownership and lifetime
|
|
283
|
+
*
|
|
284
|
+
* The returned Effect performs the update or DOM side effect only when run, preserves the declared
|
|
285
|
+
* error and service channels, and retains no resources after completion.
|
|
286
|
+
*
|
|
287
|
+
* ## Example
|
|
288
|
+
*
|
|
289
|
+
* Import with `import { moveAndFocus } from "@typed/ui/Composite";` Extend the [Composite.makeState
|
|
290
|
+
* runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside the linked Effect
|
|
291
|
+
* program run `yield* moveAndFocus({ state, collection }, "next")`; the returned state names the
|
|
292
|
+
* next id and its registered element receives native focus.
|
|
293
|
+
* @since 1.0.0
|
|
294
|
+
* @category combinators
|
|
295
|
+
*/
|
|
296
|
+
export function moveAndFocus(options, direction) {
|
|
297
|
+
return Effect.tap(move(options, direction), Effect.andThen(focusActive(options), scrollActive(options)));
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Focuses the registered active element unless state uses virtual focus or has no active id.
|
|
301
|
+
*
|
|
302
|
+
* @remarks
|
|
303
|
+
* ## Why
|
|
304
|
+
*
|
|
305
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
306
|
+
* programs and native event handlers.
|
|
307
|
+
*
|
|
308
|
+
* ## Ownership and lifetime
|
|
309
|
+
*
|
|
310
|
+
* The returned Effect performs the update or DOM side effect only when run, preserves the declared
|
|
311
|
+
* error and service channels, and retains no resources after completion.
|
|
312
|
+
*
|
|
313
|
+
* ## Example
|
|
314
|
+
*
|
|
315
|
+
* Import with `import { focusActive } from "@typed/ui/Composite";` Extend the [Composite.makeState
|
|
316
|
+
* runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside the linked Effect
|
|
317
|
+
* program run `yield* focusActive({ state, collection })`; the active registered element receives
|
|
318
|
+
* native focus, with no state transition when no matching element exists.
|
|
319
|
+
* @since 1.0.0
|
|
320
|
+
* @category combinators
|
|
321
|
+
*/
|
|
322
|
+
export function focusActive(options) {
|
|
323
|
+
return Effect.gen(function* () {
|
|
324
|
+
const state = yield* options.state;
|
|
325
|
+
if (state.virtualFocus || state.activeId === null)
|
|
326
|
+
return;
|
|
327
|
+
const item = (yield* options.collection).find((item) => item.id === state.activeId);
|
|
328
|
+
yield* focusElement(item?.element);
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Calls an existing platform focus method inside Effect.sync and otherwise does nothing.
|
|
333
|
+
*
|
|
334
|
+
* @remarks
|
|
335
|
+
* ## Why
|
|
336
|
+
*
|
|
337
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
338
|
+
* programs and native event handlers.
|
|
339
|
+
*
|
|
340
|
+
* ## Ownership and lifetime
|
|
341
|
+
*
|
|
342
|
+
* The returned Effect performs the update or DOM side effect only when run, preserves the declared
|
|
343
|
+
* error and service channels, and retains no resources after completion.
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```ts
|
|
347
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
348
|
+
*
|
|
349
|
+
* const focused = Composite.focusElement(document.querySelector("button") ?? undefined);
|
|
350
|
+
* ```
|
|
351
|
+
* @since 1.0.0
|
|
352
|
+
* @category combinators
|
|
353
|
+
*/
|
|
354
|
+
export function focusElement(element) {
|
|
355
|
+
return Effect.sync(() => {
|
|
356
|
+
const focus = element === undefined ? undefined : Reflect.get(element, "focus");
|
|
357
|
+
if (typeof focus === "function")
|
|
358
|
+
focus.call(element);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Calls scrollIntoView with nearest block/inline alignment for the mounted active item.
|
|
363
|
+
*
|
|
364
|
+
* @remarks
|
|
365
|
+
* ## Why
|
|
366
|
+
*
|
|
367
|
+
* The operation exposes Composite's transition directly so callers can compose it in Effect
|
|
368
|
+
* programs and native event handlers.
|
|
369
|
+
*
|
|
370
|
+
* ## Ownership and lifetime
|
|
371
|
+
*
|
|
372
|
+
* The returned Effect performs the update or DOM side effect only when run, preserves the declared
|
|
373
|
+
* error and service channels, and retains no resources after completion.
|
|
374
|
+
*
|
|
375
|
+
* ## Example
|
|
376
|
+
*
|
|
377
|
+
* Import with `import { scrollActive } from "@typed/ui/Composite";` Extend the [Composite.makeState
|
|
378
|
+
* runnable setup](/reference/%40typed%2Fui%2FComposite%23makeState). Inside the linked Effect
|
|
379
|
+
* program run `yield* scrollActive({ state, collection })`; the active registered element receives
|
|
380
|
+
* `scrollIntoView({ block: "nearest", inline: "nearest" })` when that method is available.
|
|
381
|
+
* @since 1.0.0
|
|
382
|
+
* @category combinators
|
|
383
|
+
*/
|
|
384
|
+
export function scrollActive(options) {
|
|
385
|
+
return Effect.gen(function* () {
|
|
386
|
+
const activeId = (yield* options.state).activeId;
|
|
387
|
+
if (activeId === null)
|
|
388
|
+
return;
|
|
389
|
+
const element = (yield* options.collection).find((item) => item.id === activeId)?.element;
|
|
390
|
+
const scrollIntoView = element === undefined ? undefined : Reflect.get(element, "scrollIntoView");
|
|
391
|
+
if (typeof scrollIntoView === "function") {
|
|
392
|
+
scrollIntoView.call(element, { block: "nearest", inline: "nearest" });
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Returns the first enabled item id in DOM order whose textValue starts with the query.
|
|
398
|
+
*
|
|
399
|
+
* @remarks
|
|
400
|
+
* ## Why
|
|
401
|
+
*
|
|
402
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
403
|
+
* reuse it in custom composites.
|
|
404
|
+
*
|
|
405
|
+
* ## Ownership and lifetime
|
|
406
|
+
*
|
|
407
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
408
|
+
* state, event, or DOM.
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```ts
|
|
412
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
413
|
+
*
|
|
414
|
+
* const id = Composite.typeahead([{ id: "alpha", textValue: "Alpha" }], "al");
|
|
415
|
+
* ```
|
|
416
|
+
* @since 1.0.0
|
|
417
|
+
* @category combinators
|
|
418
|
+
*/
|
|
419
|
+
export function typeahead(items, search, text = (item) => item.textValue ?? item.id, includeDisabled = false) {
|
|
420
|
+
return typeaheadFrom(items, search, null, text, includeDisabled);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Returns only the id selected by typeahead, or null when no item matches.
|
|
424
|
+
*
|
|
425
|
+
* @remarks
|
|
426
|
+
* ## Why
|
|
427
|
+
*
|
|
428
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
429
|
+
* reuse it in custom composites.
|
|
430
|
+
*
|
|
431
|
+
* ## Ownership and lifetime
|
|
432
|
+
*
|
|
433
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
434
|
+
* state, event, or DOM.
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* ```ts
|
|
438
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
439
|
+
*
|
|
440
|
+
* const id = Composite.typeaheadFrom([{ id: "alpha", textValue: "Alpha" }], "al", null);
|
|
441
|
+
* ```
|
|
442
|
+
* @since 1.0.0
|
|
443
|
+
* @category combinators
|
|
444
|
+
*/
|
|
445
|
+
export function typeaheadFrom(items, search, activeId, text = (item) => item.textValue ?? item.id, includeDisabled = false) {
|
|
446
|
+
const query = search.trim().toLocaleLowerCase();
|
|
447
|
+
if (query.length === 0)
|
|
448
|
+
return null;
|
|
449
|
+
const enabled = orderedItems(items, includeDisabled);
|
|
450
|
+
const index = activeId === null ? -1 : enabled.findIndex((item) => item.id === activeId);
|
|
451
|
+
const ordered = index === -1 ? enabled : [...enabled.slice(index + 1), ...enabled.slice(0, index + 1)];
|
|
452
|
+
const item = ordered.find((item) => text(item).toLocaleLowerCase().startsWith(query));
|
|
453
|
+
return item?.id ?? null;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Returns an unmodified one-character key exactly as provided; modified and multi-character keys
|
|
457
|
+
* return null.
|
|
458
|
+
*
|
|
459
|
+
* @remarks
|
|
460
|
+
* ## Why
|
|
461
|
+
*
|
|
462
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
463
|
+
* reuse it in custom composites.
|
|
464
|
+
*
|
|
465
|
+
* ## Ownership and lifetime
|
|
466
|
+
*
|
|
467
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
468
|
+
* state, event, or DOM.
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```ts
|
|
472
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
473
|
+
*
|
|
474
|
+
* const key = Composite.typeaheadKey({ key: "A" });
|
|
475
|
+
* ```
|
|
476
|
+
* @since 1.0.0
|
|
477
|
+
* @category combinators
|
|
478
|
+
*/
|
|
479
|
+
export function typeaheadKey(event) {
|
|
480
|
+
if (event.altKey || event.ctrlKey || event.metaKey)
|
|
481
|
+
return null;
|
|
482
|
+
return event.key.length === 1 ? event.key : null;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Appends within the timeout window and starts a new query after the buffer expires.
|
|
486
|
+
*
|
|
487
|
+
* @remarks
|
|
488
|
+
* ## Why
|
|
489
|
+
*
|
|
490
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
491
|
+
* reuse it in custom composites.
|
|
492
|
+
*
|
|
493
|
+
* ## Ownership and lifetime
|
|
494
|
+
*
|
|
495
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
496
|
+
* state, event, or DOM.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```ts
|
|
500
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
501
|
+
*
|
|
502
|
+
* const buffer = Composite.updateTypeaheadBuffer({ value: "a", updatedAt: 100 }, "b", 200);
|
|
503
|
+
* ```
|
|
504
|
+
* @since 1.0.0
|
|
505
|
+
* @category combinators
|
|
506
|
+
*/
|
|
507
|
+
export function updateTypeaheadBuffer(buffer, key, now, timeout = 500) {
|
|
508
|
+
return {
|
|
509
|
+
value: now - buffer.updatedAt > timeout ? key : buffer.value + key,
|
|
510
|
+
updatedAt: now,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Sorts a copy by mounted DOM order and then removes disabled items.
|
|
515
|
+
*
|
|
516
|
+
* @remarks
|
|
517
|
+
* ## Why
|
|
518
|
+
*
|
|
519
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
520
|
+
* reuse it in custom composites.
|
|
521
|
+
*
|
|
522
|
+
* ## Ownership and lifetime
|
|
523
|
+
*
|
|
524
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
525
|
+
* state, event, or DOM.
|
|
526
|
+
*
|
|
527
|
+
* @example
|
|
528
|
+
* ```ts
|
|
529
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
530
|
+
*
|
|
531
|
+
* const items = Composite.orderedEnabledItems([{ id: "a" }, { id: "b", disabled: true }]);
|
|
532
|
+
* ```
|
|
533
|
+
* @since 1.0.0
|
|
534
|
+
* @category combinators
|
|
535
|
+
*/
|
|
536
|
+
export function orderedEnabledItems(items) {
|
|
537
|
+
return Collection.enabledItems(Collection.byDomOrder(items));
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Returns the id reached by first/last/next/previous with optional disabled inclusion and loop
|
|
541
|
+
* policy.
|
|
542
|
+
*
|
|
543
|
+
* @remarks
|
|
544
|
+
* ## Why
|
|
545
|
+
*
|
|
546
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
547
|
+
* reuse it in custom composites.
|
|
548
|
+
*
|
|
549
|
+
* ## Ownership and lifetime
|
|
550
|
+
*
|
|
551
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
552
|
+
* state, event, or DOM.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
557
|
+
*
|
|
558
|
+
* const id = Composite.moveActiveId([{ id: "a" }, { id: "b" }], { activeId: "a", loop: true }, "next");
|
|
559
|
+
* ```
|
|
560
|
+
* @since 1.0.0
|
|
561
|
+
* @category combinators
|
|
562
|
+
*/
|
|
563
|
+
export function moveActiveId(items, state, direction, includeDisabled = false) {
|
|
564
|
+
return moveActiveItem(items, state, direction, includeDisabled)?.id ?? null;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Returns the full item selected by moveActiveId, or undefined when no item is reachable.
|
|
568
|
+
*
|
|
569
|
+
* @remarks
|
|
570
|
+
* ## Why
|
|
571
|
+
*
|
|
572
|
+
* Separating this deterministic policy from event wiring lets applications test it directly and
|
|
573
|
+
* reuse it in custom composites.
|
|
574
|
+
*
|
|
575
|
+
* ## Ownership and lifetime
|
|
576
|
+
*
|
|
577
|
+
* This is a synchronous calculation. It acquires no resources and does not mutate the input array,
|
|
578
|
+
* state, event, or DOM.
|
|
579
|
+
*
|
|
580
|
+
* @example
|
|
581
|
+
* ```ts
|
|
582
|
+
* import * as Composite from "@typed/ui/Composite";
|
|
583
|
+
*
|
|
584
|
+
* const item = Composite.moveActiveItem([{ id: "a" }, { id: "b" }], { activeId: "a", loop: true }, "next");
|
|
585
|
+
* ```
|
|
586
|
+
* @since 1.0.0
|
|
587
|
+
* @category combinators
|
|
588
|
+
*/
|
|
589
|
+
export function moveActiveItem(items, state, direction, includeDisabled = false) {
|
|
590
|
+
const enabled = orderedItems(items, includeDisabled);
|
|
591
|
+
if (enabled.length === 0)
|
|
592
|
+
return undefined;
|
|
593
|
+
if (direction === "first")
|
|
594
|
+
return enabled[0];
|
|
595
|
+
if (direction === "last")
|
|
596
|
+
return enabled[enabled.length - 1];
|
|
597
|
+
const index = activeIndex(enabled, state.activeId, direction, state.loop);
|
|
598
|
+
return enabled[index];
|
|
599
|
+
}
|
|
600
|
+
function orderedItems(items, includeDisabled) {
|
|
601
|
+
return includeDisabled ? Collection.byDomOrder(items) : orderedEnabledItems(items);
|
|
602
|
+
}
|
|
603
|
+
function activeIndex(items, activeId, direction, loop) {
|
|
604
|
+
if (activeId === null)
|
|
605
|
+
return direction === "previous" && loop ? items.length - 1 : 0;
|
|
606
|
+
const current = items.findIndex((item) => item.id === activeId);
|
|
607
|
+
if (current === -1)
|
|
608
|
+
return direction === "previous" && loop ? items.length - 1 : 0;
|
|
609
|
+
const index = current;
|
|
610
|
+
const delta = direction === "next" ? 1 : -1;
|
|
611
|
+
const next = index + delta;
|
|
612
|
+
if (loop)
|
|
613
|
+
return (next + items.length) % items.length;
|
|
614
|
+
return Math.min(Math.max(next, 0), items.length - 1);
|
|
615
|
+
}
|