@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.
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,487 @@
1
+ // APG Window Splitter — https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
2
+ //
3
+ // A role=separator that resizes the primary pane (aria-controls="primary").
4
+ // The spec models the widget state (`value`, `collapsed`, the remembered
5
+ // `prev`) and the rendered aria-valuenow (`ariaNow`); aria-valuemin / -max are
6
+ // the constants MIN / MAX below.
7
+ //
8
+ // Orientation names the splitter bar, as in APG: a "vertical" splitter sits
9
+ // between left/right panes (Left/Right move it), a "horizontal" one between
10
+ // top/bottom panes (Up/Down move it). Left/Up shrink the primary pane,
11
+ // Right/Down grow it.
12
+ //
13
+ // Events are the user-level alphabet shared with the scenario bridge:
14
+ // focusSplitter / blur the separator gains / loses focus
15
+ // left right up down arrow keys (only the pair matching the orientation moves)
16
+ // home end optional: jump to MIN / MAX
17
+ // enter toggle collapse; expanding restores the remembered value
18
+ // f6 optional: cycle focus splitter -> secondary -> primary -> splitter
19
+ // orientVertical / orientHorizontal / makeFixed / makeResizable prop changes
20
+ //
21
+ // A fixed splitter ignores arrows and Home/End: only Enter acts.
22
+ // While collapsed, arrows and Home/End are no-ops (collapsed => value == MIN).
23
+ //
24
+ // Request vs commit (map ticket rt1p). `value` (the primary pane size) is the
25
+ // controlled value: every movement — left, right, up, down, home, end, enter —
26
+ // is a *request* carrying its target (`requested`). It fires onValueChange
27
+ // (`requests` counts the calls) when the target differs from the current value,
28
+ // and then:
29
+ // uncontrolled the request is accepted: `value` commits the target
30
+ // controlled, holds the host ignores it ("rejected"): `value` is untouched
31
+ // controlled, accepts the host writes the target back (`accepting`)
32
+ // `valueProp` is the host's copy (meaningful while `controlled`); `setValue` /
33
+ // `hostToggle` are the host writing it, the only commit while controlled.
34
+ // `control` / `acceptRequests` configure the host (init is literal-only).
35
+ // Because collapse is entangled with `value`, its side effects (collapsed /
36
+ // prev / justRestored) are gated by the same rejection as the value commit.
37
+ //
38
+ // Invariants for the model: ControlledFollowsProp (in controlled mode no event
39
+ // commits), RejectedRequestUntouched, AcceptedRequestCommits.
40
+ //
41
+ // `outcome` is "none" | "accepted" | "rejected" for the last request; a host
42
+ // write settles it back to "none". `base` is the value when it was received.
43
+ //
44
+ // ponytail: F6 is always modelled (no on/off variant); dragging and a
45
+ // min-size above 0 are not modelled.
46
+ module apg_windowsplitter {
47
+ import aria.* from "../../shared/aria"
48
+
49
+ var orientation: str // "vertical" | "horizontal"
50
+ var fixed: bool // fixed splitter: only Enter acts
51
+ var value: int // primary pane size, MIN..MAX
52
+ var prev: int // value remembered at the last collapse
53
+ var collapsed: bool // primary pane is collapsed
54
+ var justRestored: bool // the last event was an Enter that restored `prev`
55
+ var focus: str // "none" | "splitter" | "primary" | "secondary"
56
+ var ariaNow: int // rendered aria-valuenow
57
+ var requests: int // number of requests that fired onValueChange
58
+ var controlled: bool // the host owns the value (`value` prop present)
59
+ var accepting: bool // controlled host that answers every request with its intent
60
+ var valueProp: int // the controlled value prop (meaningful while controlled)
61
+ var requested: int // target of the last request
62
+ var outcome: str // "none" | "accepted" | "rejected"
63
+ var base: int // value when the last request was received
64
+
65
+ def MIN: int = 0
66
+ def MAX: int = 6
67
+ def STEP: int = 2
68
+ def INITIAL: int = 4
69
+
70
+ def clamp(v: int): int = if (v < MIN) MIN else if (v > MAX) MAX else v
71
+ def movable: bool = and(focus == "splitter", and(not(fixed), not(collapsed)))
72
+ def arrowTarget(o: str, delta: int): int =
73
+ if (and(movable, orientation == o)) clamp(value + delta) else value
74
+ def homeTarget: int = if (movable) MIN else value
75
+ def endTarget: int = if (movable) MAX else value
76
+ def onSplitter: bool = focus == "splitter"
77
+ def f6Focus: str =
78
+ if (focus == "splitter") "secondary"
79
+ else if (focus == "secondary") "primary"
80
+ else if (focus == "primary") "splitter"
81
+ else "none"
82
+ def enterValue: int =
83
+ if (not(onSplitter)) value else if (collapsed) prev else MIN
84
+
85
+ // --- request / commit (map ticket rt1p) ----------------------------------
86
+ def moved(a: int, b: int): int = if (a == b) 0 else 1
87
+ def holds: bool = and(controlled, not(accepting))
88
+ def follows: bool = and(controlled, accepting)
89
+ def verdict: str = if (holds) "rejected" else "accepted"
90
+ // the value after a request for `want`
91
+ def landed(h: bool, v: int, want: int): int = if (h) v else want
92
+ // the value prop after a request for `want`
93
+ def propAfter(f: bool, p: int, want: int): int = if (f) want else p
94
+ // the target of each movement event
95
+ def wantLeft: int = arrowTarget("vertical", 0 - STEP)
96
+ def wantRight: int = arrowTarget("vertical", STEP)
97
+ def wantUp: int = arrowTarget("horizontal", 0 - STEP)
98
+ def wantDown: int = arrowTarget("horizontal", STEP)
99
+ def wantHome: int = homeTarget
100
+ def wantEnd: int = endTarget
101
+ def wantEnter: int = enterValue
102
+ // an Enter that would not move the value is not a rejected request: the
103
+ // collapse flag still flips (there is nothing for the host to veto)
104
+ def rejectEnter: bool = and(holds, value != wantEnter)
105
+
106
+ action init = all {
107
+ orientation' = "vertical",
108
+ fixed' = false,
109
+ value' = INITIAL,
110
+ prev' = INITIAL,
111
+ collapsed' = false,
112
+ justRestored' = false,
113
+ focus' = "none",
114
+ ariaNow' = INITIAL,
115
+ requests' = 0,
116
+ controlled' = false,
117
+ accepting' = false,
118
+ valueProp' = INITIAL,
119
+ requested' = INITIAL,
120
+ outcome' = "none",
121
+ base' = INITIAL
122
+ }
123
+
124
+ action focusSplitter = all {
125
+ orientation' = orientation,
126
+ fixed' = fixed,
127
+ value' = value,
128
+ prev' = prev,
129
+ collapsed' = collapsed,
130
+ justRestored' = false,
131
+ focus' = "splitter",
132
+ ariaNow' = ariaNow,
133
+ requests' = requests,
134
+ controlled' = controlled,
135
+ accepting' = accepting,
136
+ valueProp' = valueProp,
137
+ requested' = requested,
138
+ outcome' = outcome,
139
+ base' = base
140
+ }
141
+
142
+ action blur = all {
143
+ orientation' = orientation,
144
+ fixed' = fixed,
145
+ value' = value,
146
+ prev' = prev,
147
+ collapsed' = collapsed,
148
+ justRestored' = false,
149
+ focus' = "none",
150
+ ariaNow' = ariaNow,
151
+ requests' = requests,
152
+ controlled' = controlled,
153
+ accepting' = accepting,
154
+ valueProp' = valueProp,
155
+ requested' = requested,
156
+ outcome' = outcome,
157
+ base' = base
158
+ }
159
+
160
+ // request: move left by one step
161
+ action left = all {
162
+ orientation' = orientation,
163
+ fixed' = fixed,
164
+ value' = landed(holds, value, wantLeft),
165
+ prev' = prev,
166
+ collapsed' = collapsed,
167
+ justRestored' = false,
168
+ focus' = focus,
169
+ ariaNow' = landed(holds, value, wantLeft),
170
+ requests' = requests + moved(value, wantLeft),
171
+ controlled' = controlled,
172
+ accepting' = accepting,
173
+ valueProp' = propAfter(follows, valueProp, wantLeft),
174
+ requested' = if (value == wantLeft) requested else wantLeft,
175
+ outcome' = if (value == wantLeft) outcome else verdict,
176
+ base' = if (value == wantLeft) base else value
177
+ }
178
+
179
+ action right = all {
180
+ orientation' = orientation,
181
+ fixed' = fixed,
182
+ value' = landed(holds, value, wantRight),
183
+ prev' = prev,
184
+ collapsed' = collapsed,
185
+ justRestored' = false,
186
+ focus' = focus,
187
+ ariaNow' = landed(holds, value, wantRight),
188
+ requests' = requests + moved(value, wantRight),
189
+ controlled' = controlled,
190
+ accepting' = accepting,
191
+ valueProp' = propAfter(follows, valueProp, wantRight),
192
+ requested' = if (value == wantRight) requested else wantRight,
193
+ outcome' = if (value == wantRight) outcome else verdict,
194
+ base' = if (value == wantRight) base else value
195
+ }
196
+
197
+ action up = all {
198
+ orientation' = orientation,
199
+ fixed' = fixed,
200
+ value' = landed(holds, value, wantUp),
201
+ prev' = prev,
202
+ collapsed' = collapsed,
203
+ justRestored' = false,
204
+ focus' = focus,
205
+ ariaNow' = landed(holds, value, wantUp),
206
+ requests' = requests + moved(value, wantUp),
207
+ controlled' = controlled,
208
+ accepting' = accepting,
209
+ valueProp' = propAfter(follows, valueProp, wantUp),
210
+ requested' = if (value == wantUp) requested else wantUp,
211
+ outcome' = if (value == wantUp) outcome else verdict,
212
+ base' = if (value == wantUp) base else value
213
+ }
214
+
215
+ action down = all {
216
+ orientation' = orientation,
217
+ fixed' = fixed,
218
+ value' = landed(holds, value, wantDown),
219
+ prev' = prev,
220
+ collapsed' = collapsed,
221
+ justRestored' = false,
222
+ focus' = focus,
223
+ ariaNow' = landed(holds, value, wantDown),
224
+ requests' = requests + moved(value, wantDown),
225
+ controlled' = controlled,
226
+ accepting' = accepting,
227
+ valueProp' = propAfter(follows, valueProp, wantDown),
228
+ requested' = if (value == wantDown) requested else wantDown,
229
+ outcome' = if (value == wantDown) outcome else verdict,
230
+ base' = if (value == wantDown) base else value
231
+ }
232
+
233
+ action home = all {
234
+ orientation' = orientation,
235
+ fixed' = fixed,
236
+ value' = landed(holds, value, wantHome),
237
+ prev' = prev,
238
+ collapsed' = collapsed,
239
+ justRestored' = false,
240
+ focus' = focus,
241
+ ariaNow' = landed(holds, value, wantHome),
242
+ requests' = requests + moved(value, wantHome),
243
+ controlled' = controlled,
244
+ accepting' = accepting,
245
+ valueProp' = propAfter(follows, valueProp, wantHome),
246
+ requested' = if (value == wantHome) requested else wantHome,
247
+ outcome' = if (value == wantHome) outcome else verdict,
248
+ base' = if (value == wantHome) base else value
249
+ }
250
+
251
+ action end = all {
252
+ orientation' = orientation,
253
+ fixed' = fixed,
254
+ value' = landed(holds, value, wantEnd),
255
+ prev' = prev,
256
+ collapsed' = collapsed,
257
+ justRestored' = false,
258
+ focus' = focus,
259
+ ariaNow' = landed(holds, value, wantEnd),
260
+ requests' = requests + moved(value, wantEnd),
261
+ controlled' = controlled,
262
+ accepting' = accepting,
263
+ valueProp' = propAfter(follows, valueProp, wantEnd),
264
+ requested' = if (value == wantEnd) requested else wantEnd,
265
+ outcome' = if (value == wantEnd) outcome else verdict,
266
+ base' = if (value == wantEnd) base else value
267
+ }
268
+
269
+ // request: collapse remembers the current value; expanding restores it.
270
+ // A rejected request leaves collapsed / prev / justRestored untouched too.
271
+ action enter = all {
272
+ orientation' = orientation,
273
+ fixed' = fixed,
274
+ value' = landed(holds, value, wantEnter),
275
+ prev' = if (rejectEnter) prev else (if (and(onSplitter, not(collapsed))) value else prev),
276
+ collapsed' = if (rejectEnter) collapsed else (if (onSplitter) not(collapsed) else collapsed),
277
+ justRestored' = if (rejectEnter) justRestored else (and(onSplitter, collapsed)),
278
+ focus' = focus,
279
+ ariaNow' = landed(holds, value, wantEnter),
280
+ requests' = requests + moved(value, wantEnter),
281
+ controlled' = controlled,
282
+ accepting' = accepting,
283
+ valueProp' = propAfter(follows, valueProp, wantEnter),
284
+ requested' = if (value == wantEnter) requested else wantEnter,
285
+ outcome' = if (value == wantEnter) outcome else verdict,
286
+ base' = if (value == wantEnter) base else value
287
+ }
288
+
289
+ action f6 = all {
290
+ orientation' = orientation,
291
+ fixed' = fixed,
292
+ value' = value,
293
+ prev' = prev,
294
+ collapsed' = collapsed,
295
+ justRestored' = false,
296
+ focus' = f6Focus,
297
+ ariaNow' = ariaNow,
298
+ requests' = requests,
299
+ controlled' = controlled,
300
+ accepting' = accepting,
301
+ valueProp' = valueProp,
302
+ requested' = requested,
303
+ outcome' = outcome,
304
+ base' = base
305
+ }
306
+
307
+ action orientVertical = all {
308
+ orientation' = "vertical",
309
+ fixed' = fixed,
310
+ value' = value,
311
+ prev' = prev,
312
+ collapsed' = collapsed,
313
+ justRestored' = false,
314
+ focus' = focus,
315
+ ariaNow' = ariaNow,
316
+ requests' = requests,
317
+ controlled' = controlled,
318
+ accepting' = accepting,
319
+ valueProp' = valueProp,
320
+ requested' = requested,
321
+ outcome' = outcome,
322
+ base' = base
323
+ }
324
+
325
+ action orientHorizontal = all {
326
+ orientation' = "horizontal",
327
+ fixed' = fixed,
328
+ value' = value,
329
+ prev' = prev,
330
+ collapsed' = collapsed,
331
+ justRestored' = false,
332
+ focus' = focus,
333
+ ariaNow' = ariaNow,
334
+ requests' = requests,
335
+ controlled' = controlled,
336
+ accepting' = accepting,
337
+ valueProp' = valueProp,
338
+ requested' = requested,
339
+ outcome' = outcome,
340
+ base' = base
341
+ }
342
+
343
+ action makeFixed = all {
344
+ orientation' = orientation,
345
+ fixed' = true,
346
+ value' = value,
347
+ prev' = prev,
348
+ collapsed' = collapsed,
349
+ justRestored' = false,
350
+ focus' = focus,
351
+ ariaNow' = ariaNow,
352
+ requests' = requests,
353
+ controlled' = controlled,
354
+ accepting' = accepting,
355
+ valueProp' = valueProp,
356
+ requested' = requested,
357
+ outcome' = outcome,
358
+ base' = base
359
+ }
360
+
361
+ action makeResizable = all {
362
+ orientation' = orientation,
363
+ fixed' = false,
364
+ value' = value,
365
+ prev' = prev,
366
+ collapsed' = collapsed,
367
+ justRestored' = false,
368
+ focus' = focus,
369
+ ariaNow' = ariaNow,
370
+ requests' = requests,
371
+ controlled' = controlled,
372
+ accepting' = accepting,
373
+ valueProp' = valueProp,
374
+ requested' = requested,
375
+ outcome' = outcome,
376
+ base' = base
377
+ }
378
+
379
+ // commit: the host writes the controlled value (a no-op while uncontrolled).
380
+ // `collapsed` is kept consistent: a value other than MIN cannot be collapsed.
381
+ action setValue = {
382
+ nondet v = Set(0, 2, 4, 6).oneOf()
383
+ all {
384
+ orientation' = orientation,
385
+ fixed' = fixed,
386
+ value' = if (controlled) v else value,
387
+ prev' = if (controlled) (if (v == MIN) prev else v) else prev,
388
+ collapsed' = if (controlled) (if (v == MIN) collapsed else false) else collapsed,
389
+ justRestored' = if (controlled) false else justRestored,
390
+ focus' = focus,
391
+ ariaNow' = if (controlled) v else ariaNow,
392
+ requests' = requests,
393
+ controlled' = controlled,
394
+ accepting' = accepting,
395
+ valueProp' = if (controlled) v else valueProp,
396
+ requested' = requested,
397
+ outcome' = if (controlled) "none" else outcome,
398
+ base' = base
399
+ }
400
+ }
401
+
402
+ // commit: the host flips its own copy of the value between MIN and MAX
403
+ action hostToggle = all {
404
+ orientation' = orientation,
405
+ fixed' = fixed,
406
+ value' = if (controlled) (if (valueProp == MAX) MIN else MAX) else value,
407
+ prev' = if (controlled) (if (valueProp == MAX) prev else valueProp) else prev,
408
+ collapsed' = if (controlled) (if (valueProp == MAX) collapsed else false) else collapsed,
409
+ justRestored' = if (controlled) false else justRestored,
410
+ focus' = focus,
411
+ ariaNow' = if (controlled) (if (valueProp == MAX) MIN else MAX) else ariaNow,
412
+ requests' = requests,
413
+ controlled' = controlled,
414
+ accepting' = accepting,
415
+ valueProp' = if (controlled) (if (valueProp == MAX) MIN else MAX) else valueProp,
416
+ requested' = requested,
417
+ outcome' = if (controlled) "none" else outcome,
418
+ base' = base
419
+ }
420
+
421
+ // configuration: the host starts owning the value, adopting the current one
422
+ action control = all {
423
+ orientation' = orientation,
424
+ fixed' = fixed,
425
+ value' = value,
426
+ prev' = prev,
427
+ collapsed' = collapsed,
428
+ justRestored' = false,
429
+ focus' = focus,
430
+ ariaNow' = ariaNow,
431
+ requests' = requests,
432
+ controlled' = true,
433
+ accepting' = accepting,
434
+ valueProp' = value,
435
+ requested' = requested,
436
+ outcome' = outcome,
437
+ base' = base
438
+ }
439
+
440
+ // configuration: the controlled host answers every request with its intent
441
+ action acceptRequests = all {
442
+ orientation' = orientation,
443
+ fixed' = fixed,
444
+ value' = value,
445
+ prev' = prev,
446
+ collapsed' = collapsed,
447
+ justRestored' = false,
448
+ focus' = focus,
449
+ ariaNow' = ariaNow,
450
+ requests' = requests,
451
+ controlled' = controlled,
452
+ accepting' = true,
453
+ valueProp' = valueProp,
454
+ requested' = requested,
455
+ outcome' = outcome,
456
+ base' = base
457
+ }
458
+
459
+ action step = any {
460
+ init, focusSplitter, blur, left, right, up, down, home, end, enter, f6,
461
+ orientVertical, orientHorizontal, makeFixed, makeResizable,
462
+ setValue, hostToggle, control, acceptRequests
463
+ }
464
+
465
+ // APG: aria-valuenow stays within aria-valuemin..aria-valuemax
466
+ val ValueInRange: bool = and(value >= MIN, value <= MAX)
467
+ // the rendered aria-valuenow always mirrors the widget value
468
+ val AriaNowMirrorsValue: bool = ariaNow == value
469
+ // a collapsed primary pane sits at the minimum
470
+ val CollapsedAtMin: bool = or(not(collapsed), value == MIN)
471
+ // the remembered position is always a valid position
472
+ val PrevInRange: bool = and(prev >= MIN, prev <= MAX)
473
+ // restoring returns the splitter to exactly the remembered value
474
+ val RestoreReturnsPrev: bool = or(not(justRestored), and(value == prev, not(collapsed)))
475
+ // focus is one of the modelled positions
476
+ val FocusKnown: bool = or(focus == "none", or(focus == "splitter", or(focus == "primary", focus == "secondary")))
477
+ // orientation is one of the two APG orientations
478
+ val OrientationKnown: bool = ariaOrientationKnown(orientation)
479
+ // callbacks only ever count up from zero
480
+ val RequestsNonNegative: bool = requests >= 0
481
+ // controlled: only the value prop sets the state, so no event commits
482
+ val ControlledFollowsProp: bool = or(not(controlled), value == valueProp)
483
+ // a rejected request leaves the value as it found it
484
+ val RejectedRequestUntouched: bool = or(not(outcome == "rejected"), value == base)
485
+ // an accepted request committed exactly its target
486
+ val AcceptedRequestCommits: bool = or(not(outcome == "accepted"), value == requested)
487
+ }