@cssints/headless 0.0.0-stage → 0.5.0
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/LICENSE +21 -0
- package/README.md +108 -2
- package/dist/index.d.mts +3068 -0
- package/dist/index.mjs +6994 -0
- package/package.json +30 -4
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,3068 @@
|
|
|
1
|
+
import { Accessor } from "solid-js";
|
|
2
|
+
//#region src/canvas-math.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The fixed-point math of the canvas, copied from the Quint specs. Integers only, power-of-two
|
|
5
|
+
* denominators, every intermediate below 2^53, so doubles are exact and the DOM implementation can
|
|
6
|
+
* be compared with the generated model bit for bit.
|
|
7
|
+
*
|
|
8
|
+
* zoom z = zoom * ZONE (ZMIN 1/16 .. ZMAX 8)
|
|
9
|
+
* camera t = world units * TONE (sub-unit offsets keep zoom smooth)
|
|
10
|
+
* screen = (world + t) * zoom (world-offset form)
|
|
11
|
+
* every division is round half up (`rdiv`); `fdiv` is floor.
|
|
12
|
+
*/
|
|
13
|
+
export declare const ZONE = 4096;
|
|
14
|
+
export declare const TONE = 256;
|
|
15
|
+
export declare const ZMIN = 256;
|
|
16
|
+
export declare const ZMAX = 32768;
|
|
17
|
+
/** Viewport origin bound, world units. */
|
|
18
|
+
export declare const WORLD_BOUND = 1000000;
|
|
19
|
+
/** Screen px a press must travel before it becomes a gesture (squared distance > DRAG_PX^2). */
|
|
20
|
+
export declare const DRAG_PX = 4;
|
|
21
|
+
/** Screen px within which a dragged node snaps to the grid. */
|
|
22
|
+
export declare const SNAP_PX = 8;
|
|
23
|
+
/** The default snap candidates: every GRID world units (`gridCandidates`). */
|
|
24
|
+
export declare const GRID = 50;
|
|
25
|
+
/** Ticks of a tween; the last one lands exactly on the target. */
|
|
26
|
+
export declare const TWEEN_TICKS = 4;
|
|
27
|
+
type MarqueeMode = "contain" | "overlap";
|
|
28
|
+
type SnapAxis = "x" | "y";
|
|
29
|
+
/** The dragged node a snap policy is asked about: its id and its size, whole world units. */
|
|
30
|
+
interface SnapNode {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly width: number;
|
|
33
|
+
readonly height: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What the host's snap policy is asked: `value` is the origin (left / top edge) of `id` on `axis`
|
|
37
|
+
* while it is dragged, `width` / `height` its size. Edge alignment is `target - width` (right) or
|
|
38
|
+
* `target - height` (bottom). One object, so a field can be added without breaking a policy.
|
|
39
|
+
*/
|
|
40
|
+
interface SnapContext extends SnapNode {
|
|
41
|
+
readonly axis: SnapAxis;
|
|
42
|
+
readonly value: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The host's snap policy: candidate origins on one axis for the dragged node described by the
|
|
46
|
+
* context. WHOLE world units (never 1/256 fixed point); a fractional candidate is ignored. The
|
|
47
|
+
* primitive keeps the mechanism (nearest candidate, the 8 screen px threshold, the guides).
|
|
48
|
+
*/
|
|
49
|
+
type SnapCandidates = (context: SnapContext) => readonly number[];
|
|
50
|
+
/** The default policy and the one `specs/canvas/pointer.qnt` models: every `GRID` units. */
|
|
51
|
+
export declare const gridCandidates: SnapCandidates;
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/prefs.d.ts
|
|
54
|
+
/**
|
|
55
|
+
* The user preferences a primitive honours. The host resolves them from
|
|
56
|
+
* wherever it keeps them and injects a `Partial<Prefs>` per primitive;
|
|
57
|
+
* `@cssints/headless` never imports atproto or a lexicon. An absent pref falls back
|
|
58
|
+
* to today's behaviour, so injection is additive.
|
|
59
|
+
*
|
|
60
|
+
* `motionDurationMs` has no consumer here: canvas tweens are tick-driven, not
|
|
61
|
+
* millisecond-driven, and the look layer owns durations (ADR 0002). It is part
|
|
62
|
+
* of the shape so a host's resolved prefs can be passed in unchanged.
|
|
63
|
+
*/
|
|
64
|
+
export interface Prefs {
|
|
65
|
+
readonly motion: "reduce" | "full";
|
|
66
|
+
readonly motionDurationMs: number;
|
|
67
|
+
readonly typeaheadDelayMs: number;
|
|
68
|
+
readonly tooltipShowDelayMs: number;
|
|
69
|
+
readonly tooltipHideDelayMs: number;
|
|
70
|
+
readonly announce: boolean;
|
|
71
|
+
readonly focusFollowsCamera: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Today's behaviour, used for every pref the host does not inject. */
|
|
74
|
+
export declare const DEFAULT_PREFS: Prefs;
|
|
75
|
+
/** Merge an injected partial with the defaults; never mutates the input. */
|
|
76
|
+
export declare const withPrefs: (partial?: Partial<Prefs>) => Prefs;
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/canvas.d.ts
|
|
79
|
+
/**
|
|
80
|
+
* Headless infinite canvas: camera, selection and node-move state plus attribute / prop objects
|
|
81
|
+
* for a viewport, a camera layer, the nodes and a live region. No elements, no styling beyond the
|
|
82
|
+
* geometry the layout needs (positions, sizes, the camera transform, `content-visibility`).
|
|
83
|
+
* Behaviour is checked against `specs/canvas/{camera,pointer}.qnt` by the scenario bridge
|
|
84
|
+
* (`packages/apg-specs/src/canvas/canvas.bridge.ts`), not by running the generated machines here.
|
|
85
|
+
*
|
|
86
|
+
* ## What this module owns
|
|
87
|
+
* Conventions that mirror the specs exactly (so the bridge can compare bit for bit): integer
|
|
88
|
+
* fixed point, zoom `value * 4096`, translation `world units * 256`, `screen = (world + t) * z`,
|
|
89
|
+
* round half up, top-left-anchored resize, the viewport origin bounded to +-1e6 units, a clamp
|
|
90
|
+
* re-anchors the zoom, a 4 px SCREEN drag threshold, an 8 px SCREEN snap threshold on a 50-unit
|
|
91
|
+
* grid, reduced motion jumps to the same end state a tween reaches. Pointer positions are rounded
|
|
92
|
+
* to whole CSS px.
|
|
93
|
+
*
|
|
94
|
+
* Accessibility (effects-of-atmosphere-h9vk): every node is a real focusable element (`role`,
|
|
95
|
+
* `aria-roledescription`, `aria-label`, `tabindex=0`; never `role=application`); `nodeIds()` is the
|
|
96
|
+
* DOM order = the spatial order (rows by y, then x, then the paint order: the input order until
|
|
97
|
+
* `setOrder`) so Tab and the screen
|
|
98
|
+
* reader follow what is on screen; selected / focused / editing nodes are never culled
|
|
99
|
+
* (`content-visibility: visible`), the rest are `auto` with `contain-intrinsic-size`; focusing a
|
|
100
|
+
* node that is not fully in view pans it to the centre (no animation under reduced motion); a live
|
|
101
|
+
* region announces settled selections and keyboard moves; arrows move the focused node
|
|
102
|
+
* (`keyboardStep`, times `keyboardShiftMultiplier` with Shift) - the no-mouse path.
|
|
103
|
+
*
|
|
104
|
+
* ## What the adapter / host owns (NOT implemented here)
|
|
105
|
+
* - wheel / pinch normalisation: turn a gesture into `panBy` / `zoomBy` / `zoomTo` calls;
|
|
106
|
+
* - easing and durations: `requestFrame` drives the 4 linear ticks (default `requestAnimationFrame`);
|
|
107
|
+
* - fit-to-bounds: compute a goal and call `animateTo`;
|
|
108
|
+
* - the snap candidate policy (`snapCandidates`; a 50-unit grid by default) and hollow / rotated
|
|
109
|
+
* shape hit rules (a node is its box);
|
|
110
|
+
* - node geometry, z-order and membership when they come from elsewhere (an auto-layout, a node
|
|
111
|
+
* that grows to its text): push them with `setGeometry` / `setOrder` / `addNodes` /
|
|
112
|
+
* `removeNodes`. Membership is not in `pointer.qnt` (its node set is fixed at three): it is
|
|
113
|
+
* tested here, on the real DOM, not replayed against the model;
|
|
114
|
+
* - viewport measuring (call `resize` from a ResizeObserver), the reduced-motion preference (call
|
|
115
|
+
* `setReducedMotion` from `matchMedia`), the visually-hidden style of the live region, edges.
|
|
116
|
+
*
|
|
117
|
+
* ## Divergences from the specs that are deliberate
|
|
118
|
+
* Keyboard (arrows, Enter, F2, Escape), focus auto-pan and the announcement text are not in the
|
|
119
|
+
* specs; they are tested directly. `role=group` does not list `aria-selected` in ARIA 1.2: pass
|
|
120
|
+
* `role: 'option' | 'treeitem' | 'gridcell'` per node when that matters, otherwise the live region
|
|
121
|
+
* carries the selection.
|
|
122
|
+
*
|
|
123
|
+
* ponytail: a node's label and role are fixed while its id exists (change them by removing and
|
|
124
|
+
* re-adding it after a flush); the row tie-break is the exact y; no window-blur reset of Shift / Space.
|
|
125
|
+
*/
|
|
126
|
+
type CanvasMode = "idle" | "pointing" | "panning" | "marquee" | "dragging";
|
|
127
|
+
/** Roles that fit a focusable, nameable box; `option` / `treeitem` / `gridcell` accept `aria-selected`. */
|
|
128
|
+
type CanvasNodeRole = "group" | "article" | "figure" | "listitem" | "option" | "treeitem" | "gridcell";
|
|
129
|
+
interface CanvasNodeInput {
|
|
130
|
+
readonly id: string;
|
|
131
|
+
/** World units, whole numbers. */
|
|
132
|
+
readonly x: number;
|
|
133
|
+
readonly y: number;
|
|
134
|
+
readonly width: number;
|
|
135
|
+
readonly height: number;
|
|
136
|
+
readonly label: string;
|
|
137
|
+
/** Default `group`. */
|
|
138
|
+
readonly role?: CanvasNodeRole;
|
|
139
|
+
/** Default `canvas node`. */
|
|
140
|
+
readonly roleDescription?: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* A host-side change of one node (`Canvas.setGeometry`); omitted fields keep their value. World
|
|
144
|
+
* units; a fractional value rounds half up. A `CanvasNodeInput` is a valid patch.
|
|
145
|
+
*/
|
|
146
|
+
interface CanvasNodeGeometry {
|
|
147
|
+
readonly id: string;
|
|
148
|
+
readonly x?: number;
|
|
149
|
+
readonly y?: number;
|
|
150
|
+
readonly width?: number;
|
|
151
|
+
readonly height?: number;
|
|
152
|
+
}
|
|
153
|
+
interface CanvasMove {
|
|
154
|
+
readonly id: string;
|
|
155
|
+
readonly x: number;
|
|
156
|
+
readonly y: number;
|
|
157
|
+
}
|
|
158
|
+
interface CanvasMessages {
|
|
159
|
+
readonly selection: (labels: readonly string[]) => string;
|
|
160
|
+
readonly moved: (label: string, direction: "left" | "right" | "up" | "down", x: number, y: number) => string;
|
|
161
|
+
}
|
|
162
|
+
/** A camera goal: zoom to `zoom` (fixed point) about a screen point, or centre a world point. */
|
|
163
|
+
type CanvasGoal = {
|
|
164
|
+
readonly kind: "zoom";
|
|
165
|
+
readonly zoom: number;
|
|
166
|
+
readonly anchor: readonly [x: number, y: number];
|
|
167
|
+
} | {
|
|
168
|
+
readonly kind: "centre";
|
|
169
|
+
readonly x: number;
|
|
170
|
+
readonly y: number;
|
|
171
|
+
};
|
|
172
|
+
interface CanvasOptions {
|
|
173
|
+
/** The initial nodes, bottom of the z-order first. `addNodes` / `removeNodes` change the set later. */
|
|
174
|
+
readonly nodes: readonly CanvasNodeInput[];
|
|
175
|
+
/** Base for the generated DOM ids. */
|
|
176
|
+
readonly id?: string;
|
|
177
|
+
/** Accessible name of the viewport region. */
|
|
178
|
+
readonly label?: string;
|
|
179
|
+
/** Initial viewport size, screen px (default 800 x 600). */
|
|
180
|
+
readonly viewport?: {
|
|
181
|
+
readonly width: number;
|
|
182
|
+
readonly height: number;
|
|
183
|
+
};
|
|
184
|
+
readonly marquee?: MarqueeMode;
|
|
185
|
+
readonly snap?: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* The snap policy while `snap` is on: candidate positions per axis for a dragged node's origin,
|
|
188
|
+
* WHOLE world units (a fractional one is ignored). Called on every pointer move of a drag, so it
|
|
189
|
+
* may read live host state. The context carries the dragged node's `id`, `width` and `height`,
|
|
190
|
+
* so edge alignment is `target - width`. With several nodes selected the pressed one is asked.
|
|
191
|
+
* Default `gridCandidates` (every 50 units, what `pointer.qnt` models).
|
|
192
|
+
*/
|
|
193
|
+
readonly snapCandidates?: SnapCandidates;
|
|
194
|
+
readonly reducedMotion?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Structural mirror of `ing.lexicon.ui.prefs` (`./prefs`). The host resolves
|
|
197
|
+
* the record and injects; honoured here: `motion`, `announce`,
|
|
198
|
+
* `focusFollowsCamera`. Absent prefs keep today's behaviour.
|
|
199
|
+
*/
|
|
200
|
+
readonly prefs?: Partial<Prefs> | undefined;
|
|
201
|
+
/** World units per arrow key press (default 10). */
|
|
202
|
+
readonly keyboardStep?: number;
|
|
203
|
+
/** Shift multiplies the step (default 5, so Shift + arrow = one grid cell). */
|
|
204
|
+
readonly keyboardShiftMultiplier?: number;
|
|
205
|
+
/** Drives the tween ticks; default `requestAnimationFrame` when it exists. */
|
|
206
|
+
readonly requestFrame?: (callback: () => void) => void;
|
|
207
|
+
readonly messages?: CanvasMessages;
|
|
208
|
+
/** The settled selection (after a gesture or a key press), never mid-gesture. */
|
|
209
|
+
readonly onSelectionChange?: (ids: readonly string[]) => void;
|
|
210
|
+
/**
|
|
211
|
+
* Committed node positions (pointer up or a key press), changed nodes only. A host's own
|
|
212
|
+
* `setGeometry` echoes here too, with the position AFTER the world-bound clamp.
|
|
213
|
+
*/
|
|
214
|
+
readonly onNodesMove?: (moves: readonly CanvasMove[]) => void;
|
|
215
|
+
}
|
|
216
|
+
interface CanvasSnapshot {
|
|
217
|
+
readonly viewport: {
|
|
218
|
+
readonly width: number;
|
|
219
|
+
readonly height: number;
|
|
220
|
+
};
|
|
221
|
+
/** Fixed point: `tx`, `ty` in world units * 256, `z` = zoom * 4096. */
|
|
222
|
+
readonly camera: {
|
|
223
|
+
readonly tx: number;
|
|
224
|
+
readonly ty: number;
|
|
225
|
+
readonly z: number;
|
|
226
|
+
};
|
|
227
|
+
readonly tweening: boolean;
|
|
228
|
+
readonly reducedMotion: boolean;
|
|
229
|
+
readonly mode: CanvasMode;
|
|
230
|
+
/** Pointer id holding the capture, or `null`. */
|
|
231
|
+
readonly owner: number | null;
|
|
232
|
+
readonly selection: readonly string[];
|
|
233
|
+
readonly cursor: string | null;
|
|
234
|
+
readonly editing: string | null;
|
|
235
|
+
readonly guides: {
|
|
236
|
+
readonly x: boolean;
|
|
237
|
+
readonly y: boolean;
|
|
238
|
+
};
|
|
239
|
+
readonly culled: readonly string[];
|
|
240
|
+
readonly announcements: number;
|
|
241
|
+
readonly announcement: string;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* `overflow: clip`, not `hidden`: a `hidden` box is still a scroll container, and focusing a node
|
|
245
|
+
* that is off screen makes the browser scroll it natively (measured: scrollLeft/Top jump to the
|
|
246
|
+
* node) while the camera has not moved yet, which breaks every pointer coordinate. `clip` never
|
|
247
|
+
* scrolls.
|
|
248
|
+
*/
|
|
249
|
+
interface CanvasViewportStyle {
|
|
250
|
+
readonly position: "relative";
|
|
251
|
+
readonly overflow: "clip";
|
|
252
|
+
readonly "touch-action": "none";
|
|
253
|
+
}
|
|
254
|
+
/** Spread onto the labelled region that clips the canvas. */
|
|
255
|
+
interface CanvasViewportProps {
|
|
256
|
+
readonly ref: (element: HTMLElement) => void;
|
|
257
|
+
readonly role: "region";
|
|
258
|
+
readonly "aria-label": string;
|
|
259
|
+
/** Focusable by click only, so Shift / Space keep reaching the canvas after a click on empty space. */
|
|
260
|
+
readonly tabindex: number;
|
|
261
|
+
readonly style: CanvasViewportStyle;
|
|
262
|
+
readonly onPointerDown: (event: PointerEvent) => void;
|
|
263
|
+
readonly onPointerMove: (event: PointerEvent) => void;
|
|
264
|
+
readonly onPointerUp: (event: PointerEvent) => void;
|
|
265
|
+
readonly onPointerCancel: (event: PointerEvent) => void;
|
|
266
|
+
readonly onLostPointerCapture: (event: PointerEvent) => void;
|
|
267
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
268
|
+
readonly onKeyUp: (event: KeyboardEvent) => void;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Spread onto the single element that holds the nodes. The camera is one CSS transform
|
|
272
|
+
* (`scale(zoom) translate(t)`) driven by custom properties, so it is observable exactly.
|
|
273
|
+
* `style` is a getter returning a fresh object: Solid spreads read it inside the tracking scope.
|
|
274
|
+
*/
|
|
275
|
+
interface CanvasLayerProps {
|
|
276
|
+
readonly style: Readonly<Record<string, string>>;
|
|
277
|
+
}
|
|
278
|
+
interface CanvasNodeProps {
|
|
279
|
+
readonly id: string;
|
|
280
|
+
readonly role: CanvasNodeRole;
|
|
281
|
+
readonly "aria-roledescription": string;
|
|
282
|
+
readonly "aria-label": string;
|
|
283
|
+
readonly "aria-selected": "true" | "false";
|
|
284
|
+
readonly tabindex: number;
|
|
285
|
+
readonly style: Readonly<Record<string, string>>;
|
|
286
|
+
readonly onFocus: () => void;
|
|
287
|
+
readonly onFocusOut: (event: FocusEvent) => void;
|
|
288
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
289
|
+
}
|
|
290
|
+
interface CanvasLiveRegionProps {
|
|
291
|
+
readonly role: "status";
|
|
292
|
+
readonly "aria-live": "polite";
|
|
293
|
+
readonly "aria-atomic": "true";
|
|
294
|
+
}
|
|
295
|
+
interface PointerInput {
|
|
296
|
+
/** `PointerEvent.pointerId`. */
|
|
297
|
+
readonly id: number;
|
|
298
|
+
/** Position relative to the viewport, screen px. */
|
|
299
|
+
readonly x: number;
|
|
300
|
+
readonly y: number;
|
|
301
|
+
}
|
|
302
|
+
interface Canvas {
|
|
303
|
+
/** Reactive snapshot of everything observable. */
|
|
304
|
+
readonly state: Accessor<CanvasSnapshot>;
|
|
305
|
+
/** Synchronous truth (Solid 2 applies signal writes on the microtask flush). */
|
|
306
|
+
readonly peek: () => CanvasSnapshot;
|
|
307
|
+
/** Node ids in DOM order: rows by y, then x, then input order. */
|
|
308
|
+
readonly nodeIds: Accessor<readonly string[]>;
|
|
309
|
+
readonly isSelected: (id: string) => boolean;
|
|
310
|
+
/** The spec's cull predicate (off screen and not selected / focused / editing). */
|
|
311
|
+
readonly isCulled: (id: string) => boolean;
|
|
312
|
+
/** Live position (whole units): the committed one plus the drag delta of a dragged node. */
|
|
313
|
+
readonly position: (id: string) => {
|
|
314
|
+
readonly x: number;
|
|
315
|
+
readonly y: number;
|
|
316
|
+
};
|
|
317
|
+
readonly viewportProps: CanvasViewportProps;
|
|
318
|
+
readonly layerProps: CanvasLayerProps;
|
|
319
|
+
readonly nodeProps: (id: string) => CanvasNodeProps;
|
|
320
|
+
readonly liveRegionProps: CanvasLiveRegionProps;
|
|
321
|
+
/** Pointer entry points (the viewport props call these; adapters may too). */
|
|
322
|
+
readonly pointerDown: (input: PointerInput) => void;
|
|
323
|
+
readonly pointerMove: (input: PointerInput) => void;
|
|
324
|
+
readonly pointerUp: (id: number) => void;
|
|
325
|
+
readonly pointerCancel: (id: number) => void;
|
|
326
|
+
/** Modifiers and parameters are sampled at pointerdown: ignored while a gesture runs. */
|
|
327
|
+
readonly setShift: (held: boolean) => void;
|
|
328
|
+
readonly setSpace: (held: boolean) => void;
|
|
329
|
+
readonly setMarqueeMode: (mode: MarqueeMode) => void;
|
|
330
|
+
readonly setSnap: (on: boolean) => void;
|
|
331
|
+
/** Host resize: the top-left stays fixed. */
|
|
332
|
+
readonly resize: (width: number, height: number) => void;
|
|
333
|
+
/** Turning it on settles a running tween on its target. */
|
|
334
|
+
readonly setReducedMotion: (reduced: boolean) => void;
|
|
335
|
+
/** Pan by a screen delta; cancels a tween. */
|
|
336
|
+
readonly panBy: (dx: number, dy: number) => void;
|
|
337
|
+
/** Zoom by `factor` (fixed point, 4096 = x1) about a screen point (default: the last pointer). */
|
|
338
|
+
readonly zoomBy: (factor: number, anchor?: readonly [number, number]) => void;
|
|
339
|
+
/** Zoom to `zoom` (fixed point), clamped and re-anchored. */
|
|
340
|
+
readonly zoomTo: (zoom: number, anchor?: readonly [number, number]) => void;
|
|
341
|
+
readonly animateTo: (goal: CanvasGoal) => void;
|
|
342
|
+
/** One animation frame of a running tween. */
|
|
343
|
+
readonly tick: () => void;
|
|
344
|
+
readonly cancelTween: () => void;
|
|
345
|
+
/** Enter / leave editing of the single selected node (idle only). */
|
|
346
|
+
readonly toggleEdit: () => void;
|
|
347
|
+
/**
|
|
348
|
+
* Host-controlled geometry (an auto-layout, a node that grew to its text): set the position
|
|
349
|
+
* and / or size of the named nodes, in any mode. Positions are clamped into the world bound
|
|
350
|
+
* (with the new size), a running drag or marquee is re-derived, the DOM order follows (focus is
|
|
351
|
+
* kept) and `onNodesMove` reports the nodes that moved. Throws on an unknown id or a non-finite
|
|
352
|
+
* number, before anything changes.
|
|
353
|
+
*/
|
|
354
|
+
readonly setGeometry: (patches: readonly CanvasNodeGeometry[]) => void;
|
|
355
|
+
/**
|
|
356
|
+
* Host-controlled paint order: `ids` is EVERY node id exactly once, bottom of the z-order first
|
|
357
|
+
* (the `nodes` option's convention). Decides `z-index`, the topmost hit and the DOM order's
|
|
358
|
+
* tie-break. Throws if `ids` is not a permutation of the node ids.
|
|
359
|
+
*/
|
|
360
|
+
readonly setOrder: (ids: readonly string[]) => void;
|
|
361
|
+
/**
|
|
362
|
+
* Host-controlled membership: insert nodes on top of the z-order, in input order (call
|
|
363
|
+
* `setOrder` to place them elsewhere). Positions are clamped into the world bound and an
|
|
364
|
+
* inserted node whose position was clamped is reported through `onNodesMove`. Existing nodes keep
|
|
365
|
+
* their position, selection, cursor and focus; a running marquee is re-derived. Throws on an id
|
|
366
|
+
* that exists already or repeats, or a non-finite number, before anything changes.
|
|
367
|
+
*/
|
|
368
|
+
readonly addNodes: (nodes: readonly CanvasNodeInput[]) => void;
|
|
369
|
+
/**
|
|
370
|
+
* Remove nodes. They leave the selection (a settled selection that lost a member is announced
|
|
371
|
+
* and reported through `onSelectionChange`) and clear the cursor / editing when they held it;
|
|
372
|
+
* removing the node a pointer gesture started on cancels that gesture. Survivors keep their DOM
|
|
373
|
+
* ids and focus; the z-order closes up. Throws on an unknown or repeated id before anything
|
|
374
|
+
* changes.
|
|
375
|
+
*/
|
|
376
|
+
readonly removeNodes: (ids: readonly string[]) => void;
|
|
377
|
+
}
|
|
378
|
+
export declare const createCanvas: (options: CanvasOptions) => Canvas;
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region src/disclosure.d.ts
|
|
381
|
+
/**
|
|
382
|
+
* Options are read lazily: pass a component's `props` (or an object with
|
|
383
|
+
* getters), never destructured values, so `disabled` stays reactive.
|
|
384
|
+
*/
|
|
385
|
+
interface DisclosureOptions {
|
|
386
|
+
/** Controlled: the host owns the state and this is the only thing that changes it. */
|
|
387
|
+
readonly value?: boolean | undefined;
|
|
388
|
+
/** Uncontrolled starting state; ignored while `value` is set. */
|
|
389
|
+
readonly defaultValue?: boolean;
|
|
390
|
+
readonly disabled?: boolean;
|
|
391
|
+
/** Base for the generated trigger/panel ids. */
|
|
392
|
+
readonly id?: string;
|
|
393
|
+
/**
|
|
394
|
+
* A request to change the state, called when the intent differs from the
|
|
395
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
396
|
+
*/
|
|
397
|
+
readonly onValueChange?: (expanded: boolean) => void;
|
|
398
|
+
}
|
|
399
|
+
/** Spread onto the element with `role=button` (a native `<button>`). */
|
|
400
|
+
interface DisclosureTriggerProps {
|
|
401
|
+
readonly id: string;
|
|
402
|
+
readonly type: "button";
|
|
403
|
+
readonly "aria-expanded": "true" | "false";
|
|
404
|
+
readonly "aria-controls": string;
|
|
405
|
+
readonly disabled: boolean;
|
|
406
|
+
readonly onClick: () => void;
|
|
407
|
+
}
|
|
408
|
+
/** Spread onto the element holding the content that is shown or hidden. */
|
|
409
|
+
interface DisclosurePanelProps {
|
|
410
|
+
readonly id: string;
|
|
411
|
+
readonly hidden: boolean;
|
|
412
|
+
}
|
|
413
|
+
interface Disclosure {
|
|
414
|
+
readonly expanded: Accessor<boolean>;
|
|
415
|
+
/** Requests; `expand`, `collapse` and `toggle` are requests too. */
|
|
416
|
+
readonly setExpanded: (expanded: boolean) => void;
|
|
417
|
+
readonly expand: () => void;
|
|
418
|
+
readonly collapse: () => void;
|
|
419
|
+
readonly toggle: () => void;
|
|
420
|
+
/** Getter-backed, so spreading it keeps every attribute reactive. */
|
|
421
|
+
readonly triggerProps: DisclosureTriggerProps;
|
|
422
|
+
readonly panelProps: DisclosurePanelProps;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* APG Disclosure (https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/):
|
|
426
|
+
* state plus attribute objects for a trigger and a panel. No styling, no
|
|
427
|
+
* elements. Behaviour is checked against `specs/apg/disclosure.qnt` by the
|
|
428
|
+
* scenario bridge, not by running the generated machine here.
|
|
429
|
+
*
|
|
430
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
431
|
+
* pass `value` + `onValueChange` to own the state.
|
|
432
|
+
*/
|
|
433
|
+
export declare const createDisclosure: (options?: DisclosureOptions) => Disclosure;
|
|
434
|
+
//#endregion
|
|
435
|
+
//#region src/player.d.ts
|
|
436
|
+
/**
|
|
437
|
+
* A playback engine the primitive drives. Mediabunny is one; the primitive
|
|
438
|
+
* never imports a media library (`@cssints/headless` stays `solid-js`-only).
|
|
439
|
+
* Event names mirror `HTMLMediaElement`; the primitive mirrors the generated
|
|
440
|
+
* `specs/apg/player.qnt` machine.
|
|
441
|
+
*/
|
|
442
|
+
type PlaybackAdapterEvent = {
|
|
443
|
+
readonly type: "loadedmetadata";
|
|
444
|
+
readonly duration: number;
|
|
445
|
+
} | {
|
|
446
|
+
readonly type: "canplay" | "playing" | "pause" | "waiting" | "seeking" | "seeked" | "ended";
|
|
447
|
+
} | {
|
|
448
|
+
readonly type: "timeupdate";
|
|
449
|
+
readonly currentTime: number;
|
|
450
|
+
} | {
|
|
451
|
+
readonly type: "ratechange";
|
|
452
|
+
readonly rate: number;
|
|
453
|
+
} | {
|
|
454
|
+
readonly type: "error";
|
|
455
|
+
readonly cause: unknown;
|
|
456
|
+
};
|
|
457
|
+
interface PlaybackAdapter {
|
|
458
|
+
/** Open + decode; emits `loadedmetadata` once the duration is known. */
|
|
459
|
+
load(src: string): Promise<void>;
|
|
460
|
+
play(): Promise<void>;
|
|
461
|
+
pause(): void;
|
|
462
|
+
seek(seconds: number): Promise<void>;
|
|
463
|
+
/** 0..1, already muted-resolved by the primitive. */
|
|
464
|
+
setVolume(gain: number): void;
|
|
465
|
+
setPlaybackRate(rate: number): void;
|
|
466
|
+
dispose(): void;
|
|
467
|
+
subscribe(listener: (event: PlaybackAdapterEvent) => void): () => void;
|
|
468
|
+
}
|
|
469
|
+
type PlayerStage = "loading" | "ready" | "playing" | "paused" | "buffering" | "ended" | "error";
|
|
470
|
+
interface PlayerOptions {
|
|
471
|
+
readonly adapter: PlaybackAdapter;
|
|
472
|
+
readonly src?: string;
|
|
473
|
+
/**
|
|
474
|
+
* The four preferences below follow the request/commit model (map ticket
|
|
475
|
+
* `rt1p`): `<name>` + `on<Name>Change` to own one as a host, `default<Name>`
|
|
476
|
+
* to start an uncontrolled one. A change callback is a request the host may
|
|
477
|
+
* ignore. Transport (play / pause / seek) and the stage stay adapter-driven.
|
|
478
|
+
*/
|
|
479
|
+
/** 0..100; slider value, applied as a squared gain. */
|
|
480
|
+
readonly volume?: number | undefined;
|
|
481
|
+
readonly defaultVolume?: number;
|
|
482
|
+
readonly onVolumeChange?: (volume: number) => void;
|
|
483
|
+
readonly muted?: boolean | undefined;
|
|
484
|
+
readonly defaultMuted?: boolean;
|
|
485
|
+
readonly onMutedChange?: (muted: boolean) => void;
|
|
486
|
+
/** Percent: 50 | 100 | 150 | 200. */
|
|
487
|
+
readonly rate?: number | undefined;
|
|
488
|
+
readonly defaultRate?: number;
|
|
489
|
+
readonly onRateChange?: (percent: number) => void;
|
|
490
|
+
readonly fullscreen?: boolean | undefined;
|
|
491
|
+
readonly defaultFullscreen?: boolean;
|
|
492
|
+
readonly onFullscreenChange?: (fullscreen: boolean) => void;
|
|
493
|
+
readonly onPlay?: () => void;
|
|
494
|
+
readonly onPause?: () => void;
|
|
495
|
+
readonly onSeek?: (seconds: number) => void;
|
|
496
|
+
readonly onEnded?: () => void;
|
|
497
|
+
}
|
|
498
|
+
interface PlayerSurfaceProps {
|
|
499
|
+
readonly tabindex: number;
|
|
500
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
501
|
+
readonly onFocus: () => void;
|
|
502
|
+
readonly onBlur: () => void;
|
|
503
|
+
}
|
|
504
|
+
interface PlayerButtonProps {
|
|
505
|
+
readonly type: "button";
|
|
506
|
+
readonly onClick: () => void;
|
|
507
|
+
/** Keep a pointer click from stealing focus, so the spec's `focus` scope holds. */
|
|
508
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
509
|
+
readonly onFocus: () => void;
|
|
510
|
+
readonly onBlur: () => void;
|
|
511
|
+
}
|
|
512
|
+
interface PlayerPlayProps extends PlayerButtonProps {
|
|
513
|
+
readonly "aria-label": "Play" | "Pause";
|
|
514
|
+
}
|
|
515
|
+
interface PlayerToggleProps extends PlayerButtonProps {
|
|
516
|
+
readonly "aria-pressed": "true" | "false";
|
|
517
|
+
}
|
|
518
|
+
interface PlayerSeekProps {
|
|
519
|
+
readonly role: "slider";
|
|
520
|
+
readonly tabindex: number;
|
|
521
|
+
readonly "aria-valuemin": number;
|
|
522
|
+
readonly "aria-valuenow": number;
|
|
523
|
+
readonly "aria-valuemax": number;
|
|
524
|
+
readonly "aria-valuetext": string;
|
|
525
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
526
|
+
readonly onFocus: () => void;
|
|
527
|
+
readonly onBlur: () => void;
|
|
528
|
+
}
|
|
529
|
+
interface Player {
|
|
530
|
+
readonly stage: () => PlayerStage;
|
|
531
|
+
readonly position: () => number;
|
|
532
|
+
readonly duration: () => number;
|
|
533
|
+
readonly muted: () => boolean;
|
|
534
|
+
readonly volume: () => number;
|
|
535
|
+
readonly rate: () => number;
|
|
536
|
+
readonly fullscreen: () => boolean;
|
|
537
|
+
readonly focus: () => "none" | "surface" | "seek" | "control";
|
|
538
|
+
readonly play: () => void;
|
|
539
|
+
readonly pause: () => void;
|
|
540
|
+
readonly togglePlay: () => void;
|
|
541
|
+
readonly seek: (seconds: number) => void;
|
|
542
|
+
readonly setVolume: (value: number) => void;
|
|
543
|
+
readonly toggleMute: () => void;
|
|
544
|
+
readonly setRate: (percent: number) => void;
|
|
545
|
+
readonly toggleFullscreen: () => void;
|
|
546
|
+
readonly surfaceProps: PlayerSurfaceProps;
|
|
547
|
+
readonly playProps: PlayerPlayProps;
|
|
548
|
+
readonly seekProps: PlayerSeekProps;
|
|
549
|
+
readonly muteProps: PlayerToggleProps;
|
|
550
|
+
readonly fullscreenProps: PlayerToggleProps;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* APG media player primitive on Solid 2: playback state + attribute objects.
|
|
554
|
+
* The playback contract follows `specs/apg/player.qnt` (APG Slider seek +
|
|
555
|
+
* HTMLMediaElement playback); the adapter is injected, so no media library and
|
|
556
|
+
* no elements live here.
|
|
557
|
+
*/
|
|
558
|
+
export declare const createPlayer: (options: PlayerOptions) => Player;
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/accordion.d.ts
|
|
561
|
+
/**
|
|
562
|
+
* Options are read lazily (getters) so `multiple` / `disabled` stay reactive;
|
|
563
|
+
* pass a component's `props`, never destructured values.
|
|
564
|
+
*/
|
|
565
|
+
export interface AccordionOptions {
|
|
566
|
+
/** Number of panels (the docs' fixed three unless a page renders fewer). */
|
|
567
|
+
readonly items?: number;
|
|
568
|
+
/** Controlled: the host owns the expanded set and this is the only thing that changes it. */
|
|
569
|
+
readonly value?: ReadonlyArray<number> | undefined;
|
|
570
|
+
/** Uncontrolled starting expanded set; ignored while `value` is set. */
|
|
571
|
+
readonly defaultValue?: ReadonlyArray<number>;
|
|
572
|
+
/** Allow more than one panel expanded at once. */
|
|
573
|
+
readonly multiple?: boolean;
|
|
574
|
+
/** Allow the last expanded panel to collapse (default true). */
|
|
575
|
+
readonly collapsible?: boolean;
|
|
576
|
+
/** `aria-level` of the heading wrapping each trigger (default 3). */
|
|
577
|
+
readonly headingLevel?: number;
|
|
578
|
+
/** Disable every trigger. */
|
|
579
|
+
readonly disabled?: boolean;
|
|
580
|
+
/** Disable specific triggers by index. */
|
|
581
|
+
readonly disabledItems?: ReadonlyArray<number>;
|
|
582
|
+
/** Base for the generated ids. */
|
|
583
|
+
readonly id?: string;
|
|
584
|
+
/**
|
|
585
|
+
* A request to change the expanded set, called when the intent differs from
|
|
586
|
+
* the current value. Uncontrolled it is accepted; controlled the host may
|
|
587
|
+
* ignore it.
|
|
588
|
+
*/
|
|
589
|
+
readonly onValueChange?: (expanded: ReadonlyArray<number>) => void;
|
|
590
|
+
}
|
|
591
|
+
/** Spread onto the element with `role=button` (a native `<button>`). */
|
|
592
|
+
export interface AccordionTriggerProps {
|
|
593
|
+
readonly id: string;
|
|
594
|
+
readonly type: "button";
|
|
595
|
+
readonly "aria-expanded": "true" | "false";
|
|
596
|
+
readonly "aria-controls": string;
|
|
597
|
+
readonly "aria-disabled": "true" | undefined;
|
|
598
|
+
readonly disabled: boolean;
|
|
599
|
+
readonly onClick: () => void;
|
|
600
|
+
}
|
|
601
|
+
/** Spread onto the element with `role=region` holding the panel. */
|
|
602
|
+
export interface AccordionPanelProps {
|
|
603
|
+
readonly id: string;
|
|
604
|
+
readonly role: "region";
|
|
605
|
+
readonly "aria-labelledby": string;
|
|
606
|
+
readonly tabindex: 0;
|
|
607
|
+
readonly hidden: boolean;
|
|
608
|
+
}
|
|
609
|
+
/** Spread onto the heading element wrapping a trigger. */
|
|
610
|
+
export interface AccordionHeadingProps {
|
|
611
|
+
readonly id: string;
|
|
612
|
+
readonly role: "heading";
|
|
613
|
+
readonly "aria-level": number;
|
|
614
|
+
}
|
|
615
|
+
export interface Accordion {
|
|
616
|
+
readonly items: number;
|
|
617
|
+
readonly expanded: Accessor<ReadonlyArray<boolean>>;
|
|
618
|
+
readonly isExpanded: (index: number) => boolean;
|
|
619
|
+
/** Requests a toggle of one panel; refused panels leave state untouched. */
|
|
620
|
+
readonly toggle: (index: number) => void;
|
|
621
|
+
readonly setMultiple: (value: boolean) => void;
|
|
622
|
+
readonly setCollapsible: (value: boolean) => void;
|
|
623
|
+
readonly setHeadingLevel: (level: number) => void;
|
|
624
|
+
/** Getter-backed, so spreading keeps every attribute reactive. */
|
|
625
|
+
readonly triggerProps: (index: number) => AccordionTriggerProps;
|
|
626
|
+
readonly panelProps: (index: number) => AccordionPanelProps;
|
|
627
|
+
readonly headingProps: (index: number) => AccordionHeadingProps;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* APG Accordion (https://www.w3.org/WAI/ARIA/apg/patterns/accordion/): state
|
|
631
|
+
* plus prop objects for headings, triggers and panels. No styling, no elements.
|
|
632
|
+
* Behaviour is checked against `specs/apg/accordion.qnt` by the scenario bridge,
|
|
633
|
+
* not by running the generated machine here.
|
|
634
|
+
*
|
|
635
|
+
* Platform: **native** (`<details name>` / `:open`, Baseline newly) — this
|
|
636
|
+
* primitive only wires the markup and ARIA; exclusivity is the platform's.
|
|
637
|
+
*
|
|
638
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
639
|
+
* pass `value` + `onValueChange` to own the expanded set. A controlled host
|
|
640
|
+
* owns the set verbatim, so `setMultiple` / `setCollapsible` only re-normalize
|
|
641
|
+
* while uncontrolled.
|
|
642
|
+
*/
|
|
643
|
+
export declare const createAccordion: (options?: AccordionOptions) => Accordion;
|
|
644
|
+
//#endregion
|
|
645
|
+
//#region src/alert.d.ts
|
|
646
|
+
export interface AlertOptions {
|
|
647
|
+
/** Rendered at mount; `undefined` starts with no live region. */
|
|
648
|
+
readonly message?: string | undefined;
|
|
649
|
+
/** The user closed the alert; the host may unmount it. */
|
|
650
|
+
readonly onDismiss?: () => void;
|
|
651
|
+
}
|
|
652
|
+
/** Spread onto the `role=alert` live region. */
|
|
653
|
+
export interface AlertProps {
|
|
654
|
+
readonly role: "alert" | undefined;
|
|
655
|
+
readonly "aria-atomic": "true" | undefined;
|
|
656
|
+
readonly hidden: boolean;
|
|
657
|
+
}
|
|
658
|
+
export interface AlertDismissProps {
|
|
659
|
+
readonly "aria-label": "Close";
|
|
660
|
+
readonly onClick: () => void;
|
|
661
|
+
}
|
|
662
|
+
export interface Alert {
|
|
663
|
+
readonly mounted: Accessor<boolean>;
|
|
664
|
+
readonly message: Accessor<string | undefined>;
|
|
665
|
+
/** Render or replace the message: a fresh message is announced after load. */
|
|
666
|
+
readonly show: (message: string) => void;
|
|
667
|
+
readonly dismiss: () => void;
|
|
668
|
+
readonly alertProps: AlertProps;
|
|
669
|
+
readonly dismissProps: AlertDismissProps;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* APG Alert (https://www.w3.org/WAI/ARIA/apg/patterns/alert/): a brief message
|
|
673
|
+
* in a `role=alert` live region. Platform: native live-region markup
|
|
674
|
+
* (`ariaNotify` for imperative announcements). The machine only decides when the
|
|
675
|
+
* region is live; the primitive keeps the region mounted and unhides it.
|
|
676
|
+
*/
|
|
677
|
+
export declare const createAlert: (options?: AlertOptions) => Alert;
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/alertdialog.d.ts
|
|
680
|
+
export interface AlertDialogOptions {
|
|
681
|
+
/** Controlled: the host owns the open state and this is the only thing that changes it. */
|
|
682
|
+
readonly value?: boolean | undefined;
|
|
683
|
+
/** Uncontrolled starting state; ignored while `value` is set. */
|
|
684
|
+
readonly defaultOpen?: boolean;
|
|
685
|
+
/** Starting textarea revision (0 = empty). */
|
|
686
|
+
readonly defaultNotes?: number;
|
|
687
|
+
readonly id?: string;
|
|
688
|
+
/** A request to change the open state, called when the intent differs. */
|
|
689
|
+
readonly onOpenChange?: (open: boolean) => void;
|
|
690
|
+
}
|
|
691
|
+
export interface AlertDialogTriggerProps {
|
|
692
|
+
readonly id: string;
|
|
693
|
+
readonly "aria-haspopup": "dialog";
|
|
694
|
+
readonly "aria-expanded": "true" | "false";
|
|
695
|
+
readonly "aria-controls": string;
|
|
696
|
+
readonly "aria-disabled": "true" | undefined;
|
|
697
|
+
readonly onClick: () => void;
|
|
698
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
699
|
+
}
|
|
700
|
+
export interface AlertDialogDialogProps {
|
|
701
|
+
readonly id: string;
|
|
702
|
+
readonly role: "alertdialog";
|
|
703
|
+
readonly "aria-modal": "true" | undefined;
|
|
704
|
+
readonly "aria-labelledby": string;
|
|
705
|
+
readonly "aria-describedby": string;
|
|
706
|
+
readonly tabindex: -1;
|
|
707
|
+
readonly hidden: boolean;
|
|
708
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
709
|
+
}
|
|
710
|
+
export interface AlertDialogButtonProps {
|
|
711
|
+
readonly id: string;
|
|
712
|
+
readonly type: "button";
|
|
713
|
+
readonly "aria-disabled": "true" | undefined;
|
|
714
|
+
readonly onClick: () => void;
|
|
715
|
+
}
|
|
716
|
+
export interface AlertDialogBackdropProps {
|
|
717
|
+
readonly id: string;
|
|
718
|
+
readonly hidden: boolean;
|
|
719
|
+
readonly onClick: () => void;
|
|
720
|
+
}
|
|
721
|
+
export interface AlertDialog {
|
|
722
|
+
readonly open: Accessor<boolean>;
|
|
723
|
+
/** Encoded focus target: `layer * 100 + position` (0 = none). */
|
|
724
|
+
readonly focus: Accessor<number>;
|
|
725
|
+
/** The part currently holding focus, in scenario vocabulary. */
|
|
726
|
+
readonly focusPart: Accessor<string | null>;
|
|
727
|
+
readonly notes: Accessor<number>;
|
|
728
|
+
readonly stored: Accessor<number>;
|
|
729
|
+
readonly focusAt: (target: number) => void;
|
|
730
|
+
readonly activate: () => void;
|
|
731
|
+
readonly escape: () => void;
|
|
732
|
+
/**
|
|
733
|
+
* Call before the host writes the controlled `value` directly, so a close
|
|
734
|
+
* that is not the pending confirmation does not discard the notes.
|
|
735
|
+
*/
|
|
736
|
+
readonly prepareHostWrite: () => void;
|
|
737
|
+
readonly tab: () => void;
|
|
738
|
+
readonly shiftTab: () => void;
|
|
739
|
+
readonly typeText: () => void;
|
|
740
|
+
readonly clearText: () => void;
|
|
741
|
+
readonly ctrlS: () => void;
|
|
742
|
+
readonly pointerOutside: () => void;
|
|
743
|
+
readonly triggerProps: AlertDialogTriggerProps;
|
|
744
|
+
readonly dialogProps: AlertDialogDialogProps;
|
|
745
|
+
readonly backdropProps: AlertDialogBackdropProps;
|
|
746
|
+
readonly cancelProps: AlertDialogButtonProps;
|
|
747
|
+
readonly confirmProps: AlertDialogButtonProps;
|
|
748
|
+
readonly saveProps: AlertDialogButtonProps;
|
|
749
|
+
readonly inputProps: {
|
|
750
|
+
readonly id: string;
|
|
751
|
+
readonly "aria-label": string;
|
|
752
|
+
readonly onFocus: () => void;
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
declare const NO_POS = 1;
|
|
756
|
+
declare const YES_POS = 2;
|
|
757
|
+
/**
|
|
758
|
+
* APG Alert Dialog on Solid 2: a Discard trigger, a notes textarea with Save,
|
|
759
|
+
* and an alertdialog with No / Yes. Mirrors `specs/apg/alertdialog.qnt` —
|
|
760
|
+
* `USE_ARIA_DISABLED` is on, so the emptied Discard stays focusable.
|
|
761
|
+
*
|
|
762
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultOpen`);
|
|
763
|
+
* pass `value` + `onOpenChange` to own the open state. `activate` / `escape` are
|
|
764
|
+
* requests; the focus, return target and notes changes follow the committed
|
|
765
|
+
* open state through an effect, so a rejected request leaves them untouched.
|
|
766
|
+
*/
|
|
767
|
+
export declare const createAlertDialog: (options?: AlertDialogOptions) => AlertDialog;
|
|
768
|
+
//#endregion
|
|
769
|
+
//#region src/breadcrumb.d.ts
|
|
770
|
+
export interface BreadcrumbItem {
|
|
771
|
+
readonly label: string;
|
|
772
|
+
readonly href?: string | undefined;
|
|
773
|
+
}
|
|
774
|
+
export interface BreadcrumbOptions {
|
|
775
|
+
readonly items: ReadonlyArray<BreadcrumbItem>;
|
|
776
|
+
/** 1-based index of the current page; defaults to the last item. */
|
|
777
|
+
readonly currentIndex?: number;
|
|
778
|
+
readonly disabled?: boolean;
|
|
779
|
+
/** The navigation landmark label; defaults to "Breadcrumbs". */
|
|
780
|
+
readonly label?: string | undefined;
|
|
781
|
+
readonly labelledby?: string | undefined;
|
|
782
|
+
readonly describedby?: string | undefined;
|
|
783
|
+
readonly onPress?: (index: number) => void;
|
|
784
|
+
}
|
|
785
|
+
export interface BreadcrumbNavProps {
|
|
786
|
+
readonly "aria-label": string | undefined;
|
|
787
|
+
readonly "aria-labelledby": string | undefined;
|
|
788
|
+
readonly "aria-describedby": string | undefined;
|
|
789
|
+
}
|
|
790
|
+
export interface BreadcrumbListProps {
|
|
791
|
+
readonly role: "list";
|
|
792
|
+
}
|
|
793
|
+
export interface BreadcrumbItemProps {
|
|
794
|
+
readonly role: "listitem";
|
|
795
|
+
readonly "aria-current": "page" | undefined;
|
|
796
|
+
readonly "aria-disabled": "true" | undefined;
|
|
797
|
+
}
|
|
798
|
+
export interface BreadcrumbLinkProps {
|
|
799
|
+
readonly href: string | undefined;
|
|
800
|
+
readonly "aria-current": "page" | undefined;
|
|
801
|
+
readonly onClick: () => void;
|
|
802
|
+
}
|
|
803
|
+
export interface Breadcrumb {
|
|
804
|
+
readonly navProps: BreadcrumbNavProps;
|
|
805
|
+
readonly listProps: BreadcrumbListProps;
|
|
806
|
+
readonly itemProps: (index: number) => BreadcrumbItemProps;
|
|
807
|
+
readonly linkProps: (index: number) => BreadcrumbLinkProps;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* APG Breadcrumb (https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/): a trail
|
|
811
|
+
* of links in hierarchical order, the current page LAST and marked
|
|
812
|
+
* `aria-current=page` (on both the item and its link). Platform: native (static
|
|
813
|
+
* markup; the navigation landmark is a UA/AT concern).
|
|
814
|
+
*/
|
|
815
|
+
export declare const createBreadcrumb: (options: BreadcrumbOptions) => Breadcrumb;
|
|
816
|
+
//#endregion
|
|
817
|
+
//#region src/button.d.ts
|
|
818
|
+
export interface ButtonOptions {
|
|
819
|
+
/** A toggle button carries `aria-pressed`; a command button does not. */
|
|
820
|
+
readonly isToggle?: boolean;
|
|
821
|
+
/** Controlled: the host owns the pressed state and this is the only thing that changes it. */
|
|
822
|
+
readonly value?: boolean | undefined;
|
|
823
|
+
/** Uncontrolled starting state for a toggle. */
|
|
824
|
+
readonly defaultValue?: boolean;
|
|
825
|
+
/** aria-disabled: the button stays focusable but never activates. */
|
|
826
|
+
readonly disabled?: boolean;
|
|
827
|
+
/** The activation happened (Enter, Space or pointer). */
|
|
828
|
+
readonly onClick?: () => void;
|
|
829
|
+
/**
|
|
830
|
+
* A request to change the pressed state, called when a toggle activation
|
|
831
|
+
* differs from the current value. Uncontrolled it is accepted; controlled the
|
|
832
|
+
* host may ignore it.
|
|
833
|
+
*/
|
|
834
|
+
readonly onValueChange?: (pressed: boolean) => void;
|
|
835
|
+
}
|
|
836
|
+
/** Spread onto a native `<button>`. */
|
|
837
|
+
export interface ButtonProps {
|
|
838
|
+
readonly type: "button";
|
|
839
|
+
readonly role: "button";
|
|
840
|
+
readonly tabindex: number;
|
|
841
|
+
readonly "aria-pressed": "true" | "false" | undefined;
|
|
842
|
+
readonly "aria-disabled": "true" | undefined;
|
|
843
|
+
readonly onClick: () => void;
|
|
844
|
+
}
|
|
845
|
+
export interface Button {
|
|
846
|
+
readonly pressed: Accessor<boolean>;
|
|
847
|
+
/** Activate: a request the disabled guard may drop. */
|
|
848
|
+
readonly press: () => void;
|
|
849
|
+
/** Programmatic pressed change; a request the host may ignore. */
|
|
850
|
+
readonly setPressed: (pressed: boolean) => void;
|
|
851
|
+
/** Getter-backed, so spreading it keeps every attribute reactive. */
|
|
852
|
+
readonly buttonProps: ButtonProps;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* APG Button (https://www.w3.org/WAI/ARIA/apg/patterns/button/): a command
|
|
856
|
+
* button and (with `isToggle`) a toggle button exposing `aria-pressed`.
|
|
857
|
+
* Platform: native (`<button>` handles Enter/Space, focus and pointer); the
|
|
858
|
+
* primitive only derives the ARIA contract and the toggle state.
|
|
859
|
+
*
|
|
860
|
+
* Request/commit (map ticket `rt1p`): a toggle activation is a request the host
|
|
861
|
+
* may ignore; `value` + `onValueChange` own the state, `defaultValue` is
|
|
862
|
+
* uncontrolled. A command button's activation never carries a value.
|
|
863
|
+
*/
|
|
864
|
+
export declare const createButton: (options?: ButtonOptions) => Button;
|
|
865
|
+
//#endregion
|
|
866
|
+
//#region src/carousel.d.ts
|
|
867
|
+
export type CarouselRole = "region" | "group";
|
|
868
|
+
/**
|
|
869
|
+
* Options are read lazily (getters); pass a component's `props`.
|
|
870
|
+
*/
|
|
871
|
+
export interface CarouselOptions {
|
|
872
|
+
/** Number of slides. */
|
|
873
|
+
readonly items?: number;
|
|
874
|
+
/** Controlled: the host owns the shown slide and this is the only thing that changes it. */
|
|
875
|
+
readonly value?: number | undefined;
|
|
876
|
+
/** Shown slide at mount (uncontrolled). */
|
|
877
|
+
readonly defaultIndex?: number;
|
|
878
|
+
/** Auto-rotation feature; false removes the rotation control. */
|
|
879
|
+
readonly autoRotate?: boolean;
|
|
880
|
+
/** Start rotating (ignored when `reducedMotion`). */
|
|
881
|
+
readonly playing?: boolean;
|
|
882
|
+
/** `prefers-reduced-motion` (or pause-on-load): start paused. */
|
|
883
|
+
readonly reducedMotion?: boolean;
|
|
884
|
+
/** Tabbed slide picker (tablist/tab/tabpanel) instead of plain groups. */
|
|
885
|
+
readonly tabbed?: boolean;
|
|
886
|
+
/** Pattern text: focus inside stops rotation until the control restarts it. */
|
|
887
|
+
readonly stickyFocusPause?: boolean;
|
|
888
|
+
/** Pointer starts over the carousel. */
|
|
889
|
+
readonly hovering?: boolean;
|
|
890
|
+
readonly role?: CarouselRole;
|
|
891
|
+
readonly labelledBy?: string | undefined;
|
|
892
|
+
/** Base for the generated ids. */
|
|
893
|
+
readonly id?: string;
|
|
894
|
+
/**
|
|
895
|
+
* A request to show a slide, called when the intent differs from the current
|
|
896
|
+
* index. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
897
|
+
*/
|
|
898
|
+
readonly onValueChange?: (index: number) => void;
|
|
899
|
+
readonly onRotatingChange?: (rotating: boolean) => void;
|
|
900
|
+
}
|
|
901
|
+
/** Spread onto the rotation (play/pause) control. */
|
|
902
|
+
export interface CarouselRotationProps {
|
|
903
|
+
readonly type: "button";
|
|
904
|
+
readonly id: string;
|
|
905
|
+
readonly "aria-label": string;
|
|
906
|
+
readonly "aria-controls": string;
|
|
907
|
+
readonly hidden: boolean;
|
|
908
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
909
|
+
readonly onClick: () => void;
|
|
910
|
+
readonly onFocus: () => void;
|
|
911
|
+
readonly onBlur: () => void;
|
|
912
|
+
}
|
|
913
|
+
/** Spread onto the previous / next controls. */
|
|
914
|
+
export interface CarouselButtonProps {
|
|
915
|
+
readonly type: "button";
|
|
916
|
+
readonly id: string;
|
|
917
|
+
readonly "aria-controls": string;
|
|
918
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
919
|
+
readonly onClick: () => void;
|
|
920
|
+
readonly onFocus: () => void;
|
|
921
|
+
readonly onBlur: () => void;
|
|
922
|
+
}
|
|
923
|
+
export interface CarouselProps {
|
|
924
|
+
readonly role: CarouselRole;
|
|
925
|
+
readonly "aria-roledescription": "carousel";
|
|
926
|
+
readonly "aria-live": "off" | "polite";
|
|
927
|
+
readonly "aria-labelledby": string | undefined;
|
|
928
|
+
readonly onMouseMove: () => void;
|
|
929
|
+
readonly onMouseLeave: () => void;
|
|
930
|
+
}
|
|
931
|
+
export interface CarouselSlideProps {
|
|
932
|
+
readonly id: string;
|
|
933
|
+
readonly role: "group" | "tabpanel";
|
|
934
|
+
readonly "aria-roledescription": "slide";
|
|
935
|
+
readonly "aria-label": string;
|
|
936
|
+
readonly tabindex: 0 | -1;
|
|
937
|
+
readonly hidden: boolean;
|
|
938
|
+
readonly onFocus: () => void;
|
|
939
|
+
readonly onBlur: () => void;
|
|
940
|
+
}
|
|
941
|
+
export interface CarouselTablistProps {
|
|
942
|
+
readonly role: "tablist";
|
|
943
|
+
}
|
|
944
|
+
export interface CarouselTabProps {
|
|
945
|
+
readonly id: string;
|
|
946
|
+
readonly role: "tab";
|
|
947
|
+
readonly "aria-label": string;
|
|
948
|
+
readonly "aria-selected": "true" | "false";
|
|
949
|
+
readonly "aria-controls": string;
|
|
950
|
+
readonly tabindex: -1 | undefined;
|
|
951
|
+
readonly onClick: () => void;
|
|
952
|
+
readonly onFocus: () => void;
|
|
953
|
+
readonly onBlur: () => void;
|
|
954
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
955
|
+
}
|
|
956
|
+
export interface Carousel {
|
|
957
|
+
readonly items: number;
|
|
958
|
+
readonly current: Accessor<number>;
|
|
959
|
+
readonly rotating: Accessor<boolean>;
|
|
960
|
+
/** The part holding focus (`rotation`, `prev`, `tab:1`, `slide:0`), or null. */
|
|
961
|
+
readonly focused: Accessor<string | null>;
|
|
962
|
+
readonly next: () => void;
|
|
963
|
+
readonly prev: () => void;
|
|
964
|
+
readonly selectSlide: (index: number) => void;
|
|
965
|
+
readonly toggleRotation: () => void;
|
|
966
|
+
/** One auto-rotation interval. */
|
|
967
|
+
readonly tick: () => void;
|
|
968
|
+
readonly carouselProps: CarouselProps;
|
|
969
|
+
readonly rotationProps: CarouselRotationProps;
|
|
970
|
+
readonly prevProps: CarouselButtonProps;
|
|
971
|
+
readonly nextProps: CarouselButtonProps;
|
|
972
|
+
readonly slideProps: (index: number) => CarouselSlideProps;
|
|
973
|
+
readonly tablistProps: CarouselTablistProps;
|
|
974
|
+
readonly tabProps: (index: number) => CarouselTabProps;
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* APG Carousel (https://www.w3.org/WAI/ARIA/apg/patterns/carousel/): state plus
|
|
978
|
+
* prop objects for the rotation/prev/next controls, slides and the optional
|
|
979
|
+
* tabbed picker. Auto-rotation pauses while focus or hover is inside (unless the
|
|
980
|
+
* user explicitly started it); slides are `group` or `tabpanel`. No styling, no
|
|
981
|
+
* elements. Checked against `specs/apg/carousel.qnt` by the scenario bridge.
|
|
982
|
+
*
|
|
983
|
+
* Platform: **hybrid** — CSS scroll-snap (widely) scrolls slides; the library
|
|
984
|
+
* owns rotation control, pause-on-focus/hover, live region and slide semantics.
|
|
985
|
+
*
|
|
986
|
+
* The rotation/prev/next controls suppress the focus a mouse press would give
|
|
987
|
+
* them (APG: activating a control does not move focus); tabs and slides still
|
|
988
|
+
* take focus so the tab sequence can be walked.
|
|
989
|
+
*
|
|
990
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default
|
|
991
|
+
* (`defaultIndex`); pass `value` + `onValueChange` to own the shown slide.
|
|
992
|
+
* `next`, `prev`, `selectSlide` and `tick` are requests too.
|
|
993
|
+
*
|
|
994
|
+
* ponytail: rotation advances only through the exposed `tick`; wire a real timer
|
|
995
|
+
* (and `prefers-reduced-motion`) in the consumer.
|
|
996
|
+
*/
|
|
997
|
+
export declare const createCarousel: (options?: CarouselOptions) => Carousel;
|
|
998
|
+
//#endregion
|
|
999
|
+
//#region src/checkbox.d.ts
|
|
1000
|
+
export type CheckboxState = "true" | "false" | "mixed";
|
|
1001
|
+
export interface CheckboxOptions {
|
|
1002
|
+
/** Controlled: the host owns the value and this is the only thing that changes it. */
|
|
1003
|
+
readonly value?: CheckboxState | undefined;
|
|
1004
|
+
/** Uncontrolled starting value; ignored while `value` is set. */
|
|
1005
|
+
readonly defaultValue?: CheckboxState;
|
|
1006
|
+
/** Tri-state: the state may be `mixed`. */
|
|
1007
|
+
readonly allowMixed?: boolean;
|
|
1008
|
+
readonly disabled?: boolean;
|
|
1009
|
+
readonly readOnly?: boolean;
|
|
1010
|
+
readonly id?: string;
|
|
1011
|
+
/**
|
|
1012
|
+
* A request to change the value, called when the intent differs from the
|
|
1013
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
1014
|
+
*/
|
|
1015
|
+
readonly onValueChange?: (state: CheckboxState) => void;
|
|
1016
|
+
}
|
|
1017
|
+
/** Spread onto the focusable element with `role=checkbox`. */
|
|
1018
|
+
export interface CheckboxProps {
|
|
1019
|
+
readonly role: "checkbox";
|
|
1020
|
+
readonly "aria-checked": CheckboxState;
|
|
1021
|
+
readonly "aria-disabled": "true" | undefined;
|
|
1022
|
+
readonly "aria-readonly": "true" | undefined;
|
|
1023
|
+
readonly tabindex: 0;
|
|
1024
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1025
|
+
readonly onKeyUp: (event: KeyboardEvent) => void;
|
|
1026
|
+
readonly onClick: () => void;
|
|
1027
|
+
readonly onFocus: () => void;
|
|
1028
|
+
readonly onBlur: () => void;
|
|
1029
|
+
}
|
|
1030
|
+
export interface Checkbox {
|
|
1031
|
+
/** `aria-checked` value; `mixed` only while `allowMixed`. */
|
|
1032
|
+
readonly checked: Accessor<CheckboxState>;
|
|
1033
|
+
readonly allowMixed: Accessor<boolean>;
|
|
1034
|
+
readonly disabled: Accessor<boolean>;
|
|
1035
|
+
readonly readOnly: Accessor<boolean>;
|
|
1036
|
+
readonly focused: Accessor<boolean>;
|
|
1037
|
+
/** Programmatic state change; a request the host may ignore. */
|
|
1038
|
+
readonly setChecked: (state: CheckboxState) => void;
|
|
1039
|
+
readonly setAllowMixed: (value: boolean) => void;
|
|
1040
|
+
readonly setDisabled: (value: boolean) => void;
|
|
1041
|
+
readonly toggle: () => void;
|
|
1042
|
+
readonly checkboxProps: CheckboxProps;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* APG Checkbox (https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/): one
|
|
1046
|
+
* dual-state, optionally tri-state, checkbox. Platform choice (docs/research/
|
|
1047
|
+
* apg-platform-features.md): **native** `<input type=checkbox>` (+ the
|
|
1048
|
+
* `indeterminate` JS property); this primitive exposes the polyfillable
|
|
1049
|
+
* `role=checkbox` + `aria-checked` contract so the same state drives either
|
|
1050
|
+
* element. Space arms on keydown and fires on keyup; Enter is a no-op.
|
|
1051
|
+
*
|
|
1052
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
1053
|
+
* pass `value` + `onValueChange` to own the state.
|
|
1054
|
+
*/
|
|
1055
|
+
export declare const createCheckbox: (options?: CheckboxOptions) => Checkbox;
|
|
1056
|
+
//#endregion
|
|
1057
|
+
//#region src/checkbox-group.d.ts
|
|
1058
|
+
export type CheckboxGroupFocus = "none" | "parent" | "c0" | "c1" | "c2";
|
|
1059
|
+
export type ParentChecked = "true" | "false" | "mixed";
|
|
1060
|
+
export interface CheckboxGroupOptions<T = unknown> {
|
|
1061
|
+
/** Exactly three child values; the observable value is the checked subset. */
|
|
1062
|
+
readonly values: ReadonlyArray<T>;
|
|
1063
|
+
/** Controlled: the host owns the checked subset and this is the only thing that changes it. */
|
|
1064
|
+
readonly value?: ReadonlyArray<T> | undefined;
|
|
1065
|
+
/** Uncontrolled starting checked subset; ignored while `value` is set. */
|
|
1066
|
+
readonly defaultValue?: ReadonlyArray<T>;
|
|
1067
|
+
/** Controller participates in focus order (its `aria-checked` is always derived). */
|
|
1068
|
+
readonly parent?: boolean;
|
|
1069
|
+
/** Remember the last mixed combination for the controller's third activation. */
|
|
1070
|
+
readonly remember?: boolean;
|
|
1071
|
+
/**
|
|
1072
|
+
* A request to change the checked subset, called when the intent differs from
|
|
1073
|
+
* the current value. Uncontrolled it is accepted; controlled the host may
|
|
1074
|
+
* ignore it.
|
|
1075
|
+
*/
|
|
1076
|
+
readonly onValueChange?: (checked: ReadonlyArray<T>) => void;
|
|
1077
|
+
readonly onFocus?: (target: string) => void;
|
|
1078
|
+
readonly onBlur?: () => void;
|
|
1079
|
+
}
|
|
1080
|
+
/** Spread onto the controller element with `role=checkbox`. */
|
|
1081
|
+
export interface CheckboxGroupParentProps {
|
|
1082
|
+
readonly role: "checkbox";
|
|
1083
|
+
readonly "aria-checked": ParentChecked;
|
|
1084
|
+
readonly tabindex: 0;
|
|
1085
|
+
readonly onClick: () => void;
|
|
1086
|
+
readonly onFocus: () => void;
|
|
1087
|
+
}
|
|
1088
|
+
/** Spread onto a child element with `role=checkbox`. */
|
|
1089
|
+
export interface CheckboxGroupChildProps {
|
|
1090
|
+
readonly id: string;
|
|
1091
|
+
readonly role: "checkbox";
|
|
1092
|
+
readonly "aria-checked": "true" | "false";
|
|
1093
|
+
readonly tabindex: 0;
|
|
1094
|
+
readonly onClick: () => void;
|
|
1095
|
+
readonly onFocus: () => void;
|
|
1096
|
+
}
|
|
1097
|
+
export interface CheckboxGroupProps {
|
|
1098
|
+
readonly role: "group";
|
|
1099
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1100
|
+
}
|
|
1101
|
+
export interface CheckboxGroup<T = unknown> {
|
|
1102
|
+
readonly children: Accessor<ReadonlyArray<boolean>>;
|
|
1103
|
+
readonly memory: Accessor<ReadonlyArray<boolean>>;
|
|
1104
|
+
readonly remember: Accessor<boolean>;
|
|
1105
|
+
readonly focused: Accessor<CheckboxGroupFocus>;
|
|
1106
|
+
readonly parentChecked: Accessor<ParentChecked>;
|
|
1107
|
+
readonly checkedValues: () => ReadonlyArray<T>;
|
|
1108
|
+
readonly click: (target: CheckboxGroupFocus) => void;
|
|
1109
|
+
readonly keySpace: () => void;
|
|
1110
|
+
readonly keyTab: () => void;
|
|
1111
|
+
readonly focusOn: (target: CheckboxGroupFocus) => void;
|
|
1112
|
+
readonly blur: () => void;
|
|
1113
|
+
readonly setRemember: (value: boolean) => void;
|
|
1114
|
+
readonly setChildren: (children: ReadonlyArray<boolean>) => void;
|
|
1115
|
+
readonly groupProps: CheckboxGroupProps;
|
|
1116
|
+
readonly parentProps: CheckboxGroupParentProps;
|
|
1117
|
+
readonly childProps: (index: 0 | 1 | 2) => CheckboxGroupChildProps;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* APG Checkbox pattern's tri-state group controller
|
|
1121
|
+
* (https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/): a controller whose
|
|
1122
|
+
* `aria-checked` is derived from three children (all/none/mixed), plus the
|
|
1123
|
+
* optional "remember the last mixed combination" cycle. Platform choice
|
|
1124
|
+
* (docs/research/apg-platform-features.md): **native** checkboxes; the
|
|
1125
|
+
* controller is a third checkbox with `aria-checked=mixed`.
|
|
1126
|
+
*
|
|
1127
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
1128
|
+
* pass `value` + `onValueChange` to own the checked subset. The checked set is
|
|
1129
|
+
* packed internally so it rides the scalar `createControllable`.
|
|
1130
|
+
*
|
|
1131
|
+
* Behaviour is checked against `specs/apg/checkbox-group.qnt` by the bridge.
|
|
1132
|
+
*/
|
|
1133
|
+
export declare const createCheckboxGroup: <T = unknown>(options: CheckboxGroupOptions<T>) => CheckboxGroup<T>;
|
|
1134
|
+
//#endregion
|
|
1135
|
+
//#region src/combobox.d.ts
|
|
1136
|
+
/**
|
|
1137
|
+
* APG combobox (https://www.w3.org/WAI/ARIA/apg/patterns/combobox/): state plus
|
|
1138
|
+
* getter-backed attribute objects for a listbox popup. No elements, no styling.
|
|
1139
|
+
*
|
|
1140
|
+
* The behaviour mirrors `specs/apg/combobox-select-only.qnt`: DOM focus never
|
|
1141
|
+
* leaves the combobox, the popup's active option is exposed through
|
|
1142
|
+
* `aria-activedescendant`, and navigating never commits — Enter / Space / Tab /
|
|
1143
|
+
* blur / Alt+ArrowUp / an option click / `setValue` do. `filter: true` switches
|
|
1144
|
+
* typing from typeahead to a prefix filter (the autocomplete variant).
|
|
1145
|
+
*
|
|
1146
|
+
* Request/commit (map ticket `rt1p`): both the selected value and the popup are
|
|
1147
|
+
* controllable. Uncontrolled by default (`defaultSelectedValue` / `defaultOpen`);
|
|
1148
|
+
* pass `value` / `open` (+ `onValueChange` / `onOpenChange`) to own them. Every
|
|
1149
|
+
* user change is a request the host may ignore; `setValue` / `setOpen` are
|
|
1150
|
+
* programmatic requests too.
|
|
1151
|
+
*
|
|
1152
|
+
* ponytail: `activeIndex` is an index into `options()` (the filtered list), not
|
|
1153
|
+
* the full corpus.
|
|
1154
|
+
*/
|
|
1155
|
+
export type ComboboxAutoComplete = "none" | "list" | "both";
|
|
1156
|
+
export interface ComboboxOption {
|
|
1157
|
+
readonly value: string;
|
|
1158
|
+
readonly label: string;
|
|
1159
|
+
}
|
|
1160
|
+
export interface ComboboxOptionState {
|
|
1161
|
+
readonly value: string;
|
|
1162
|
+
readonly label: string;
|
|
1163
|
+
readonly selected: boolean;
|
|
1164
|
+
readonly id: string;
|
|
1165
|
+
readonly props: {
|
|
1166
|
+
readonly id: string;
|
|
1167
|
+
readonly role: "option";
|
|
1168
|
+
readonly "aria-selected": "true" | "false";
|
|
1169
|
+
readonly onClick: () => void;
|
|
1170
|
+
/** Keep a pointer click from stealing DOM focus away from the combobox. */
|
|
1171
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
export interface ComboboxInputProps {
|
|
1175
|
+
readonly id: string;
|
|
1176
|
+
readonly role: "combobox";
|
|
1177
|
+
readonly value: string;
|
|
1178
|
+
readonly "aria-autocomplete": ComboboxAutoComplete;
|
|
1179
|
+
readonly "aria-haspopup": "listbox";
|
|
1180
|
+
readonly "aria-expanded": "true" | "false";
|
|
1181
|
+
readonly "aria-controls": string | undefined;
|
|
1182
|
+
readonly "aria-activedescendant": string | undefined;
|
|
1183
|
+
readonly onInput: (event: InputEvent) => void;
|
|
1184
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1185
|
+
readonly onFocus: () => void;
|
|
1186
|
+
readonly onBlur: () => void;
|
|
1187
|
+
readonly onClick: () => void;
|
|
1188
|
+
}
|
|
1189
|
+
export interface ComboboxTriggerProps {
|
|
1190
|
+
readonly id: string;
|
|
1191
|
+
readonly type: "button";
|
|
1192
|
+
readonly tabindex: -1;
|
|
1193
|
+
readonly "aria-haspopup": "listbox";
|
|
1194
|
+
readonly "aria-expanded": "true" | "false";
|
|
1195
|
+
readonly "aria-controls": string | undefined;
|
|
1196
|
+
readonly onClick: () => void;
|
|
1197
|
+
/** Keep a pointer click from stealing focus, so DOM focus stays on the combobox. */
|
|
1198
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
1199
|
+
}
|
|
1200
|
+
export interface ComboboxListboxProps {
|
|
1201
|
+
readonly id: string;
|
|
1202
|
+
readonly role: "listbox";
|
|
1203
|
+
readonly hidden: boolean;
|
|
1204
|
+
}
|
|
1205
|
+
export interface Combobox {
|
|
1206
|
+
readonly open: Accessor<boolean>;
|
|
1207
|
+
/** Index into `options()`; `-1` = visual focus on the combobox. */
|
|
1208
|
+
readonly activeIndex: Accessor<number>;
|
|
1209
|
+
readonly value: Accessor<string | null>;
|
|
1210
|
+
readonly focused: Accessor<boolean>;
|
|
1211
|
+
/** The typeahead / filter text (empty until the user types). */
|
|
1212
|
+
readonly query: Accessor<string>;
|
|
1213
|
+
readonly options: Accessor<ReadonlyArray<ComboboxOptionState>>;
|
|
1214
|
+
readonly setOpen: (open: boolean) => void;
|
|
1215
|
+
readonly setValue: (value: string | null) => void;
|
|
1216
|
+
readonly comboboxProps: ComboboxInputProps;
|
|
1217
|
+
readonly triggerProps: ComboboxTriggerProps;
|
|
1218
|
+
readonly listboxProps: ComboboxListboxProps;
|
|
1219
|
+
}
|
|
1220
|
+
export interface ComboboxOptions {
|
|
1221
|
+
readonly options: ReadonlyArray<ComboboxOption | string>;
|
|
1222
|
+
/** Controlled: the host owns the selection and this is the only thing that changes it. */
|
|
1223
|
+
readonly value?: string | null | undefined;
|
|
1224
|
+
/** Uncontrolled starting selection; ignored while `value` is set. */
|
|
1225
|
+
readonly defaultSelectedValue?: string | null;
|
|
1226
|
+
/** Uncontrolled starting selection (alias for `defaultSelectedValue`). */
|
|
1227
|
+
readonly defaultValue?: string | null;
|
|
1228
|
+
/** Controlled: the host owns the popup and this is the only thing that opens/closes it. */
|
|
1229
|
+
readonly open?: boolean | undefined;
|
|
1230
|
+
/** Uncontrolled starting popup state; ignored while `open` is set. */
|
|
1231
|
+
readonly defaultOpen?: boolean;
|
|
1232
|
+
readonly autoComplete?: ComboboxAutoComplete;
|
|
1233
|
+
/** `true` = prefix filtering while typing; `false` = single-letter typeahead. */
|
|
1234
|
+
readonly filter?: boolean;
|
|
1235
|
+
readonly placeholder?: string;
|
|
1236
|
+
readonly id?: string;
|
|
1237
|
+
/**
|
|
1238
|
+
* A request to change the popup, called when the intent differs from the
|
|
1239
|
+
* current value; uncontrolled it is accepted, controlled the host may ignore it.
|
|
1240
|
+
*/
|
|
1241
|
+
readonly onOpenChange?: (open: boolean) => void;
|
|
1242
|
+
/**
|
|
1243
|
+
* A request to change the selection, called when the intent differs from the
|
|
1244
|
+
* current value; uncontrolled it is accepted, controlled the host may ignore it.
|
|
1245
|
+
*/
|
|
1246
|
+
readonly onValueChange?: (value: string | null) => void;
|
|
1247
|
+
/**
|
|
1248
|
+
* A host request to move focus off the widget. The primitive has already set
|
|
1249
|
+
* its own `focused` false; this only lets a DOM host release native focus (the
|
|
1250
|
+
* default host blurs `document.activeElement`). A non-DOM target needs nothing,
|
|
1251
|
+
* so the primitive never reads the DOM itself.
|
|
1252
|
+
*/
|
|
1253
|
+
readonly onFocusRelease?: () => void;
|
|
1254
|
+
}
|
|
1255
|
+
export declare const createCombobox: (options: ComboboxOptions) => Combobox;
|
|
1256
|
+
//#endregion
|
|
1257
|
+
//#region src/dialog-modal.d.ts
|
|
1258
|
+
export type DialogRole = "dialog" | "alertdialog";
|
|
1259
|
+
export interface DialogModalOptions {
|
|
1260
|
+
/** Controlled: the host owns the open state and this is the only thing that changes it. */
|
|
1261
|
+
readonly value?: boolean | undefined;
|
|
1262
|
+
/** Uncontrolled starting state; ignored while `value` is set. */
|
|
1263
|
+
readonly defaultOpen?: boolean;
|
|
1264
|
+
readonly modal?: boolean;
|
|
1265
|
+
readonly role?: DialogRole;
|
|
1266
|
+
/** Id of the element naming the dialog. */
|
|
1267
|
+
readonly labelledby?: string | undefined;
|
|
1268
|
+
/** Id of the element describing the dialog (required for alertdialog). */
|
|
1269
|
+
readonly describedby?: string | undefined;
|
|
1270
|
+
/** A click on the backdrop / outside closes the dialog (`isDismissable`). */
|
|
1271
|
+
readonly dismissable?: boolean;
|
|
1272
|
+
/** Escape cannot close the dialog (`isKeyboardDismissDisabled`). */
|
|
1273
|
+
readonly keyboardDismissDisabled?: boolean;
|
|
1274
|
+
readonly id?: string;
|
|
1275
|
+
/** A request to change the open state, called when the intent differs. */
|
|
1276
|
+
readonly onOpenChange?: (open: boolean) => void;
|
|
1277
|
+
}
|
|
1278
|
+
export interface DialogTriggerProps {
|
|
1279
|
+
readonly id: string;
|
|
1280
|
+
readonly "aria-haspopup": "dialog";
|
|
1281
|
+
readonly "aria-expanded": "true" | "false";
|
|
1282
|
+
readonly "aria-controls": string;
|
|
1283
|
+
readonly onClick: () => void;
|
|
1284
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1285
|
+
}
|
|
1286
|
+
export interface DialogContentProps {
|
|
1287
|
+
readonly id: string;
|
|
1288
|
+
readonly role: DialogRole;
|
|
1289
|
+
readonly "aria-modal": "true" | undefined;
|
|
1290
|
+
readonly "aria-labelledby": string | undefined;
|
|
1291
|
+
readonly "aria-describedby": string | undefined;
|
|
1292
|
+
readonly tabindex: -1;
|
|
1293
|
+
readonly hidden: boolean;
|
|
1294
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1295
|
+
}
|
|
1296
|
+
export interface DialogBackdropProps {
|
|
1297
|
+
readonly id: string;
|
|
1298
|
+
readonly hidden: boolean;
|
|
1299
|
+
readonly onClick: () => void;
|
|
1300
|
+
}
|
|
1301
|
+
export interface DialogModal {
|
|
1302
|
+
readonly open: Accessor<boolean>;
|
|
1303
|
+
readonly openDialog: () => void;
|
|
1304
|
+
readonly close: () => void;
|
|
1305
|
+
readonly triggerProps: DialogTriggerProps;
|
|
1306
|
+
readonly dialogProps: DialogContentProps;
|
|
1307
|
+
readonly backdropProps: DialogBackdropProps;
|
|
1308
|
+
readonly dismissProps: {
|
|
1309
|
+
readonly id: string;
|
|
1310
|
+
readonly type: "button";
|
|
1311
|
+
readonly onClick: () => void;
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* APG Dialog (Modal) on Solid 2. `modal` adds `aria-modal`. Focus moves into
|
|
1316
|
+
* the dialog on open and is the host's to restore on close (the fixture does,
|
|
1317
|
+
* for dismiss / Escape). The full stack/focus-trap machine lives in
|
|
1318
|
+
* `specs/apg/dialog-modal.qnt`; the generated model is the oracle in tests, not
|
|
1319
|
+
* loaded here.
|
|
1320
|
+
*
|
|
1321
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultOpen`);
|
|
1322
|
+
* pass `value` + `onOpenChange` to own the open state. `openDialog` / `close`
|
|
1323
|
+
* are requests the host may ignore.
|
|
1324
|
+
*/
|
|
1325
|
+
export declare const createDialogModal: (options?: DialogModalOptions) => DialogModal;
|
|
1326
|
+
//#endregion
|
|
1327
|
+
//#region src/feed.d.ts
|
|
1328
|
+
/**
|
|
1329
|
+
* Options are read lazily (getters); pass a component's `props`.
|
|
1330
|
+
*/
|
|
1331
|
+
export interface FeedOptions {
|
|
1332
|
+
/** Article slots rendered in the DOM (loaded ones are shown). */
|
|
1333
|
+
readonly items?: number;
|
|
1334
|
+
/** Server total: a number, or null when undetermined. */
|
|
1335
|
+
readonly totalArticles?: number | null;
|
|
1336
|
+
/** Controlled: the host owns the loaded count and this is the only thing that changes it. */
|
|
1337
|
+
readonly value?: number | undefined;
|
|
1338
|
+
/** Uncontrolled starting count; ignored while `value` is set. */
|
|
1339
|
+
readonly defaultValue?: number;
|
|
1340
|
+
readonly sizeMode?: "loaded" | "total";
|
|
1341
|
+
readonly totalKnown?: boolean;
|
|
1342
|
+
readonly labelledBy?: string | undefined;
|
|
1343
|
+
/** Base for the generated ids. */
|
|
1344
|
+
readonly id?: string;
|
|
1345
|
+
/**
|
|
1346
|
+
* A request to change the loaded count, called when an arriving batch differs
|
|
1347
|
+
* from the current value. Uncontrolled it is accepted; controlled the host
|
|
1348
|
+
* may ignore it.
|
|
1349
|
+
*/
|
|
1350
|
+
readonly onValueChange?: (loaded: number) => void;
|
|
1351
|
+
}
|
|
1352
|
+
export interface FeedProps {
|
|
1353
|
+
readonly id: string;
|
|
1354
|
+
readonly role: "feed";
|
|
1355
|
+
readonly "aria-busy": "true" | undefined;
|
|
1356
|
+
readonly "aria-labelledby": string | undefined;
|
|
1357
|
+
}
|
|
1358
|
+
export interface FeedArticleProps {
|
|
1359
|
+
readonly id: string;
|
|
1360
|
+
readonly role: "article";
|
|
1361
|
+
readonly "aria-posinset": number;
|
|
1362
|
+
readonly "aria-setsize": number;
|
|
1363
|
+
readonly "aria-labelledby": string;
|
|
1364
|
+
readonly "aria-describedby": string;
|
|
1365
|
+
readonly tabindex: 0;
|
|
1366
|
+
readonly hidden: boolean;
|
|
1367
|
+
readonly onFocus: () => void;
|
|
1368
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1369
|
+
}
|
|
1370
|
+
/** Spread onto the focusable element just before / after the feed. */
|
|
1371
|
+
export interface FeedSentinelProps {
|
|
1372
|
+
readonly id: string;
|
|
1373
|
+
readonly tabindex: 0;
|
|
1374
|
+
readonly onFocus: () => void;
|
|
1375
|
+
}
|
|
1376
|
+
export type FeedActive = number | "before" | "after";
|
|
1377
|
+
export interface Feed {
|
|
1378
|
+
readonly items: number;
|
|
1379
|
+
readonly loaded: Accessor<number>;
|
|
1380
|
+
readonly busy: Accessor<boolean>;
|
|
1381
|
+
/** Focused article (1-based posinset) or the outside zone. */
|
|
1382
|
+
readonly active: Accessor<FeedActive>;
|
|
1383
|
+
readonly focusArticle: (posinset: number) => void;
|
|
1384
|
+
readonly pageDown: () => void;
|
|
1385
|
+
readonly pageUp: () => void;
|
|
1386
|
+
readonly ctrlEnd: () => void;
|
|
1387
|
+
readonly ctrlHome: () => void;
|
|
1388
|
+
/** Scroll/position asked for more: `aria-busy` goes true while in flight. */
|
|
1389
|
+
readonly loadStart: () => void;
|
|
1390
|
+
/** A batch arrived; a request to grow the loaded count. */
|
|
1391
|
+
readonly loadFinish: (count: number) => void;
|
|
1392
|
+
readonly loadFail: () => void;
|
|
1393
|
+
readonly setSizeMode: (mode: "loaded" | "total") => void;
|
|
1394
|
+
readonly setTotalKnown: (known: boolean) => void;
|
|
1395
|
+
readonly feedProps: FeedProps;
|
|
1396
|
+
readonly articleProps: (posinset: number) => FeedArticleProps;
|
|
1397
|
+
readonly beforeProps: FeedSentinelProps;
|
|
1398
|
+
readonly afterProps: FeedSentinelProps;
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* APG Feed (https://www.w3.org/WAI/ARIA/apg/patterns/feed/): state plus prop
|
|
1402
|
+
* objects for the feed container, its articles and the two outside focus
|
|
1403
|
+
* sentinels. Focus reaching the last loaded article triggers a load; `aria-busy`
|
|
1404
|
+
* brackets it; Page Up/Down move by article; Control+End/Home leave the feed.
|
|
1405
|
+
* No styling, no elements. Checked against `specs/apg/feed.qnt`.
|
|
1406
|
+
*
|
|
1407
|
+
* Platform: **library** — focusgroup V2 `feed` is explainer-only, so the load
|
|
1408
|
+
* trigger, busy bracketing and Page Up/Down / Control+End/Home keys are ours.
|
|
1409
|
+
*
|
|
1410
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
1411
|
+
* pass `value` + `onValueChange` to own the loaded count. `loadFinish` is the
|
|
1412
|
+
* request; `loadStart` / `loadFail` are adapter lifecycle events.
|
|
1413
|
+
*/
|
|
1414
|
+
export declare const createFeed: (options?: FeedOptions) => Feed;
|
|
1415
|
+
//#endregion
|
|
1416
|
+
//#region src/grid.d.ts
|
|
1417
|
+
/**
|
|
1418
|
+
* APG grid primitive on Solid 2: roving focus, 2-D navigation, edit mode, a
|
|
1419
|
+
* cell/row/column selection rectangle and sortable column headers. State plus
|
|
1420
|
+
* getter-backed attribute objects; no elements, no styling.
|
|
1421
|
+
*
|
|
1422
|
+
* Behaviour mirrors `specs/apg/grid.qnt` (navigation/edit), `grid-selection.qnt`
|
|
1423
|
+
* (selection) and `grid-sort.qnt` (sortable headers); the generated machines are
|
|
1424
|
+
* the oracles in tests, not runtime dependencies.
|
|
1425
|
+
*
|
|
1426
|
+
* Request/commit (map ticket `rt1p`): navigation is internal and uncontrolled;
|
|
1427
|
+
* the two user-changeable values are the selection and the sort. Both are
|
|
1428
|
+
* uncontrolled by default (`defaultValue` / `defaultSortValue`) and can be owned
|
|
1429
|
+
* by the host with `value` + `onValueChange` / `sortValue` + `onSortChange`.
|
|
1430
|
+
* Programmatic methods (`clickCell`, `setSelectable`, `setSortEnabled` and the
|
|
1431
|
+
* keyboard/pointer handlers) are requests; a controlled host may ignore them.
|
|
1432
|
+
*/
|
|
1433
|
+
export type GridKind = "data" | "layout";
|
|
1434
|
+
export type GridFocusTarget = "cell" | "widget";
|
|
1435
|
+
export type GridSortDirection = "none" | "ascending" | "descending";
|
|
1436
|
+
/** A mounted row range: rows `[start, start + count)` are rendered. */
|
|
1437
|
+
export interface GridWindow {
|
|
1438
|
+
readonly start: number;
|
|
1439
|
+
readonly count: number;
|
|
1440
|
+
}
|
|
1441
|
+
export interface GridOptions {
|
|
1442
|
+
readonly rows?: number;
|
|
1443
|
+
readonly cols?: number;
|
|
1444
|
+
readonly gridKind?: GridKind;
|
|
1445
|
+
readonly wrapCols?: boolean;
|
|
1446
|
+
readonly wrapRows?: boolean;
|
|
1447
|
+
readonly focusTarget?: GridFocusTarget;
|
|
1448
|
+
readonly gridReadonly?: boolean;
|
|
1449
|
+
/** Adds cell selection (`aria-selected`) and the selection keys. */
|
|
1450
|
+
readonly selectable?: boolean;
|
|
1451
|
+
/** Adds sortable `columnheader`s (`aria-sort`). */
|
|
1452
|
+
readonly sortEnabled?: boolean;
|
|
1453
|
+
/**
|
|
1454
|
+
* Controlled: the host owns the selected rectangle, packed (`0` = nothing
|
|
1455
|
+
* selected; see `packSelection`). This is the only thing that changes it.
|
|
1456
|
+
*/
|
|
1457
|
+
readonly value?: number | undefined;
|
|
1458
|
+
/** Uncontrolled starting packed selection; ignored while `value` is set. */
|
|
1459
|
+
readonly defaultValue?: number;
|
|
1460
|
+
/**
|
|
1461
|
+
* A request to change the selection, called when the intent differs from the
|
|
1462
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
1463
|
+
*/
|
|
1464
|
+
readonly onValueChange?: (value: number) => void;
|
|
1465
|
+
/**
|
|
1466
|
+
* Controlled: the host owns the sort, packed (`-1` = unsorted; see
|
|
1467
|
+
* `packSort`). This is the only thing that changes it.
|
|
1468
|
+
*/
|
|
1469
|
+
readonly sortValue?: number | undefined;
|
|
1470
|
+
/** Uncontrolled starting packed sort; ignored while `sortValue` is set. */
|
|
1471
|
+
readonly defaultSortValue?: number;
|
|
1472
|
+
/**
|
|
1473
|
+
* A request to change the sort, called when the intent differs from the
|
|
1474
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
1475
|
+
*/
|
|
1476
|
+
readonly onSortChange?: (value: number) => void;
|
|
1477
|
+
/** PageUp/PageDown step, in rows (the spec's PAGE constant). */
|
|
1478
|
+
readonly page?: number;
|
|
1479
|
+
/** Base for generated ids. */
|
|
1480
|
+
readonly id?: string;
|
|
1481
|
+
/**
|
|
1482
|
+
* ponytail: virtualization seam only — a windowed row range; add measurement /
|
|
1483
|
+
* overscan when a real scroller drives it. Cell/row indices stay absolute.
|
|
1484
|
+
*/
|
|
1485
|
+
readonly rowWindow?: GridWindow;
|
|
1486
|
+
}
|
|
1487
|
+
export interface GridProps {
|
|
1488
|
+
readonly role: "grid";
|
|
1489
|
+
readonly "aria-rowcount": number;
|
|
1490
|
+
readonly "aria-colcount": number;
|
|
1491
|
+
readonly "aria-readonly": "true" | "false";
|
|
1492
|
+
readonly "aria-activedescendant": string | undefined;
|
|
1493
|
+
readonly onFocusOut: (event: FocusEvent) => void;
|
|
1494
|
+
}
|
|
1495
|
+
export interface GridRowProps {
|
|
1496
|
+
readonly role: "row";
|
|
1497
|
+
readonly id: string;
|
|
1498
|
+
readonly "aria-rowindex": number;
|
|
1499
|
+
}
|
|
1500
|
+
export interface GridCellProps {
|
|
1501
|
+
readonly role: "gridcell";
|
|
1502
|
+
readonly id: string;
|
|
1503
|
+
readonly tabindex: number;
|
|
1504
|
+
readonly "aria-rowindex": number;
|
|
1505
|
+
readonly "aria-colindex": number;
|
|
1506
|
+
readonly "aria-readonly": "true" | "false";
|
|
1507
|
+
readonly "aria-selected": "true" | "false" | undefined;
|
|
1508
|
+
readonly onClick: () => void;
|
|
1509
|
+
readonly onFocus: () => void;
|
|
1510
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1511
|
+
}
|
|
1512
|
+
/** A focusable widget rendered inside a cell (focusTarget = "widget"). */
|
|
1513
|
+
export interface GridWidgetProps {
|
|
1514
|
+
readonly tabindex: number;
|
|
1515
|
+
readonly onFocus: () => void;
|
|
1516
|
+
}
|
|
1517
|
+
export interface GridHeaderProps {
|
|
1518
|
+
readonly role: "columnheader";
|
|
1519
|
+
readonly id: string;
|
|
1520
|
+
readonly tabindex: number;
|
|
1521
|
+
readonly "aria-sort": "none" | "ascending" | "descending" | undefined;
|
|
1522
|
+
readonly onClick: () => void;
|
|
1523
|
+
readonly onFocus: () => void;
|
|
1524
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1525
|
+
}
|
|
1526
|
+
export interface Grid {
|
|
1527
|
+
readonly rows: Accessor<number>;
|
|
1528
|
+
readonly cols: Accessor<number>;
|
|
1529
|
+
readonly row: Accessor<number>;
|
|
1530
|
+
readonly col: Accessor<number>;
|
|
1531
|
+
readonly hasFocus: Accessor<boolean>;
|
|
1532
|
+
readonly mode: Accessor<"navigate" | "edit">;
|
|
1533
|
+
readonly gridKind: Accessor<GridKind>;
|
|
1534
|
+
readonly wrapCols: Accessor<boolean>;
|
|
1535
|
+
readonly wrapRows: Accessor<boolean>;
|
|
1536
|
+
readonly focusTarget: Accessor<GridFocusTarget>;
|
|
1537
|
+
readonly gridReadonly: Accessor<boolean>;
|
|
1538
|
+
readonly lastEdit: Accessor<"none" | "committed" | "cancelled">;
|
|
1539
|
+
/** The row indices currently mounted (the virtualization window). */
|
|
1540
|
+
readonly renderedRows: Accessor<ReadonlyArray<number>>;
|
|
1541
|
+
readonly isSelected: (row: number, col: number) => boolean;
|
|
1542
|
+
/** The controlled selection value (packed; `0` = nothing selected). */
|
|
1543
|
+
readonly selection: Accessor<number>;
|
|
1544
|
+
readonly sortColumn: Accessor<number>;
|
|
1545
|
+
readonly sortDirection: Accessor<GridSortDirection>;
|
|
1546
|
+
/** The controlled sort value (packed; `-1` = unsorted). */
|
|
1547
|
+
readonly sort: Accessor<number>;
|
|
1548
|
+
/** The `data-part` target the roving tab stop should focus, for fixtures. */
|
|
1549
|
+
readonly focusTargetPart: Accessor<string | null>;
|
|
1550
|
+
readonly gridProps: GridProps;
|
|
1551
|
+
readonly rowProps: (row: number) => GridRowProps;
|
|
1552
|
+
readonly cellProps: (row: number, col: number) => GridCellProps;
|
|
1553
|
+
readonly widgetProps: (row: number, col: number) => GridWidgetProps;
|
|
1554
|
+
readonly headerProps: (col: number) => GridHeaderProps;
|
|
1555
|
+
/** Programmatic enter (spec `focus_in`); keeps the roving tab stop. */
|
|
1556
|
+
readonly focus: () => void;
|
|
1557
|
+
readonly blur: () => void;
|
|
1558
|
+
readonly clickCell: (row: number, col: number) => void;
|
|
1559
|
+
readonly setGridKind: (kind: GridKind) => void;
|
|
1560
|
+
readonly setWrapCols: (flag: boolean) => void;
|
|
1561
|
+
readonly setWrapRows: (flag: boolean) => void;
|
|
1562
|
+
readonly setFocusTarget: (target: GridFocusTarget) => void;
|
|
1563
|
+
readonly setGridReadonly: (flag: boolean) => void;
|
|
1564
|
+
readonly setSelectable: (flag: boolean) => void;
|
|
1565
|
+
readonly setSortEnabled: (flag: boolean) => void;
|
|
1566
|
+
}
|
|
1567
|
+
/** The `data-part` address of a cell/widget/header, shared with the bridge. */
|
|
1568
|
+
export declare const cellPart: (row: number, col: number) => string;
|
|
1569
|
+
export declare const rowPart: (row: number) => string;
|
|
1570
|
+
export declare const headerPart: (col: number) => string;
|
|
1571
|
+
/**
|
|
1572
|
+
* The controlled selection value (map ticket `rt1p`): `0` = nothing selected,
|
|
1573
|
+
* else `1 + r0 + rows*r1 + rows^2*c0 + rows^2*cols*c1` for the active rectangle.
|
|
1574
|
+
* Injective over the ordered rectangles of the bounded grid.
|
|
1575
|
+
*/
|
|
1576
|
+
export declare const packSelection: (rows: number, cols: number, active: boolean, r0: number, r1: number, c0: number, c1: number) => number;
|
|
1577
|
+
/**
|
|
1578
|
+
* The controlled sort value: `-1` = unsorted, else `2*column + (descending ?
|
|
1579
|
+
* 1 : 0)`. Injective for the columns the sort spec encodes.
|
|
1580
|
+
*/
|
|
1581
|
+
export declare const packSort: (column: number, dir: GridSortDirection) => number;
|
|
1582
|
+
/**
|
|
1583
|
+
* Every option is read lazily, so pass props/getters (never a destructured
|
|
1584
|
+
* snapshot); `rows`/`cols`/`page` are captured once.
|
|
1585
|
+
*/
|
|
1586
|
+
export declare const createGrid: (options?: GridOptions) => Grid;
|
|
1587
|
+
//#endregion
|
|
1588
|
+
//#region src/link.d.ts
|
|
1589
|
+
export interface LinkOptions {
|
|
1590
|
+
readonly href?: string | undefined;
|
|
1591
|
+
/** Render a native `<a href>` (the default when `href` is set). */
|
|
1592
|
+
readonly native?: boolean;
|
|
1593
|
+
readonly disabled?: boolean;
|
|
1594
|
+
/** aria-current="page" for the current page in a trail. */
|
|
1595
|
+
readonly current?: boolean;
|
|
1596
|
+
/** The link executed (Enter or pointer activation). */
|
|
1597
|
+
readonly onActivate?: () => void;
|
|
1598
|
+
/** Router integration: the href the activation would navigate to. */
|
|
1599
|
+
readonly onNavigate?: (href: string) => void;
|
|
1600
|
+
}
|
|
1601
|
+
/** Spread onto the `<a>` or the custom `role=link` element. */
|
|
1602
|
+
export interface LinkProps {
|
|
1603
|
+
readonly role: "link" | undefined;
|
|
1604
|
+
readonly tabindex: number | undefined;
|
|
1605
|
+
readonly href: string | undefined;
|
|
1606
|
+
readonly "aria-disabled": "true" | undefined;
|
|
1607
|
+
readonly "aria-current": "page" | undefined;
|
|
1608
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1609
|
+
readonly onClick: (event: MouseEvent) => void;
|
|
1610
|
+
}
|
|
1611
|
+
export interface Link {
|
|
1612
|
+
readonly native: Accessor<boolean>;
|
|
1613
|
+
readonly href: Accessor<string | undefined>;
|
|
1614
|
+
readonly disabled: Accessor<boolean>;
|
|
1615
|
+
/** Getter-backed, so spreading it keeps every attribute reactive. */
|
|
1616
|
+
readonly linkProps: LinkProps;
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* APG Link (https://www.w3.org/WAI/ARIA/apg/patterns/link/): a native `<a href>`
|
|
1620
|
+
* brings focus and activation for free; a custom element takes `role=link` and
|
|
1621
|
+
* `tabindex=0` plus an Enter handler, because "applying the link role will not
|
|
1622
|
+
* cause browsers to enhance the element". Platform: native.
|
|
1623
|
+
*/
|
|
1624
|
+
export declare const createLink: (options?: LinkOptions) => Link;
|
|
1625
|
+
//#endregion
|
|
1626
|
+
//#region src/listbox.d.ts
|
|
1627
|
+
export type ListboxSelectionMode = "single" | "multiple" | "none";
|
|
1628
|
+
export interface ListboxItem {
|
|
1629
|
+
readonly id: string;
|
|
1630
|
+
readonly label: string;
|
|
1631
|
+
/** Text matched by type-ahead (defaults to the label). */
|
|
1632
|
+
readonly textValue: string;
|
|
1633
|
+
readonly disabled: boolean;
|
|
1634
|
+
}
|
|
1635
|
+
export type ListboxItemInput = string | {
|
|
1636
|
+
readonly id?: string;
|
|
1637
|
+
readonly label: string;
|
|
1638
|
+
readonly textValue?: string;
|
|
1639
|
+
readonly disabled?: boolean;
|
|
1640
|
+
};
|
|
1641
|
+
export interface ListboxModifiers {
|
|
1642
|
+
readonly shift?: boolean;
|
|
1643
|
+
readonly ctrl?: boolean;
|
|
1644
|
+
readonly meta?: boolean;
|
|
1645
|
+
readonly alt?: boolean;
|
|
1646
|
+
}
|
|
1647
|
+
export interface ListboxOptions {
|
|
1648
|
+
readonly items: ReadonlyArray<ListboxItemInput>;
|
|
1649
|
+
readonly selectionMode?: ListboxSelectionMode;
|
|
1650
|
+
/** Single-select only: moving the active option moves the selection with it. */
|
|
1651
|
+
readonly followsFocus?: boolean;
|
|
1652
|
+
/** Controlled selection: the host owns it and `value` is the only thing that changes it. */
|
|
1653
|
+
readonly value?: ReadonlyArray<number> | undefined;
|
|
1654
|
+
/** Uncontrolled starting selection; ignored while `value` is set. */
|
|
1655
|
+
readonly defaultValue?: ReadonlyArray<number>;
|
|
1656
|
+
/** Alias for `defaultValue` (kept for the existing API). */
|
|
1657
|
+
readonly defaultSelected?: ReadonlyArray<number>;
|
|
1658
|
+
/** Uncontrolled active option at mount. */
|
|
1659
|
+
readonly defaultActive?: number;
|
|
1660
|
+
/** Controlled active option: the type-ahead focus move's value. */
|
|
1661
|
+
readonly activeValue?: number | undefined;
|
|
1662
|
+
/** Option indices that cannot be selected or reached by navigation. */
|
|
1663
|
+
readonly disabled?: ReadonlyArray<number>;
|
|
1664
|
+
readonly focusOnHover?: boolean;
|
|
1665
|
+
/** Base for the generated option ids. */
|
|
1666
|
+
readonly id?: string;
|
|
1667
|
+
readonly label?: string | undefined;
|
|
1668
|
+
/**
|
|
1669
|
+
* A request to change the selection, called when the intent differs from the
|
|
1670
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
1671
|
+
*/
|
|
1672
|
+
readonly onValueChange?: (selected: ReadonlyArray<number>) => void;
|
|
1673
|
+
/** Alias for `onValueChange` (kept for the existing API). */
|
|
1674
|
+
readonly onSelectionChange?: (selected: ReadonlyArray<number>) => void;
|
|
1675
|
+
/** A request to move the active option (the type-ahead focus move). */
|
|
1676
|
+
readonly onActiveChange?: (index: number) => void;
|
|
1677
|
+
}
|
|
1678
|
+
export interface ListboxProps {
|
|
1679
|
+
readonly role: "listbox";
|
|
1680
|
+
readonly "aria-multiselectable": "true" | "false" | undefined;
|
|
1681
|
+
readonly "aria-activedescendant": string | undefined;
|
|
1682
|
+
readonly "aria-label": string | undefined;
|
|
1683
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1684
|
+
readonly onFocusOut: (event: FocusEvent) => void;
|
|
1685
|
+
}
|
|
1686
|
+
/** Spread onto an element with `role=option`. */
|
|
1687
|
+
export interface ListboxOptionProps {
|
|
1688
|
+
readonly id: string;
|
|
1689
|
+
readonly role: "option";
|
|
1690
|
+
readonly "aria-selected": "true" | "false";
|
|
1691
|
+
readonly "aria-disabled": "true" | undefined;
|
|
1692
|
+
readonly "aria-posinset": number;
|
|
1693
|
+
readonly "aria-setsize": number;
|
|
1694
|
+
readonly tabindex: 0 | -1;
|
|
1695
|
+
readonly onClick: () => void;
|
|
1696
|
+
readonly onFocus: () => void;
|
|
1697
|
+
readonly onMouseEnter: () => void;
|
|
1698
|
+
}
|
|
1699
|
+
export interface Listbox {
|
|
1700
|
+
readonly active: Accessor<number>;
|
|
1701
|
+
readonly selected: Accessor<ReadonlyArray<number>>;
|
|
1702
|
+
readonly focused: Accessor<boolean>;
|
|
1703
|
+
readonly items: ReadonlyArray<ListboxItem>;
|
|
1704
|
+
readonly selectionMode: ListboxSelectionMode;
|
|
1705
|
+
/** Focus entry: the first selected option, else the first option. Returns the entry index. */
|
|
1706
|
+
readonly focus: () => number;
|
|
1707
|
+
readonly blur: () => void;
|
|
1708
|
+
readonly keyDown: (key: string, modifiers?: ListboxModifiers) => void;
|
|
1709
|
+
/** Type-ahead input (one printable char). */
|
|
1710
|
+
readonly type: (text: string) => void;
|
|
1711
|
+
readonly clearTypeahead: () => void;
|
|
1712
|
+
readonly clickOption: (index: number) => void;
|
|
1713
|
+
readonly hoverOption: (index: number) => void;
|
|
1714
|
+
readonly setFollowsFocus: (value: boolean) => void;
|
|
1715
|
+
readonly listboxProps: ListboxProps;
|
|
1716
|
+
readonly optionProps: (index: number) => ListboxOptionProps;
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* APG Listbox on Solid 2: state plus attribute objects, no elements, no
|
|
1720
|
+
* styling. One widget serves the three spec machines the scenarios use —
|
|
1721
|
+
* single-select (with optional selection-follows-focus), multi-select
|
|
1722
|
+
* (recommended model) and type-ahead — and is verified against each generated
|
|
1723
|
+
* model by a differential walk in `test/listbox.browser.test.tsx`.
|
|
1724
|
+
*
|
|
1725
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default
|
|
1726
|
+
* (`defaultValue` / `defaultSelected`); pass `value` + `onValueChange` to own
|
|
1727
|
+
* the selection, or `activeValue` + `onActiveChange` to own the active option
|
|
1728
|
+
* (the type-ahead value). Every user-intent selection change and every focus
|
|
1729
|
+
* move is a request the host may ignore.
|
|
1730
|
+
*/
|
|
1731
|
+
export declare const createListbox: (options: ListboxOptions) => Listbox;
|
|
1732
|
+
//#endregion
|
|
1733
|
+
//#region src/menu.d.ts
|
|
1734
|
+
/**
|
|
1735
|
+
* APG menu family on Solid 2: `createMenu` (menu button + the checkable /
|
|
1736
|
+
* menuitemcheckbox / menuitemradio capability) and `createMenubar` (roving
|
|
1737
|
+
* focus, nested submenus, typeahead). Plain Solid signals, zero deps beyond
|
|
1738
|
+
* `solid-js`; no elements, no styling.
|
|
1739
|
+
*
|
|
1740
|
+
* State is a faithful transcription of `specs/apg/menu-button.qnt`,
|
|
1741
|
+
* `menu-checkable.qnt` and `menu-menubar.qnt` (the generated models are the
|
|
1742
|
+
* differential-test oracle, see `test/menu-button.browser.test.tsx`).
|
|
1743
|
+
*
|
|
1744
|
+
* Platform: **hybrid** (map ticket `etfq`). `popover` gives top layer, light
|
|
1745
|
+
* dismiss and Esc; `focusgroup` gives the menubar's roving tabindex. The
|
|
1746
|
+
* library owns the menu keys, typeahead, submenus and checkable state.
|
|
1747
|
+
*
|
|
1748
|
+
* ponytail: uncontrolled; add a `value`/`onValueChange` host pair when a
|
|
1749
|
+
* consumer owns open/checked state.
|
|
1750
|
+
*/
|
|
1751
|
+
export type MenuItemKind = "item" | "checkbox" | "radio";
|
|
1752
|
+
export interface MenuItemInput {
|
|
1753
|
+
readonly label: string;
|
|
1754
|
+
readonly kind?: MenuItemKind;
|
|
1755
|
+
readonly checked?: boolean;
|
|
1756
|
+
readonly disabled?: boolean;
|
|
1757
|
+
}
|
|
1758
|
+
export type MenuVariant = "plain" | "checkbox-items" | "radio-items" | "mixed";
|
|
1759
|
+
export interface MenuOptions {
|
|
1760
|
+
readonly items: ReadonlyArray<string | MenuItemInput>;
|
|
1761
|
+
readonly variant?: MenuVariant;
|
|
1762
|
+
readonly defaultOpen?: boolean;
|
|
1763
|
+
/**
|
|
1764
|
+
* `outside` (menu button): focus can sit outside the widget, so keys are
|
|
1765
|
+
* inert until the trigger is focused. `held` (checkable menu / menubar item
|
|
1766
|
+
* owner): the trigger always holds focus while closed.
|
|
1767
|
+
*/
|
|
1768
|
+
readonly triggerMode?: "outside" | "held";
|
|
1769
|
+
/** Checkbox item indices checked at mount (`checkbox-items` / `mixed`). */
|
|
1770
|
+
readonly defaultChecked?: ReadonlyArray<number>;
|
|
1771
|
+
/** Radio item index selected at mount (`radio-items` / `mixed`). */
|
|
1772
|
+
readonly defaultChoice?: number;
|
|
1773
|
+
/**
|
|
1774
|
+
* Controlled: the host owns the checked items. Packed as
|
|
1775
|
+
* `checkboxItem0 * 5 + radioChoice` (1-based), matching `menu-checkable.qnt`'s
|
|
1776
|
+
* `checked(wrap, choice)`; pass `undefined` for uncontrolled. The open state
|
|
1777
|
+
* stays uncontrolled.
|
|
1778
|
+
*/
|
|
1779
|
+
readonly value?: number | undefined;
|
|
1780
|
+
/** The uncontrolled packed checked state at mount. */
|
|
1781
|
+
readonly defaultValue?: number;
|
|
1782
|
+
/** A request to change the checked items (a packed `value` the host may ignore). */
|
|
1783
|
+
readonly onCheckedValueChange?: (value: number) => void;
|
|
1784
|
+
/** Item indices disabled (focusable, not activatable). */
|
|
1785
|
+
readonly disabled?: ReadonlyArray<number>;
|
|
1786
|
+
readonly id?: string;
|
|
1787
|
+
readonly onOpenChange?: (open: boolean) => void;
|
|
1788
|
+
readonly onItemClick?: (index: number) => void;
|
|
1789
|
+
readonly onCheckedChange?: (checked: boolean, index: number) => void;
|
|
1790
|
+
readonly onValueChange?: (index: number) => void;
|
|
1791
|
+
/**
|
|
1792
|
+
* Structural mirror of `ing.lexicon.ui.prefs` (`./prefs`). Honoured here:
|
|
1793
|
+
* `typeaheadDelayMs` (the buffer timeout). Absent prefs keep the default.
|
|
1794
|
+
*/
|
|
1795
|
+
readonly prefs?: Partial<Prefs> | undefined;
|
|
1796
|
+
/**
|
|
1797
|
+
* While this returns `true`, a DOM `focus` on the trigger/items is ignored, so
|
|
1798
|
+
* a fixture applying focus programmatically does not write back into the
|
|
1799
|
+
* primitive (bean 209r — the differential walk's focus feedback loop).
|
|
1800
|
+
*/
|
|
1801
|
+
readonly suppressFocusWrite?: (() => boolean) | undefined;
|
|
1802
|
+
}
|
|
1803
|
+
export interface MenuItem {
|
|
1804
|
+
readonly id: string;
|
|
1805
|
+
readonly label: string;
|
|
1806
|
+
readonly kind: MenuItemKind;
|
|
1807
|
+
readonly disabled: boolean;
|
|
1808
|
+
}
|
|
1809
|
+
export interface MenuTriggerProps {
|
|
1810
|
+
readonly id: string;
|
|
1811
|
+
readonly type: "button";
|
|
1812
|
+
readonly "aria-haspopup": "menu";
|
|
1813
|
+
readonly "aria-expanded": "true" | "false";
|
|
1814
|
+
readonly "aria-controls": string;
|
|
1815
|
+
readonly disabled: boolean;
|
|
1816
|
+
readonly onClick: () => void;
|
|
1817
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1818
|
+
readonly onFocus: () => void;
|
|
1819
|
+
}
|
|
1820
|
+
export interface MenuProps {
|
|
1821
|
+
readonly id: string;
|
|
1822
|
+
readonly role: "menu";
|
|
1823
|
+
readonly hidden: boolean;
|
|
1824
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1825
|
+
}
|
|
1826
|
+
export interface MenuItemProps {
|
|
1827
|
+
readonly id: string;
|
|
1828
|
+
readonly role: "menuitem" | "menuitemcheckbox" | "menuitemradio";
|
|
1829
|
+
readonly "aria-checked": "true" | "false" | undefined;
|
|
1830
|
+
readonly "aria-disabled": "true" | undefined;
|
|
1831
|
+
readonly tabindex: 0 | -1;
|
|
1832
|
+
readonly onClick: () => void;
|
|
1833
|
+
readonly onFocus: () => void;
|
|
1834
|
+
readonly onMouseEnter: () => void;
|
|
1835
|
+
}
|
|
1836
|
+
export interface Menu {
|
|
1837
|
+
readonly open: Accessor<boolean>;
|
|
1838
|
+
/** 0 outside, 1 on the trigger, 2 on a menu item. */
|
|
1839
|
+
readonly focusLevel: Accessor<0 | 1 | 2>;
|
|
1840
|
+
readonly focusIndex: Accessor<number>;
|
|
1841
|
+
readonly items: ReadonlyArray<MenuItem>;
|
|
1842
|
+
readonly checked: Accessor<ReadonlyArray<number>>;
|
|
1843
|
+
readonly choice: Accessor<number>;
|
|
1844
|
+
readonly focusTrigger: () => void;
|
|
1845
|
+
readonly leave: () => void;
|
|
1846
|
+
readonly clickTrigger: () => void;
|
|
1847
|
+
readonly openMenu: () => void;
|
|
1848
|
+
readonly closeMenu: () => void;
|
|
1849
|
+
readonly keyDown: (key: string) => void;
|
|
1850
|
+
readonly type: (text: string) => void;
|
|
1851
|
+
/** Differential-test hook: typeahead by the spec's letter code. */
|
|
1852
|
+
readonly typeaheadCode: (code: number) => void;
|
|
1853
|
+
readonly setFocusItem: (index: number) => void;
|
|
1854
|
+
readonly triggerProps: MenuTriggerProps;
|
|
1855
|
+
readonly menuProps: MenuProps;
|
|
1856
|
+
readonly itemProps: (index: number) => MenuItemProps;
|
|
1857
|
+
}
|
|
1858
|
+
export declare const createMenu: (options: MenuOptions) => Menu;
|
|
1859
|
+
export interface MenubarMenuItemInput {
|
|
1860
|
+
readonly label: string;
|
|
1861
|
+
readonly submenu?: ReadonlyArray<MenubarMenuItemInput>;
|
|
1862
|
+
readonly disabled?: boolean;
|
|
1863
|
+
}
|
|
1864
|
+
export interface MenubarItemInput {
|
|
1865
|
+
readonly label: string;
|
|
1866
|
+
readonly menu?: ReadonlyArray<MenubarMenuItemInput>;
|
|
1867
|
+
readonly disabled?: boolean;
|
|
1868
|
+
}
|
|
1869
|
+
export interface MenubarOptions {
|
|
1870
|
+
readonly items: ReadonlyArray<string | MenubarItemInput>;
|
|
1871
|
+
readonly orientation?: "horizontal" | "vertical";
|
|
1872
|
+
readonly wrap?: boolean;
|
|
1873
|
+
readonly id?: string;
|
|
1874
|
+
/**
|
|
1875
|
+
* Structural mirror of `ing.lexicon.ui.prefs` (`./prefs`). Honoured here:
|
|
1876
|
+
* `typeaheadDelayMs` (the buffer timeout). Absent prefs keep the default.
|
|
1877
|
+
*/
|
|
1878
|
+
readonly prefs?: Partial<Prefs> | undefined;
|
|
1879
|
+
/**
|
|
1880
|
+
* While this returns `true`, a DOM `focus` on the trigger/items is ignored, so
|
|
1881
|
+
* a fixture applying focus programmatically does not write back into the
|
|
1882
|
+
* primitive (bean 209r — the differential walk's focus feedback loop).
|
|
1883
|
+
*/
|
|
1884
|
+
readonly suppressFocusWrite?: (() => boolean) | undefined;
|
|
1885
|
+
}
|
|
1886
|
+
export interface MenubarItem {
|
|
1887
|
+
readonly id: string;
|
|
1888
|
+
readonly label: string;
|
|
1889
|
+
readonly menu: ReadonlyArray<MenubarMenuItem>;
|
|
1890
|
+
readonly disabled: boolean;
|
|
1891
|
+
}
|
|
1892
|
+
export interface MenubarMenuItem {
|
|
1893
|
+
readonly id: string;
|
|
1894
|
+
readonly label: string;
|
|
1895
|
+
readonly submenu: ReadonlyArray<MenubarMenuItem>;
|
|
1896
|
+
readonly disabled: boolean;
|
|
1897
|
+
}
|
|
1898
|
+
export interface Menubar {
|
|
1899
|
+
/** 0 outside, 1 menubar, 2 top-level menu, 3 submenu. */
|
|
1900
|
+
readonly focusLevel: Accessor<0 | 1 | 2 | 3>;
|
|
1901
|
+
readonly focusIndex: Accessor<number>;
|
|
1902
|
+
readonly barStop: Accessor<number>;
|
|
1903
|
+
readonly openTop: Accessor<number>;
|
|
1904
|
+
readonly openSub: Accessor<number>;
|
|
1905
|
+
readonly items: ReadonlyArray<MenubarItem>;
|
|
1906
|
+
readonly focusIn: () => void;
|
|
1907
|
+
readonly leave: () => void;
|
|
1908
|
+
readonly keyDown: (key: string) => void;
|
|
1909
|
+
readonly type: (text: string) => void;
|
|
1910
|
+
/** Differential-test hook: typeahead by the spec's letter code. */
|
|
1911
|
+
readonly typeaheadCode: (code: number) => void;
|
|
1912
|
+
readonly menubarProps: {
|
|
1913
|
+
readonly role: "menubar";
|
|
1914
|
+
readonly "aria-orientation": "horizontal" | "vertical";
|
|
1915
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
1916
|
+
};
|
|
1917
|
+
readonly barItemProps: (index: number) => {
|
|
1918
|
+
readonly id: string;
|
|
1919
|
+
readonly role: "menuitem";
|
|
1920
|
+
readonly "aria-haspopup": "menu" | undefined;
|
|
1921
|
+
readonly "aria-expanded": "true" | "false" | undefined;
|
|
1922
|
+
readonly tabindex: 0 | -1;
|
|
1923
|
+
readonly onClick: () => void;
|
|
1924
|
+
readonly onFocus: () => void;
|
|
1925
|
+
};
|
|
1926
|
+
readonly menuProps: (index: number) => {
|
|
1927
|
+
readonly id: string;
|
|
1928
|
+
readonly role: "menu";
|
|
1929
|
+
readonly hidden: boolean;
|
|
1930
|
+
};
|
|
1931
|
+
readonly menuItemProps: (bar: number, item: number) => {
|
|
1932
|
+
readonly id: string;
|
|
1933
|
+
readonly role: "menuitem";
|
|
1934
|
+
readonly "aria-haspopup": "menu" | undefined;
|
|
1935
|
+
readonly "aria-expanded": "true" | "false" | undefined;
|
|
1936
|
+
readonly tabindex: 0 | -1;
|
|
1937
|
+
readonly onClick: () => void;
|
|
1938
|
+
readonly onMouseEnter: () => void;
|
|
1939
|
+
readonly onFocus: () => void;
|
|
1940
|
+
};
|
|
1941
|
+
readonly submenuProps: (bar: number, item: number) => {
|
|
1942
|
+
readonly id: string;
|
|
1943
|
+
readonly role: "menu";
|
|
1944
|
+
readonly hidden: boolean;
|
|
1945
|
+
};
|
|
1946
|
+
readonly submenuItemProps: (bar: number, item: number, sub: number) => {
|
|
1947
|
+
readonly id: string;
|
|
1948
|
+
readonly role: "menuitem";
|
|
1949
|
+
readonly tabindex: 0 | -1;
|
|
1950
|
+
readonly onClick: () => void;
|
|
1951
|
+
readonly onMouseEnter: () => void;
|
|
1952
|
+
readonly onFocus: () => void;
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
export declare const createMenubar: (options: MenubarOptions) => Menubar;
|
|
1956
|
+
//#endregion
|
|
1957
|
+
//#region src/meter.d.ts
|
|
1958
|
+
/**
|
|
1959
|
+
* A controlled numeric display; the consumer feeds `value`/`min`/`max`.
|
|
1960
|
+
* `valueText` overrides the default percentage `aria-valuetext`.
|
|
1961
|
+
*/
|
|
1962
|
+
export interface MeterOptions {
|
|
1963
|
+
readonly value?: number;
|
|
1964
|
+
readonly min?: number;
|
|
1965
|
+
readonly max?: number;
|
|
1966
|
+
readonly valueText?: string | undefined;
|
|
1967
|
+
readonly label?: string | undefined;
|
|
1968
|
+
readonly labelId?: string | undefined;
|
|
1969
|
+
}
|
|
1970
|
+
/** Spread onto the element with `role=meter`. */
|
|
1971
|
+
export interface MeterProps {
|
|
1972
|
+
readonly role: "meter";
|
|
1973
|
+
readonly "aria-valuemin": number;
|
|
1974
|
+
readonly "aria-valuemax": number;
|
|
1975
|
+
readonly "aria-valuenow": number;
|
|
1976
|
+
readonly "aria-valuetext": string;
|
|
1977
|
+
readonly "aria-label": string | undefined;
|
|
1978
|
+
readonly "aria-labelledby": string | undefined;
|
|
1979
|
+
}
|
|
1980
|
+
export interface Meter {
|
|
1981
|
+
readonly min: Accessor<number>;
|
|
1982
|
+
readonly max: Accessor<number>;
|
|
1983
|
+
/** The raw value clamped into `[min, max]`. */
|
|
1984
|
+
readonly value: Accessor<number>;
|
|
1985
|
+
readonly percent: Accessor<number>;
|
|
1986
|
+
/** Getter-backed, so spreading it keeps every attribute reactive. */
|
|
1987
|
+
readonly meterProps: MeterProps;
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* APG Meter (https://www.w3.org/WAI/ARIA/apg/patterns/meter/): a graphical
|
|
1991
|
+
* display of a numeric value within a range. Platform: native (`<meter>`), so
|
|
1992
|
+
* this primitive only derives the ARIA contract; no state machine runs here.
|
|
1993
|
+
*/
|
|
1994
|
+
export declare const createMeter: (options?: MeterOptions) => Meter;
|
|
1995
|
+
//#endregion
|
|
1996
|
+
//#region src/radio.d.ts
|
|
1997
|
+
export type RadioFlavor = "roving" | "activedescendant";
|
|
1998
|
+
export interface RadioGroupOptions<T = unknown> {
|
|
1999
|
+
/** Radio values, one per radio; the observable `value` is the checked one. */
|
|
2000
|
+
readonly values: ReadonlyArray<T>;
|
|
2001
|
+
/** Controlled: the host owns the checked value and this is the only thing that changes it. */
|
|
2002
|
+
readonly value?: T | undefined;
|
|
2003
|
+
/** Uncontrolled starting checked value; ignored while `value` is set. */
|
|
2004
|
+
readonly defaultValue?: T | undefined;
|
|
2005
|
+
/** Roving tabindex (default) or `aria-activedescendant` on the group. */
|
|
2006
|
+
readonly flavor?: RadioFlavor;
|
|
2007
|
+
/** Indices that are never focused, checked or the tab stop. */
|
|
2008
|
+
readonly disabled?: ReadonlyArray<number>;
|
|
2009
|
+
readonly orientation?: "horizontal" | "vertical";
|
|
2010
|
+
readonly dir?: "ltr" | "rtl";
|
|
2011
|
+
readonly id?: string;
|
|
2012
|
+
readonly describedby?: string | undefined;
|
|
2013
|
+
/**
|
|
2014
|
+
* A request to check a radio, called with its value when the intent differs
|
|
2015
|
+
* from the current one. Uncontrolled it is accepted; controlled the host may
|
|
2016
|
+
* ignore it.
|
|
2017
|
+
*/
|
|
2018
|
+
readonly onValueChange?: (value: T) => void;
|
|
2019
|
+
readonly onFocus?: (target: string) => void;
|
|
2020
|
+
readonly onBlur?: (target: string) => void;
|
|
2021
|
+
}
|
|
2022
|
+
/** Spread onto the element with `role=radiogroup`. */
|
|
2023
|
+
export interface RadioGroupProps {
|
|
2024
|
+
readonly role: "radiogroup";
|
|
2025
|
+
readonly "aria-orientation": "horizontal" | "vertical" | undefined;
|
|
2026
|
+
readonly "aria-activedescendant": string | undefined;
|
|
2027
|
+
readonly tabindex: 0 | undefined;
|
|
2028
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2029
|
+
readonly onFocus: () => void;
|
|
2030
|
+
readonly onBlur: (event: FocusEvent) => void;
|
|
2031
|
+
}
|
|
2032
|
+
/** Spread onto an element with `role=radio`. */
|
|
2033
|
+
export interface RadioProps {
|
|
2034
|
+
readonly id: string;
|
|
2035
|
+
readonly role: "radio";
|
|
2036
|
+
readonly "aria-checked": "true" | "false";
|
|
2037
|
+
readonly "aria-disabled": "true" | "false";
|
|
2038
|
+
readonly "aria-describedby": string | undefined;
|
|
2039
|
+
readonly tabindex: 0 | -1 | undefined;
|
|
2040
|
+
readonly onClick: () => void;
|
|
2041
|
+
readonly onFocus: () => void;
|
|
2042
|
+
}
|
|
2043
|
+
export interface RadioGroup<T = unknown> {
|
|
2044
|
+
readonly checked: Accessor<number | null>;
|
|
2045
|
+
readonly focused: Accessor<number | null>;
|
|
2046
|
+
readonly flavor: Accessor<RadioFlavor>;
|
|
2047
|
+
readonly values: ReadonlyArray<T>;
|
|
2048
|
+
readonly value: () => T | null;
|
|
2049
|
+
readonly focus: () => void;
|
|
2050
|
+
readonly blur: () => void;
|
|
2051
|
+
readonly keyDown: (key: string) => void;
|
|
2052
|
+
readonly click: (index: number) => void;
|
|
2053
|
+
readonly setDisabled: (indices: ReadonlyArray<number>) => void;
|
|
2054
|
+
readonly setFlavor: (flavor: RadioFlavor) => void;
|
|
2055
|
+
readonly setPreselect: (index: number | null) => void;
|
|
2056
|
+
readonly radiogroupProps: RadioGroupProps;
|
|
2057
|
+
readonly radioProps: (index: number) => RadioProps;
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* APG Radio Group (https://www.w3.org/WAI/ARIA/apg/patterns/radio/): a
|
|
2061
|
+
* `radiogroup` with `role=radio` items. Platform choice (docs/research/
|
|
2062
|
+
* apg-platform-features.md): **native** `<input type=radio name>`; custom
|
|
2063
|
+
* `role=radio` needs selection-follows-focus, which this primitive implements —
|
|
2064
|
+
* arrows move focus AND check the next/previous enabled radio (wrapping), Space
|
|
2065
|
+
* checks the focused radio, Tab enters on the checked radio.
|
|
2066
|
+
*
|
|
2067
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
2068
|
+
* pass `value` + `onValueChange` to own the check. `click`, `keyDown` and Space
|
|
2069
|
+
* are requests. Behaviour is checked against `specs/apg/radio.qnt` by the
|
|
2070
|
+
* scenario bridge.
|
|
2071
|
+
*
|
|
2072
|
+
* ponytail: the request/commit split is inlined rather than using
|
|
2073
|
+
* `createControllable`, because the config remounts (`setPreselect`,
|
|
2074
|
+
* `setDisabled`) must write the uncontrolled local without firing the request
|
|
2075
|
+
* callback, which `createControllable` does not expose.
|
|
2076
|
+
*/
|
|
2077
|
+
export declare const createRadioGroup: <T = unknown>(options: RadioGroupOptions<T>) => RadioGroup<T>;
|
|
2078
|
+
//#endregion
|
|
2079
|
+
//#region src/slider.d.ts
|
|
2080
|
+
export type SliderOrientation = "horizontal" | "vertical";
|
|
2081
|
+
export type SliderDirection = "ltr" | "rtl";
|
|
2082
|
+
export interface SliderOptions {
|
|
2083
|
+
readonly min?: number;
|
|
2084
|
+
readonly max?: number;
|
|
2085
|
+
readonly step?: number;
|
|
2086
|
+
/** The jump distance of PageUp / PageDown. */
|
|
2087
|
+
readonly largeStep?: number;
|
|
2088
|
+
/** Controlled: the host owns the value and this is the only thing that changes it. */
|
|
2089
|
+
readonly value?: number | undefined;
|
|
2090
|
+
/** Starting value when uncontrolled; ignored while `value` is set. */
|
|
2091
|
+
readonly defaultValue?: number;
|
|
2092
|
+
readonly orientation?: SliderOrientation;
|
|
2093
|
+
readonly direction?: SliderDirection;
|
|
2094
|
+
/** Base for the generated thumb id. */
|
|
2095
|
+
readonly id?: string;
|
|
2096
|
+
/**
|
|
2097
|
+
* A request to change the value, called when the intent differs from the
|
|
2098
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
2099
|
+
*/
|
|
2100
|
+
readonly onValueChange?: (value: number) => void;
|
|
2101
|
+
}
|
|
2102
|
+
export interface SliderThumbProps {
|
|
2103
|
+
readonly id: string;
|
|
2104
|
+
readonly role: "slider";
|
|
2105
|
+
readonly "aria-valuenow": string;
|
|
2106
|
+
readonly "aria-valuemin": string;
|
|
2107
|
+
readonly "aria-valuemax": string;
|
|
2108
|
+
readonly "aria-valuetext": string;
|
|
2109
|
+
readonly "aria-orientation": SliderOrientation;
|
|
2110
|
+
readonly tabindex: number;
|
|
2111
|
+
readonly onFocus: () => void;
|
|
2112
|
+
readonly onBlur: () => void;
|
|
2113
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2114
|
+
}
|
|
2115
|
+
export interface SliderTrackProps {
|
|
2116
|
+
readonly onPointerDown: (event: PointerEvent) => void;
|
|
2117
|
+
}
|
|
2118
|
+
export interface Slider {
|
|
2119
|
+
readonly value: Accessor<number>;
|
|
2120
|
+
readonly focused: Accessor<boolean>;
|
|
2121
|
+
/** Requests; `setValue` and a rail press are requests too. */
|
|
2122
|
+
readonly setValue: (value: number) => void;
|
|
2123
|
+
readonly focus: () => void;
|
|
2124
|
+
readonly blur: () => void;
|
|
2125
|
+
/** Returns whether the key moved the value (the caller preventDefaults it). */
|
|
2126
|
+
readonly keyDown: (key: string) => boolean;
|
|
2127
|
+
/** A rail press at `position` (a 0..1 fraction of the track length). */
|
|
2128
|
+
readonly pointerDown: (position: number) => void;
|
|
2129
|
+
readonly thumbProps: SliderThumbProps;
|
|
2130
|
+
readonly trackProps: SliderTrackProps;
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* APG Slider (https://www.w3.org/WAI/ARIA/apg/patterns/slider/): a single
|
|
2134
|
+
* `role=slider` thumb on a rail. Plain Solid signals, no elements, no styling;
|
|
2135
|
+
* verified against `specs/apg/slider.qnt` by the differential walk in
|
|
2136
|
+
* `test/slider.browser.test.tsx`.
|
|
2137
|
+
*
|
|
2138
|
+
* Native / polyfill / hybrid / library: **native** — the platform
|
|
2139
|
+
* `<input type=range>` already owns this pattern; this primitive is the
|
|
2140
|
+
* `role=slider` markup and keyboard contract for hosts that need the ARIA
|
|
2141
|
+
* element (styling the native thumb is not shipped).
|
|
2142
|
+
*
|
|
2143
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
2144
|
+
* pass `value` + `onValueChange` to own the value. A key or rail press is a
|
|
2145
|
+
* request the host may ignore.
|
|
2146
|
+
*
|
|
2147
|
+
* ponytail: dragging (pointer capture/move) is not modelled — the spec
|
|
2148
|
+
* deliberately only fixes a discrete rail press.
|
|
2149
|
+
*/
|
|
2150
|
+
export declare const createSlider: (options?: SliderOptions) => Slider;
|
|
2151
|
+
//#endregion
|
|
2152
|
+
//#region src/slider-multithumb.d.ts
|
|
2153
|
+
export type SliderMultiThumbIndex = 0 | 1;
|
|
2154
|
+
export interface SliderMultiThumbOptions {
|
|
2155
|
+
readonly min?: number;
|
|
2156
|
+
readonly max?: number;
|
|
2157
|
+
readonly step?: number;
|
|
2158
|
+
/** The jump distance of PageUp / PageDown. */
|
|
2159
|
+
readonly largeStep?: number;
|
|
2160
|
+
/** Controlled: the host owns the pair and this is the only thing that changes it. */
|
|
2161
|
+
readonly value?: readonly [number, number] | undefined;
|
|
2162
|
+
/** Uncontrolled starting `[lo, hi]` values; ignored while `value` is set. */
|
|
2163
|
+
readonly defaultValue?: readonly [number, number];
|
|
2164
|
+
readonly orientation?: SliderOrientation;
|
|
2165
|
+
readonly direction?: SliderDirection;
|
|
2166
|
+
/** Base for the generated thumb ids. */
|
|
2167
|
+
readonly id?: string;
|
|
2168
|
+
/**
|
|
2169
|
+
* A request to change the pair, called when the intent differs from the
|
|
2170
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore
|
|
2171
|
+
* it.
|
|
2172
|
+
*/
|
|
2173
|
+
readonly onValueChange?: (values: readonly [number, number]) => void;
|
|
2174
|
+
}
|
|
2175
|
+
export interface SliderMultiThumbProps {
|
|
2176
|
+
readonly id: string;
|
|
2177
|
+
readonly role: "slider";
|
|
2178
|
+
readonly "aria-valuenow": string;
|
|
2179
|
+
readonly "aria-valuemin": string;
|
|
2180
|
+
readonly "aria-valuemax": string;
|
|
2181
|
+
readonly "aria-valuetext": string;
|
|
2182
|
+
readonly "aria-orientation": SliderOrientation;
|
|
2183
|
+
readonly tabindex: number;
|
|
2184
|
+
readonly onFocus: () => void;
|
|
2185
|
+
readonly onBlur: () => void;
|
|
2186
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2187
|
+
}
|
|
2188
|
+
export interface SliderMultiThumb {
|
|
2189
|
+
readonly values: Accessor<readonly [number, number]>;
|
|
2190
|
+
readonly focused: Accessor<SliderMultiThumbIndex | null>;
|
|
2191
|
+
readonly setValues: (values: readonly [number, number]) => void;
|
|
2192
|
+
/** Programmatic press on one thumb at `position` (a 0..1 track fraction). */
|
|
2193
|
+
readonly pointerDown: (index: SliderMultiThumbIndex, position: number) => void;
|
|
2194
|
+
readonly focusThumb: (index: SliderMultiThumbIndex) => void;
|
|
2195
|
+
readonly blur: () => void;
|
|
2196
|
+
/** Returns whether the key moved a value (the caller preventDefaults it). */
|
|
2197
|
+
readonly keyDown: (key: string) => boolean;
|
|
2198
|
+
readonly thumbProps: (index: SliderMultiThumbIndex) => SliderMultiThumbProps;
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* APG Slider (Multi-Thumb) (https://www.w3.org/WAI/ARIA/apg/patterns/slider-multithumb/):
|
|
2202
|
+
* two `role=slider` thumbs sharing one rail, each bounded by its neighbour so
|
|
2203
|
+
* they cannot cross. Verified against `specs/apg/slider-multithumb.qnt` by the
|
|
2204
|
+
* differential walk in `test/slider-multithumb.browser.test.tsx`.
|
|
2205
|
+
*
|
|
2206
|
+
* Native / polyfill / hybrid / library: **library** — the Open UI
|
|
2207
|
+
* `<rangegroup>` is an explainer only, so the library owns the behaviour
|
|
2208
|
+
* (neighbour-bounded thumbs, constant tab order). The spec generalises the bound
|
|
2209
|
+
* rule to N thumbs; Quint has no list state, so this primitive fixes two, like
|
|
2210
|
+
* the spec.
|
|
2211
|
+
*
|
|
2212
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
2213
|
+
* pass `value` + `onValueChange` to own the pair. The pair is packed into a
|
|
2214
|
+
* string so it rides the scalar `createControllable`.
|
|
2215
|
+
*
|
|
2216
|
+
* ponytail: dragging and nearest-thumb rail presses are not modelled — the spec
|
|
2217
|
+
* omits them (the event names the thumb).
|
|
2218
|
+
*/
|
|
2219
|
+
export declare const createSliderMultiThumb: (options?: SliderMultiThumbOptions) => SliderMultiThumb;
|
|
2220
|
+
//#endregion
|
|
2221
|
+
//#region src/spinbutton.d.ts
|
|
2222
|
+
export interface SpinbuttonOptions {
|
|
2223
|
+
readonly min?: number;
|
|
2224
|
+
readonly max?: number;
|
|
2225
|
+
/** Small step: Up / Down Arrow and the buttons. */
|
|
2226
|
+
readonly step?: number;
|
|
2227
|
+
/** Large step: Page Up / Page Down. */
|
|
2228
|
+
readonly largeStep?: number;
|
|
2229
|
+
/** Controlled: the host owns the value and this is the only thing that changes it. */
|
|
2230
|
+
readonly value?: number | undefined;
|
|
2231
|
+
/** Starting value when uncontrolled; ignored while `value` is set. */
|
|
2232
|
+
readonly defaultValue?: number;
|
|
2233
|
+
/** Wrap around at the bounds instead of clamping. */
|
|
2234
|
+
readonly wrap?: boolean;
|
|
2235
|
+
/** Base for the generated input / button ids. */
|
|
2236
|
+
readonly id?: string;
|
|
2237
|
+
/**
|
|
2238
|
+
* A request to change the value, called when the intent differs from the
|
|
2239
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
2240
|
+
*/
|
|
2241
|
+
readonly onValueChange?: (value: number) => void;
|
|
2242
|
+
}
|
|
2243
|
+
export interface SpinbuttonInputProps {
|
|
2244
|
+
readonly id: string;
|
|
2245
|
+
readonly role: "spinbutton";
|
|
2246
|
+
readonly "aria-valuenow": string;
|
|
2247
|
+
readonly "aria-valuemin": string;
|
|
2248
|
+
readonly "aria-valuemax": string;
|
|
2249
|
+
readonly "aria-valuetext": string;
|
|
2250
|
+
readonly "aria-invalid": "true" | undefined;
|
|
2251
|
+
readonly tabindex: number;
|
|
2252
|
+
readonly value: string;
|
|
2253
|
+
readonly onFocus: () => void;
|
|
2254
|
+
readonly onBlur: () => void;
|
|
2255
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2256
|
+
readonly onInput: (event: InputEvent) => void;
|
|
2257
|
+
}
|
|
2258
|
+
export interface SpinbuttonButtonProps {
|
|
2259
|
+
readonly role: "button";
|
|
2260
|
+
readonly tabindex: number;
|
|
2261
|
+
readonly "aria-label": string;
|
|
2262
|
+
readonly "aria-disabled": "true" | undefined;
|
|
2263
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
2264
|
+
readonly onClick: () => void;
|
|
2265
|
+
}
|
|
2266
|
+
export interface Spinbutton {
|
|
2267
|
+
readonly value: Accessor<number>;
|
|
2268
|
+
readonly focused: Accessor<boolean>;
|
|
2269
|
+
readonly editing: Accessor<boolean>;
|
|
2270
|
+
readonly draft: Accessor<string>;
|
|
2271
|
+
/** What the text field shows: the draft while editing, else the value. */
|
|
2272
|
+
readonly displayed: Accessor<string>;
|
|
2273
|
+
readonly setValue: (value: number) => void;
|
|
2274
|
+
readonly focus: () => void;
|
|
2275
|
+
readonly blur: () => void;
|
|
2276
|
+
/** Returns whether the key was consumed (the caller preventDefaults it). */
|
|
2277
|
+
readonly keyDown: (key: string) => boolean;
|
|
2278
|
+
/** Replaces the uncommitted draft (Base UI's "change"); needs focus. */
|
|
2279
|
+
readonly typeInput: (text: string) => void;
|
|
2280
|
+
readonly commit: () => void;
|
|
2281
|
+
readonly revert: () => void;
|
|
2282
|
+
readonly increment: () => void;
|
|
2283
|
+
readonly decrement: () => void;
|
|
2284
|
+
readonly setWrap: (wrap: boolean) => void;
|
|
2285
|
+
readonly inputProps: SpinbuttonInputProps;
|
|
2286
|
+
readonly incrementProps: SpinbuttonButtonProps;
|
|
2287
|
+
readonly decrementProps: SpinbuttonButtonProps;
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* APG Spinbutton (https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/): a text
|
|
2291
|
+
* field plus increase / decrease buttons. Plain Solid signals, no elements, no
|
|
2292
|
+
* styling; verified against `specs/apg/spinbutton.qnt` by the differential walk
|
|
2293
|
+
* in `test/spinbutton.browser.test.tsx`.
|
|
2294
|
+
*
|
|
2295
|
+
* Native / polyfill / hybrid / library: **native** (`<input type=number>`) for
|
|
2296
|
+
* the clamping model; this primitive is the **hybrid** `role=spinbutton` text
|
|
2297
|
+
* field for the APG extras the native control lacks — Home/End jump to the
|
|
2298
|
+
* bounds, Page Up/Down use the large step, and a typed draft commits on
|
|
2299
|
+
* Enter/blur or reverts on Escape.
|
|
2300
|
+
*
|
|
2301
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
2302
|
+
* pass `value` + `onValueChange` to own the committed value. Spin keys, the
|
|
2303
|
+
* stepper buttons, Enter and blur are requests the host may ignore; typing and
|
|
2304
|
+
* Escape change no committed value.
|
|
2305
|
+
*
|
|
2306
|
+
* ponytail: commit/revert on Enter/blur/Escape is the common convention, not
|
|
2307
|
+
* APG text.
|
|
2308
|
+
*/
|
|
2309
|
+
export declare const createSpinbutton: (options?: SpinbuttonOptions) => Spinbutton;
|
|
2310
|
+
//#endregion
|
|
2311
|
+
//#region src/switch.d.ts
|
|
2312
|
+
export interface SwitchOptions {
|
|
2313
|
+
/** Controlled: the host owns the state and this is the only thing that changes it. */
|
|
2314
|
+
readonly value?: boolean | undefined;
|
|
2315
|
+
/** Uncontrolled starting state; ignored while `value` is set. */
|
|
2316
|
+
readonly defaultValue?: boolean;
|
|
2317
|
+
readonly disabled?: boolean;
|
|
2318
|
+
readonly readOnly?: boolean;
|
|
2319
|
+
/** The APG's optional Enter key also flips the switch. */
|
|
2320
|
+
readonly enterToggles?: boolean;
|
|
2321
|
+
readonly id?: string;
|
|
2322
|
+
/**
|
|
2323
|
+
* A request to change the state, called when the intent differs from the
|
|
2324
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
2325
|
+
*/
|
|
2326
|
+
readonly onValueChange?: (checked: boolean) => void;
|
|
2327
|
+
}
|
|
2328
|
+
/** Spread onto the focusable element with `role=switch`. */
|
|
2329
|
+
export interface SwitchProps {
|
|
2330
|
+
readonly role: "switch";
|
|
2331
|
+
readonly "aria-checked": "true" | "false";
|
|
2332
|
+
readonly "aria-disabled": "true" | undefined;
|
|
2333
|
+
readonly "aria-readonly": "true" | undefined;
|
|
2334
|
+
readonly tabindex: 0;
|
|
2335
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2336
|
+
readonly onKeyUp: (event: KeyboardEvent) => void;
|
|
2337
|
+
readonly onClick: () => void;
|
|
2338
|
+
readonly onFocus: () => void;
|
|
2339
|
+
readonly onBlur: () => void;
|
|
2340
|
+
}
|
|
2341
|
+
export interface Switch {
|
|
2342
|
+
readonly checked: Accessor<boolean>;
|
|
2343
|
+
readonly disabled: Accessor<boolean>;
|
|
2344
|
+
readonly readOnly: Accessor<boolean>;
|
|
2345
|
+
readonly enterToggles: Accessor<boolean>;
|
|
2346
|
+
readonly focused: Accessor<boolean>;
|
|
2347
|
+
/** Programmatic state change; a request the host may ignore. */
|
|
2348
|
+
readonly setChecked: (checked: boolean) => void;
|
|
2349
|
+
readonly setDisabled: (value: boolean) => void;
|
|
2350
|
+
readonly setEnterToggles: (value: boolean) => void;
|
|
2351
|
+
readonly toggle: () => void;
|
|
2352
|
+
readonly switchProps: SwitchProps;
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* APG Switch (https://www.w3.org/WAI/ARIA/apg/patterns/switch/): a binary on/off
|
|
2356
|
+
* input. Platform choice (docs/research/apg-platform-features.md): **native
|
|
2357
|
+
* `<input type=checkbox switch>`** as progressive enhancement (Safari only),
|
|
2358
|
+
* with checkbox + `role=switch` as the safe baseline; this primitive exposes
|
|
2359
|
+
* the `role=switch` + `aria-checked` contract. Space arms on keydown and fires
|
|
2360
|
+
* on keyup; Enter flips only when `enterToggles`.
|
|
2361
|
+
*
|
|
2362
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default (`defaultValue`);
|
|
2363
|
+
* pass `value` + `onValueChange` to own the state.
|
|
2364
|
+
*/
|
|
2365
|
+
export declare const createSwitch: (options?: SwitchOptions) => Switch;
|
|
2366
|
+
//#endregion
|
|
2367
|
+
//#region src/tabs.d.ts
|
|
2368
|
+
export type TabsOrientation = "horizontal" | "vertical";
|
|
2369
|
+
export type TabsActivation = "automatic" | "manual";
|
|
2370
|
+
/**
|
|
2371
|
+
* Options are read lazily (getters) so `orientation` / `activation` /
|
|
2372
|
+
* `disabled` stay reactive; pass a component's `props`.
|
|
2373
|
+
*/
|
|
2374
|
+
export interface TabsOptions {
|
|
2375
|
+
/** Number of tabs (needed for wrapping). */
|
|
2376
|
+
readonly items?: number;
|
|
2377
|
+
/** Controlled: the host owns the selection and this is the only thing that changes it. */
|
|
2378
|
+
readonly value?: number | undefined;
|
|
2379
|
+
/** Uncontrolled starting selection; ignored while `value` is set. */
|
|
2380
|
+
readonly defaultSelected?: number;
|
|
2381
|
+
readonly orientation?: TabsOrientation;
|
|
2382
|
+
readonly activation?: TabsActivation;
|
|
2383
|
+
/** Tab indices that cannot be selected. */
|
|
2384
|
+
readonly disabled?: ReadonlyArray<number>;
|
|
2385
|
+
/** Panels that already hold focusable content (no `tabindex` needed). */
|
|
2386
|
+
readonly focusablePanels?: ReadonlyArray<number>;
|
|
2387
|
+
/** Base for the generated ids. */
|
|
2388
|
+
readonly id?: string;
|
|
2389
|
+
/**
|
|
2390
|
+
* A request to change the selection, called when the intent differs from the
|
|
2391
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
2392
|
+
*/
|
|
2393
|
+
readonly onValueChange?: (index: number) => void;
|
|
2394
|
+
}
|
|
2395
|
+
/** Spread onto the element with `role=tab` (a native `<button>`). */
|
|
2396
|
+
export interface TabProps {
|
|
2397
|
+
readonly id: string;
|
|
2398
|
+
readonly role: "tab";
|
|
2399
|
+
readonly "aria-selected": "true" | "false";
|
|
2400
|
+
readonly "aria-controls": string;
|
|
2401
|
+
readonly "aria-disabled": "true" | undefined;
|
|
2402
|
+
readonly tabindex: 0 | -1;
|
|
2403
|
+
readonly onClick: () => void;
|
|
2404
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
2405
|
+
readonly onFocus: () => void;
|
|
2406
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2407
|
+
}
|
|
2408
|
+
/** Spread onto the element with `role=tablist`. */
|
|
2409
|
+
export interface TablistProps {
|
|
2410
|
+
readonly role: "tablist";
|
|
2411
|
+
readonly "aria-orientation": "vertical" | undefined;
|
|
2412
|
+
}
|
|
2413
|
+
/** Spread onto the element with `role=tabpanel`. */
|
|
2414
|
+
export interface TabpanelProps {
|
|
2415
|
+
readonly id: string;
|
|
2416
|
+
readonly role: "tabpanel";
|
|
2417
|
+
readonly "aria-labelledby": string;
|
|
2418
|
+
readonly tabindex: 0 | undefined;
|
|
2419
|
+
readonly hidden: boolean;
|
|
2420
|
+
}
|
|
2421
|
+
export interface Tabs {
|
|
2422
|
+
readonly items: number;
|
|
2423
|
+
readonly selected: Accessor<number>;
|
|
2424
|
+
/** Tab holding (or last holding) DOM focus; -1 when focus is outside. */
|
|
2425
|
+
readonly focused: Accessor<number>;
|
|
2426
|
+
/** Programmatic selection request; ignores disabled tabs. */
|
|
2427
|
+
readonly select: (index: number) => void;
|
|
2428
|
+
/** Programmatic selection request (may point at a disabled tab, like base-ui). */
|
|
2429
|
+
readonly setSelected: (index: number) => void;
|
|
2430
|
+
/** Programmatic focus (does not select). */
|
|
2431
|
+
readonly focusTab: (index: number) => void;
|
|
2432
|
+
readonly keyDown: (key: string) => void;
|
|
2433
|
+
readonly tablistProps: TablistProps;
|
|
2434
|
+
readonly tabProps: (index: number) => TabProps;
|
|
2435
|
+
readonly tabpanelProps: (index: number) => TabpanelProps;
|
|
2436
|
+
}
|
|
2437
|
+
/**
|
|
2438
|
+
* APG Tabs (https://www.w3.org/WAI/ARIA/apg/patterns/tabs/): state plus prop
|
|
2439
|
+
* objects for a tablist, tabs and panels. Roving tabindex, wrapping arrows,
|
|
2440
|
+
* Home/End, automatic vs manual activation and disabled-tab skipping; no
|
|
2441
|
+
* styling, no elements. Checked against `specs/apg/tabs.qnt` by the scenario
|
|
2442
|
+
* bridge, not by running the generated machine here.
|
|
2443
|
+
*
|
|
2444
|
+
* Platform: **hybrid** — focusgroup `tablist` owns arrows/wrap/tab stop; the
|
|
2445
|
+
* library owns selection, activation mode and panel visibility.
|
|
2446
|
+
*
|
|
2447
|
+
* Request/commit (map ticket `rt1p`): uncontrolled by default
|
|
2448
|
+
* (`defaultSelected`); pass `value` + `onValueChange` to own the selection.
|
|
2449
|
+
* Programmatic methods (`select`, `setSelected`, `keyDown`) are requests too.
|
|
2450
|
+
*/
|
|
2451
|
+
export declare const createTabs: (options?: TabsOptions) => Tabs;
|
|
2452
|
+
//#endregion
|
|
2453
|
+
//#region src/timeline.d.ts
|
|
2454
|
+
/**
|
|
2455
|
+
* APG has no waterfall pattern. This is the span timeline the o11y dashboard
|
|
2456
|
+
* needs: spans laid out on a shared tick axis with nesting/indentation, a
|
|
2457
|
+
* duration ruler, single selection, per-parent collapse and a zoom window.
|
|
2458
|
+
*
|
|
2459
|
+
* State plus attribute objects, no elements, no styling. Behaviour is checked
|
|
2460
|
+
* against `specs/apg/timeline.qnt` by the scenario bridge, not by running the
|
|
2461
|
+
* generated machine here.
|
|
2462
|
+
*
|
|
2463
|
+
* Request/commit (map ticket `rt1p`): the selected row is the value. It is
|
|
2464
|
+
* uncontrolled by default (`defaultValue` / `defaultSelected`); pass `value` +
|
|
2465
|
+
* `onValueChange` to own it. `select`, a click and Enter are requests the host
|
|
2466
|
+
* may ignore. The zoom window (`viewStart`/`viewEnd`) is not the controlled
|
|
2467
|
+
* value and stays uncontrolled.
|
|
2468
|
+
*
|
|
2469
|
+
* ponytail: the fixed 5-span tree is a demo fixture; a real consumer owns the
|
|
2470
|
+
* span list (layout is theirs). Roving tabindex only — a consumer must call
|
|
2471
|
+
* `focus()` on the row whose `tabindex` is 0 after arrow navigation (the tests'
|
|
2472
|
+
* fixture does this with an effect).
|
|
2473
|
+
*/
|
|
2474
|
+
/** One span row: a fraction of the parent span on a shared 0..1 axis. */
|
|
2475
|
+
export interface TimelineRow {
|
|
2476
|
+
readonly id: number;
|
|
2477
|
+
readonly level: number;
|
|
2478
|
+
readonly parent: number;
|
|
2479
|
+
readonly start: number;
|
|
2480
|
+
readonly end: number;
|
|
2481
|
+
/** A parent whose children are hidden. */
|
|
2482
|
+
readonly collapsed: boolean;
|
|
2483
|
+
}
|
|
2484
|
+
export interface TimelineOptions {
|
|
2485
|
+
/** Node ids whose children start expanded. */
|
|
2486
|
+
readonly defaultExpanded?: ReadonlyArray<number>;
|
|
2487
|
+
/** Controlled: the host owns the selection and this is the only thing that changes it. */
|
|
2488
|
+
readonly value?: number | undefined;
|
|
2489
|
+
/** Uncontrolled starting selection (0 = none); alias of `defaultSelected`. */
|
|
2490
|
+
readonly defaultValue?: number;
|
|
2491
|
+
/** Initially selected node id (0 = none). */
|
|
2492
|
+
readonly defaultSelected?: number;
|
|
2493
|
+
readonly viewStart?: number;
|
|
2494
|
+
readonly viewEnd?: number;
|
|
2495
|
+
/** Base for generated ids. */
|
|
2496
|
+
readonly id?: string;
|
|
2497
|
+
/**
|
|
2498
|
+
* A request to change the selection, called when the intent differs from the
|
|
2499
|
+
* current value. Uncontrolled it is accepted; controlled the host may ignore it.
|
|
2500
|
+
*/
|
|
2501
|
+
readonly onValueChange?: (id: number) => void;
|
|
2502
|
+
/** Alias of `onValueChange`. */
|
|
2503
|
+
readonly onSelectedChange?: (id: number) => void;
|
|
2504
|
+
}
|
|
2505
|
+
export interface TimelineTreeProps {
|
|
2506
|
+
readonly role: "tree";
|
|
2507
|
+
readonly "aria-label": string;
|
|
2508
|
+
readonly "aria-multiselectable": "false";
|
|
2509
|
+
}
|
|
2510
|
+
export interface TimelineRowProps {
|
|
2511
|
+
readonly role: "treeitem";
|
|
2512
|
+
readonly "aria-level": number;
|
|
2513
|
+
/** Absent on leaves, like treeview's aria-expanded. */
|
|
2514
|
+
readonly "aria-expanded": "true" | "false" | undefined;
|
|
2515
|
+
readonly "aria-selected": "true" | "false";
|
|
2516
|
+
readonly tabindex: number;
|
|
2517
|
+
readonly hidden: boolean;
|
|
2518
|
+
readonly onClick: () => void;
|
|
2519
|
+
readonly onFocus: () => void;
|
|
2520
|
+
readonly onBlur: () => void;
|
|
2521
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2522
|
+
}
|
|
2523
|
+
/** The zoom window as a slider over the fixed 0..100 tick domain. */
|
|
2524
|
+
export interface TimelineRulerProps {
|
|
2525
|
+
readonly role: "slider";
|
|
2526
|
+
readonly "aria-label": string;
|
|
2527
|
+
readonly "aria-valuemin": number;
|
|
2528
|
+
readonly "aria-valuenow": number;
|
|
2529
|
+
readonly "aria-valuemax": number;
|
|
2530
|
+
}
|
|
2531
|
+
export interface Timeline {
|
|
2532
|
+
readonly rows: () => ReadonlyArray<TimelineRow>;
|
|
2533
|
+
readonly zoom: () => {
|
|
2534
|
+
readonly start: number;
|
|
2535
|
+
readonly end: number;
|
|
2536
|
+
};
|
|
2537
|
+
readonly selected: Accessor<number>;
|
|
2538
|
+
readonly focus: () => number;
|
|
2539
|
+
readonly hasFocus: () => boolean;
|
|
2540
|
+
/** Programmatic selection request; a controlled host may ignore it. */
|
|
2541
|
+
readonly select: (id: number) => void;
|
|
2542
|
+
readonly setFocus: (id: number) => void;
|
|
2543
|
+
readonly expand: (id: number) => void;
|
|
2544
|
+
readonly collapse: (id: number) => void;
|
|
2545
|
+
readonly toggle: (id: number) => void;
|
|
2546
|
+
readonly zoomIn: () => void;
|
|
2547
|
+
readonly zoomOut: () => void;
|
|
2548
|
+
readonly zoomReset: () => void;
|
|
2549
|
+
readonly panLeft: () => void;
|
|
2550
|
+
readonly panRight: () => void;
|
|
2551
|
+
readonly treeProps: TimelineTreeProps;
|
|
2552
|
+
readonly rowProps: (id: number) => TimelineRowProps;
|
|
2553
|
+
readonly rulerProps: TimelineRulerProps;
|
|
2554
|
+
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Headless span timeline on Solid 2. The exported accessors are signals; the
|
|
2557
|
+
* prop objects are getter-backed, so spreading them keeps every attribute
|
|
2558
|
+
* reactive.
|
|
2559
|
+
*/
|
|
2560
|
+
export declare const createTimeline: (options?: TimelineOptions) => Timeline;
|
|
2561
|
+
//#endregion
|
|
2562
|
+
//#region src/toolbar.d.ts
|
|
2563
|
+
/**
|
|
2564
|
+
* APG Toolbar on Solid 2: state plus attribute objects, no elements, no
|
|
2565
|
+
* styling. A faithful transcription of `specs/apg/toolbar.qnt`.
|
|
2566
|
+
*
|
|
2567
|
+
* Platform: **polyfill** (map ticket `etfq`). The roving tabindex is the
|
|
2568
|
+
* `focusgroup="toolbar"` contract (see `shared/focusgroup.qnt`); ship the
|
|
2569
|
+
* `@microsoft/focusgroup-polyfill` first and switch to the native attribute
|
|
2570
|
+
* when Chrome/Edge is the floor. The library owns only the spec's delta:
|
|
2571
|
+
* `aria-orientation`, the wrap choice, disabled-but-focusable items, embedded
|
|
2572
|
+
* controls that consume keys, and activation.
|
|
2573
|
+
*
|
|
2574
|
+
* ponytail: item count and kinds come from the constructor; separators, RTL and
|
|
2575
|
+
* the embedded controls' own values are not modelled. Add them when a consumer
|
|
2576
|
+
* needs them.
|
|
2577
|
+
*/
|
|
2578
|
+
export type ToolbarOrientation = "horizontal" | "vertical";
|
|
2579
|
+
export type ToolbarItemKind = "button" | "text" | "spin";
|
|
2580
|
+
export interface ToolbarItemInput {
|
|
2581
|
+
readonly label: string;
|
|
2582
|
+
readonly kind?: ToolbarItemKind;
|
|
2583
|
+
readonly disabled?: boolean;
|
|
2584
|
+
}
|
|
2585
|
+
export interface ToolbarOptions {
|
|
2586
|
+
readonly items: ReadonlyArray<string | ToolbarItemInput>;
|
|
2587
|
+
readonly kinds?: ReadonlyArray<ToolbarItemKind>;
|
|
2588
|
+
readonly orientation?: ToolbarOrientation;
|
|
2589
|
+
readonly wrap?: boolean;
|
|
2590
|
+
readonly defaultFocus?: number;
|
|
2591
|
+
/** Item indices disabled at mount (aria-disabled, still focusable). */
|
|
2592
|
+
readonly disabled?: ReadonlyArray<number>;
|
|
2593
|
+
readonly id?: string;
|
|
2594
|
+
readonly onActivate?: (index: number) => void;
|
|
2595
|
+
}
|
|
2596
|
+
export interface ToolbarItem {
|
|
2597
|
+
readonly id: string;
|
|
2598
|
+
readonly label: string;
|
|
2599
|
+
readonly kind: ToolbarItemKind;
|
|
2600
|
+
readonly disabled: boolean;
|
|
2601
|
+
}
|
|
2602
|
+
export interface ToolbarItemProps {
|
|
2603
|
+
readonly id: string;
|
|
2604
|
+
readonly role: "button";
|
|
2605
|
+
readonly "aria-disabled": "true" | undefined;
|
|
2606
|
+
readonly tabindex: 0 | -1;
|
|
2607
|
+
readonly onClick: () => void;
|
|
2608
|
+
readonly onFocus: () => void;
|
|
2609
|
+
}
|
|
2610
|
+
export interface Toolbar {
|
|
2611
|
+
readonly focus: Accessor<number>;
|
|
2612
|
+
readonly tabStop: Accessor<number>;
|
|
2613
|
+
readonly orientation: Accessor<ToolbarOrientation>;
|
|
2614
|
+
readonly wrap: Accessor<boolean>;
|
|
2615
|
+
readonly items: ReadonlyArray<ToolbarItem>;
|
|
2616
|
+
readonly focusIn: () => void;
|
|
2617
|
+
readonly leave: () => void;
|
|
2618
|
+
readonly focusItem: (index: number) => void;
|
|
2619
|
+
readonly keyDown: (key: string) => void;
|
|
2620
|
+
readonly tab: (back: boolean) => void;
|
|
2621
|
+
readonly clickItem: (index: number) => void;
|
|
2622
|
+
readonly activate: () => void;
|
|
2623
|
+
readonly toggleDisabled: (index: number) => void;
|
|
2624
|
+
readonly setOrientation: (orientation: ToolbarOrientation) => void;
|
|
2625
|
+
readonly setWrap: (wrap: boolean) => void;
|
|
2626
|
+
readonly toolbarProps: {
|
|
2627
|
+
readonly role: "toolbar";
|
|
2628
|
+
readonly "aria-orientation": ToolbarOrientation;
|
|
2629
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2630
|
+
};
|
|
2631
|
+
readonly itemProps: (index: number) => ToolbarItemProps;
|
|
2632
|
+
}
|
|
2633
|
+
export declare const createToolbar: (options: ToolbarOptions) => Toolbar;
|
|
2634
|
+
//#endregion
|
|
2635
|
+
//#region src/tooltip.d.ts
|
|
2636
|
+
export interface TooltipTriggerInput {
|
|
2637
|
+
readonly id?: string;
|
|
2638
|
+
/** Id of this trigger's tooltip element (needed for `aria-describedby`). */
|
|
2639
|
+
readonly tooltipId?: string;
|
|
2640
|
+
readonly label?: string;
|
|
2641
|
+
}
|
|
2642
|
+
export interface TooltipGroupOptions {
|
|
2643
|
+
readonly triggers: ReadonlyArray<TooltipTriggerInput>;
|
|
2644
|
+
/** Ignore focus / pointer entirely while disabled. */
|
|
2645
|
+
readonly disabled?: boolean;
|
|
2646
|
+
/** Keyboard focus opens the tooltip immediately (the host has applied its show delay). */
|
|
2647
|
+
readonly openOnFocus?: boolean;
|
|
2648
|
+
/** Hover opens the tooltip immediately (a `delay: 0` configuration). */
|
|
2649
|
+
readonly openOnHover?: boolean;
|
|
2650
|
+
/**
|
|
2651
|
+
* Whether native pointerenter/pointerleave on the rendered elements drive the
|
|
2652
|
+
* state. Disable for a host that feeds `pointerEnter`/`pointerLeave` itself
|
|
2653
|
+
* (the differential walk does), so a stray pointer sitting over the fixture
|
|
2654
|
+
* cannot inject events the other side never saw. @default true
|
|
2655
|
+
*/
|
|
2656
|
+
readonly nativePointer?: boolean;
|
|
2657
|
+
/** Trigger index whose tooltip starts visible (`defaultOpen`). */
|
|
2658
|
+
readonly defaultOpen?: number;
|
|
2659
|
+
/**
|
|
2660
|
+
* Controlled: the host owns the visible tooltip. `0` = none, `1` = first,
|
|
2661
|
+
* `2` = second (`undefined` = uncontrolled).
|
|
2662
|
+
*/
|
|
2663
|
+
readonly value?: number | undefined;
|
|
2664
|
+
/** A request to change the visible tooltip when the intent differs. */
|
|
2665
|
+
readonly onOpenChange?: (open: boolean, index: number) => void;
|
|
2666
|
+
/**
|
|
2667
|
+
* Structural mirror of `ing.lexicon.ui.prefs` (`./prefs`). Honoured here:
|
|
2668
|
+
* `tooltipShowDelayMs` / `tooltipHideDelayMs`, exposed as accessors the host
|
|
2669
|
+
* wires its `setTimeout` from. Absent prefs keep the defaults.
|
|
2670
|
+
*/
|
|
2671
|
+
readonly prefs?: Partial<Prefs> | undefined;
|
|
2672
|
+
}
|
|
2673
|
+
export interface TooltipTriggerProps {
|
|
2674
|
+
readonly id: string;
|
|
2675
|
+
readonly "aria-describedby": string | undefined;
|
|
2676
|
+
readonly onFocus: () => void;
|
|
2677
|
+
readonly onPointerEnter: () => void;
|
|
2678
|
+
readonly onPointerLeave: () => void;
|
|
2679
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2680
|
+
}
|
|
2681
|
+
export interface TooltipContentProps {
|
|
2682
|
+
readonly id: string;
|
|
2683
|
+
readonly role: "tooltip";
|
|
2684
|
+
readonly hidden: boolean;
|
|
2685
|
+
readonly onPointerEnter: () => void;
|
|
2686
|
+
readonly onPointerLeave: () => void;
|
|
2687
|
+
}
|
|
2688
|
+
export interface TooltipGroup {
|
|
2689
|
+
/** Index of the visible tooltip, or `null`. */
|
|
2690
|
+
readonly visibleIndex: Accessor<number | null>;
|
|
2691
|
+
/** Resolved show delay (ms) the host wires its `setTimeout` from. */
|
|
2692
|
+
readonly showDelayMs: Accessor<number>;
|
|
2693
|
+
/** Resolved hide delay (ms) the host wires its `setTimeout` from. */
|
|
2694
|
+
readonly hideDelayMs: Accessor<number>;
|
|
2695
|
+
readonly described: (index: number) => boolean;
|
|
2696
|
+
readonly triggerProps: (index: number) => TooltipTriggerProps;
|
|
2697
|
+
readonly tooltipProps: (index: number) => TooltipContentProps;
|
|
2698
|
+
readonly focusTrigger: (index: number) => void;
|
|
2699
|
+
readonly blur: () => void;
|
|
2700
|
+
readonly pointerEnter: (index: number) => void;
|
|
2701
|
+
readonly pointerEnterTip: () => void;
|
|
2702
|
+
readonly pointerLeave: () => void;
|
|
2703
|
+
readonly showTimer: (index: number) => void;
|
|
2704
|
+
readonly hideTimer: () => void;
|
|
2705
|
+
readonly escape: () => void;
|
|
2706
|
+
readonly warmExpire: () => void;
|
|
2707
|
+
readonly setWarmup: (on: boolean) => void;
|
|
2708
|
+
/**
|
|
2709
|
+
* Call before the host writes the controlled visible value directly: it drops
|
|
2710
|
+
* the timer / dismissal memory the new value invalidates (mirrors the spec's
|
|
2711
|
+
* `setValue`).
|
|
2712
|
+
*/
|
|
2713
|
+
readonly hostSet: (vis: number) => void;
|
|
2714
|
+
/**
|
|
2715
|
+
* Call before the host flips its controlled value between none and the first
|
|
2716
|
+
* tooltip directly (mirrors the spec's `hostToggle`, which also drops B).
|
|
2717
|
+
*/
|
|
2718
|
+
readonly hostToggle: () => void;
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* APG Tooltip + WCAG 1.4.13 (dismissable, hoverable, persistent) on Solid 2.
|
|
2722
|
+
* Two triggers stand in for N (`ponytail:` a pair is all the spec models). The
|
|
2723
|
+
* state machine mirrors `specs/apg/tooltip.qnt`: timers are **events**, not
|
|
2724
|
+
* clocks — the host wires `setTimeout` to `showTimer` / `hideTimer` /
|
|
2725
|
+
* `warmExpire`.
|
|
2726
|
+
*
|
|
2727
|
+
* Request/commit (map ticket `rt1p`): the visible tooltip is the controlled
|
|
2728
|
+
* value (0 none / 1 first / 2 second). Uncontrolled by default (`defaultOpen`);
|
|
2729
|
+
* pass `value` + `onOpenChange` to own it. The non-visible state (focus,
|
|
2730
|
+
* pointer, pending timers) still tracks every event; only the visible commit is
|
|
2731
|
+
* gated, so a rejected request leaves the visible tooltip untouched.
|
|
2732
|
+
*/
|
|
2733
|
+
export declare const createTooltipGroup: (options: TooltipGroupOptions) => TooltipGroup;
|
|
2734
|
+
//#endregion
|
|
2735
|
+
//#region src/treegrid.d.ts
|
|
2736
|
+
/**
|
|
2737
|
+
* APG treegrid primitive on Solid 2: a fixed tree of rows, row/cell roving focus
|
|
2738
|
+
* across the three APG navigation models, expansion and row selection. State plus
|
|
2739
|
+
* getter-backed attribute objects; no elements, no styling.
|
|
2740
|
+
*
|
|
2741
|
+
* Mirrors `specs/apg/treegrid.qnt` (the Email Inbox tree: A > {A.1, A.2 >
|
|
2742
|
+
* {A.2.1}}, B > {B.1, B.2}); the generated machine is the oracle in tests.
|
|
2743
|
+
*/
|
|
2744
|
+
export type TreegridNav = "rowsFirst" | "cellsFirst" | "cellsOnly";
|
|
2745
|
+
export type TreegridSelection = "single" | "multi";
|
|
2746
|
+
export interface TreegridOptions {
|
|
2747
|
+
readonly nav?: TreegridNav;
|
|
2748
|
+
readonly selection?: TreegridSelection;
|
|
2749
|
+
/** Column count; the spec fixes three. */
|
|
2750
|
+
readonly cols?: number;
|
|
2751
|
+
/** Controlled: the host owns the selected rows and this is the only thing that changes them. */
|
|
2752
|
+
readonly value?: ReadonlyArray<number> | undefined;
|
|
2753
|
+
/** Uncontrolled starting selected rows; ignored while `value` is set. */
|
|
2754
|
+
readonly defaultValue?: ReadonlyArray<number>;
|
|
2755
|
+
/** Base for generated ids. */
|
|
2756
|
+
readonly id?: string;
|
|
2757
|
+
/**
|
|
2758
|
+
* A request to change the selected rows, called when the intent differs from
|
|
2759
|
+
* the current value. Uncontrolled it is accepted; controlled the host may
|
|
2760
|
+
* ignore it.
|
|
2761
|
+
*/
|
|
2762
|
+
readonly onValueChange?: (selected: ReadonlyArray<number>) => void;
|
|
2763
|
+
}
|
|
2764
|
+
export interface TreegridProps {
|
|
2765
|
+
readonly role: "treegrid";
|
|
2766
|
+
readonly "aria-multiselectable": "true" | undefined;
|
|
2767
|
+
readonly onFocusOut: (event: FocusEvent) => void;
|
|
2768
|
+
}
|
|
2769
|
+
export interface TreegridRowProps {
|
|
2770
|
+
readonly role: "row";
|
|
2771
|
+
readonly id: string;
|
|
2772
|
+
readonly "aria-level": number;
|
|
2773
|
+
readonly "aria-setsize": number;
|
|
2774
|
+
readonly "aria-posinset": number;
|
|
2775
|
+
readonly "aria-expanded": "true" | "false" | undefined;
|
|
2776
|
+
readonly "aria-selected": "true" | "false" | undefined;
|
|
2777
|
+
readonly tabindex: number;
|
|
2778
|
+
readonly onFocus: () => void;
|
|
2779
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2780
|
+
}
|
|
2781
|
+
export interface TreegridCellProps {
|
|
2782
|
+
readonly role: "gridcell";
|
|
2783
|
+
readonly id: string;
|
|
2784
|
+
readonly tabindex: number;
|
|
2785
|
+
readonly onClick: () => void;
|
|
2786
|
+
readonly onFocus: () => void;
|
|
2787
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2788
|
+
}
|
|
2789
|
+
export interface TreegridExpanderProps {
|
|
2790
|
+
readonly role: "button";
|
|
2791
|
+
readonly "aria-label": "Expand" | "Collapse";
|
|
2792
|
+
readonly "aria-expanded": "true" | "false";
|
|
2793
|
+
readonly onMouseDown: (event: MouseEvent) => void;
|
|
2794
|
+
readonly onClick: (event: MouseEvent) => void;
|
|
2795
|
+
}
|
|
2796
|
+
export interface Treegrid {
|
|
2797
|
+
readonly rows: Accessor<number>;
|
|
2798
|
+
readonly cols: Accessor<number>;
|
|
2799
|
+
readonly nav: Accessor<TreegridNav>;
|
|
2800
|
+
readonly selection: Accessor<TreegridSelection>;
|
|
2801
|
+
readonly focusedRow: Accessor<number>;
|
|
2802
|
+
readonly focusedCol: Accessor<number>;
|
|
2803
|
+
readonly kind: Accessor<"row" | "cell">;
|
|
2804
|
+
readonly inGrid: Accessor<boolean>;
|
|
2805
|
+
readonly visible: (row: number) => boolean;
|
|
2806
|
+
readonly hasChildren: (row: number) => boolean;
|
|
2807
|
+
readonly isExpanded: (row: number) => boolean;
|
|
2808
|
+
readonly isSelected: (row: number) => boolean;
|
|
2809
|
+
readonly focusTargetPart: Accessor<string | null>;
|
|
2810
|
+
readonly gridProps: TreegridProps;
|
|
2811
|
+
readonly rowProps: (row: number) => TreegridRowProps;
|
|
2812
|
+
readonly cellProps: (row: number, col: number) => TreegridCellProps;
|
|
2813
|
+
readonly expanderProps: (row: number) => TreegridExpanderProps;
|
|
2814
|
+
readonly focus: () => void;
|
|
2815
|
+
readonly blur: () => void;
|
|
2816
|
+
readonly clickCell: (row: number, col: number) => void;
|
|
2817
|
+
readonly toggleRow: (row: number) => void;
|
|
2818
|
+
readonly configureNav: (nav: TreegridNav) => void;
|
|
2819
|
+
readonly configureSelection: (selection: TreegridSelection) => void;
|
|
2820
|
+
readonly setExpanded: (row: number, expanded: boolean) => void;
|
|
2821
|
+
}
|
|
2822
|
+
export declare const treegridRowPart: (row: number) => string;
|
|
2823
|
+
export declare const treegridCellPart: (row: number, col: number) => string;
|
|
2824
|
+
export declare const createTreegrid: (options?: TreegridOptions) => Treegrid;
|
|
2825
|
+
/** The `data-part` target the treegrid roving tab stop should focus. */
|
|
2826
|
+
export declare const treegridFocusTargetPart: (grid: Treegrid) => string | null;
|
|
2827
|
+
//#endregion
|
|
2828
|
+
//#region src/treeview.d.ts
|
|
2829
|
+
/** A tree node: `parent` is 0 for a root; order in `nodes` is document order. */
|
|
2830
|
+
export interface TreeviewNode {
|
|
2831
|
+
readonly id: number;
|
|
2832
|
+
readonly parent: number;
|
|
2833
|
+
readonly label: string;
|
|
2834
|
+
}
|
|
2835
|
+
/**
|
|
2836
|
+
* The fixed tree of `specs/apg/treeview.qnt` (ids are pre-order = document
|
|
2837
|
+
* order): 1 Projects > {2 Reports > {3 Roadmap, 4 Summary}, 5 Notes}, 6 Photos
|
|
2838
|
+
* > {7 Trip}.
|
|
2839
|
+
*/
|
|
2840
|
+
export declare const TREEVIEW_NODES: ReadonlyArray<TreeviewNode>;
|
|
2841
|
+
export interface TreeviewOptions {
|
|
2842
|
+
readonly nodes?: ReadonlyArray<TreeviewNode>;
|
|
2843
|
+
readonly multiSelectable?: boolean;
|
|
2844
|
+
/** Controlled: the host owns the selected node ids and this is the only thing that changes them. */
|
|
2845
|
+
readonly value?: ReadonlyArray<number> | undefined;
|
|
2846
|
+
/** Uncontrolled starting selected node ids; ignored while `value` is set. */
|
|
2847
|
+
readonly defaultValue?: ReadonlyArray<number>;
|
|
2848
|
+
/** Expanded node ids at mount. */
|
|
2849
|
+
readonly defaultExpanded?: ReadonlyArray<number>;
|
|
2850
|
+
/** Selected node ids at mount (an alias for `defaultValue`). */
|
|
2851
|
+
readonly defaultSelected?: ReadonlyArray<number>;
|
|
2852
|
+
/** Roving-tabindex node id at mount. */
|
|
2853
|
+
readonly defaultFocus?: number;
|
|
2854
|
+
readonly label?: string;
|
|
2855
|
+
readonly orientation?: "vertical" | "horizontal";
|
|
2856
|
+
/** Fires for each node whose expanded state changed, in document order. */
|
|
2857
|
+
readonly onExpandedChange?: (node: TreeviewNode) => void;
|
|
2858
|
+
/** Fires for each node that becomes selected. */
|
|
2859
|
+
readonly onSelectionChange?: (node: TreeviewNode) => void;
|
|
2860
|
+
/**
|
|
2861
|
+
* A request to change the selected node ids, called when the intent differs
|
|
2862
|
+
* from the current value. Uncontrolled it is accepted; controlled the host may
|
|
2863
|
+
* ignore it.
|
|
2864
|
+
*/
|
|
2865
|
+
readonly onValueChange?: (selected: ReadonlyArray<number>) => void;
|
|
2866
|
+
/** Enter's default action. */
|
|
2867
|
+
readonly onActivate?: (node: TreeviewNode) => void;
|
|
2868
|
+
}
|
|
2869
|
+
/** Spread onto the element with `role=tree`. */
|
|
2870
|
+
export interface TreeviewTreeProps {
|
|
2871
|
+
readonly role: "tree";
|
|
2872
|
+
readonly "aria-multiselectable": "true" | "false";
|
|
2873
|
+
readonly "aria-label": string | undefined;
|
|
2874
|
+
readonly "aria-orientation": "horizontal" | undefined;
|
|
2875
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2876
|
+
readonly onFocusIn: () => void;
|
|
2877
|
+
readonly onFocusOut: (event: FocusEvent) => void;
|
|
2878
|
+
}
|
|
2879
|
+
/** Spread onto the element with `role=treeitem` for `id`. */
|
|
2880
|
+
export interface TreeviewItemProps {
|
|
2881
|
+
readonly role: "treeitem";
|
|
2882
|
+
readonly "aria-level": number;
|
|
2883
|
+
readonly "aria-setsize": number;
|
|
2884
|
+
readonly "aria-posinset": number;
|
|
2885
|
+
readonly "aria-expanded": "true" | "false" | undefined;
|
|
2886
|
+
readonly "aria-selected": "true" | "false";
|
|
2887
|
+
readonly tabindex: number;
|
|
2888
|
+
readonly hidden: boolean;
|
|
2889
|
+
readonly onClick: () => void;
|
|
2890
|
+
}
|
|
2891
|
+
export interface Treeview {
|
|
2892
|
+
readonly nodeIds: Accessor<ReadonlyArray<number>>;
|
|
2893
|
+
readonly expanded: Accessor<ReadonlyArray<number>>;
|
|
2894
|
+
readonly selected: Accessor<ReadonlyArray<number>>;
|
|
2895
|
+
readonly focus: Accessor<number>;
|
|
2896
|
+
readonly hasFocus: Accessor<boolean>;
|
|
2897
|
+
readonly isExpanded: (id: number) => boolean;
|
|
2898
|
+
readonly isSelected: (id: number) => boolean;
|
|
2899
|
+
readonly isVisible: (id: number) => boolean;
|
|
2900
|
+
readonly isParent: (id: number) => boolean;
|
|
2901
|
+
readonly expand: (id: number) => void;
|
|
2902
|
+
readonly collapse: (id: number) => void;
|
|
2903
|
+
readonly toggleExpanded: (id: number) => void;
|
|
2904
|
+
readonly select: (id: number) => void;
|
|
2905
|
+
readonly toggleSelected: (id: number) => void;
|
|
2906
|
+
/** Models the type-ahead timeout. */
|
|
2907
|
+
readonly resetTypeahead: () => void;
|
|
2908
|
+
/** Getter-backed, so spreading it keeps every attribute reactive. */
|
|
2909
|
+
readonly treeProps: TreeviewTreeProps;
|
|
2910
|
+
readonly itemProps: (id: number) => TreeviewItemProps;
|
|
2911
|
+
}
|
|
2912
|
+
/**
|
|
2913
|
+
* APG Tree View (https://www.w3.org/WAI/ARIA/apg/patterns/treeview/): state
|
|
2914
|
+
* plus attribute objects for a `tree` and its `treeitem`s. No styling, no
|
|
2915
|
+
* elements. Behaviour is checked against `specs/apg/treeview.qnt` by the
|
|
2916
|
+
* scenario bridge and the differential walk, not by running the generated
|
|
2917
|
+
* machine here.
|
|
2918
|
+
*
|
|
2919
|
+
* ponytail: the buffer is capped at two characters and its timeout is the
|
|
2920
|
+
* explicit `resetTypeahead` method; add a timer when a consumer wants one.
|
|
2921
|
+
*/
|
|
2922
|
+
export declare const createTreeview: (options?: TreeviewOptions) => Treeview;
|
|
2923
|
+
//#endregion
|
|
2924
|
+
//#region src/windowsplitter.d.ts
|
|
2925
|
+
export type SplitterOrientation = "vertical" | "horizontal";
|
|
2926
|
+
export type SplitterFocus = "none" | "splitter" | "primary" | "secondary";
|
|
2927
|
+
/** Options are read lazily: pass an object with getters, never destructured values. */
|
|
2928
|
+
export interface WindowSplitterOptions {
|
|
2929
|
+
readonly orientation?: SplitterOrientation;
|
|
2930
|
+
readonly fixed?: boolean;
|
|
2931
|
+
/** Controlled: the host owns the primary-pane size. */
|
|
2932
|
+
readonly value?: number | undefined;
|
|
2933
|
+
/** Uncontrolled starting size (`defaultValue`); ignored while `value` is set. */
|
|
2934
|
+
readonly defaultValue?: number;
|
|
2935
|
+
readonly min?: number;
|
|
2936
|
+
readonly max?: number;
|
|
2937
|
+
readonly step?: number;
|
|
2938
|
+
/** Base for the generated separator / pane ids. */
|
|
2939
|
+
readonly id?: string;
|
|
2940
|
+
readonly primaryId?: string;
|
|
2941
|
+
/** Accessible name of the separator when no visible primary label exists. */
|
|
2942
|
+
readonly label?: string | undefined;
|
|
2943
|
+
/** Id of the visible primary-pane label, referenced by `aria-labelledby`. */
|
|
2944
|
+
readonly labelledby?: string | undefined;
|
|
2945
|
+
/** A request to change the size the host may ignore. */
|
|
2946
|
+
readonly onValueChange?: (value: number) => void;
|
|
2947
|
+
readonly onCollapsedChange?: (collapsed: boolean) => void;
|
|
2948
|
+
}
|
|
2949
|
+
/** Spread onto the focusable `role=separator` bar. */
|
|
2950
|
+
export interface WindowSplitterSeparatorProps {
|
|
2951
|
+
readonly id: string;
|
|
2952
|
+
readonly role: "separator";
|
|
2953
|
+
readonly "aria-orientation": SplitterOrientation;
|
|
2954
|
+
readonly "aria-valuenow": number;
|
|
2955
|
+
readonly "aria-valuemin": number;
|
|
2956
|
+
readonly "aria-valuemax": number;
|
|
2957
|
+
readonly "aria-controls": string;
|
|
2958
|
+
readonly "aria-label": string | undefined;
|
|
2959
|
+
readonly "aria-labelledby": string | undefined;
|
|
2960
|
+
readonly tabindex: 0;
|
|
2961
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2962
|
+
readonly onFocus: () => void;
|
|
2963
|
+
readonly onBlur: () => void;
|
|
2964
|
+
}
|
|
2965
|
+
/** Spread onto a pane the F6 cycle can move focus to. */
|
|
2966
|
+
export interface WindowSplitterPaneProps {
|
|
2967
|
+
readonly tabindex: 0;
|
|
2968
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
2969
|
+
readonly onFocus: () => void;
|
|
2970
|
+
readonly onBlur: () => void;
|
|
2971
|
+
}
|
|
2972
|
+
export interface WindowSplitter {
|
|
2973
|
+
readonly value: Accessor<number>;
|
|
2974
|
+
readonly collapsed: Accessor<boolean>;
|
|
2975
|
+
readonly focusPart: Accessor<SplitterFocus>;
|
|
2976
|
+
readonly focusSeparator: () => void;
|
|
2977
|
+
readonly setFocusPart: (part: SplitterFocus) => void;
|
|
2978
|
+
readonly keyDown: (key: string) => void;
|
|
2979
|
+
/**
|
|
2980
|
+
* Call before the host writes the controlled `value` directly: it pins the
|
|
2981
|
+
* derived collapse state so the write does not silently uncollapse (or
|
|
2982
|
+
* collapse) the pane.
|
|
2983
|
+
*/
|
|
2984
|
+
readonly freezeCollapse: () => void;
|
|
2985
|
+
readonly collapse: () => void;
|
|
2986
|
+
readonly expand: () => void;
|
|
2987
|
+
readonly separatorProps: WindowSplitterSeparatorProps;
|
|
2988
|
+
readonly primaryProps: WindowSplitterPaneProps;
|
|
2989
|
+
readonly secondaryProps: WindowSplitterPaneProps;
|
|
2990
|
+
}
|
|
2991
|
+
/**
|
|
2992
|
+
* APG Window Splitter (https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/):
|
|
2993
|
+
* a focusable `role=separator` resizing the primary pane.
|
|
2994
|
+
*
|
|
2995
|
+
* Request/commit (map ticket `rt1p`): the primary-pane size is the value.
|
|
2996
|
+
* Uncontrolled by default (`defaultValue`); pass `value` + `onValueChange` to
|
|
2997
|
+
* own it. Movement keys are requests the host may ignore. While controlled a
|
|
2998
|
+
* collapse is only committed once the host's value reaches the target, so
|
|
2999
|
+
* `collapsed` is derived from the controlled value plus the last request.
|
|
3000
|
+
*/
|
|
3001
|
+
export declare const createWindowSplitter: (options?: WindowSplitterOptions) => WindowSplitter;
|
|
3002
|
+
//#endregion
|
|
3003
|
+
//#region src/listbox-rearrangeable.d.ts
|
|
3004
|
+
export type RearrangeableList = "a" | "b";
|
|
3005
|
+
export interface RearrangeableListboxOptions {
|
|
3006
|
+
/** Option labels; the option id is the index, and every option starts in list A. */
|
|
3007
|
+
readonly items: ReadonlyArray<string>;
|
|
3008
|
+
/** Multi-select (APG example 2): Space toggles, Enter/Delete transfer the selected. */
|
|
3009
|
+
readonly multi?: boolean;
|
|
3010
|
+
/** Controlled selection (option ids); the host owns it. */
|
|
3011
|
+
readonly value?: ReadonlyArray<number> | undefined;
|
|
3012
|
+
readonly defaultValue?: ReadonlyArray<number>;
|
|
3013
|
+
readonly id?: string;
|
|
3014
|
+
readonly labels?: Readonly<Record<RearrangeableList, string>>;
|
|
3015
|
+
/** A request to change the selection; uncontrolled it is accepted. */
|
|
3016
|
+
readonly onValueChange?: (selected: ReadonlyArray<number>) => void;
|
|
3017
|
+
}
|
|
3018
|
+
export interface RearrangeableListProps {
|
|
3019
|
+
readonly role: "listbox";
|
|
3020
|
+
readonly tabindex: 0;
|
|
3021
|
+
readonly "aria-multiselectable": "true" | "false";
|
|
3022
|
+
readonly "aria-activedescendant": string | undefined;
|
|
3023
|
+
readonly "aria-label": string | undefined;
|
|
3024
|
+
readonly onFocus: () => void;
|
|
3025
|
+
readonly onBlur: () => void;
|
|
3026
|
+
readonly onKeyDown: (event: KeyboardEvent) => void;
|
|
3027
|
+
}
|
|
3028
|
+
export interface RearrangeableOptionProps {
|
|
3029
|
+
readonly id: string;
|
|
3030
|
+
readonly role: "option";
|
|
3031
|
+
readonly "aria-selected": "true" | "false";
|
|
3032
|
+
readonly onClick: () => void;
|
|
3033
|
+
}
|
|
3034
|
+
export interface RearrangeableListbox {
|
|
3035
|
+
readonly items: ReadonlyArray<string>;
|
|
3036
|
+
/** The option ids a list holds, in order. */
|
|
3037
|
+
readonly order: (list: RearrangeableList) => ReadonlyArray<number>;
|
|
3038
|
+
/** A list's active option id, `null` when the list is empty. */
|
|
3039
|
+
readonly active: (list: RearrangeableList) => number | null;
|
|
3040
|
+
readonly focusedList: Accessor<RearrangeableList | null>;
|
|
3041
|
+
readonly selected: Accessor<ReadonlyArray<number>>;
|
|
3042
|
+
readonly multi: Accessor<boolean>;
|
|
3043
|
+
readonly focus: (list: RearrangeableList) => void;
|
|
3044
|
+
readonly blur: () => void;
|
|
3045
|
+
readonly keyDown: (key: string, modifiers?: ListboxModifiers) => void;
|
|
3046
|
+
/** Single-select: swap the focused list's active option with its neighbour. */
|
|
3047
|
+
readonly move: (direction: "up" | "down") => void;
|
|
3048
|
+
/** Move the focused list's selected options to the other list (deselected). */
|
|
3049
|
+
readonly transfer: () => void;
|
|
3050
|
+
readonly setMulti: (flag: boolean) => void;
|
|
3051
|
+
readonly listProps: (list: RearrangeableList) => RearrangeableListProps;
|
|
3052
|
+
readonly optionProps: (optionId: number) => RearrangeableOptionProps;
|
|
3053
|
+
}
|
|
3054
|
+
/**
|
|
3055
|
+
* APG rearrangeable listbox (two lists sharing one option set) on Solid 2,
|
|
3056
|
+
* verified against `specs/apg/listbox-rearrangeable.qnt` by a differential
|
|
3057
|
+
* walk. Single-select: selection follows focus, Alt+Up/Down reorder, Delete
|
|
3058
|
+
* moves the focused option across. Multi-select: Space toggles, Enter/Delete
|
|
3059
|
+
* move every selected option of the focused list across. A transferred option
|
|
3060
|
+
* arrives deselected.
|
|
3061
|
+
*
|
|
3062
|
+
* Controlled, the list surgery of a transfer is gated on the request landing:
|
|
3063
|
+
* it is applied when the `value` prop comes back equal to the intent, so a
|
|
3064
|
+
* host that holds the value moves nothing.
|
|
3065
|
+
*/
|
|
3066
|
+
export declare const createRearrangeableListbox: (options: RearrangeableListboxOptions) => RearrangeableListbox;
|
|
3067
|
+
//#endregion
|
|
3068
|
+
export { type Canvas, type CanvasGoal, type CanvasLayerProps, type CanvasLiveRegionProps, type CanvasMessages, type CanvasMode, type CanvasMove, type CanvasNodeGeometry, type CanvasNodeInput, type CanvasNodeProps, type CanvasNodeRole, type CanvasOptions, type CanvasSnapshot, type CanvasViewportProps, type Disclosure, type DisclosureOptions, type DisclosurePanelProps, type DisclosureTriggerProps, type MarqueeMode, NO_POS, type PlaybackAdapter, type PlaybackAdapterEvent, type Player, type PlayerButtonProps, type PlayerOptions, type PlayerPlayProps, type PlayerSeekProps, type PlayerStage, type PlayerSurfaceProps, type PlayerToggleProps, type PointerInput, type SnapAxis, type SnapCandidates, type SnapContext, YES_POS };
|