@cssints/apg-specs 0.0.0-stage → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +65 -2
- package/dist/apg/accordion.bridge.d.mts +35 -0
- package/dist/apg/accordion.bridge.mjs +326 -0
- package/dist/apg/accordion.model.json +8397 -0
- package/dist/apg/alert.bridge.d.mts +22 -0
- package/dist/apg/alert.bridge.mjs +86 -0
- package/dist/apg/alert.model.json +871 -0
- package/dist/apg/alertdialog.bridge.d.mts +21 -0
- package/dist/apg/alertdialog.bridge.mjs +221 -0
- package/dist/apg/alertdialog.model.json +5232 -0
- package/dist/apg/breadcrumb.bridge.d.mts +26 -0
- package/dist/apg/breadcrumb.bridge.mjs +120 -0
- package/dist/apg/breadcrumb.model.json +574 -0
- package/dist/apg/button.bridge.d.mts +35 -0
- package/dist/apg/button.bridge.mjs +208 -0
- package/dist/apg/button.model.json +5814 -0
- package/dist/apg/carousel.bridge.d.mts +44 -0
- package/dist/apg/carousel.bridge.mjs +262 -0
- package/dist/apg/carousel.model.json +8963 -0
- package/dist/apg/checkbox-group.bridge.d.mts +25 -0
- package/dist/apg/checkbox-group.bridge.mjs +276 -0
- package/dist/apg/checkbox-group.model.json +3863 -0
- package/dist/apg/checkbox.bridge.d.mts +34 -0
- package/dist/apg/checkbox.bridge.mjs +253 -0
- package/dist/apg/checkbox.model.json +3158 -0
- package/dist/apg/combobox-autocomplete.bridge.d.mts +14 -0
- package/dist/apg/combobox-autocomplete.bridge.mjs +326 -0
- package/dist/apg/combobox-autocomplete.model.json +11586 -0
- package/dist/apg/combobox-dialog.bridge.d.mts +17 -0
- package/dist/apg/combobox-dialog.bridge.mjs +250 -0
- package/dist/apg/combobox-dialog.model.json +8981 -0
- package/dist/apg/combobox-grid.bridge.d.mts +17 -0
- package/dist/apg/combobox-grid.bridge.mjs +227 -0
- package/dist/apg/combobox-grid.model.json +8554 -0
- package/dist/apg/combobox-select-only.bridge.d.mts +24 -0
- package/dist/apg/combobox-select-only.bridge.mjs +304 -0
- package/dist/apg/combobox-select-only.model.json +7846 -0
- package/dist/apg/combobox-tree.bridge.d.mts +17 -0
- package/dist/apg/combobox-tree.bridge.mjs +211 -0
- package/dist/apg/combobox-tree.model.json +9345 -0
- package/dist/apg/dialog-modal.bridge.d.mts +35 -0
- package/dist/apg/dialog-modal.bridge.mjs +186 -0
- package/dist/apg/dialog-modal.model.json +7580 -0
- package/dist/apg/disclosure.bridge.d.mts +25 -0
- package/dist/apg/disclosure.bridge.mjs +202 -0
- package/dist/apg/disclosure.model.json +4703 -0
- package/dist/apg/feed.bridge.d.mts +31 -0
- package/dist/apg/feed.bridge.mjs +246 -0
- package/dist/apg/feed.model.json +4523 -0
- package/dist/apg/grid-selection.bridge.d.mts +13 -0
- package/dist/apg/grid-selection.bridge.mjs +160 -0
- package/dist/apg/grid-selection.model.json +6974 -0
- package/dist/apg/grid-sort.bridge.d.mts +12 -0
- package/dist/apg/grid-sort.bridge.mjs +138 -0
- package/dist/apg/grid-sort.model.json +3799 -0
- package/dist/apg/grid.bridge.d.mts +54 -0
- package/dist/apg/grid.bridge.mjs +441 -0
- package/dist/apg/grid.model.json +3802 -0
- package/dist/apg/link.bridge.d.mts +24 -0
- package/dist/apg/link.bridge.mjs +126 -0
- package/dist/apg/link.model.json +694 -0
- package/dist/apg/listbox-multi.bridge.d.mts +10 -0
- package/dist/apg/listbox-multi.bridge.mjs +261 -0
- package/dist/apg/listbox-multi.model.json +10893 -0
- package/dist/apg/listbox-rearrangeable.bridge.d.mts +20 -0
- package/dist/apg/listbox-rearrangeable.bridge.mjs +237 -0
- package/dist/apg/listbox-rearrangeable.model.json +17592 -0
- package/dist/apg/listbox-shared.d.mts +58 -0
- package/dist/apg/listbox-shared.mjs +171 -0
- package/dist/apg/listbox-single.bridge.d.mts +10 -0
- package/dist/apg/listbox-single.bridge.mjs +196 -0
- package/dist/apg/listbox-single.model.json +8024 -0
- package/dist/apg/listbox-typeahead.bridge.d.mts +10 -0
- package/dist/apg/listbox-typeahead.bridge.mjs +153 -0
- package/dist/apg/listbox-typeahead.model.json +5615 -0
- package/dist/apg/menu-button.bridge.d.mts +29 -0
- package/dist/apg/menu-button.bridge.mjs +259 -0
- package/dist/apg/menu-button.model.json +5706 -0
- package/dist/apg/menu-checkable.bridge.d.mts +25 -0
- package/dist/apg/menu-checkable.bridge.mjs +226 -0
- package/dist/apg/menu-checkable.model.json +4277 -0
- package/dist/apg/menu-menubar.bridge.d.mts +27 -0
- package/dist/apg/menu-menubar.bridge.mjs +273 -0
- package/dist/apg/menu-menubar.model.json +9540 -0
- package/dist/apg/meter.bridge.d.mts +28 -0
- package/dist/apg/meter.bridge.mjs +159 -0
- package/dist/apg/meter.model.json +1185 -0
- package/dist/apg/player.bridge.d.mts +24 -0
- package/dist/apg/player.bridge.mjs +342 -0
- package/dist/apg/player.model.json +11013 -0
- package/dist/apg/radio.bridge.d.mts +35 -0
- package/dist/apg/radio.bridge.mjs +371 -0
- package/dist/apg/radio.model.json +5011 -0
- package/dist/apg/slider-multithumb.bridge.d.mts +34 -0
- package/dist/apg/slider-multithumb.bridge.mjs +365 -0
- package/dist/apg/slider-multithumb.model.json +5774 -0
- package/dist/apg/slider.bridge.d.mts +54 -0
- package/dist/apg/slider.bridge.mjs +359 -0
- package/dist/apg/slider.model.json +4881 -0
- package/dist/apg/spinbutton.bridge.d.mts +41 -0
- package/dist/apg/spinbutton.bridge.mjs +327 -0
- package/dist/apg/spinbutton.model.json +7571 -0
- package/dist/apg/switch.bridge.d.mts +30 -0
- package/dist/apg/switch.bridge.mjs +243 -0
- package/dist/apg/switch.model.json +3159 -0
- package/dist/apg/tabs.bridge.d.mts +41 -0
- package/dist/apg/tabs.bridge.mjs +294 -0
- package/dist/apg/tabs.model.json +7659 -0
- package/dist/apg/timeline.bridge.d.mts +36 -0
- package/dist/apg/timeline.bridge.mjs +265 -0
- package/dist/apg/timeline.model.json +6189 -0
- package/dist/apg/toolbar.bridge.d.mts +25 -0
- package/dist/apg/toolbar.bridge.mjs +307 -0
- package/dist/apg/toolbar.model.json +6616 -0
- package/dist/apg/tooltip.bridge.d.mts +36 -0
- package/dist/apg/tooltip.bridge.mjs +208 -0
- package/dist/apg/tooltip.model.json +8295 -0
- package/dist/apg/treegrid.bridge.d.mts +28 -0
- package/dist/apg/treegrid.bridge.mjs +311 -0
- package/dist/apg/treegrid.model.json +17358 -0
- package/dist/apg/treeview.bridge.d.mts +26 -0
- package/dist/apg/treeview.bridge.mjs +396 -0
- package/dist/apg/treeview.model.json +13745 -0
- package/dist/apg/windowsplitter.bridge.d.mts +36 -0
- package/dist/apg/windowsplitter.bridge.mjs +242 -0
- package/dist/apg/windowsplitter.model.json +6253 -0
- package/dist/canvas/camera.model.json +6568 -0
- package/dist/canvas/canvas.bridge.d.mts +190 -0
- package/dist/canvas/canvas.bridge.mjs +729 -0
- package/dist/canvas/pointer.model.json +17095 -0
- package/dist/scenario.d.mts +233 -0
- package/dist/scenario.mjs +313 -0
- package/package.json +57 -4
- package/scenarios/apg/README.md +221 -0
- package/scenarios/apg/accordion.base-ui.json +1005 -0
- package/scenarios/apg/accordion.react-aria.json +929 -0
- package/scenarios/apg/alert.base-ui.json +81 -0
- package/scenarios/apg/alert.react-aria.json +98 -0
- package/scenarios/apg/alertdialog.base-ui.json +331 -0
- package/scenarios/apg/alertdialog.react-aria.json +225 -0
- package/scenarios/apg/breadcrumb.react-aria.json +312 -0
- package/scenarios/apg/button.ariakit.json +136 -0
- package/scenarios/apg/button.base-ui.json +747 -0
- package/scenarios/apg/button.react-aria.json +443 -0
- package/scenarios/apg/carousel.apg.json +909 -0
- package/scenarios/apg/checkbox.ariakit.json +299 -0
- package/scenarios/apg/checkbox.base-ui.json +1126 -0
- package/scenarios/apg/checkbox.react-aria.json +518 -0
- package/scenarios/apg/combobox.ariakit.json +945 -0
- package/scenarios/apg/combobox.base-ui.json +2444 -0
- package/scenarios/apg/combobox.react-aria.json +845 -0
- package/scenarios/apg/dialog-modal.ariakit.json +1102 -0
- package/scenarios/apg/dialog-modal.base-ui.json +775 -0
- package/scenarios/apg/dialog-modal.react-aria.json +638 -0
- package/scenarios/apg/disclosure.apg.json +349 -0
- package/scenarios/apg/disclosure.ariakit.json +163 -0
- package/scenarios/apg/disclosure.base-ui.json +451 -0
- package/scenarios/apg/disclosure.react-aria.json +643 -0
- package/scenarios/apg/feed.apg.json +442 -0
- package/scenarios/apg/grid.ariakit.json +197 -0
- package/scenarios/apg/grid.base-ui.json +720 -0
- package/scenarios/apg/grid.react-aria.json +969 -0
- package/scenarios/apg/landmarks.ariakit.json +93 -0
- package/scenarios/apg/landmarks.react-aria.json +330 -0
- package/scenarios/apg/link.ariakit.json +83 -0
- package/scenarios/apg/link.base-ui.json +88 -0
- package/scenarios/apg/link.react-aria.json +186 -0
- package/scenarios/apg/listbox.ariakit.json +78 -0
- package/scenarios/apg/listbox.base-ui.json +163 -0
- package/scenarios/apg/listbox.react-aria.json +1118 -0
- package/scenarios/apg/menu-button.ariakit.json +924 -0
- package/scenarios/apg/menu-button.base-ui.json +1155 -0
- package/scenarios/apg/menu-button.react-aria.json +1080 -0
- package/scenarios/apg/menubar.ariakit.json +758 -0
- package/scenarios/apg/menubar.base-ui.json +673 -0
- package/scenarios/apg/meter.base-ui.json +243 -0
- package/scenarios/apg/meter.react-aria.json +201 -0
- package/scenarios/apg/radio.ariakit.json +459 -0
- package/scenarios/apg/radio.base-ui.json +581 -0
- package/scenarios/apg/radio.react-aria.json +576 -0
- package/scenarios/apg/slider-multithumb.base-ui.json +316 -0
- package/scenarios/apg/slider-multithumb.react-aria.json +363 -0
- package/scenarios/apg/slider.base-ui.json +544 -0
- package/scenarios/apg/slider.react-aria.json +456 -0
- package/scenarios/apg/spinbutton.base-ui.json +1688 -0
- package/scenarios/apg/spinbutton.react-aria.json +426 -0
- package/scenarios/apg/switch.base-ui.json +456 -0
- package/scenarios/apg/switch.react-aria.json +303 -0
- package/scenarios/apg/table.ariakit.json +65 -0
- package/scenarios/apg/table.react-aria.json +221 -0
- package/scenarios/apg/tabs.ariakit.json +329 -0
- package/scenarios/apg/tabs.base-ui.json +875 -0
- package/scenarios/apg/tabs.react-aria.json +644 -0
- package/scenarios/apg/toolbar.ariakit.json +595 -0
- package/scenarios/apg/toolbar.base-ui.json +1189 -0
- package/scenarios/apg/toolbar.react-aria.json +383 -0
- package/scenarios/apg/tooltip.ariakit.json +441 -0
- package/scenarios/apg/tooltip.base-ui.json +411 -0
- package/scenarios/apg/tooltip.react-aria.json +454 -0
- package/scenarios/apg/treegrid.apg.json +2558 -0
- package/scenarios/apg/treegrid.react-aria.json +5094 -0
- package/scenarios/apg/treeview.apg.json +928 -0
- package/scenarios/apg/windowsplitter.apg.json +557 -0
- package/scenarios/canvas/README.md +69 -0
- package/scenarios/canvas/camera.contract.json +237 -0
- package/scenarios/canvas/camera.excalidraw.json +57 -0
- package/scenarios/canvas/marquee.excalidraw.json +122 -0
- package/scenarios/canvas/pan.excalidraw.json +46 -0
- package/scenarios/canvas/pointer.contract.json +528 -0
- package/scenarios/parts.json +291 -0
- package/shared/aria.qnt +186 -0
- package/shared/details-group.qnt +50 -0
- package/shared/focusgroup.qnt +152 -0
- package/shared/invokers.qnt +81 -0
- package/shared/popover.qnt +71 -0
- package/specs/apg/STATIC.md +103 -0
- package/specs/apg/accordion.qnt +578 -0
- package/specs/apg/alert.qnt +131 -0
- package/specs/apg/alertdialog.qnt +455 -0
- package/specs/apg/breadcrumb.qnt +88 -0
- package/specs/apg/button.qnt +445 -0
- package/specs/apg/carousel.qnt +732 -0
- package/specs/apg/checkbox-group.qnt +388 -0
- package/specs/apg/checkbox.qnt +394 -0
- package/specs/apg/combobox-autocomplete.qnt +764 -0
- package/specs/apg/combobox-dialog.qnt +627 -0
- package/specs/apg/combobox-grid.qnt +632 -0
- package/specs/apg/combobox-select-only.qnt +626 -0
- package/specs/apg/combobox-tree.qnt +633 -0
- package/specs/apg/dialog-modal.qnt +499 -0
- package/specs/apg/disclosure.qnt +309 -0
- package/specs/apg/feed.qnt +485 -0
- package/specs/apg/grid-selection.qnt +512 -0
- package/specs/apg/grid-sort.qnt +330 -0
- package/specs/apg/grid.qnt +360 -0
- package/specs/apg/link.qnt +119 -0
- package/specs/apg/listbox-multi.qnt +799 -0
- package/specs/apg/listbox-rearrangeable.qnt +801 -0
- package/specs/apg/listbox-single.qnt +511 -0
- package/specs/apg/listbox-typeahead.qnt +396 -0
- package/specs/apg/menu-button.qnt +363 -0
- package/specs/apg/menu-checkable.qnt +304 -0
- package/specs/apg/menu-menubar.qnt +531 -0
- package/specs/apg/meter.qnt +129 -0
- package/specs/apg/player.qnt +1081 -0
- package/specs/apg/radio.qnt +394 -0
- package/specs/apg/slider-multithumb.qnt +441 -0
- package/specs/apg/slider.qnt +396 -0
- package/specs/apg/spinbutton.qnt +494 -0
- package/specs/apg/switch.qnt +389 -0
- package/specs/apg/tabs.qnt +412 -0
- package/specs/apg/timeline.qnt +612 -0
- package/specs/apg/toolbar.qnt +364 -0
- package/specs/apg/tooltip.qnt +553 -0
- package/specs/apg/treegrid.qnt +983 -0
- package/specs/apg/treeview.qnt +1004 -0
- package/specs/apg/windowsplitter.qnt +487 -0
- package/specs/canvas/camera.qnt +407 -0
- package/specs/canvas/pointer.qnt +841 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ActionStep, Probe, ScenarioConfig, SpecBridge } from "../scenario.mjs";
|
|
2
|
+
//#region src/apg/grid.bridge.d.ts
|
|
3
|
+
/** The spec's fixed grid size (`specs/apg/grid.qnt`). */
|
|
4
|
+
export declare const GRID_ROWS = 4;
|
|
5
|
+
export declare const GRID_COLS = 3;
|
|
6
|
+
export type GridKindName = "data" | "layout";
|
|
7
|
+
export type GridFocusTargetName = "cell" | "widget";
|
|
8
|
+
/**
|
|
9
|
+
* The options `createGrid` is mounted with; the bridge and the fixture share it.
|
|
10
|
+
* The spec is fixed at 4x3, so differential walks only use that size.
|
|
11
|
+
*/
|
|
12
|
+
export interface GridConfig {
|
|
13
|
+
readonly rows: number;
|
|
14
|
+
readonly cols: number;
|
|
15
|
+
readonly gridKind: GridKindName;
|
|
16
|
+
readonly wrapCols: boolean;
|
|
17
|
+
readonly wrapRows: boolean;
|
|
18
|
+
readonly focusTarget: GridFocusTargetName;
|
|
19
|
+
readonly gridReadonly: boolean;
|
|
20
|
+
readonly selectable: boolean;
|
|
21
|
+
readonly sortEnabled: boolean;
|
|
22
|
+
/** The host owns the selected rectangle (`value` prop present). */
|
|
23
|
+
readonly selectionControlled: boolean;
|
|
24
|
+
/** Controlled selection only: the host writes every request's intent back. */
|
|
25
|
+
readonly selectionAccepts: boolean;
|
|
26
|
+
/** Controlled selection only: a pinned packed value the host holds (rejects). */
|
|
27
|
+
readonly selectionValue: number | null;
|
|
28
|
+
/** The host owns the sort (`sortValue` prop present). */
|
|
29
|
+
readonly sortControlled: boolean;
|
|
30
|
+
/** Controlled sort only: the host writes every request's intent back. */
|
|
31
|
+
readonly sortAccepts: boolean;
|
|
32
|
+
/** Controlled sort only: a pinned packed value the host holds (rejects). */
|
|
33
|
+
readonly sortValue: number | null;
|
|
34
|
+
}
|
|
35
|
+
export declare const isSelectionValue: (value: number) => boolean;
|
|
36
|
+
export declare const isSortValue: (value: number) => boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Scenario `config` -> what both targets are mounted with. Keys the primitive
|
|
39
|
+
* does not model (orientation, rtl, virtualized windows, looping callbacks,
|
|
40
|
+
* ragged columns, ...) make the scenario `Unsupported` on every target.
|
|
41
|
+
*/
|
|
42
|
+
export declare const normalizeGridConfig: (config: ScenarioConfig, base?: Partial<GridConfig>) => GridConfig;
|
|
43
|
+
export declare const gridConfig: (config: ScenarioConfig) => GridConfig;
|
|
44
|
+
/**
|
|
45
|
+
* `grid.qnt` is NAVIGATION-ONLY (map ticket rt1p): focus is roving internal
|
|
46
|
+
* state, so there is no host-owned value here and the bridge has no `value` /
|
|
47
|
+
* `events`. Controlled selection and sort are modelled by
|
|
48
|
+
* `grid-selection.bridge.ts` / `grid-sort.bridge.ts`.
|
|
49
|
+
*/
|
|
50
|
+
export declare const gridBridge: SpecBridge;
|
|
51
|
+
/** The user-level steps a differential walk draws from. */
|
|
52
|
+
export declare const gridSteps: ReadonlyArray<ActionStep>;
|
|
53
|
+
export declare const gridProbe: Probe;
|
|
54
|
+
//#endregion
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import { Unsupported } from "../scenario.mjs";
|
|
2
|
+
//#region src/apg/grid.bridge.ts
|
|
3
|
+
const specEvent = (tag, params = {}) => ({
|
|
4
|
+
_tag: tag,
|
|
5
|
+
...params
|
|
6
|
+
});
|
|
7
|
+
const num = (value) => Number(value ?? 0);
|
|
8
|
+
/** The spec's fixed grid size (`specs/apg/grid.qnt`). */
|
|
9
|
+
const GRID_ROWS = 4;
|
|
10
|
+
const GRID_COLS = 3;
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
rows: 4,
|
|
13
|
+
cols: 3,
|
|
14
|
+
gridKind: "data",
|
|
15
|
+
wrapCols: false,
|
|
16
|
+
wrapRows: false,
|
|
17
|
+
focusTarget: "cell",
|
|
18
|
+
gridReadonly: false,
|
|
19
|
+
selectable: false,
|
|
20
|
+
sortEnabled: false,
|
|
21
|
+
selectionControlled: false,
|
|
22
|
+
selectionAccepts: false,
|
|
23
|
+
selectionValue: null,
|
|
24
|
+
sortControlled: false,
|
|
25
|
+
sortAccepts: false,
|
|
26
|
+
sortValue: null
|
|
27
|
+
};
|
|
28
|
+
/** The valid packed selection values the fixed 4x3 grid can hold (0 = none). */
|
|
29
|
+
const SELECTION_VALUES = /* @__PURE__ */ new Set([
|
|
30
|
+
0,
|
|
31
|
+
1,
|
|
32
|
+
5,
|
|
33
|
+
6,
|
|
34
|
+
9,
|
|
35
|
+
10,
|
|
36
|
+
11,
|
|
37
|
+
13,
|
|
38
|
+
14,
|
|
39
|
+
15,
|
|
40
|
+
16,
|
|
41
|
+
49,
|
|
42
|
+
53,
|
|
43
|
+
54,
|
|
44
|
+
57,
|
|
45
|
+
58,
|
|
46
|
+
59,
|
|
47
|
+
61,
|
|
48
|
+
62,
|
|
49
|
+
63,
|
|
50
|
+
64,
|
|
51
|
+
65,
|
|
52
|
+
69,
|
|
53
|
+
70,
|
|
54
|
+
73,
|
|
55
|
+
74,
|
|
56
|
+
75,
|
|
57
|
+
77,
|
|
58
|
+
78,
|
|
59
|
+
79,
|
|
60
|
+
80,
|
|
61
|
+
97,
|
|
62
|
+
101,
|
|
63
|
+
102,
|
|
64
|
+
105,
|
|
65
|
+
106,
|
|
66
|
+
107,
|
|
67
|
+
109,
|
|
68
|
+
110,
|
|
69
|
+
111,
|
|
70
|
+
112,
|
|
71
|
+
113,
|
|
72
|
+
117,
|
|
73
|
+
118,
|
|
74
|
+
121,
|
|
75
|
+
122,
|
|
76
|
+
123,
|
|
77
|
+
125,
|
|
78
|
+
126,
|
|
79
|
+
127,
|
|
80
|
+
128,
|
|
81
|
+
129,
|
|
82
|
+
133,
|
|
83
|
+
134,
|
|
84
|
+
137,
|
|
85
|
+
138,
|
|
86
|
+
139,
|
|
87
|
+
141,
|
|
88
|
+
142,
|
|
89
|
+
143,
|
|
90
|
+
144
|
|
91
|
+
]);
|
|
92
|
+
/** The valid packed sort values: -1 unsorted, else 2*column + (desc ? 1 : 0). */
|
|
93
|
+
const SORT_VALUES = /* @__PURE__ */ new Set([
|
|
94
|
+
-1,
|
|
95
|
+
0,
|
|
96
|
+
1,
|
|
97
|
+
4,
|
|
98
|
+
5
|
|
99
|
+
]);
|
|
100
|
+
const isSelectionValue = (value) => SELECTION_VALUES.has(value);
|
|
101
|
+
const isSortValue = (value) => SORT_VALUES.has(value);
|
|
102
|
+
const positiveInt = (key, value) => {
|
|
103
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
|
|
104
|
+
return value;
|
|
105
|
+
};
|
|
106
|
+
const bool = (key, value) => {
|
|
107
|
+
if (typeof value !== "boolean") throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
|
|
108
|
+
return value;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Scenario `config` -> what both targets are mounted with. Keys the primitive
|
|
112
|
+
* does not model (orientation, rtl, virtualized windows, looping callbacks,
|
|
113
|
+
* ragged columns, ...) make the scenario `Unsupported` on every target.
|
|
114
|
+
*/
|
|
115
|
+
const normalizeGridConfig = (config, base = {}) => {
|
|
116
|
+
let rows = base.rows ?? DEFAULTS.rows;
|
|
117
|
+
let cols = base.cols ?? DEFAULTS.cols;
|
|
118
|
+
let gridKind = base.gridKind ?? DEFAULTS.gridKind;
|
|
119
|
+
let wrapCols = base.wrapCols ?? DEFAULTS.wrapCols;
|
|
120
|
+
let wrapRows = base.wrapRows ?? DEFAULTS.wrapRows;
|
|
121
|
+
let focusTarget = base.focusTarget ?? DEFAULTS.focusTarget;
|
|
122
|
+
let gridReadonly = base.gridReadonly ?? DEFAULTS.gridReadonly;
|
|
123
|
+
let selectable = base.selectable ?? DEFAULTS.selectable;
|
|
124
|
+
let sortEnabled = base.sortEnabled ?? DEFAULTS.sortEnabled;
|
|
125
|
+
let selectionControlled = base.selectionControlled ?? DEFAULTS.selectionControlled;
|
|
126
|
+
let selectionValue = base.selectionValue ?? DEFAULTS.selectionValue;
|
|
127
|
+
let sortControlled = base.sortControlled ?? DEFAULTS.sortControlled;
|
|
128
|
+
let sortValue = base.sortValue ?? DEFAULTS.sortValue;
|
|
129
|
+
for (const [key, value] of Object.entries(config)) switch (key) {
|
|
130
|
+
case "rows":
|
|
131
|
+
rows = positiveInt(key, value);
|
|
132
|
+
break;
|
|
133
|
+
case "cols":
|
|
134
|
+
cols = positiveInt(key, value);
|
|
135
|
+
break;
|
|
136
|
+
case "layout":
|
|
137
|
+
if (value !== "grid") throw new Unsupported(`config layout=${JSON.stringify(value)}`);
|
|
138
|
+
break;
|
|
139
|
+
case "gridKind":
|
|
140
|
+
if (value !== "data" && value !== "layout") throw new Unsupported(`config gridKind=${JSON.stringify(value)}`);
|
|
141
|
+
gridKind = value;
|
|
142
|
+
break;
|
|
143
|
+
case "wrapCols":
|
|
144
|
+
wrapCols = bool(key, value);
|
|
145
|
+
break;
|
|
146
|
+
case "wrapRows":
|
|
147
|
+
wrapRows = bool(key, value);
|
|
148
|
+
break;
|
|
149
|
+
case "focusTarget":
|
|
150
|
+
if (value !== "cell" && value !== "widget") throw new Unsupported(`config focusTarget=${JSON.stringify(value)}`);
|
|
151
|
+
focusTarget = value;
|
|
152
|
+
break;
|
|
153
|
+
case "gridReadonly":
|
|
154
|
+
case "readonly":
|
|
155
|
+
gridReadonly = bool(key, value);
|
|
156
|
+
break;
|
|
157
|
+
case "selectable":
|
|
158
|
+
selectable = bool(key, value);
|
|
159
|
+
break;
|
|
160
|
+
case "sortEnabled":
|
|
161
|
+
sortEnabled = bool(key, value);
|
|
162
|
+
break;
|
|
163
|
+
case "controlled":
|
|
164
|
+
if (typeof value !== "boolean") throw new Unsupported(`config controlled=${JSON.stringify(value)}`);
|
|
165
|
+
selectionControlled ||= value;
|
|
166
|
+
break;
|
|
167
|
+
case "selection":
|
|
168
|
+
if (typeof value !== "number" || !SELECTION_VALUES.has(value)) throw new Unsupported(`config selection=${JSON.stringify(value)}`);
|
|
169
|
+
selectionValue = value;
|
|
170
|
+
selectionControlled = true;
|
|
171
|
+
break;
|
|
172
|
+
case "sortControlled":
|
|
173
|
+
if (typeof value !== "boolean") throw new Unsupported(`config sortControlled=${JSON.stringify(value)}`);
|
|
174
|
+
sortControlled ||= value;
|
|
175
|
+
break;
|
|
176
|
+
case "sort":
|
|
177
|
+
if (typeof value !== "number" || !SORT_VALUES.has(value)) throw new Unsupported(`config sort=${JSON.stringify(value)}`);
|
|
178
|
+
sortValue = value;
|
|
179
|
+
sortControlled = true;
|
|
180
|
+
break;
|
|
181
|
+
case "enableHomeAndEndKeys":
|
|
182
|
+
if (value !== true) throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
|
|
183
|
+
break;
|
|
184
|
+
case "loopFocus":
|
|
185
|
+
if (value !== false) throw new Unsupported(`config loopFocus=${JSON.stringify(value)}`);
|
|
186
|
+
break;
|
|
187
|
+
default: throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
|
|
188
|
+
}
|
|
189
|
+
if (gridKind === "data") {
|
|
190
|
+
wrapCols = false;
|
|
191
|
+
wrapRows = false;
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
rows,
|
|
195
|
+
cols,
|
|
196
|
+
gridKind,
|
|
197
|
+
wrapCols,
|
|
198
|
+
wrapRows,
|
|
199
|
+
focusTarget,
|
|
200
|
+
gridReadonly,
|
|
201
|
+
selectable,
|
|
202
|
+
sortEnabled,
|
|
203
|
+
selectionControlled,
|
|
204
|
+
selectionAccepts: selectionControlled && selectionValue === null,
|
|
205
|
+
selectionValue,
|
|
206
|
+
sortControlled,
|
|
207
|
+
sortAccepts: sortControlled && sortValue === null,
|
|
208
|
+
sortValue
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
const gridConfig = (config) => normalizeGridConfig(config);
|
|
212
|
+
const CELL = /^cell:(\d+),(\d+)$/;
|
|
213
|
+
const WIDGET = /^cell:(\d+),(\d+)\/widget$/;
|
|
214
|
+
const cellOf = (target) => {
|
|
215
|
+
const match = CELL.exec(target ?? "");
|
|
216
|
+
if (match === null) throw new Unsupported(`target ${target ?? ""}`);
|
|
217
|
+
return {
|
|
218
|
+
row: Number(match[1]),
|
|
219
|
+
col: Number(match[2])
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
const widgetOf = (target) => {
|
|
223
|
+
const match = WIDGET.exec(target ?? "");
|
|
224
|
+
if (match === null) throw new Unsupported(`target ${target ?? ""}`);
|
|
225
|
+
return {
|
|
226
|
+
row: Number(match[1]),
|
|
227
|
+
col: Number(match[2])
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
const isGridTarget = (target) => target === void 0 || target === "grid";
|
|
231
|
+
/** The spec key pool (`grid.qnt`'s `key` nondet set). */
|
|
232
|
+
const GRID_KEYS = /* @__PURE__ */ new Set([
|
|
233
|
+
"ArrowRight",
|
|
234
|
+
"ArrowLeft",
|
|
235
|
+
"ArrowDown",
|
|
236
|
+
"ArrowUp",
|
|
237
|
+
"Home",
|
|
238
|
+
"End",
|
|
239
|
+
"Control+Home",
|
|
240
|
+
"Control+End",
|
|
241
|
+
"PageUp",
|
|
242
|
+
"PageDown",
|
|
243
|
+
"Enter",
|
|
244
|
+
"F2",
|
|
245
|
+
"Escape",
|
|
246
|
+
"a",
|
|
247
|
+
"Tab",
|
|
248
|
+
"Shift+Tab"
|
|
249
|
+
]);
|
|
250
|
+
/**
|
|
251
|
+
* `grid.qnt` is NAVIGATION-ONLY (map ticket rt1p): focus is roving internal
|
|
252
|
+
* state, so there is no host-owned value here and the bridge has no `value` /
|
|
253
|
+
* `events`. Controlled selection and sort are modelled by
|
|
254
|
+
* `grid-selection.bridge.ts` / `grid-sort.bridge.ts`.
|
|
255
|
+
*/
|
|
256
|
+
const gridBridge = {
|
|
257
|
+
setup: (config) => {
|
|
258
|
+
const value = gridConfig(config);
|
|
259
|
+
return [
|
|
260
|
+
...value.gridKind === "layout" ? [specEvent("set_kind", { choice: "layout" })] : [],
|
|
261
|
+
...value.wrapCols ? [specEvent("set_wrap_cols", { flag: true })] : [],
|
|
262
|
+
...value.wrapRows ? [specEvent("set_wrap_rows", { flag: true })] : [],
|
|
263
|
+
...value.focusTarget === "widget" ? [specEvent("set_focus_target", { choice: "widget" })] : [],
|
|
264
|
+
...value.gridReadonly ? [specEvent("set_readonly", { flag: true })] : []
|
|
265
|
+
];
|
|
266
|
+
},
|
|
267
|
+
event: (step) => {
|
|
268
|
+
switch (step.event) {
|
|
269
|
+
case "focus": {
|
|
270
|
+
if (isGridTarget(step.target)) return [specEvent("focus_in")];
|
|
271
|
+
const { row, col } = cellOf(step.target);
|
|
272
|
+
return [specEvent("click", { cell: BigInt(row * 3 + col) })];
|
|
273
|
+
}
|
|
274
|
+
case "blur": return [specEvent("focus_out")];
|
|
275
|
+
case "click": {
|
|
276
|
+
const { row, col } = cellOf(step.target);
|
|
277
|
+
return [specEvent("click", { cell: BigInt(row * 3 + col) })];
|
|
278
|
+
}
|
|
279
|
+
case "key":
|
|
280
|
+
if (!GRID_KEYS.has(step.key ?? "")) throw new Unsupported(`key ${step.key ?? ""}`);
|
|
281
|
+
return [specEvent("key", { key: step.key ?? "" })];
|
|
282
|
+
case "hover":
|
|
283
|
+
case "unhover": return [];
|
|
284
|
+
case "prop": switch (step.prop) {
|
|
285
|
+
case "gridKind":
|
|
286
|
+
case "kind":
|
|
287
|
+
if (step.value !== "data" && step.value !== "layout") throw new Unsupported(`prop ${step.prop}=${JSON.stringify(step.value)}`);
|
|
288
|
+
return [specEvent("set_kind", { choice: step.value })];
|
|
289
|
+
case "wrapCols": return [specEvent("set_wrap_cols", { flag: step.value === true })];
|
|
290
|
+
case "wrapRows": return [specEvent("set_wrap_rows", { flag: step.value === true })];
|
|
291
|
+
case "focusTarget":
|
|
292
|
+
if (step.value !== "cell" && step.value !== "widget") throw new Unsupported(`prop focusTarget=${JSON.stringify(step.value)}`);
|
|
293
|
+
return [specEvent("set_focus_target", { choice: step.value })];
|
|
294
|
+
case "readonly":
|
|
295
|
+
case "gridReadonly": return [specEvent("set_readonly", { flag: step.value === true })];
|
|
296
|
+
default: throw new Unsupported(`prop ${step.prop ?? ""}`);
|
|
297
|
+
}
|
|
298
|
+
default: throw new Unsupported(`event ${step.event}`);
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
focus: (state) => {
|
|
302
|
+
if (state["hasFocus"] !== true) return null;
|
|
303
|
+
return `cell:${num(state["row"])},${num(state["col"])}`;
|
|
304
|
+
},
|
|
305
|
+
attr: (state, target, name) => {
|
|
306
|
+
if (isGridTarget(target)) {
|
|
307
|
+
if (name === "aria-rowcount") return String(4);
|
|
308
|
+
if (name === "aria-colcount") return String(3);
|
|
309
|
+
if (name === "aria-readonly") return state["gridReadonly"] === true ? "true" : "false";
|
|
310
|
+
throw new Unsupported(`attribute ${name} on grid`);
|
|
311
|
+
}
|
|
312
|
+
if (WIDGET.exec(target) !== null) {
|
|
313
|
+
if (name !== "tabindex") throw new Unsupported(`attribute ${name} on ${target}`);
|
|
314
|
+
const cell = widgetOf(target);
|
|
315
|
+
return state["focusTarget"] === "widget" && cell.row === num(state["row"]) && cell.col === num(state["col"]) ? "0" : "-1";
|
|
316
|
+
}
|
|
317
|
+
const cell = cellOf(target);
|
|
318
|
+
switch (name) {
|
|
319
|
+
case "tabindex": return state["focusTarget"] === "cell" && cell.row === num(state["row"]) && cell.col === num(state["col"]) ? "0" : "-1";
|
|
320
|
+
case "aria-rowindex": return String(cell.row + 1);
|
|
321
|
+
case "aria-colindex": return String(cell.col + 1);
|
|
322
|
+
case "aria-readonly": return state["gridReadonly"] === true || cell.col === 2 ? "true" : "false";
|
|
323
|
+
case "aria-selected": throw new Unsupported("selection is modelled by grid-selection.bridge");
|
|
324
|
+
default: throw new Unsupported(`attribute ${name} on ${target}`);
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
visible: () => [],
|
|
328
|
+
events: () => []
|
|
329
|
+
};
|
|
330
|
+
/** The user-level steps a differential walk draws from. */
|
|
331
|
+
const gridSteps = [
|
|
332
|
+
...Array.from({ length: 12 }, (_, index) => ({
|
|
333
|
+
event: "click",
|
|
334
|
+
target: `cell:${Math.floor(index / 3)},${index % 3}`
|
|
335
|
+
})),
|
|
336
|
+
...[
|
|
337
|
+
"ArrowRight",
|
|
338
|
+
"ArrowLeft",
|
|
339
|
+
"ArrowDown",
|
|
340
|
+
"ArrowUp",
|
|
341
|
+
"Home",
|
|
342
|
+
"End",
|
|
343
|
+
"Control+Home",
|
|
344
|
+
"Control+End",
|
|
345
|
+
"PageUp",
|
|
346
|
+
"PageDown",
|
|
347
|
+
"Enter",
|
|
348
|
+
"F2",
|
|
349
|
+
"Escape",
|
|
350
|
+
"a",
|
|
351
|
+
"Tab",
|
|
352
|
+
"Shift+Tab"
|
|
353
|
+
].map((key) => ({
|
|
354
|
+
event: "key",
|
|
355
|
+
key,
|
|
356
|
+
target: "grid"
|
|
357
|
+
})),
|
|
358
|
+
{
|
|
359
|
+
event: "focus",
|
|
360
|
+
target: "grid"
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
event: "blur",
|
|
364
|
+
target: "grid"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
event: "prop",
|
|
368
|
+
prop: "gridKind",
|
|
369
|
+
value: "layout"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
event: "prop",
|
|
373
|
+
prop: "gridKind",
|
|
374
|
+
value: "data"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
event: "prop",
|
|
378
|
+
prop: "wrapCols",
|
|
379
|
+
value: true
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
event: "prop",
|
|
383
|
+
prop: "wrapCols",
|
|
384
|
+
value: false
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
event: "prop",
|
|
388
|
+
prop: "wrapRows",
|
|
389
|
+
value: true
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
event: "prop",
|
|
393
|
+
prop: "wrapRows",
|
|
394
|
+
value: false
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
event: "prop",
|
|
398
|
+
prop: "focusTarget",
|
|
399
|
+
value: "widget"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
event: "prop",
|
|
403
|
+
prop: "focusTarget",
|
|
404
|
+
value: "cell"
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
event: "prop",
|
|
408
|
+
prop: "readonly",
|
|
409
|
+
value: true
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
event: "prop",
|
|
413
|
+
prop: "readonly",
|
|
414
|
+
value: false
|
|
415
|
+
}
|
|
416
|
+
];
|
|
417
|
+
const GRID_CELL_ATTRS = [
|
|
418
|
+
"tabindex",
|
|
419
|
+
"aria-rowindex",
|
|
420
|
+
"aria-colindex",
|
|
421
|
+
"aria-readonly"
|
|
422
|
+
];
|
|
423
|
+
const cellTargets = () => Array.from({ length: 12 }, (_, index) => {
|
|
424
|
+
const row = Math.floor(index / 3);
|
|
425
|
+
const col = index % 3;
|
|
426
|
+
return GRID_CELL_ATTRS.map((name) => [`cell:${row},${col}`, name]);
|
|
427
|
+
}).flat();
|
|
428
|
+
const gridProbe = {
|
|
429
|
+
attrs: [
|
|
430
|
+
["grid", "aria-rowcount"],
|
|
431
|
+
["grid", "aria-colcount"],
|
|
432
|
+
["grid", "aria-readonly"],
|
|
433
|
+
...cellTargets(),
|
|
434
|
+
...Array.from({ length: 12 }, (_, index) => {
|
|
435
|
+
return [`cell:${Math.floor(index / 3)},${index % 3}/widget`, "tabindex"];
|
|
436
|
+
})
|
|
437
|
+
],
|
|
438
|
+
events: []
|
|
439
|
+
};
|
|
440
|
+
//#endregion
|
|
441
|
+
export { GRID_COLS, GRID_ROWS, gridBridge, gridConfig, gridProbe, gridSteps, isSelectionValue, isSortValue, normalizeGridConfig };
|