@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.
@@ -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 };