@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,367 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Menubar is a persistent horizontal composite. Its root establishes DOM-order roving focus and
|
|
3
|
+
* typeahead; items expose menuitem semantics and invoke caller-provided activation Effects.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* The module keeps policy, state transitions, and DOM rendering separable so applications can use
|
|
7
|
+
* the state and pure operations without mounting UI, or supply custom hosts without replacing native
|
|
8
|
+
* events and browser-owned focus.
|
|
9
|
+
*
|
|
10
|
+
* @since 1.0.0
|
|
11
|
+
* @category modules
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
import * as Effect from "effect/Effect";
|
|
15
|
+
import type * as Scope from "effect/Scope";
|
|
16
|
+
import * as Schema from "effect/Schema";
|
|
17
|
+
import type { Fx } from "@typed/fx/Fx";
|
|
18
|
+
import { RefSubject } from "@typed/fx";
|
|
19
|
+
import { EventHandler, type Renderable, type RenderEvent, type RenderTemplate } from "@typed/template";
|
|
20
|
+
import * as Collection from "./Collection.js";
|
|
21
|
+
import * as Composite from "./Composite.js";
|
|
22
|
+
import * as Dom from "./Dom.js";
|
|
23
|
+
import type { HostResult } from "./Dom/Types.js";
|
|
24
|
+
/**
|
|
25
|
+
* Complete renderer-independent state for Menubar.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* ## Why
|
|
29
|
+
*
|
|
30
|
+
* Applications can inspect, update, and test Menubar behavior without mounting or coupling the
|
|
31
|
+
* state to a renderer.
|
|
32
|
+
*
|
|
33
|
+
* ## Ownership and lifetime
|
|
34
|
+
*
|
|
35
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
36
|
+
*
|
|
37
|
+
* ## Example
|
|
38
|
+
*
|
|
39
|
+
* Import with `import type { State } from "@typed/ui/Menubar";` Extend the [Menubar.makeState
|
|
40
|
+
* runnable setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Inside the linked program,
|
|
41
|
+
* `const snapshot: State = yield* state` exposes the shared composite focus policy.
|
|
42
|
+
* @since 1.0.0
|
|
43
|
+
* @category models
|
|
44
|
+
*/
|
|
45
|
+
export interface State extends Composite.State {
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Initial Menubar values. Uses Composite defaults unless overridden.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* ## Why
|
|
52
|
+
*
|
|
53
|
+
* Making initialization explicit documents hydration-sensitive defaults and lets servers and
|
|
54
|
+
* clients construct matching state.
|
|
55
|
+
*
|
|
56
|
+
* ## Ownership and lifetime
|
|
57
|
+
*
|
|
58
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
59
|
+
*
|
|
60
|
+
* ## Example
|
|
61
|
+
*
|
|
62
|
+
* Import with `import type { InitialState } from "@typed/ui/Menubar";` Extend the
|
|
63
|
+
* [Menubar.makeState runnable setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Construct a
|
|
64
|
+
* horizontal menubar with
|
|
65
|
+
* `const initial: InitialState = { orientation: "horizontal" }; const state = yield* Menubar.makeState(initial)`.
|
|
66
|
+
* @since 1.0.0
|
|
67
|
+
* @category models
|
|
68
|
+
*/
|
|
69
|
+
export type InitialState = Composite.InitialState;
|
|
70
|
+
/**
|
|
71
|
+
* Effect Schema used by makeState to encode, decode, and hydrate Menubar state.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* ## Why
|
|
75
|
+
*
|
|
76
|
+
* A public schema makes hydration and serialized state use the same runtime validation as direct
|
|
77
|
+
* construction.
|
|
78
|
+
*
|
|
79
|
+
* ## Ownership and lifetime
|
|
80
|
+
*
|
|
81
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* import * as Schema from "effect/Schema";
|
|
86
|
+
* import * as Menubar from "@typed/ui/Menubar";
|
|
87
|
+
*
|
|
88
|
+
* const decodeState = Schema.decodeUnknownEffect(Menubar.StateSchema);
|
|
89
|
+
* ```
|
|
90
|
+
* @since 1.0.0
|
|
91
|
+
* @category schemas
|
|
92
|
+
*/
|
|
93
|
+
export declare const StateSchema: Schema.Struct<{
|
|
94
|
+
readonly activeId: Schema.NullOr<Schema.String>;
|
|
95
|
+
readonly orientation: Schema.Literals<readonly ["horizontal", "vertical", "both"]>;
|
|
96
|
+
readonly loop: Schema.Boolean;
|
|
97
|
+
readonly rtl: Schema.Boolean;
|
|
98
|
+
readonly virtualFocus: Schema.Boolean;
|
|
99
|
+
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Creates hydrated Menubar state. Uses Composite defaults unless overridden.
|
|
102
|
+
*
|
|
103
|
+
* @remarks
|
|
104
|
+
* ## Why
|
|
105
|
+
*
|
|
106
|
+
* State and collection ownership can be composed and tested independently from any renderer.
|
|
107
|
+
*
|
|
108
|
+
* ## Ownership and lifetime
|
|
109
|
+
*
|
|
110
|
+
* The returned Effect creates the RefSubject when run. That state is renderer-independent;
|
|
111
|
+
* collection registrations belong to the separate Scope that runs register or ref, not to state
|
|
112
|
+
* creation.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* import * as Effect from "effect/Effect";
|
|
117
|
+
* import * as Menubar from "@typed/ui/Menubar";
|
|
118
|
+
*
|
|
119
|
+
* const program = Effect.scoped(
|
|
120
|
+
* Effect.gen(function* () {
|
|
121
|
+
* const state = yield* Menubar.makeState({});
|
|
122
|
+
* const collection = yield* Menubar.makeCollection();
|
|
123
|
+
* return { state: yield* state, collection: yield* collection };
|
|
124
|
+
* }),
|
|
125
|
+
* );
|
|
126
|
+
* ```
|
|
127
|
+
* @since 1.0.0
|
|
128
|
+
* @category constructors
|
|
129
|
+
*/
|
|
130
|
+
export declare function makeState(initial?: InitialState): Effect.Effect<RefSubject.HydratedRefSubject<{
|
|
131
|
+
readonly activeId: string | null;
|
|
132
|
+
readonly orientation: "both" | "horizontal" | "vertical";
|
|
133
|
+
readonly loop: boolean;
|
|
134
|
+
readonly rtl: boolean;
|
|
135
|
+
readonly virtualFocus: boolean;
|
|
136
|
+
}, Schema.SchemaError, never, never>, never, Scope.Scope>;
|
|
137
|
+
/**
|
|
138
|
+
* Creates a scoped Collection for Menubar items.
|
|
139
|
+
*
|
|
140
|
+
* @remarks
|
|
141
|
+
* ## Why
|
|
142
|
+
*
|
|
143
|
+
* State and collection ownership can be composed and tested independently from any renderer.
|
|
144
|
+
*
|
|
145
|
+
* ## Ownership and lifetime
|
|
146
|
+
*
|
|
147
|
+
* The returned Effect allocates the RefSubject in the caller's Scope. Each later registration is
|
|
148
|
+
* owned by the Scope that runs register, independently of this construction Effect.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* import * as Effect from "effect/Effect";
|
|
153
|
+
* import * as Menubar from "@typed/ui/Menubar";
|
|
154
|
+
*
|
|
155
|
+
* const program = Effect.scoped(
|
|
156
|
+
* Effect.gen(function* () {
|
|
157
|
+
* const collection = yield* Menubar.makeCollection();
|
|
158
|
+
* return yield* collection;
|
|
159
|
+
* }),
|
|
160
|
+
* );
|
|
161
|
+
* ```
|
|
162
|
+
* @since 1.0.0
|
|
163
|
+
* @category constructors
|
|
164
|
+
*/
|
|
165
|
+
export declare const makeCollection: (initial?: Collection.State<string, Element>) => Effect.Effect<RefSubject.RefSubject<Collection.State<string, Element>, never, never>, never, Scope.Scope>;
|
|
166
|
+
/**
|
|
167
|
+
* Inputs accepted by Menubar.Root in addition to the shared DOM host options.
|
|
168
|
+
*
|
|
169
|
+
* @remarks
|
|
170
|
+
* ## Why
|
|
171
|
+
*
|
|
172
|
+
* The options type makes required state, content, accessible relationships, and custom-host inputs
|
|
173
|
+
* visible before rendering.
|
|
174
|
+
*
|
|
175
|
+
* ## Ownership and lifetime
|
|
176
|
+
*
|
|
177
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
178
|
+
*
|
|
179
|
+
* ## Example
|
|
180
|
+
*
|
|
181
|
+
* Import with `import type { RootOptions } from "@typed/ui/Menubar";` Extend the [Menubar.makeState
|
|
182
|
+
* runnable setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Enable top-level menu movement
|
|
183
|
+
* with `const options: RootOptions = { state, collection, label: "Application", content: "Menus" }`.
|
|
184
|
+
* @since 1.0.0
|
|
185
|
+
* @category models
|
|
186
|
+
*/
|
|
187
|
+
export interface RootOptions extends Dom.HostOptions<HTMLDivElement> {
|
|
188
|
+
/**
|
|
189
|
+
* Renderer-independent RefSubject state consumed by this component or operation.
|
|
190
|
+
* @since 1.0.0
|
|
191
|
+
* @category models
|
|
192
|
+
*/
|
|
193
|
+
readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
|
|
194
|
+
/**
|
|
195
|
+
* Item registry used for collection-driven keyboard behavior and mounted ordering.
|
|
196
|
+
* @since 1.0.0
|
|
197
|
+
* @category models
|
|
198
|
+
*/
|
|
199
|
+
readonly collection?: RefSubject.RefSubject<Collection.State<string>>;
|
|
200
|
+
/**
|
|
201
|
+
* Renderable child content for the component host.
|
|
202
|
+
* @since 1.0.0
|
|
203
|
+
* @category models
|
|
204
|
+
*/
|
|
205
|
+
readonly content: Renderable.Any;
|
|
206
|
+
/**
|
|
207
|
+
* Accessible label rendered through aria-label.
|
|
208
|
+
* @since 1.0.0
|
|
209
|
+
* @category models
|
|
210
|
+
*/
|
|
211
|
+
readonly label?: Renderable.Any<string | null | undefined>;
|
|
212
|
+
}
|
|
213
|
+
declare function rootProps<const Options extends RootOptions>(options: Options): ({ property }: Dom.InternalPropsHelpers<Options>) => {
|
|
214
|
+
readonly role: "menubar";
|
|
215
|
+
readonly "aria-label": Exclude<Dom.Property<Options, "label">, Dom.Nullish> | undefined;
|
|
216
|
+
readonly "aria-orientation": RefSubject.Computed<Composite.Orientation, Schema.SchemaError, never>;
|
|
217
|
+
readonly tabindex: RefSubject.Computed<-1 | 0, Schema.SchemaError, never>;
|
|
218
|
+
readonly onfocus: Effect.Effect<void, Schema.SchemaError, never> | undefined;
|
|
219
|
+
readonly onkeydown: EventHandler.EventHandler<KeyboardEvent, Schema.SchemaError, never> | undefined;
|
|
220
|
+
readonly ref: RefSubject.HydratedRefSubject<State, Schema.SchemaError, never, never>;
|
|
221
|
+
};
|
|
222
|
+
type RootProps<Options extends RootOptions> = ReturnType<ReturnType<typeof rootProps<Options>>>;
|
|
223
|
+
/**
|
|
224
|
+
* Renders the persistent menubar root with roving focus, orientation-aware keys, and typeahead.
|
|
225
|
+
*
|
|
226
|
+
* @remarks
|
|
227
|
+
* ## Why
|
|
228
|
+
*
|
|
229
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
230
|
+
* through the shared DOM boundary.
|
|
231
|
+
*
|
|
232
|
+
* ## Ownership and lifetime
|
|
233
|
+
*
|
|
234
|
+
* The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
|
|
235
|
+
* collection registrations only when rendered. The rendering Scope removes those resources;
|
|
236
|
+
* unrelated nodes and attributes remain caller-owned.
|
|
237
|
+
*
|
|
238
|
+
* ## Example
|
|
239
|
+
*
|
|
240
|
+
* Import with `import { Root } from "@typed/ui/Menubar";` Extend the [Menubar.makeState runnable
|
|
241
|
+
* setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Replace the linked program's final
|
|
242
|
+
* snapshot read with `Root({ state, label: "Application", content: "Menus" })`; render that Fx
|
|
243
|
+
* before the same Scope closes.
|
|
244
|
+
* @since 1.0.0
|
|
245
|
+
* @category components
|
|
246
|
+
*/
|
|
247
|
+
export declare function Root<const Options extends RootOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, RootProps<Options>>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
|
|
248
|
+
/**
|
|
249
|
+
* Consumer-facing alias of the canonical Menubar component with identical behavior and lifetime.
|
|
250
|
+
*
|
|
251
|
+
* @remarks
|
|
252
|
+
* ## Why
|
|
253
|
+
*
|
|
254
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
255
|
+
* through the shared DOM boundary.
|
|
256
|
+
*
|
|
257
|
+
* ## Ownership and lifetime
|
|
258
|
+
*
|
|
259
|
+
* The alias acquires nothing. Rendering it has exactly the canonical component's Scope and DOM
|
|
260
|
+
* ownership contract.
|
|
261
|
+
*
|
|
262
|
+
* ## Example
|
|
263
|
+
*
|
|
264
|
+
* Import with `import { Menubar } from "@typed/ui/Menubar";` Extend the [Menubar.makeState runnable
|
|
265
|
+
* setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Replace the linked program's final
|
|
266
|
+
* snapshot read with `Menubar({ state, label: "Application", content: "Menus" })`; render that Fx
|
|
267
|
+
* before the same Scope closes.
|
|
268
|
+
* @since 1.0.0
|
|
269
|
+
* @category components
|
|
270
|
+
*/
|
|
271
|
+
export declare const Menubar: typeof Root;
|
|
272
|
+
/**
|
|
273
|
+
* Inputs accepted by Menubar.Item in addition to the shared DOM host options.
|
|
274
|
+
*
|
|
275
|
+
* @remarks
|
|
276
|
+
* ## Why
|
|
277
|
+
*
|
|
278
|
+
* The options type makes required state, content, accessible relationships, and custom-host inputs
|
|
279
|
+
* visible before rendering.
|
|
280
|
+
*
|
|
281
|
+
* ## Ownership and lifetime
|
|
282
|
+
*
|
|
283
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
284
|
+
*
|
|
285
|
+
* ## Example
|
|
286
|
+
*
|
|
287
|
+
* Import with `import type { ItemOptions } from "@typed/ui/Menubar";` Extend the [Menubar.makeState
|
|
288
|
+
* runnable setup](/reference/%40typed%2Fui%2FMenubar%23makeState). A top-level menu item is
|
|
289
|
+
* `const options: ItemOptions = { state, collection, id: "file", content: "File" }`.
|
|
290
|
+
* @since 1.0.0
|
|
291
|
+
* @category models
|
|
292
|
+
*/
|
|
293
|
+
export interface ItemOptions extends Dom.HostOptions<HTMLDivElement> {
|
|
294
|
+
/**
|
|
295
|
+
* Renderer-independent RefSubject state consumed by this component or operation.
|
|
296
|
+
* @since 1.0.0
|
|
297
|
+
* @category models
|
|
298
|
+
*/
|
|
299
|
+
readonly state: RefSubject.HydratedRefSubject<State, Schema.SchemaError>;
|
|
300
|
+
/**
|
|
301
|
+
* Item registry used for collection-driven keyboard behavior and mounted ordering.
|
|
302
|
+
* @since 1.0.0
|
|
303
|
+
* @category models
|
|
304
|
+
*/
|
|
305
|
+
readonly collection?: RefSubject.RefSubject<Collection.State<string>>;
|
|
306
|
+
/**
|
|
307
|
+
* Stable id used for collection identity and ARIA relationships.
|
|
308
|
+
* @since 1.0.0
|
|
309
|
+
* @category models
|
|
310
|
+
*/
|
|
311
|
+
readonly id: string;
|
|
312
|
+
/**
|
|
313
|
+
* Renderable child content for the component host.
|
|
314
|
+
* @since 1.0.0
|
|
315
|
+
* @category models
|
|
316
|
+
*/
|
|
317
|
+
readonly content: Renderable.Any;
|
|
318
|
+
/**
|
|
319
|
+
* Search text used by typeahead independently of rendered markup.
|
|
320
|
+
* @since 1.0.0
|
|
321
|
+
* @category models
|
|
322
|
+
*/
|
|
323
|
+
readonly textValue?: string;
|
|
324
|
+
/**
|
|
325
|
+
* Flag used by collection movement and widget handlers to skip activation by default.
|
|
326
|
+
* @since 1.0.0
|
|
327
|
+
* @category models
|
|
328
|
+
*/
|
|
329
|
+
readonly disabled?: boolean;
|
|
330
|
+
}
|
|
331
|
+
declare function itemProps<const Options extends ItemOptions>(options: Options): () => {
|
|
332
|
+
readonly id: string;
|
|
333
|
+
readonly role: "menuitem";
|
|
334
|
+
readonly "aria-disabled": boolean;
|
|
335
|
+
readonly tabindex: RefSubject.Computed<-1 | 0, Schema.SchemaError, never>;
|
|
336
|
+
readonly onfocus: Effect.Effect<State, Schema.SchemaError, never>;
|
|
337
|
+
readonly onclick: Effect.Effect<void, never, never> | Effect.Effect<State, Schema.SchemaError, never>;
|
|
338
|
+
readonly ref: ((element: Element & HTMLDivElement & RefSubject.HydrationElement) => Effect.Effect<void, any, any>) | undefined;
|
|
339
|
+
};
|
|
340
|
+
type ItemProps<Options extends ItemOptions> = ReturnType<ReturnType<typeof itemProps<Options>>>;
|
|
341
|
+
/**
|
|
342
|
+
* Renders a registered menuitem and runs onActivate for native focus or click unless disabled.
|
|
343
|
+
*
|
|
344
|
+
* @remarks
|
|
345
|
+
* ## Why
|
|
346
|
+
*
|
|
347
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
348
|
+
* through the shared DOM boundary.
|
|
349
|
+
*
|
|
350
|
+
* ## Ownership and lifetime
|
|
351
|
+
*
|
|
352
|
+
* The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
|
|
353
|
+
* collection registrations only when rendered. The rendering Scope removes those resources;
|
|
354
|
+
* unrelated nodes and attributes remain caller-owned.
|
|
355
|
+
*
|
|
356
|
+
* ## Example
|
|
357
|
+
*
|
|
358
|
+
* Import with `import { Item } from "@typed/ui/Menubar";` Extend the [Menubar.makeState runnable
|
|
359
|
+
* setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Replace the linked program's final
|
|
360
|
+
* snapshot read with `Item({ state, id: "file", content: "File" })`; render that Fx before the same
|
|
361
|
+
* Scope closes.
|
|
362
|
+
* @since 1.0.0
|
|
363
|
+
* @category components
|
|
364
|
+
*/
|
|
365
|
+
export declare function Item<const Options extends ItemOptions, const Host extends HostResult = never>(options: Options, host?: Dom.HostOverride<Dom.RenderHostProps<Options, ItemProps<Options>>, Options["content"], Host>): Fx<RenderEvent, Renderable.Error<Options | Host>, Renderable.Services<Options | Host> | Scope.Scope | RenderTemplate>;
|
|
366
|
+
export {};
|
|
367
|
+
//# sourceMappingURL=Menubar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menubar.d.ts","sourceRoot":"","sources":["../src/Menubar.ts"],"names":[],"mappings":"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"}
|
package/dist/Menubar.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Menubar is a persistent horizontal composite. Its root establishes DOM-order roving focus and
|
|
3
|
+
* typeahead; items expose menuitem semantics and invoke caller-provided activation Effects.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* The module keeps policy, state transitions, and DOM rendering separable so applications can use
|
|
7
|
+
* the state and pure operations without mounting UI, or supply custom hosts without replacing native
|
|
8
|
+
* events and browser-owned focus.
|
|
9
|
+
*
|
|
10
|
+
* @since 1.0.0
|
|
11
|
+
* @category modules
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
import * as Effect from "effect/Effect";
|
|
15
|
+
import { RefSubject } from "@typed/fx";
|
|
16
|
+
import { EventHandler, html, } from "@typed/template";
|
|
17
|
+
import * as Collection from "./Collection.js";
|
|
18
|
+
import * as Composite from "./Composite.js";
|
|
19
|
+
import * as Dom from "./Dom.js";
|
|
20
|
+
/**
|
|
21
|
+
* Effect Schema used by makeState to encode, decode, and hydrate Menubar state.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* ## Why
|
|
25
|
+
*
|
|
26
|
+
* A public schema makes hydration and serialized state use the same runtime validation as direct
|
|
27
|
+
* construction.
|
|
28
|
+
*
|
|
29
|
+
* ## Ownership and lifetime
|
|
30
|
+
*
|
|
31
|
+
* This declaration is data or schema metadata and acquires no resources.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import * as Schema from "effect/Schema";
|
|
36
|
+
* import * as Menubar from "@typed/ui/Menubar";
|
|
37
|
+
*
|
|
38
|
+
* const decodeState = Schema.decodeUnknownEffect(Menubar.StateSchema);
|
|
39
|
+
* ```
|
|
40
|
+
* @since 1.0.0
|
|
41
|
+
* @category schemas
|
|
42
|
+
*/
|
|
43
|
+
export const StateSchema = Composite.StateSchema;
|
|
44
|
+
/**
|
|
45
|
+
* Creates hydrated Menubar state. Uses Composite defaults unless overridden.
|
|
46
|
+
*
|
|
47
|
+
* @remarks
|
|
48
|
+
* ## Why
|
|
49
|
+
*
|
|
50
|
+
* State and collection ownership can be composed and tested independently from any renderer.
|
|
51
|
+
*
|
|
52
|
+
* ## Ownership and lifetime
|
|
53
|
+
*
|
|
54
|
+
* The returned Effect creates the RefSubject when run. That state is renderer-independent;
|
|
55
|
+
* collection registrations belong to the separate Scope that runs register or ref, not to state
|
|
56
|
+
* creation.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import * as Effect from "effect/Effect";
|
|
61
|
+
* import * as Menubar from "@typed/ui/Menubar";
|
|
62
|
+
*
|
|
63
|
+
* const program = Effect.scoped(
|
|
64
|
+
* Effect.gen(function* () {
|
|
65
|
+
* const state = yield* Menubar.makeState({});
|
|
66
|
+
* const collection = yield* Menubar.makeCollection();
|
|
67
|
+
* return { state: yield* state, collection: yield* collection };
|
|
68
|
+
* }),
|
|
69
|
+
* );
|
|
70
|
+
* ```
|
|
71
|
+
* @since 1.0.0
|
|
72
|
+
* @category constructors
|
|
73
|
+
*/
|
|
74
|
+
export function makeState(initial = {}) {
|
|
75
|
+
return Composite.makeState({ ...initial, orientation: initial.orientation ?? "horizontal" });
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Creates a scoped Collection for Menubar items.
|
|
79
|
+
*
|
|
80
|
+
* @remarks
|
|
81
|
+
* ## Why
|
|
82
|
+
*
|
|
83
|
+
* State and collection ownership can be composed and tested independently from any renderer.
|
|
84
|
+
*
|
|
85
|
+
* ## Ownership and lifetime
|
|
86
|
+
*
|
|
87
|
+
* The returned Effect allocates the RefSubject in the caller's Scope. Each later registration is
|
|
88
|
+
* owned by the Scope that runs register, independently of this construction Effect.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* import * as Effect from "effect/Effect";
|
|
93
|
+
* import * as Menubar from "@typed/ui/Menubar";
|
|
94
|
+
*
|
|
95
|
+
* const program = Effect.scoped(
|
|
96
|
+
* Effect.gen(function* () {
|
|
97
|
+
* const collection = yield* Menubar.makeCollection();
|
|
98
|
+
* return yield* collection;
|
|
99
|
+
* }),
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
* @since 1.0.0
|
|
103
|
+
* @category constructors
|
|
104
|
+
*/
|
|
105
|
+
export const makeCollection = Collection.makeState;
|
|
106
|
+
function rootProps(options) {
|
|
107
|
+
let typeahead = { value: "", updatedAt: 0 };
|
|
108
|
+
const onfocus = options.collection === undefined
|
|
109
|
+
? undefined
|
|
110
|
+
: Effect.gen(function* () {
|
|
111
|
+
if ((yield* options.state).activeId !== null)
|
|
112
|
+
return;
|
|
113
|
+
yield* Composite.moveAndFocus({
|
|
114
|
+
state: options.state,
|
|
115
|
+
collection: options.collection,
|
|
116
|
+
includeDisabled: true,
|
|
117
|
+
}, "first");
|
|
118
|
+
});
|
|
119
|
+
const onkeydown = options.collection === undefined
|
|
120
|
+
? undefined
|
|
121
|
+
: EventHandler.make(Effect.fn(function* (event) {
|
|
122
|
+
if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
|
|
123
|
+
const activeId = (yield* options.state).activeId;
|
|
124
|
+
const item = activeId === null
|
|
125
|
+
? undefined
|
|
126
|
+
: (yield* options.collection).find((item) => item.id === activeId);
|
|
127
|
+
const shouldActivate = event.key !== "ArrowDown" || item?.submenu === true;
|
|
128
|
+
const element = shouldActivate && item?.disabled !== true ? item?.element : undefined;
|
|
129
|
+
const click = element === undefined ? undefined : Reflect.get(element, "click");
|
|
130
|
+
if (typeof click === "function") {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
yield* Effect.sync(() => click.call(element));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const direction = Composite.keyMove(event, yield* options.state);
|
|
137
|
+
if (direction !== undefined) {
|
|
138
|
+
event.preventDefault();
|
|
139
|
+
yield* Composite.moveAndFocus({ state: options.state, collection: options.collection, includeDisabled: true }, direction);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const key = Composite.typeaheadKey(event);
|
|
143
|
+
if (key === null)
|
|
144
|
+
return;
|
|
145
|
+
typeahead = Composite.updateTypeaheadBuffer(typeahead, key, Date.now());
|
|
146
|
+
const activeId = Composite.typeaheadFrom(yield* options.collection, typeahead.value, (yield* options.state).activeId, undefined, true);
|
|
147
|
+
if (activeId === null)
|
|
148
|
+
return;
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
yield* RefSubject.update(options.state, (state) => ({ ...state, activeId }));
|
|
151
|
+
yield* Composite.focusActive({ state: options.state, collection: options.collection });
|
|
152
|
+
}));
|
|
153
|
+
return ({ property }) => ({
|
|
154
|
+
role: "menubar",
|
|
155
|
+
"aria-label": property("label", undefined),
|
|
156
|
+
"aria-orientation": RefSubject.map(options.state, (state) => state.orientation),
|
|
157
|
+
tabindex: Composite.rootTabIndex(options.state),
|
|
158
|
+
onfocus,
|
|
159
|
+
onkeydown,
|
|
160
|
+
ref: options.state,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Renders the persistent menubar root with roving focus, orientation-aware keys, and typeahead.
|
|
165
|
+
*
|
|
166
|
+
* @remarks
|
|
167
|
+
* ## Why
|
|
168
|
+
*
|
|
169
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
170
|
+
* through the shared DOM boundary.
|
|
171
|
+
*
|
|
172
|
+
* ## Ownership and lifetime
|
|
173
|
+
*
|
|
174
|
+
* The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
|
|
175
|
+
* collection registrations only when rendered. The rendering Scope removes those resources;
|
|
176
|
+
* unrelated nodes and attributes remain caller-owned.
|
|
177
|
+
*
|
|
178
|
+
* ## Example
|
|
179
|
+
*
|
|
180
|
+
* Import with `import { Root } from "@typed/ui/Menubar";` Extend the [Menubar.makeState runnable
|
|
181
|
+
* setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Replace the linked program's final
|
|
182
|
+
* snapshot read with `Root({ state, label: "Application", content: "Menus" })`; render that Fx
|
|
183
|
+
* before the same Scope closes.
|
|
184
|
+
* @since 1.0.0
|
|
185
|
+
* @category components
|
|
186
|
+
*/
|
|
187
|
+
export function Root(options, host) {
|
|
188
|
+
return Dom.renderHost()(options, host, rootProps(options), options.content, (props, content) => {
|
|
189
|
+
return html `<div ...${props}>${content}</div>`;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Consumer-facing alias of the canonical Menubar component with identical behavior and lifetime.
|
|
194
|
+
*
|
|
195
|
+
* @remarks
|
|
196
|
+
* ## Why
|
|
197
|
+
*
|
|
198
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
199
|
+
* through the shared DOM boundary.
|
|
200
|
+
*
|
|
201
|
+
* ## Ownership and lifetime
|
|
202
|
+
*
|
|
203
|
+
* The alias acquires nothing. Rendering it has exactly the canonical component's Scope and DOM
|
|
204
|
+
* ownership contract.
|
|
205
|
+
*
|
|
206
|
+
* ## Example
|
|
207
|
+
*
|
|
208
|
+
* Import with `import { Menubar } from "@typed/ui/Menubar";` Extend the [Menubar.makeState runnable
|
|
209
|
+
* setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Replace the linked program's final
|
|
210
|
+
* snapshot read with `Menubar({ state, label: "Application", content: "Menus" })`; render that Fx
|
|
211
|
+
* before the same Scope closes.
|
|
212
|
+
* @since 1.0.0
|
|
213
|
+
* @category components
|
|
214
|
+
*/
|
|
215
|
+
export const Menubar = Root;
|
|
216
|
+
function itemProps(options) {
|
|
217
|
+
const activate = options.disabled === true
|
|
218
|
+
? Effect.void
|
|
219
|
+
: RefSubject.update(options.state, (state) => ({ ...state, activeId: options.id }));
|
|
220
|
+
const focus = RefSubject.update(options.state, (state) => ({ ...state, activeId: options.id }));
|
|
221
|
+
const register = options.collection === undefined
|
|
222
|
+
? undefined
|
|
223
|
+
: Collection.ref(options.collection, {
|
|
224
|
+
id: options.id,
|
|
225
|
+
value: options.id,
|
|
226
|
+
textValue: options.textValue ?? options.id,
|
|
227
|
+
disabled: options.disabled,
|
|
228
|
+
});
|
|
229
|
+
return () => ({
|
|
230
|
+
id: options.id,
|
|
231
|
+
role: "menuitem",
|
|
232
|
+
"aria-disabled": options.disabled ?? false,
|
|
233
|
+
tabindex: Composite.tabIndex(options.state, options.id),
|
|
234
|
+
onfocus: focus,
|
|
235
|
+
onclick: activate,
|
|
236
|
+
ref: Dom.composeRefs(register, options.ref),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Renders a registered menuitem and runs onActivate for native focus or click unless disabled.
|
|
241
|
+
*
|
|
242
|
+
* @remarks
|
|
243
|
+
* ## Why
|
|
244
|
+
*
|
|
245
|
+
* The component applies the family behavior while leaving callers free to supply a custom host
|
|
246
|
+
* through the shared DOM boundary.
|
|
247
|
+
*
|
|
248
|
+
* ## Ownership and lifetime
|
|
249
|
+
*
|
|
250
|
+
* The returned Fx installs DOM refs, native listeners, state subscriptions, and optional
|
|
251
|
+
* collection registrations only when rendered. The rendering Scope removes those resources;
|
|
252
|
+
* unrelated nodes and attributes remain caller-owned.
|
|
253
|
+
*
|
|
254
|
+
* ## Example
|
|
255
|
+
*
|
|
256
|
+
* Import with `import { Item } from "@typed/ui/Menubar";` Extend the [Menubar.makeState runnable
|
|
257
|
+
* setup](/reference/%40typed%2Fui%2FMenubar%23makeState). Replace the linked program's final
|
|
258
|
+
* snapshot read with `Item({ state, id: "file", content: "File" })`; render that Fx before the same
|
|
259
|
+
* Scope closes.
|
|
260
|
+
* @since 1.0.0
|
|
261
|
+
* @category components
|
|
262
|
+
*/
|
|
263
|
+
export function Item(options, host) {
|
|
264
|
+
return Dom.renderHost()(options, host, itemProps(options), options.content, (props, content) => html `<div ...${props}>${content}</div>`);
|
|
265
|
+
}
|