@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,309 @@
|
|
|
1
|
+
// APG Disclosure (Show/Hide) — https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/
|
|
2
|
+
//
|
|
3
|
+
// One trigger (role=button) controls one panel. The spec models both the
|
|
4
|
+
// widget state (`expanded`) and what it renders (`ariaExpanded` on the trigger,
|
|
5
|
+
// `panelHidden` on the panel), so the central APG requirement — aria-expanded
|
|
6
|
+
// mirrors the panel's visibility — is a checkable invariant, not a comment.
|
|
7
|
+
//
|
|
8
|
+
// Events are the user-level alphabet shared with the scenario bridge:
|
|
9
|
+
// focus / blur the trigger gains / loses focus
|
|
10
|
+
// click pointer activation (focuses the trigger, then requests a toggle)
|
|
11
|
+
// press Enter or Space on the focused trigger (APG keyboard section)
|
|
12
|
+
// expand/collapse/toggle programmatic control (the same request path)
|
|
13
|
+
// disable/enable the trigger becomes inert (cannot gain focus, ignores
|
|
14
|
+
// click and keys). Focus it already held is browser-defined
|
|
15
|
+
// (Chromium keeps document.activeElement until the next key
|
|
16
|
+
// input), so the spec records it as "unknown" until the next
|
|
17
|
+
// focus / blur / click settles it
|
|
18
|
+
//
|
|
19
|
+
// Request vs commit (map ticket rt1p). Everything a user sees as a change —
|
|
20
|
+
// click, press, expand, collapse, toggle — is a *request* carrying an intent
|
|
21
|
+
// (`requested`). It fires onValueChange (`requests` counts the calls) when the
|
|
22
|
+
// intent differs from the current value, and then:
|
|
23
|
+
// uncontrolled the request is accepted: the state commits the intent
|
|
24
|
+
// controlled, holds the host ignores it ("rejected"): the state is untouched
|
|
25
|
+
// controlled, accepts the host answers with the same value (`accepting`): the
|
|
26
|
+
// state commits through the controlled value, not the event
|
|
27
|
+
// The controlled value (`valueProp`) is the only thing that changes state while
|
|
28
|
+
// `controlled`; `setValue` / `hostToggle` are the host writing it. `control` and
|
|
29
|
+
// `acceptRequests` are configuration events (init is literal-only), like `disable`.
|
|
30
|
+
//
|
|
31
|
+
// Invariants for the model: ControlledFollowsProp (in controlled mode no event
|
|
32
|
+
// commits), RejectedRequestUntouched, AcceptedRequestCommits.
|
|
33
|
+
//
|
|
34
|
+
// `outcome` is "none" | "accepted" | "rejected" for the last request; a host
|
|
35
|
+
// write settles it back to "none". `base` is the value when it was received.
|
|
36
|
+
//
|
|
37
|
+
// ponytail: a single trigger/panel only; groups (DisclosureGroup, expandedKeys)
|
|
38
|
+
// need collections the generator does not have.
|
|
39
|
+
module apg_disclosure {
|
|
40
|
+
import aria.* from "../../shared/aria"
|
|
41
|
+
|
|
42
|
+
var expanded: bool // widget state: the content is shown
|
|
43
|
+
var disabled: bool // the trigger is inert
|
|
44
|
+
var focus: str // "none" | "trigger" | "unknown"
|
|
45
|
+
var ariaExpanded: str // rendered aria-expanded on the trigger: "true" | "false"
|
|
46
|
+
var panelHidden: bool // rendered hidden attribute on the panel
|
|
47
|
+
var requests: int // number of requests that fired onValueChange
|
|
48
|
+
var controlled: bool // the host owns the value (`value` prop present)
|
|
49
|
+
var accepting: bool // controlled host that answers every request with its intent
|
|
50
|
+
var valueProp: bool // the controlled value prop (meaningful while controlled)
|
|
51
|
+
var requested: bool // intent of the last request
|
|
52
|
+
var outcome: str // "none" | "accepted" | "rejected"
|
|
53
|
+
var base: bool // value when the last request was received
|
|
54
|
+
|
|
55
|
+
def moved(before: bool, after: bool): int = if (before == after) 0 else 1
|
|
56
|
+
// controlled host that keeps the old value: the request is rejected
|
|
57
|
+
def holds: bool = and(controlled, not(accepting))
|
|
58
|
+
// controlled host that writes the intent back through the value prop
|
|
59
|
+
def follows: bool = and(controlled, accepting)
|
|
60
|
+
def verdict: str = if (holds) "rejected" else "accepted"
|
|
61
|
+
// the value after a request for `want`
|
|
62
|
+
def landed(h: bool, e: bool, want: bool): bool = if (h) e else want
|
|
63
|
+
// the value prop after a request for `want`
|
|
64
|
+
def propAfter(f: bool, p: bool, want: bool): bool = if (f) want else p
|
|
65
|
+
|
|
66
|
+
action init = all {
|
|
67
|
+
expanded' = false,
|
|
68
|
+
disabled' = false,
|
|
69
|
+
focus' = "none",
|
|
70
|
+
ariaExpanded' = "false",
|
|
71
|
+
panelHidden' = true,
|
|
72
|
+
requests' = 0,
|
|
73
|
+
controlled' = false,
|
|
74
|
+
accepting' = false,
|
|
75
|
+
valueProp' = false,
|
|
76
|
+
requested' = false,
|
|
77
|
+
outcome' = "none",
|
|
78
|
+
base' = false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
action focusTrigger = all {
|
|
82
|
+
expanded' = expanded,
|
|
83
|
+
disabled' = disabled,
|
|
84
|
+
focus' = if (disabled) focus else "trigger",
|
|
85
|
+
ariaExpanded' = ariaExpanded,
|
|
86
|
+
panelHidden' = panelHidden,
|
|
87
|
+
requests' = requests,
|
|
88
|
+
controlled' = controlled,
|
|
89
|
+
accepting' = accepting,
|
|
90
|
+
valueProp' = valueProp,
|
|
91
|
+
requested' = requested,
|
|
92
|
+
outcome' = outcome,
|
|
93
|
+
base' = base
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
action blurTrigger = all {
|
|
97
|
+
expanded' = expanded,
|
|
98
|
+
disabled' = disabled,
|
|
99
|
+
focus' = "none",
|
|
100
|
+
ariaExpanded' = ariaExpanded,
|
|
101
|
+
panelHidden' = panelHidden,
|
|
102
|
+
requests' = requests,
|
|
103
|
+
controlled' = controlled,
|
|
104
|
+
accepting' = accepting,
|
|
105
|
+
valueProp' = valueProp,
|
|
106
|
+
requested' = requested,
|
|
107
|
+
outcome' = outcome,
|
|
108
|
+
base' = base
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// request: toggle, fired by a pointer click on an enabled trigger
|
|
112
|
+
action click = all {
|
|
113
|
+
expanded' = if (disabled) expanded else landed(holds, expanded, not(expanded)),
|
|
114
|
+
disabled' = disabled,
|
|
115
|
+
focus' = if (disabled) focus else "trigger",
|
|
116
|
+
ariaExpanded' = if (disabled) ariaExpanded else ariaBoolToken(landed(holds, expanded, not(expanded))),
|
|
117
|
+
panelHidden' = if (disabled) panelHidden else not(landed(holds, expanded, not(expanded))),
|
|
118
|
+
requests' = if (disabled) requests else requests + 1,
|
|
119
|
+
controlled' = controlled,
|
|
120
|
+
accepting' = accepting,
|
|
121
|
+
valueProp' = if (disabled) valueProp else propAfter(follows, valueProp, not(expanded)),
|
|
122
|
+
requested' = if (disabled) requested else not(expanded),
|
|
123
|
+
outcome' = if (disabled) outcome else verdict,
|
|
124
|
+
base' = if (disabled) base else expanded
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// request: toggle, fired by Enter / Space on the focused, enabled trigger
|
|
128
|
+
action press = all {
|
|
129
|
+
expanded' = if (and(not(disabled), focus == "trigger")) landed(holds, expanded, not(expanded)) else expanded,
|
|
130
|
+
disabled' = disabled,
|
|
131
|
+
focus' = focus,
|
|
132
|
+
ariaExpanded' = if (and(not(disabled), focus == "trigger")) ariaBoolToken(landed(holds, expanded, not(expanded))) else ariaExpanded,
|
|
133
|
+
panelHidden' = if (and(not(disabled), focus == "trigger")) not(landed(holds, expanded, not(expanded))) else panelHidden,
|
|
134
|
+
requests' = if (and(not(disabled), focus == "trigger")) requests + 1 else requests,
|
|
135
|
+
controlled' = controlled,
|
|
136
|
+
accepting' = accepting,
|
|
137
|
+
valueProp' = if (and(not(disabled), focus == "trigger")) propAfter(follows, valueProp, not(expanded)) else valueProp,
|
|
138
|
+
requested' = if (and(not(disabled), focus == "trigger")) not(expanded) else requested,
|
|
139
|
+
outcome' = if (and(not(disabled), focus == "trigger")) verdict else outcome,
|
|
140
|
+
base' = if (and(not(disabled), focus == "trigger")) expanded else base
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// request: expand (a redundant request fires nothing)
|
|
144
|
+
action expand = all {
|
|
145
|
+
expanded' = if (expanded) expanded else landed(holds, expanded, true),
|
|
146
|
+
disabled' = disabled,
|
|
147
|
+
focus' = focus,
|
|
148
|
+
ariaExpanded' = if (expanded) ariaExpanded else ariaBoolToken(landed(holds, expanded, true)),
|
|
149
|
+
panelHidden' = if (expanded) panelHidden else not(landed(holds, expanded, true)),
|
|
150
|
+
requests' = requests + moved(expanded, true),
|
|
151
|
+
controlled' = controlled,
|
|
152
|
+
accepting' = accepting,
|
|
153
|
+
valueProp' = if (expanded) valueProp else propAfter(follows, valueProp, true),
|
|
154
|
+
requested' = if (expanded) requested else true,
|
|
155
|
+
outcome' = if (expanded) outcome else verdict,
|
|
156
|
+
base' = if (expanded) base else expanded
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// request: collapse (a redundant request fires nothing)
|
|
160
|
+
action collapse = all {
|
|
161
|
+
expanded' = if (expanded) landed(holds, expanded, false) else expanded,
|
|
162
|
+
disabled' = disabled,
|
|
163
|
+
focus' = focus,
|
|
164
|
+
ariaExpanded' = if (expanded) ariaBoolToken(landed(holds, expanded, false)) else ariaExpanded,
|
|
165
|
+
panelHidden' = if (expanded) not(landed(holds, expanded, false)) else panelHidden,
|
|
166
|
+
requests' = requests + moved(expanded, false),
|
|
167
|
+
controlled' = controlled,
|
|
168
|
+
accepting' = accepting,
|
|
169
|
+
valueProp' = if (expanded) propAfter(follows, valueProp, false) else valueProp,
|
|
170
|
+
requested' = if (expanded) false else requested,
|
|
171
|
+
outcome' = if (expanded) verdict else outcome,
|
|
172
|
+
base' = if (expanded) expanded else base
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// request: toggle, the programmatic API (ignores `disabled`)
|
|
176
|
+
action toggle = all {
|
|
177
|
+
expanded' = landed(holds, expanded, not(expanded)),
|
|
178
|
+
disabled' = disabled,
|
|
179
|
+
focus' = focus,
|
|
180
|
+
ariaExpanded' = ariaBoolToken(landed(holds, expanded, not(expanded))),
|
|
181
|
+
panelHidden' = not(landed(holds, expanded, not(expanded))),
|
|
182
|
+
requests' = requests + 1,
|
|
183
|
+
controlled' = controlled,
|
|
184
|
+
accepting' = accepting,
|
|
185
|
+
valueProp' = propAfter(follows, valueProp, not(expanded)),
|
|
186
|
+
requested' = not(expanded),
|
|
187
|
+
outcome' = verdict,
|
|
188
|
+
base' = expanded
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// commit: the host writes the controlled value (a no-op while uncontrolled)
|
|
192
|
+
action setValue = {
|
|
193
|
+
nondet v = Set(true, false).oneOf()
|
|
194
|
+
all {
|
|
195
|
+
expanded' = if (controlled) v else expanded,
|
|
196
|
+
disabled' = disabled,
|
|
197
|
+
focus' = focus,
|
|
198
|
+
ariaExpanded' = if (controlled) ariaBoolToken(v) else ariaExpanded,
|
|
199
|
+
panelHidden' = if (controlled) not(v) else panelHidden,
|
|
200
|
+
requests' = requests,
|
|
201
|
+
controlled' = controlled,
|
|
202
|
+
accepting' = accepting,
|
|
203
|
+
valueProp' = if (controlled) v else valueProp,
|
|
204
|
+
requested' = requested,
|
|
205
|
+
outcome' = if (controlled) "none" else outcome,
|
|
206
|
+
base' = base
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// commit: the host flips its own copy of the value (an outside button)
|
|
211
|
+
action hostToggle = all {
|
|
212
|
+
expanded' = if (controlled) not(valueProp) else expanded,
|
|
213
|
+
disabled' = disabled,
|
|
214
|
+
focus' = focus,
|
|
215
|
+
ariaExpanded' = if (controlled) ariaBoolToken(not(valueProp)) else ariaExpanded,
|
|
216
|
+
panelHidden' = if (controlled) valueProp else panelHidden,
|
|
217
|
+
requests' = requests,
|
|
218
|
+
controlled' = controlled,
|
|
219
|
+
accepting' = accepting,
|
|
220
|
+
valueProp' = if (controlled) not(valueProp) else valueProp,
|
|
221
|
+
requested' = requested,
|
|
222
|
+
outcome' = if (controlled) "none" else outcome,
|
|
223
|
+
base' = base
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// configuration: the host starts owning the value, adopting the current one
|
|
227
|
+
action control = all {
|
|
228
|
+
expanded' = expanded,
|
|
229
|
+
disabled' = disabled,
|
|
230
|
+
focus' = focus,
|
|
231
|
+
ariaExpanded' = ariaExpanded,
|
|
232
|
+
panelHidden' = panelHidden,
|
|
233
|
+
requests' = requests,
|
|
234
|
+
controlled' = true,
|
|
235
|
+
accepting' = accepting,
|
|
236
|
+
valueProp' = expanded,
|
|
237
|
+
requested' = requested,
|
|
238
|
+
outcome' = outcome,
|
|
239
|
+
base' = base
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// configuration: the controlled host answers every request with its intent
|
|
243
|
+
action acceptRequests = all {
|
|
244
|
+
expanded' = expanded,
|
|
245
|
+
disabled' = disabled,
|
|
246
|
+
focus' = focus,
|
|
247
|
+
ariaExpanded' = ariaExpanded,
|
|
248
|
+
panelHidden' = panelHidden,
|
|
249
|
+
requests' = requests,
|
|
250
|
+
controlled' = controlled,
|
|
251
|
+
accepting' = true,
|
|
252
|
+
valueProp' = valueProp,
|
|
253
|
+
requested' = requested,
|
|
254
|
+
outcome' = outcome,
|
|
255
|
+
base' = base
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
action disable = all {
|
|
259
|
+
expanded' = expanded,
|
|
260
|
+
disabled' = true,
|
|
261
|
+
focus' = if (focus == "trigger") "unknown" else focus,
|
|
262
|
+
ariaExpanded' = ariaExpanded,
|
|
263
|
+
panelHidden' = panelHidden,
|
|
264
|
+
requests' = requests,
|
|
265
|
+
controlled' = controlled,
|
|
266
|
+
accepting' = accepting,
|
|
267
|
+
valueProp' = valueProp,
|
|
268
|
+
requested' = requested,
|
|
269
|
+
outcome' = outcome,
|
|
270
|
+
base' = base
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
action enable = all {
|
|
274
|
+
expanded' = expanded,
|
|
275
|
+
disabled' = false,
|
|
276
|
+
focus' = focus,
|
|
277
|
+
ariaExpanded' = ariaExpanded,
|
|
278
|
+
panelHidden' = panelHidden,
|
|
279
|
+
requests' = requests,
|
|
280
|
+
controlled' = controlled,
|
|
281
|
+
accepting' = accepting,
|
|
282
|
+
valueProp' = valueProp,
|
|
283
|
+
requested' = requested,
|
|
284
|
+
outcome' = outcome,
|
|
285
|
+
base' = base
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
action step = any {
|
|
289
|
+
init, focusTrigger, blurTrigger, click, press, expand, collapse, toggle,
|
|
290
|
+
setValue, hostToggle, control, acceptRequests, disable, enable
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// APG: aria-expanded is "true" exactly when the panel is visible
|
|
294
|
+
val AriaMirrorsPanel: bool = (ariaExpanded == "true") == not(panelHidden)
|
|
295
|
+
// the rendered attributes always follow the widget state
|
|
296
|
+
val RenderFollowsState: bool = (ariaExpanded == "true") == expanded
|
|
297
|
+
// an inert trigger is never *known* to hold focus
|
|
298
|
+
val DisabledNotFocused: bool = or(not(disabled), not(focus == "trigger"))
|
|
299
|
+
// focus is one of the modelled positions
|
|
300
|
+
val FocusKnown: bool = or(focus == "none", or(focus == "trigger", focus == "unknown"))
|
|
301
|
+
// callbacks only ever count up from zero
|
|
302
|
+
val RequestsNonNegative: bool = requests >= 0
|
|
303
|
+
// controlled: only the value prop sets the state, so no event commits
|
|
304
|
+
val ControlledFollowsProp: bool = or(not(controlled), expanded == valueProp)
|
|
305
|
+
// a rejected request leaves the state as it found it
|
|
306
|
+
val RejectedRequestUntouched: bool = or(not(outcome == "rejected"), expanded == base)
|
|
307
|
+
// an accepted request committed exactly its intent
|
|
308
|
+
val AcceptedRequestCommits: bool = or(not(outcome == "accepted"), expanded == requested)
|
|
309
|
+
}
|