@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.
Files changed (260) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -2
  3. package/dist/apg/accordion.bridge.d.mts +35 -0
  4. package/dist/apg/accordion.bridge.mjs +326 -0
  5. package/dist/apg/accordion.model.json +8397 -0
  6. package/dist/apg/alert.bridge.d.mts +22 -0
  7. package/dist/apg/alert.bridge.mjs +86 -0
  8. package/dist/apg/alert.model.json +871 -0
  9. package/dist/apg/alertdialog.bridge.d.mts +21 -0
  10. package/dist/apg/alertdialog.bridge.mjs +221 -0
  11. package/dist/apg/alertdialog.model.json +5232 -0
  12. package/dist/apg/breadcrumb.bridge.d.mts +26 -0
  13. package/dist/apg/breadcrumb.bridge.mjs +120 -0
  14. package/dist/apg/breadcrumb.model.json +574 -0
  15. package/dist/apg/button.bridge.d.mts +35 -0
  16. package/dist/apg/button.bridge.mjs +208 -0
  17. package/dist/apg/button.model.json +5814 -0
  18. package/dist/apg/carousel.bridge.d.mts +44 -0
  19. package/dist/apg/carousel.bridge.mjs +262 -0
  20. package/dist/apg/carousel.model.json +8963 -0
  21. package/dist/apg/checkbox-group.bridge.d.mts +25 -0
  22. package/dist/apg/checkbox-group.bridge.mjs +276 -0
  23. package/dist/apg/checkbox-group.model.json +3863 -0
  24. package/dist/apg/checkbox.bridge.d.mts +34 -0
  25. package/dist/apg/checkbox.bridge.mjs +253 -0
  26. package/dist/apg/checkbox.model.json +3158 -0
  27. package/dist/apg/combobox-autocomplete.bridge.d.mts +14 -0
  28. package/dist/apg/combobox-autocomplete.bridge.mjs +326 -0
  29. package/dist/apg/combobox-autocomplete.model.json +11586 -0
  30. package/dist/apg/combobox-dialog.bridge.d.mts +17 -0
  31. package/dist/apg/combobox-dialog.bridge.mjs +250 -0
  32. package/dist/apg/combobox-dialog.model.json +8981 -0
  33. package/dist/apg/combobox-grid.bridge.d.mts +17 -0
  34. package/dist/apg/combobox-grid.bridge.mjs +227 -0
  35. package/dist/apg/combobox-grid.model.json +8554 -0
  36. package/dist/apg/combobox-select-only.bridge.d.mts +24 -0
  37. package/dist/apg/combobox-select-only.bridge.mjs +304 -0
  38. package/dist/apg/combobox-select-only.model.json +7846 -0
  39. package/dist/apg/combobox-tree.bridge.d.mts +17 -0
  40. package/dist/apg/combobox-tree.bridge.mjs +211 -0
  41. package/dist/apg/combobox-tree.model.json +9345 -0
  42. package/dist/apg/dialog-modal.bridge.d.mts +35 -0
  43. package/dist/apg/dialog-modal.bridge.mjs +186 -0
  44. package/dist/apg/dialog-modal.model.json +7580 -0
  45. package/dist/apg/disclosure.bridge.d.mts +25 -0
  46. package/dist/apg/disclosure.bridge.mjs +202 -0
  47. package/dist/apg/disclosure.model.json +4703 -0
  48. package/dist/apg/feed.bridge.d.mts +31 -0
  49. package/dist/apg/feed.bridge.mjs +246 -0
  50. package/dist/apg/feed.model.json +4523 -0
  51. package/dist/apg/grid-selection.bridge.d.mts +13 -0
  52. package/dist/apg/grid-selection.bridge.mjs +160 -0
  53. package/dist/apg/grid-selection.model.json +6974 -0
  54. package/dist/apg/grid-sort.bridge.d.mts +12 -0
  55. package/dist/apg/grid-sort.bridge.mjs +138 -0
  56. package/dist/apg/grid-sort.model.json +3799 -0
  57. package/dist/apg/grid.bridge.d.mts +54 -0
  58. package/dist/apg/grid.bridge.mjs +441 -0
  59. package/dist/apg/grid.model.json +3802 -0
  60. package/dist/apg/link.bridge.d.mts +24 -0
  61. package/dist/apg/link.bridge.mjs +126 -0
  62. package/dist/apg/link.model.json +694 -0
  63. package/dist/apg/listbox-multi.bridge.d.mts +10 -0
  64. package/dist/apg/listbox-multi.bridge.mjs +261 -0
  65. package/dist/apg/listbox-multi.model.json +10893 -0
  66. package/dist/apg/listbox-rearrangeable.bridge.d.mts +20 -0
  67. package/dist/apg/listbox-rearrangeable.bridge.mjs +237 -0
  68. package/dist/apg/listbox-rearrangeable.model.json +17592 -0
  69. package/dist/apg/listbox-shared.d.mts +58 -0
  70. package/dist/apg/listbox-shared.mjs +171 -0
  71. package/dist/apg/listbox-single.bridge.d.mts +10 -0
  72. package/dist/apg/listbox-single.bridge.mjs +196 -0
  73. package/dist/apg/listbox-single.model.json +8024 -0
  74. package/dist/apg/listbox-typeahead.bridge.d.mts +10 -0
  75. package/dist/apg/listbox-typeahead.bridge.mjs +153 -0
  76. package/dist/apg/listbox-typeahead.model.json +5615 -0
  77. package/dist/apg/menu-button.bridge.d.mts +29 -0
  78. package/dist/apg/menu-button.bridge.mjs +259 -0
  79. package/dist/apg/menu-button.model.json +5706 -0
  80. package/dist/apg/menu-checkable.bridge.d.mts +25 -0
  81. package/dist/apg/menu-checkable.bridge.mjs +226 -0
  82. package/dist/apg/menu-checkable.model.json +4277 -0
  83. package/dist/apg/menu-menubar.bridge.d.mts +27 -0
  84. package/dist/apg/menu-menubar.bridge.mjs +273 -0
  85. package/dist/apg/menu-menubar.model.json +9540 -0
  86. package/dist/apg/meter.bridge.d.mts +28 -0
  87. package/dist/apg/meter.bridge.mjs +159 -0
  88. package/dist/apg/meter.model.json +1185 -0
  89. package/dist/apg/player.bridge.d.mts +24 -0
  90. package/dist/apg/player.bridge.mjs +342 -0
  91. package/dist/apg/player.model.json +11013 -0
  92. package/dist/apg/radio.bridge.d.mts +35 -0
  93. package/dist/apg/radio.bridge.mjs +371 -0
  94. package/dist/apg/radio.model.json +5011 -0
  95. package/dist/apg/slider-multithumb.bridge.d.mts +34 -0
  96. package/dist/apg/slider-multithumb.bridge.mjs +365 -0
  97. package/dist/apg/slider-multithumb.model.json +5774 -0
  98. package/dist/apg/slider.bridge.d.mts +54 -0
  99. package/dist/apg/slider.bridge.mjs +359 -0
  100. package/dist/apg/slider.model.json +4881 -0
  101. package/dist/apg/spinbutton.bridge.d.mts +41 -0
  102. package/dist/apg/spinbutton.bridge.mjs +327 -0
  103. package/dist/apg/spinbutton.model.json +7571 -0
  104. package/dist/apg/switch.bridge.d.mts +30 -0
  105. package/dist/apg/switch.bridge.mjs +243 -0
  106. package/dist/apg/switch.model.json +3159 -0
  107. package/dist/apg/tabs.bridge.d.mts +41 -0
  108. package/dist/apg/tabs.bridge.mjs +294 -0
  109. package/dist/apg/tabs.model.json +7659 -0
  110. package/dist/apg/timeline.bridge.d.mts +36 -0
  111. package/dist/apg/timeline.bridge.mjs +265 -0
  112. package/dist/apg/timeline.model.json +6189 -0
  113. package/dist/apg/toolbar.bridge.d.mts +25 -0
  114. package/dist/apg/toolbar.bridge.mjs +307 -0
  115. package/dist/apg/toolbar.model.json +6616 -0
  116. package/dist/apg/tooltip.bridge.d.mts +36 -0
  117. package/dist/apg/tooltip.bridge.mjs +208 -0
  118. package/dist/apg/tooltip.model.json +8295 -0
  119. package/dist/apg/treegrid.bridge.d.mts +28 -0
  120. package/dist/apg/treegrid.bridge.mjs +311 -0
  121. package/dist/apg/treegrid.model.json +17358 -0
  122. package/dist/apg/treeview.bridge.d.mts +26 -0
  123. package/dist/apg/treeview.bridge.mjs +396 -0
  124. package/dist/apg/treeview.model.json +13745 -0
  125. package/dist/apg/windowsplitter.bridge.d.mts +36 -0
  126. package/dist/apg/windowsplitter.bridge.mjs +242 -0
  127. package/dist/apg/windowsplitter.model.json +6253 -0
  128. package/dist/canvas/camera.model.json +6568 -0
  129. package/dist/canvas/canvas.bridge.d.mts +190 -0
  130. package/dist/canvas/canvas.bridge.mjs +729 -0
  131. package/dist/canvas/pointer.model.json +17095 -0
  132. package/dist/scenario.d.mts +233 -0
  133. package/dist/scenario.mjs +313 -0
  134. package/package.json +57 -4
  135. package/scenarios/apg/README.md +221 -0
  136. package/scenarios/apg/accordion.base-ui.json +1005 -0
  137. package/scenarios/apg/accordion.react-aria.json +929 -0
  138. package/scenarios/apg/alert.base-ui.json +81 -0
  139. package/scenarios/apg/alert.react-aria.json +98 -0
  140. package/scenarios/apg/alertdialog.base-ui.json +331 -0
  141. package/scenarios/apg/alertdialog.react-aria.json +225 -0
  142. package/scenarios/apg/breadcrumb.react-aria.json +312 -0
  143. package/scenarios/apg/button.ariakit.json +136 -0
  144. package/scenarios/apg/button.base-ui.json +747 -0
  145. package/scenarios/apg/button.react-aria.json +443 -0
  146. package/scenarios/apg/carousel.apg.json +909 -0
  147. package/scenarios/apg/checkbox.ariakit.json +299 -0
  148. package/scenarios/apg/checkbox.base-ui.json +1126 -0
  149. package/scenarios/apg/checkbox.react-aria.json +518 -0
  150. package/scenarios/apg/combobox.ariakit.json +945 -0
  151. package/scenarios/apg/combobox.base-ui.json +2444 -0
  152. package/scenarios/apg/combobox.react-aria.json +845 -0
  153. package/scenarios/apg/dialog-modal.ariakit.json +1102 -0
  154. package/scenarios/apg/dialog-modal.base-ui.json +775 -0
  155. package/scenarios/apg/dialog-modal.react-aria.json +638 -0
  156. package/scenarios/apg/disclosure.apg.json +349 -0
  157. package/scenarios/apg/disclosure.ariakit.json +163 -0
  158. package/scenarios/apg/disclosure.base-ui.json +451 -0
  159. package/scenarios/apg/disclosure.react-aria.json +643 -0
  160. package/scenarios/apg/feed.apg.json +442 -0
  161. package/scenarios/apg/grid.ariakit.json +197 -0
  162. package/scenarios/apg/grid.base-ui.json +720 -0
  163. package/scenarios/apg/grid.react-aria.json +969 -0
  164. package/scenarios/apg/landmarks.ariakit.json +93 -0
  165. package/scenarios/apg/landmarks.react-aria.json +330 -0
  166. package/scenarios/apg/link.ariakit.json +83 -0
  167. package/scenarios/apg/link.base-ui.json +88 -0
  168. package/scenarios/apg/link.react-aria.json +186 -0
  169. package/scenarios/apg/listbox.ariakit.json +78 -0
  170. package/scenarios/apg/listbox.base-ui.json +163 -0
  171. package/scenarios/apg/listbox.react-aria.json +1118 -0
  172. package/scenarios/apg/menu-button.ariakit.json +924 -0
  173. package/scenarios/apg/menu-button.base-ui.json +1155 -0
  174. package/scenarios/apg/menu-button.react-aria.json +1080 -0
  175. package/scenarios/apg/menubar.ariakit.json +758 -0
  176. package/scenarios/apg/menubar.base-ui.json +673 -0
  177. package/scenarios/apg/meter.base-ui.json +243 -0
  178. package/scenarios/apg/meter.react-aria.json +201 -0
  179. package/scenarios/apg/radio.ariakit.json +459 -0
  180. package/scenarios/apg/radio.base-ui.json +581 -0
  181. package/scenarios/apg/radio.react-aria.json +576 -0
  182. package/scenarios/apg/slider-multithumb.base-ui.json +316 -0
  183. package/scenarios/apg/slider-multithumb.react-aria.json +363 -0
  184. package/scenarios/apg/slider.base-ui.json +544 -0
  185. package/scenarios/apg/slider.react-aria.json +456 -0
  186. package/scenarios/apg/spinbutton.base-ui.json +1688 -0
  187. package/scenarios/apg/spinbutton.react-aria.json +426 -0
  188. package/scenarios/apg/switch.base-ui.json +456 -0
  189. package/scenarios/apg/switch.react-aria.json +303 -0
  190. package/scenarios/apg/table.ariakit.json +65 -0
  191. package/scenarios/apg/table.react-aria.json +221 -0
  192. package/scenarios/apg/tabs.ariakit.json +329 -0
  193. package/scenarios/apg/tabs.base-ui.json +875 -0
  194. package/scenarios/apg/tabs.react-aria.json +644 -0
  195. package/scenarios/apg/toolbar.ariakit.json +595 -0
  196. package/scenarios/apg/toolbar.base-ui.json +1189 -0
  197. package/scenarios/apg/toolbar.react-aria.json +383 -0
  198. package/scenarios/apg/tooltip.ariakit.json +441 -0
  199. package/scenarios/apg/tooltip.base-ui.json +411 -0
  200. package/scenarios/apg/tooltip.react-aria.json +454 -0
  201. package/scenarios/apg/treegrid.apg.json +2558 -0
  202. package/scenarios/apg/treegrid.react-aria.json +5094 -0
  203. package/scenarios/apg/treeview.apg.json +928 -0
  204. package/scenarios/apg/windowsplitter.apg.json +557 -0
  205. package/scenarios/canvas/README.md +69 -0
  206. package/scenarios/canvas/camera.contract.json +237 -0
  207. package/scenarios/canvas/camera.excalidraw.json +57 -0
  208. package/scenarios/canvas/marquee.excalidraw.json +122 -0
  209. package/scenarios/canvas/pan.excalidraw.json +46 -0
  210. package/scenarios/canvas/pointer.contract.json +528 -0
  211. package/scenarios/parts.json +291 -0
  212. package/shared/aria.qnt +186 -0
  213. package/shared/details-group.qnt +50 -0
  214. package/shared/focusgroup.qnt +152 -0
  215. package/shared/invokers.qnt +81 -0
  216. package/shared/popover.qnt +71 -0
  217. package/specs/apg/STATIC.md +103 -0
  218. package/specs/apg/accordion.qnt +578 -0
  219. package/specs/apg/alert.qnt +131 -0
  220. package/specs/apg/alertdialog.qnt +455 -0
  221. package/specs/apg/breadcrumb.qnt +88 -0
  222. package/specs/apg/button.qnt +445 -0
  223. package/specs/apg/carousel.qnt +732 -0
  224. package/specs/apg/checkbox-group.qnt +388 -0
  225. package/specs/apg/checkbox.qnt +394 -0
  226. package/specs/apg/combobox-autocomplete.qnt +764 -0
  227. package/specs/apg/combobox-dialog.qnt +627 -0
  228. package/specs/apg/combobox-grid.qnt +632 -0
  229. package/specs/apg/combobox-select-only.qnt +626 -0
  230. package/specs/apg/combobox-tree.qnt +633 -0
  231. package/specs/apg/dialog-modal.qnt +499 -0
  232. package/specs/apg/disclosure.qnt +309 -0
  233. package/specs/apg/feed.qnt +485 -0
  234. package/specs/apg/grid-selection.qnt +512 -0
  235. package/specs/apg/grid-sort.qnt +330 -0
  236. package/specs/apg/grid.qnt +360 -0
  237. package/specs/apg/link.qnt +119 -0
  238. package/specs/apg/listbox-multi.qnt +799 -0
  239. package/specs/apg/listbox-rearrangeable.qnt +801 -0
  240. package/specs/apg/listbox-single.qnt +511 -0
  241. package/specs/apg/listbox-typeahead.qnt +396 -0
  242. package/specs/apg/menu-button.qnt +363 -0
  243. package/specs/apg/menu-checkable.qnt +304 -0
  244. package/specs/apg/menu-menubar.qnt +531 -0
  245. package/specs/apg/meter.qnt +129 -0
  246. package/specs/apg/player.qnt +1081 -0
  247. package/specs/apg/radio.qnt +394 -0
  248. package/specs/apg/slider-multithumb.qnt +441 -0
  249. package/specs/apg/slider.qnt +396 -0
  250. package/specs/apg/spinbutton.qnt +494 -0
  251. package/specs/apg/switch.qnt +389 -0
  252. package/specs/apg/tabs.qnt +412 -0
  253. package/specs/apg/timeline.qnt +612 -0
  254. package/specs/apg/toolbar.qnt +364 -0
  255. package/specs/apg/tooltip.qnt +553 -0
  256. package/specs/apg/treegrid.qnt +983 -0
  257. package/specs/apg/treeview.qnt +1004 -0
  258. package/specs/apg/windowsplitter.qnt +487 -0
  259. package/specs/canvas/camera.qnt +407 -0
  260. package/specs/canvas/pointer.qnt +841 -0
@@ -0,0 +1,41 @@
1
+ import { ActionStep, Probe, ScenarioConfig, SpecBridge } from "../scenario.mjs";
2
+ //#region src/apg/tabs.bridge.d.ts
3
+ export interface TabsConfig {
4
+ readonly items: number;
5
+ readonly orientation: "horizontal" | "vertical";
6
+ readonly activation: "automatic" | "manual";
7
+ readonly deletable: boolean;
8
+ /** Bit mask of disabled tabs (only `configure`-reachable masks). */
9
+ readonly disabled: number;
10
+ /** Initial selection (uncontrolled) or the accepting host's starting value. */
11
+ readonly selected: number;
12
+ /** The host owns the selection (`value` prop present). */
13
+ readonly controlled: boolean;
14
+ /** Controlled only: the host writes every request's intent back. */
15
+ readonly accepts: boolean;
16
+ /** Controlled only: a pinned value the host holds (rejects every request). */
17
+ readonly pinned: number | null;
18
+ }
19
+ /**
20
+ * Scenario `config` -> what both targets are mounted with. The spec models a
21
+ * fixed four-tab tablist: RTL (`direction`), non-wrapping (`loopFocus`), forced
22
+ * mount, programmatic labels and tab counts beyond four are not modelled and
23
+ * make the scenario `Unsupported`.
24
+ */
25
+ export declare const tabsConfig: (config: ScenarioConfig) => TabsConfig;
26
+ /** Callback names differ per library; the primitive fires `onSelectionChange`. */
27
+ export declare const tabsEventName: (name: string) => string;
28
+ /**
29
+ * Tabs vocabulary <-> `specs/apg/tabs.qnt`, shared by the spec driver and the
30
+ * DOM driver so both gate scenarios identically. Parts: `tablist`, `tab:i`,
31
+ * `tabpanel:i`.
32
+ *
33
+ * The spec has no "focus a tab without selecting" event (focus is the
34
+ * keyboard/pointer driven `zone`), so a `focus` step is a no-op here; a
35
+ * differential walk enters the tablist with `click`.
36
+ */
37
+ export declare const tabsBridge: SpecBridge;
38
+ /** The user-level steps a differential walk draws from. */
39
+ export declare const tabsSteps: ReadonlyArray<ActionStep>;
40
+ export declare const tabsProbe: Probe;
41
+ //#endregion
@@ -0,0 +1,294 @@
1
+ import { Unsupported } from "../scenario.mjs";
2
+ //#region src/apg/tabs.bridge.ts
3
+ const specEvent = (tag, params = {}) => ({
4
+ _tag: tag,
5
+ ...params
6
+ });
7
+ /** The generator models four tab slots as a bit mask (`present` is always 15). */
8
+ const TABS = 4;
9
+ /** Disabled masks the packed `configure` variant can produce (see the spec). */
10
+ const DISABLED_MASKS = [
11
+ 0,
12
+ 1,
13
+ 2,
14
+ 5,
15
+ 14
16
+ ];
17
+ const partOf = (target) => {
18
+ const [name = "tab", indexText] = (target ?? "tab").split(":");
19
+ const index = Number(indexText ?? "0");
20
+ if (name === "tablist") return {
21
+ kind: "tablist",
22
+ index: 0
23
+ };
24
+ if (name === "tab") return {
25
+ kind: "tab",
26
+ index
27
+ };
28
+ if (name === "tabpanel") return {
29
+ kind: "tabpanel",
30
+ index
31
+ };
32
+ throw new Unsupported(`part ${name}`);
33
+ };
34
+ const maskOf = (disabled) => disabled.reduce((mask, index) => mask | 1 << index, 0);
35
+ const isDisabled = (mask, index) => (mask >> index & 1) === 1;
36
+ const firstEnabled = (mask) => {
37
+ for (let index = 0; index < TABS; index += 1) if (!isDisabled(mask, index)) return index;
38
+ return 0;
39
+ };
40
+ /**
41
+ * Scenario `config` -> what both targets are mounted with. The spec models a
42
+ * fixed four-tab tablist: RTL (`direction`), non-wrapping (`loopFocus`), forced
43
+ * mount, programmatic labels and tab counts beyond four are not modelled and
44
+ * make the scenario `Unsupported`.
45
+ */
46
+ const tabsConfig = (config) => {
47
+ let items = TABS;
48
+ let orientation = "horizontal";
49
+ let activation = "automatic";
50
+ let deletable = false;
51
+ let mask = 0;
52
+ let explicitSelected;
53
+ let controlled = false;
54
+ let pinned = null;
55
+ for (const [key, value] of Object.entries(config)) if (key === "items") {
56
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > TABS) throw new Unsupported(`config items=${JSON.stringify(value)}`);
57
+ items = value;
58
+ } else if (key === "orientation") {
59
+ if (value !== "horizontal" && value !== "vertical") throw new Unsupported(`config orientation=${JSON.stringify(value)}`);
60
+ orientation = value;
61
+ } else if (key === "activation") {
62
+ if (value !== "automatic" && value !== "manual") throw new Unsupported(`config activation=${JSON.stringify(value)}`);
63
+ activation = value;
64
+ } else if (key === "deletable") {
65
+ if (typeof value !== "boolean") throw new Unsupported(`config deletable=${JSON.stringify(value)}`);
66
+ deletable = value;
67
+ } else if (key === "disabled") {
68
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "number")) throw new Unsupported(`config disabled=${JSON.stringify(value)}`);
69
+ mask = maskOf(value);
70
+ } else if (key === "selected") {
71
+ if (typeof value !== "number" || !Number.isInteger(value)) throw new Unsupported(`config selected=${JSON.stringify(value)}`);
72
+ explicitSelected = value;
73
+ } else if (key === "controlled") {
74
+ if (typeof value !== "boolean") throw new Unsupported(`config controlled=${JSON.stringify(value)}`);
75
+ controlled ||= value;
76
+ } else if (key === "value") {
77
+ if (typeof value !== "number" || !Number.isInteger(value)) throw new Unsupported(`config value=${JSON.stringify(value)}`);
78
+ pinned = value;
79
+ controlled = true;
80
+ } else throw new Unsupported(`config ${key}=${JSON.stringify(value)}`);
81
+ if (!DISABLED_MASKS.includes(mask)) throw new Unsupported(`disabled mask ${mask}`);
82
+ const selected = explicitSelected ?? firstEnabled(mask);
83
+ if (selected < 0 || selected >= items || isDisabled(mask, selected)) throw new Unsupported(`selected ${selected} cannot be selected`);
84
+ if (pinned !== null && (pinned < 0 || pinned >= items || isDisabled(mask, pinned))) throw new Unsupported(`value ${pinned} cannot be selected`);
85
+ return {
86
+ items,
87
+ orientation,
88
+ activation,
89
+ deletable,
90
+ disabled: mask,
91
+ selected,
92
+ controlled,
93
+ accepts: controlled && pinned === null,
94
+ pinned
95
+ };
96
+ };
97
+ const variantOrientation = (orientation) => orientation === "horizontal" ? 0 : 1;
98
+ const variantActivation = (activation) => activation === "automatic" ? 0 : 1;
99
+ const variantDisabledTier = (mask) => DISABLED_MASKS.indexOf(mask);
100
+ const flavorOf = (config) => variantActivation(config.activation) + 2 * variantOrientation(config.orientation) + 4 * (config.deletable ? 1 : 0) + 8 * variantDisabledTier(config.disabled);
101
+ const NAV_KEYS = /* @__PURE__ */ new Set([
102
+ "ArrowLeft",
103
+ "ArrowRight",
104
+ "ArrowUp",
105
+ "ArrowDown",
106
+ "Home",
107
+ "End",
108
+ "Enter",
109
+ " ",
110
+ "Space",
111
+ "Delete"
112
+ ]);
113
+ const navKey = (key) => {
114
+ if (key === void 0 || !NAV_KEYS.has(key)) throw new Unsupported(`key ${key ?? ""}`);
115
+ return key === "Space" ? " " : key;
116
+ };
117
+ const programmatic = (step) => {
118
+ switch (step.prop) {
119
+ case "selectedKey":
120
+ case "selected": {
121
+ const index = typeof step.value === "number" ? step.value : -1;
122
+ if (!Number.isInteger(index) || index < 0 || index >= TABS) throw new Unsupported(`selectedKey ${JSON.stringify(step.value)}`);
123
+ return [specEvent("click", { tab: BigInt(index) })];
124
+ }
125
+ case "setValue": {
126
+ const index = typeof step.value === "number" ? step.value : -1;
127
+ if (!Number.isInteger(index) || index < 0 || index >= TABS) throw new Unsupported(`setValue ${JSON.stringify(step.value)}`);
128
+ return [specEvent("setValue", { v: BigInt(index) })];
129
+ }
130
+ default: throw new Unsupported(`prop ${step.prop ?? ""}`);
131
+ }
132
+ };
133
+ /** Callback names differ per library; the primitive fires `onSelectionChange`. */
134
+ const tabsEventName = (name) => name === "onValueChange" ? "onSelectionChange" : name;
135
+ /**
136
+ * Tabs vocabulary <-> `specs/apg/tabs.qnt`, shared by the spec driver and the
137
+ * DOM driver so both gate scenarios identically. Parts: `tablist`, `tab:i`,
138
+ * `tabpanel:i`.
139
+ *
140
+ * The spec has no "focus a tab without selecting" event (focus is the
141
+ * keyboard/pointer driven `zone`), so a `focus` step is a no-op here; a
142
+ * differential walk enters the tablist with `click`.
143
+ */
144
+ const tabsBridge = {
145
+ setup: (config) => {
146
+ const tabs = tabsConfig(config);
147
+ const events = [specEvent("configure", { flavor: BigInt(flavorOf(tabs)) })];
148
+ const first = firstEnabled(tabs.disabled);
149
+ if (tabs.controlled) {
150
+ events.push(specEvent("control"));
151
+ const initial = tabs.pinned ?? tabs.selected;
152
+ if (initial !== first) events.push(specEvent("setValue", { v: BigInt(initial) }));
153
+ if (tabs.accepts) events.push(specEvent("acceptRequests"));
154
+ return events;
155
+ }
156
+ if (tabs.selected !== first) events.push(specEvent("click", { tab: BigInt(tabs.selected) }), specEvent("tab_key", { shift: false }), specEvent("tab_key", { shift: false }));
157
+ return events;
158
+ },
159
+ event: (step) => {
160
+ if (step.event === "focus" || step.event === "blur") return [];
161
+ switch (step.event) {
162
+ case "click": {
163
+ if (step.value !== void 0) throw new Unsupported(`click with value ${JSON.stringify(step.value)}`);
164
+ const { kind, index } = partOf(step.target);
165
+ if (kind !== "tab") throw new Unsupported(`click on ${step.target ?? ""}`);
166
+ return [specEvent("click", { tab: BigInt(index) })];
167
+ }
168
+ case "key":
169
+ if (step.value !== void 0) throw new Unsupported(`key with value ${JSON.stringify(step.value)}`);
170
+ if (step.key === "Tab") return [specEvent("tab_key", { shift: false })];
171
+ if (step.key === "Shift+Tab") return [specEvent("tab_key", { shift: true })];
172
+ return [specEvent("press", { key: navKey(step.key) })];
173
+ case "hover":
174
+ case "unhover": return [];
175
+ case "prop": return programmatic(step);
176
+ default: throw new Unsupported(`event ${step.event}`);
177
+ }
178
+ },
179
+ focus: (state) => {
180
+ const zone = state["zone"];
181
+ if (zone === "tablist") return `tab:${state["focused"]}`;
182
+ if (zone === "panel") return `tabpanel:${state["selected"]}`;
183
+ return null;
184
+ },
185
+ attr: (state, target, name) => {
186
+ const { kind, index } = partOf(target);
187
+ const selected = Number(state["selected"]);
188
+ const mask = Number(state["disabled"]);
189
+ if (kind === "tablist") {
190
+ if (name === "aria-orientation") return state["orientation"] === "vertical" ? "vertical" : null;
191
+ } else if (kind === "tab") {
192
+ if (name === "aria-selected") return selected === index ? "true" : "false";
193
+ if (name === "tabindex") return selected === index ? "0" : "-1";
194
+ if (name === "aria-controls") return `tabpanel:${index}`;
195
+ if (name === "aria-disabled") return isDisabled(mask, index) ? "true" : null;
196
+ } else if (kind === "tabpanel") {
197
+ if (name === "aria-labelledby") return `tab:${index}`;
198
+ if (name === "hidden") return selected === index ? null : "true";
199
+ }
200
+ throw new Unsupported(`attribute ${name} on ${target}`);
201
+ },
202
+ visible: (state) => [`tabpanel:${state["selected"]}`],
203
+ eventName: tabsEventName,
204
+ events: (before, after) => before["requests"] === after["requests"] ? [] : [["onSelectionChange", `tab:${after["requested"]}`]]
205
+ };
206
+ /** The user-level steps a differential walk draws from. */
207
+ const tabsSteps = [
208
+ {
209
+ event: "click",
210
+ target: "tab:0"
211
+ },
212
+ {
213
+ event: "click",
214
+ target: "tab:1"
215
+ },
216
+ {
217
+ event: "click",
218
+ target: "tab:2"
219
+ },
220
+ {
221
+ event: "click",
222
+ target: "tab:3"
223
+ },
224
+ {
225
+ event: "key",
226
+ key: "ArrowRight",
227
+ target: "tab:0"
228
+ },
229
+ {
230
+ event: "key",
231
+ key: "ArrowLeft",
232
+ target: "tab:0"
233
+ },
234
+ {
235
+ event: "key",
236
+ key: "ArrowDown",
237
+ target: "tab:0"
238
+ },
239
+ {
240
+ event: "key",
241
+ key: "ArrowUp",
242
+ target: "tab:0"
243
+ },
244
+ {
245
+ event: "key",
246
+ key: "Home",
247
+ target: "tab:0"
248
+ },
249
+ {
250
+ event: "key",
251
+ key: "End",
252
+ target: "tab:0"
253
+ },
254
+ {
255
+ event: "key",
256
+ key: "Enter",
257
+ target: "tab:0"
258
+ },
259
+ {
260
+ event: "key",
261
+ key: " ",
262
+ target: "tab:0"
263
+ },
264
+ {
265
+ event: "prop",
266
+ prop: "setValue",
267
+ value: 2
268
+ },
269
+ {
270
+ event: "prop",
271
+ prop: "setValue",
272
+ value: 0
273
+ }
274
+ ];
275
+ const tabsProbe = {
276
+ attrs: [
277
+ ["tablist", "aria-orientation"],
278
+ ["tab:0", "aria-selected"],
279
+ ["tab:1", "aria-selected"],
280
+ ["tab:2", "aria-selected"],
281
+ ["tab:3", "aria-selected"],
282
+ ["tab:0", "tabindex"],
283
+ ["tab:1", "tabindex"],
284
+ ["tab:2", "tabindex"],
285
+ ["tab:3", "tabindex"],
286
+ ["tabpanel:0", "hidden"],
287
+ ["tabpanel:1", "hidden"],
288
+ ["tabpanel:2", "hidden"],
289
+ ["tabpanel:3", "hidden"]
290
+ ],
291
+ events: ["onSelectionChange"]
292
+ };
293
+ //#endregion
294
+ export { tabsBridge, tabsConfig, tabsEventName, tabsProbe, tabsSteps };