@cssints/apg-specs 0.0.0-stage → 0.5.1
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,31 @@
|
|
|
1
|
+
import { ActionStep, Probe, ScenarioConfig, SpecBridge } from "../scenario.mjs";
|
|
2
|
+
//#region src/apg/feed.bridge.d.ts
|
|
3
|
+
export interface FeedConfig {
|
|
4
|
+
readonly loaded: number;
|
|
5
|
+
readonly totalArticles: number | null;
|
|
6
|
+
readonly sizeMode: "loaded" | "total";
|
|
7
|
+
readonly totalKnown: boolean;
|
|
8
|
+
readonly labelledBy: string | undefined;
|
|
9
|
+
/** The host owns the loaded count (`value` prop present). */
|
|
10
|
+
readonly controlled: boolean;
|
|
11
|
+
/** Controlled only: the host writes every request's intent back. */
|
|
12
|
+
readonly accepts: boolean;
|
|
13
|
+
/** Controlled only: a pinned loaded count the host holds (rejects). */
|
|
14
|
+
readonly pinned: number | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Scenario `config` -> what both targets are mounted with. The generated model
|
|
18
|
+
* fixes the server total at six articles, so a page total is only informational
|
|
19
|
+
* for the spec side; `loaded` beyond six is not modelled and is `Unsupported`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const feedConfig: (config: ScenarioConfig) => FeedConfig;
|
|
22
|
+
/**
|
|
23
|
+
* Feed vocabulary <-> `specs/apg/feed.qnt`, shared by the spec driver and the
|
|
24
|
+
* DOM driver. Parts: `feed`, `article:N` (aria-posinset N), and the
|
|
25
|
+
* `before-feed` / `after-feed` focus sentinels for the two outside zones.
|
|
26
|
+
*/
|
|
27
|
+
export declare const feedBridge: SpecBridge;
|
|
28
|
+
/** The user-level steps a differential walk draws from. */
|
|
29
|
+
export declare const feedSteps: ReadonlyArray<ActionStep>;
|
|
30
|
+
export declare const feedProbe: Probe;
|
|
31
|
+
//#endregion
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { Unsupported } from "../scenario.mjs";
|
|
2
|
+
//#region src/apg/feed.bridge.ts
|
|
3
|
+
const specEvent = (tag, params = {}) => ({
|
|
4
|
+
_tag: tag,
|
|
5
|
+
...params
|
|
6
|
+
});
|
|
7
|
+
/** The spec's feed grows to at most six articles (fixed `totalArticles`). */
|
|
8
|
+
const MAX_LOADED = 6;
|
|
9
|
+
/**
|
|
10
|
+
* Scenario `config` -> what both targets are mounted with. The generated model
|
|
11
|
+
* fixes the server total at six articles, so a page total is only informational
|
|
12
|
+
* for the spec side; `loaded` beyond six is not modelled and is `Unsupported`.
|
|
13
|
+
*/
|
|
14
|
+
const feedConfig = (config) => {
|
|
15
|
+
let loaded = 1;
|
|
16
|
+
let totalArticles = 6;
|
|
17
|
+
let sizeMode = "loaded";
|
|
18
|
+
let totalKnown = false;
|
|
19
|
+
let labelledBy;
|
|
20
|
+
let controlled = false;
|
|
21
|
+
let pinned = null;
|
|
22
|
+
for (const [key, value] of Object.entries(config)) if (key === "loaded") {
|
|
23
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > MAX_LOADED) throw new Unsupported(`config loaded=${JSON.stringify(value)}`);
|
|
24
|
+
loaded = value;
|
|
25
|
+
} else if (key === "totalArticles") {
|
|
26
|
+
if (value !== null && (typeof value !== "number" || value < 1)) throw new Unsupported(`config totalArticles=${JSON.stringify(value)}`);
|
|
27
|
+
totalArticles = value;
|
|
28
|
+
} else if (key === "sizeMode") {
|
|
29
|
+
if (value !== "loaded" && value !== "total") throw new Unsupported(`config sizeMode=${JSON.stringify(value)}`);
|
|
30
|
+
sizeMode = value;
|
|
31
|
+
} else if (key === "totalKnown") {
|
|
32
|
+
if (typeof value !== "boolean") throw new Unsupported(`config totalKnown=${JSON.stringify(value)}`);
|
|
33
|
+
totalKnown = value;
|
|
34
|
+
} else if (key === "labelledby") {
|
|
35
|
+
if (typeof value !== "string") throw new Unsupported(`config labelledby=${JSON.stringify(value)}`);
|
|
36
|
+
labelledBy = value;
|
|
37
|
+
} else if (key === "controlled") {
|
|
38
|
+
if (typeof value !== "boolean") throw new Unsupported(`config controlled=${JSON.stringify(value)}`);
|
|
39
|
+
controlled ||= value;
|
|
40
|
+
} else if (key === "value") {
|
|
41
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > MAX_LOADED) throw new Unsupported(`config value=${JSON.stringify(value)}`);
|
|
42
|
+
pinned = value;
|
|
43
|
+
controlled = true;
|
|
44
|
+
} else throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
|
|
45
|
+
return {
|
|
46
|
+
loaded,
|
|
47
|
+
totalArticles,
|
|
48
|
+
sizeMode,
|
|
49
|
+
totalKnown,
|
|
50
|
+
labelledBy,
|
|
51
|
+
controlled,
|
|
52
|
+
accepts: controlled && pinned === null,
|
|
53
|
+
pinned
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const articleIndex = (target) => {
|
|
57
|
+
const [name = "", indexText] = target.split(":");
|
|
58
|
+
if (name !== "article") throw new Unsupported(`part ${name}`);
|
|
59
|
+
const index = Number(indexText ?? "0");
|
|
60
|
+
if (!Number.isInteger(index) || index < 1) throw new Unsupported(`article ${target}`);
|
|
61
|
+
return index;
|
|
62
|
+
};
|
|
63
|
+
const programmatic = (step) => {
|
|
64
|
+
switch (step.prop) {
|
|
65
|
+
case "loadStart": return [specEvent("load_start")];
|
|
66
|
+
case "loadFinish": {
|
|
67
|
+
const n = typeof step.value === "number" ? step.value : 0;
|
|
68
|
+
if (!Number.isInteger(n) || n < 1 || n > 3) throw new Unsupported(`loadFinish ${JSON.stringify(step.value)}`);
|
|
69
|
+
return [specEvent("load_finish", { n: BigInt(n) })];
|
|
70
|
+
}
|
|
71
|
+
case "loadFail": return [specEvent("load_fail")];
|
|
72
|
+
case "setValue": {
|
|
73
|
+
const n = typeof step.value === "number" ? step.value : 0;
|
|
74
|
+
if (!Number.isInteger(n) || n < 1 || n > MAX_LOADED) throw new Unsupported(`setValue ${JSON.stringify(step.value)}`);
|
|
75
|
+
return [specEvent("setValue", { v: BigInt(n) })];
|
|
76
|
+
}
|
|
77
|
+
case "sizeMode":
|
|
78
|
+
if (step.value !== "loaded" && step.value !== "total") throw new Unsupported(`sizeMode ${JSON.stringify(step.value)}`);
|
|
79
|
+
return [specEvent("set_size_mode", { mode: step.value })];
|
|
80
|
+
case "totalKnown": return [specEvent("set_total_known", { known: step.value === true })];
|
|
81
|
+
default: throw new Unsupported(`prop ${step.prop ?? ""}`);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/** The load batches that grow a fresh model to `target` articles. */
|
|
85
|
+
const batches = (target) => {
|
|
86
|
+
const out = [];
|
|
87
|
+
let remaining = target - 1;
|
|
88
|
+
while (remaining > 0) {
|
|
89
|
+
const size = Math.min(remaining, 3);
|
|
90
|
+
out.push(size);
|
|
91
|
+
remaining -= size;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
};
|
|
95
|
+
/** The focus event a step's target implies before its key acts (`key` first focuses). */
|
|
96
|
+
const focusEvents = (target) => {
|
|
97
|
+
if (target === "before-feed") return [specEvent("focus_outside", { side: "before" })];
|
|
98
|
+
if (target === "after-feed") return [specEvent("focus_outside", { side: "after" })];
|
|
99
|
+
return [specEvent("focus_article", { n: BigInt(articleIndex(target ?? "")) })];
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Feed vocabulary <-> `specs/apg/feed.qnt`, shared by the spec driver and the
|
|
103
|
+
* DOM driver. Parts: `feed`, `article:N` (aria-posinset N), and the
|
|
104
|
+
* `before-feed` / `after-feed` focus sentinels for the two outside zones.
|
|
105
|
+
*/
|
|
106
|
+
const feedBridge = {
|
|
107
|
+
setup: (config) => {
|
|
108
|
+
const feed = feedConfig(config);
|
|
109
|
+
const events = [];
|
|
110
|
+
for (const size of batches(feed.loaded)) {
|
|
111
|
+
events.push(specEvent("load_start"));
|
|
112
|
+
events.push(specEvent("load_finish", { n: BigInt(size) }));
|
|
113
|
+
}
|
|
114
|
+
if (feed.sizeMode === "total") events.push(specEvent("set_size_mode", { mode: "total" }));
|
|
115
|
+
if (feed.totalKnown) events.push(specEvent("set_total_known", { known: true }));
|
|
116
|
+
if (feed.controlled) {
|
|
117
|
+
events.push(specEvent("control"));
|
|
118
|
+
const initial = feed.pinned ?? feed.loaded;
|
|
119
|
+
if (initial !== feed.loaded) events.push(specEvent("setValue", { v: BigInt(initial) }));
|
|
120
|
+
if (feed.accepts) events.push(specEvent("acceptRequests"));
|
|
121
|
+
}
|
|
122
|
+
return events;
|
|
123
|
+
},
|
|
124
|
+
event: (step) => {
|
|
125
|
+
switch (step.event) {
|
|
126
|
+
case "focus":
|
|
127
|
+
if (step.target === "before-feed") return [specEvent("focus_outside", { side: "before" })];
|
|
128
|
+
if (step.target === "after-feed") return [specEvent("focus_outside", { side: "after" })];
|
|
129
|
+
return [specEvent("focus_article", { n: BigInt(articleIndex(step.target ?? "")) })];
|
|
130
|
+
case "key": {
|
|
131
|
+
const focus = focusEvents(step.target);
|
|
132
|
+
switch (step.key) {
|
|
133
|
+
case "PageDown": return [...focus, specEvent("page_down")];
|
|
134
|
+
case "PageUp": return [...focus, specEvent("page_up")];
|
|
135
|
+
case "Control+End": return [...focus, specEvent("ctrl_end")];
|
|
136
|
+
case "Control+Home": return [...focus, specEvent("ctrl_home")];
|
|
137
|
+
default: throw new Unsupported(`key ${step.key ?? ""}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
case "hover":
|
|
141
|
+
case "unhover": return [];
|
|
142
|
+
case "prop": return programmatic(step);
|
|
143
|
+
default: throw new Unsupported(`event ${step.event}`);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
focus: (state) => {
|
|
147
|
+
const zone = state["zone"];
|
|
148
|
+
if (zone === "feed") return `article:${state["focus"]}`;
|
|
149
|
+
if (zone === "before") return "before-feed";
|
|
150
|
+
if (zone === "after") return "after-feed";
|
|
151
|
+
return null;
|
|
152
|
+
},
|
|
153
|
+
attr: (state, target, name) => {
|
|
154
|
+
if (target === "feed") {
|
|
155
|
+
if (name === "aria-busy") return state["busy"] === true ? "true" : null;
|
|
156
|
+
if (name === "role") return "feed";
|
|
157
|
+
}
|
|
158
|
+
const index = articleIndex(target);
|
|
159
|
+
if (name === "aria-posinset") return String(index);
|
|
160
|
+
if (name === "aria-setsize") {
|
|
161
|
+
if (state["sizeMode"] === "loaded") return String(state["loaded"]);
|
|
162
|
+
return state["totalKnown"] === true ? "6" : "-1";
|
|
163
|
+
}
|
|
164
|
+
if (name === "role") return "article";
|
|
165
|
+
if (name === "tabindex") return "0";
|
|
166
|
+
throw new Unsupported(`attribute ${name} on ${target}`);
|
|
167
|
+
},
|
|
168
|
+
visible: (state) => Array.from({ length: Number(state["loaded"]) }, (_, i) => `article:${i + 1}`),
|
|
169
|
+
events: (before, after) => before["requests"] === after["requests"] ? [] : [["onLoadedChange", Number(after["requested"])]]
|
|
170
|
+
};
|
|
171
|
+
/** The user-level steps a differential walk draws from. */
|
|
172
|
+
const feedSteps = [
|
|
173
|
+
{
|
|
174
|
+
event: "focus",
|
|
175
|
+
target: "article:1"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
event: "focus",
|
|
179
|
+
target: "article:2"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
event: "focus",
|
|
183
|
+
target: "article:3"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
event: "key",
|
|
187
|
+
key: "PageDown",
|
|
188
|
+
target: "article:1"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
event: "key",
|
|
192
|
+
key: "PageUp",
|
|
193
|
+
target: "article:1"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
event: "key",
|
|
197
|
+
key: "Control+End",
|
|
198
|
+
target: "article:1"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
event: "key",
|
|
202
|
+
key: "Control+Home",
|
|
203
|
+
target: "article:1"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
event: "prop",
|
|
207
|
+
prop: "loadFinish",
|
|
208
|
+
value: 2
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
event: "prop",
|
|
212
|
+
prop: "loadFail"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
event: "prop",
|
|
216
|
+
prop: "sizeMode",
|
|
217
|
+
value: "total"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
event: "prop",
|
|
221
|
+
prop: "totalKnown",
|
|
222
|
+
value: true
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
event: "prop",
|
|
226
|
+
prop: "setValue",
|
|
227
|
+
value: 4
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
event: "prop",
|
|
231
|
+
prop: "setValue",
|
|
232
|
+
value: 2
|
|
233
|
+
}
|
|
234
|
+
];
|
|
235
|
+
const feedProbe = {
|
|
236
|
+
attrs: [
|
|
237
|
+
["feed", "aria-busy"],
|
|
238
|
+
["article:1", "aria-posinset"],
|
|
239
|
+
["article:1", "aria-setsize"],
|
|
240
|
+
["article:2", "aria-setsize"],
|
|
241
|
+
["article:3", "aria-setsize"]
|
|
242
|
+
],
|
|
243
|
+
events: ["onLoadedChange"]
|
|
244
|
+
};
|
|
245
|
+
//#endregion
|
|
246
|
+
export { feedBridge, feedConfig, feedProbe, feedSteps };
|