@cssints/apg-specs 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apg/menu-button.bridge.d.mts +3 -5
- package/dist/apg/menu-button.model.json +45 -5
- package/dist/apg/slider.model.json +1 -1
- package/dist/apg/spinbutton.model.json +1 -1
- package/package.json +1 -1
- package/specs/apg/menu-button.qnt +9 -4
- package/specs/apg/slider.qnt +9 -4
- package/specs/apg/spinbutton.qnt +6 -0
|
@@ -6,11 +6,9 @@ import { ActionStep, Probe, ScenarioConfig, SpecBridge } from "../scenario.mjs";
|
|
|
6
6
|
* cannot model a menu longer than four); the labels never reach the model.
|
|
7
7
|
*
|
|
8
8
|
* The spec's `menuOpen` axis is its own controlled contract (`controlled` /
|
|
9
|
-
* `accepts` / `value`),
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* controlled vocabulary is accepted here only so a scenario carrying it is
|
|
13
|
-
* modelled instead of silently dropped (see the menu-button DIVERGENCES).
|
|
9
|
+
* `accepts` / `value`), and `createMenu`'s `open` pair implements it: the walk
|
|
10
|
+
* runs it uncontrolled, accepting, holding and controlled-open, and `setValue` /
|
|
11
|
+
* `hostToggle` are the host writing its own copy.
|
|
14
12
|
*/
|
|
15
13
|
export interface MenuButtonConfig {
|
|
16
14
|
readonly items: ReadonlyArray<string>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generatedFrom": "../../specs/apg/menu-button.qnt",
|
|
3
|
-
"specHash": "
|
|
3
|
+
"specHash": "8636cd7e8489abe537f78fcdfd70fef48666a438e85da14e781d9e13aec55014",
|
|
4
4
|
"module": "apg_menu_button",
|
|
5
5
|
"vars": [
|
|
6
6
|
{
|
|
@@ -5159,8 +5159,28 @@
|
|
|
5159
5159
|
"opcode": "ite",
|
|
5160
5160
|
"args": [
|
|
5161
5161
|
{
|
|
5162
|
-
"kind": "
|
|
5163
|
-
"
|
|
5162
|
+
"kind": "app",
|
|
5163
|
+
"opcode": "and",
|
|
5164
|
+
"args": [
|
|
5165
|
+
{
|
|
5166
|
+
"kind": "name",
|
|
5167
|
+
"name": "controlled"
|
|
5168
|
+
},
|
|
5169
|
+
{
|
|
5170
|
+
"kind": "app",
|
|
5171
|
+
"opcode": "neq",
|
|
5172
|
+
"args": [
|
|
5173
|
+
{
|
|
5174
|
+
"kind": "name",
|
|
5175
|
+
"name": "v"
|
|
5176
|
+
},
|
|
5177
|
+
{
|
|
5178
|
+
"kind": "name",
|
|
5179
|
+
"name": "menuOpen"
|
|
5180
|
+
}
|
|
5181
|
+
]
|
|
5182
|
+
}
|
|
5183
|
+
]
|
|
5164
5184
|
},
|
|
5165
5185
|
{
|
|
5166
5186
|
"kind": "app",
|
|
@@ -5194,8 +5214,28 @@
|
|
|
5194
5214
|
"opcode": "ite",
|
|
5195
5215
|
"args": [
|
|
5196
5216
|
{
|
|
5197
|
-
"kind": "
|
|
5198
|
-
"
|
|
5217
|
+
"kind": "app",
|
|
5218
|
+
"opcode": "and",
|
|
5219
|
+
"args": [
|
|
5220
|
+
{
|
|
5221
|
+
"kind": "name",
|
|
5222
|
+
"name": "controlled"
|
|
5223
|
+
},
|
|
5224
|
+
{
|
|
5225
|
+
"kind": "app",
|
|
5226
|
+
"opcode": "neq",
|
|
5227
|
+
"args": [
|
|
5228
|
+
{
|
|
5229
|
+
"kind": "name",
|
|
5230
|
+
"name": "v"
|
|
5231
|
+
},
|
|
5232
|
+
{
|
|
5233
|
+
"kind": "name",
|
|
5234
|
+
"name": "menuOpen"
|
|
5235
|
+
}
|
|
5236
|
+
]
|
|
5237
|
+
}
|
|
5238
|
+
]
|
|
5199
5239
|
},
|
|
5200
5240
|
{
|
|
5201
5241
|
"kind": "app",
|
package/package.json
CHANGED
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
// controlled, accepts the host answers with the same value (`accepting`): the
|
|
25
25
|
// transition commits through the controlled value
|
|
26
26
|
// `valueProp` is the only thing that changes `menuOpen` while `controlled`;
|
|
27
|
-
// `setValue` / `hostToggle` are the host writing it
|
|
27
|
+
// `setValue` / `hostToggle` are the host writing it, and a write that changes it
|
|
28
|
+
// takes the focus with it (a write of the value it already has is unobservable
|
|
29
|
+
// and moves no focus). `control` / `acceptRequests`
|
|
28
30
|
// are configuration events (init is literal-only). A rejected request reverts
|
|
29
31
|
// the *whole* transition (focus included), so the ARIA invariants still hold.
|
|
30
32
|
//
|
|
@@ -272,12 +274,15 @@ module apg_menu_button {
|
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
|
|
275
|
-
// commit: the host writes the controlled open state (a no-op while uncontrolled)
|
|
277
|
+
// commit: the host writes the controlled open state (a no-op while uncontrolled).
|
|
278
|
+
// A controlled host writing the value it already has is unobservable to the
|
|
279
|
+
// widget (nothing changes, so nothing notifies), so it moves no focus: only a
|
|
280
|
+
// write that changes the open state takes the focus with it.
|
|
276
281
|
action setValue = {
|
|
277
282
|
nondet v = Set(true, false).oneOf()
|
|
278
283
|
all {
|
|
279
|
-
focusLevel' = if (controlled) (if (v) 2 else 1) else focusLevel,
|
|
280
|
-
focusIdx' = if (controlled) (if (v) 1 else 0) else focusIdx,
|
|
284
|
+
focusLevel' = if (and(controlled, v != menuOpen)) (if (v) 2 else 1) else focusLevel,
|
|
285
|
+
focusIdx' = if (and(controlled, v != menuOpen)) (if (v) 1 else 0) else focusIdx,
|
|
281
286
|
menuOpen' = if (controlled) v else menuOpen,
|
|
282
287
|
lastActivated' = lastActivated,
|
|
283
288
|
controlled' = controlled,
|
package/specs/apg/slider.qnt
CHANGED
|
@@ -45,10 +45,15 @@
|
|
|
45
45
|
// press always land on the grid.
|
|
46
46
|
// - Dragging (pointer capture, move events): only a discrete pointer
|
|
47
47
|
// "press at value" is modelled, snapped to the step grid and clamped.
|
|
48
|
-
// - Disabled / read-only
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
48
|
+
// - Disabled / read-only: the primitive's widget-level `disabled` is a
|
|
49
|
+
// deliberate extension of the pattern, and the machine has no `disabled`
|
|
50
|
+
// var. So the model carries no invariant for it ("a disabled thumb drops
|
|
51
|
+
// every key and rail press and announces aria-disabled"); its oracle is the
|
|
52
|
+
// DOM test in `packages/headless/test/slider.browser.test.tsx`. Read-only is
|
|
53
|
+
// not offered at all.
|
|
54
|
+
// - Touch assistive-technology key synthesis, and aria-valuetext formatting
|
|
55
|
+
// (units, locale): a real widget formats the text from its own value; the
|
|
56
|
+
// spec only fixes WHEN the text is derived (always, from valuenow).
|
|
52
57
|
// - The range (0..10) is fixed; `configure` varies step, large step and the
|
|
53
58
|
// starting value. The bridge rejects a config outside the variant table.
|
|
54
59
|
module apg_slider {
|
package/specs/apg/spinbutton.qnt
CHANGED
|
@@ -49,6 +49,12 @@
|
|
|
49
49
|
// is replaced wholesale by a sample text.
|
|
50
50
|
// - the commit/revert policy (Enter/Escape/blur) is a widely used
|
|
51
51
|
// convention, not APG text; so is the buttons' aria-disabled-at-bound.
|
|
52
|
+
// - a widget-level `disabled`: a deliberate extension of the pattern, not
|
|
53
|
+
// modelled. The machine's only disabled state is the steppers' at-bound
|
|
54
|
+
// one (`DisabledOnlyAtBound`); it has no `disabled` var, so it cannot say
|
|
55
|
+
// "while disabled, keys, typing and both steppers change nothing and both
|
|
56
|
+
// steppers announce aria-disabled". The DOM test in
|
|
57
|
+
// `packages/headless/test/spinbutton.browser.test.tsx` is its oracle.
|
|
52
58
|
// - press-and-hold auto-repeat on the buttons, mouse wheel.
|
|
53
59
|
//
|
|
54
60
|
// Generator notes: no action preconditions (guards live in the assignment
|