@cssints/apg-specs 0.5.2 → 0.6.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 (96) hide show
  1. package/README.md +10 -5
  2. package/dist/apg/accordion.bridge.d.mts +1 -3
  3. package/dist/apg/accordion.bridge.mjs +1 -2
  4. package/dist/apg/alert.bridge.d.mts +1 -3
  5. package/dist/apg/alert.bridge.mjs +1 -2
  6. package/dist/apg/alertdialog.bridge.d.mts +1 -3
  7. package/dist/apg/alertdialog.bridge.mjs +1 -2
  8. package/dist/apg/breadcrumb.bridge.d.mts +1 -3
  9. package/dist/apg/breadcrumb.bridge.mjs +1 -2
  10. package/dist/apg/button.bridge.d.mts +1 -3
  11. package/dist/apg/button.bridge.mjs +1 -2
  12. package/dist/apg/carousel.bridge.d.mts +1 -3
  13. package/dist/apg/carousel.bridge.mjs +1 -2
  14. package/dist/apg/checkbox-group.bridge.d.mts +1 -3
  15. package/dist/apg/checkbox-group.bridge.mjs +1 -2
  16. package/dist/apg/checkbox.bridge.d.mts +1 -3
  17. package/dist/apg/checkbox.bridge.mjs +1 -2
  18. package/dist/apg/combobox-autocomplete.bridge.d.mts +1 -3
  19. package/dist/apg/combobox-autocomplete.bridge.mjs +1 -2
  20. package/dist/apg/combobox-dialog.bridge.d.mts +1 -3
  21. package/dist/apg/combobox-dialog.bridge.mjs +1 -2
  22. package/dist/apg/combobox-grid.bridge.d.mts +1 -3
  23. package/dist/apg/combobox-grid.bridge.mjs +1 -2
  24. package/dist/apg/combobox-select-only.bridge.d.mts +1 -3
  25. package/dist/apg/combobox-select-only.bridge.mjs +1 -2
  26. package/dist/apg/combobox-tree.bridge.d.mts +1 -3
  27. package/dist/apg/combobox-tree.bridge.mjs +1 -2
  28. package/dist/apg/dialog-modal.bridge.d.mts +1 -3
  29. package/dist/apg/dialog-modal.bridge.mjs +1 -2
  30. package/dist/apg/disclosure.bridge.d.mts +1 -3
  31. package/dist/apg/disclosure.bridge.mjs +1 -2
  32. package/dist/apg/feed.bridge.d.mts +1 -3
  33. package/dist/apg/feed.bridge.mjs +1 -2
  34. package/dist/apg/grid-selection.bridge.d.mts +1 -3
  35. package/dist/apg/grid-selection.bridge.mjs +1 -2
  36. package/dist/apg/grid-sort.bridge.d.mts +1 -3
  37. package/dist/apg/grid-sort.bridge.mjs +1 -2
  38. package/dist/apg/grid.bridge.d.mts +1 -3
  39. package/dist/apg/grid.bridge.mjs +1 -2
  40. package/dist/apg/link.bridge.d.mts +1 -3
  41. package/dist/apg/link.bridge.mjs +1 -2
  42. package/dist/apg/listbox-multi.bridge.d.mts +1 -3
  43. package/dist/apg/listbox-multi.bridge.mjs +1 -2
  44. package/dist/apg/listbox-rearrangeable.bridge.d.mts +1 -3
  45. package/dist/apg/listbox-rearrangeable.bridge.mjs +1 -2
  46. package/dist/apg/listbox-shared.d.mts +1 -3
  47. package/dist/apg/listbox-shared.mjs +1 -2
  48. package/dist/apg/listbox-single.bridge.d.mts +1 -3
  49. package/dist/apg/listbox-single.bridge.mjs +1 -2
  50. package/dist/apg/listbox-typeahead.bridge.d.mts +1 -3
  51. package/dist/apg/listbox-typeahead.bridge.mjs +1 -2
  52. package/dist/apg/menu-button.bridge.d.mts +1 -3
  53. package/dist/apg/menu-button.bridge.mjs +1 -2
  54. package/dist/apg/menu-checkable.bridge.d.mts +1 -3
  55. package/dist/apg/menu-checkable.bridge.mjs +1 -2
  56. package/dist/apg/menu-menubar.bridge.d.mts +1 -3
  57. package/dist/apg/menu-menubar.bridge.mjs +1 -2
  58. package/dist/apg/meter.bridge.d.mts +1 -3
  59. package/dist/apg/meter.bridge.mjs +1 -2
  60. package/dist/apg/player.bridge.d.mts +1 -3
  61. package/dist/apg/player.bridge.mjs +1 -2
  62. package/dist/apg/radio.bridge.d.mts +1 -3
  63. package/dist/apg/radio.bridge.mjs +1 -2
  64. package/dist/apg/slider-multithumb.bridge.d.mts +1 -3
  65. package/dist/apg/slider-multithumb.bridge.mjs +1 -2
  66. package/dist/apg/slider.bridge.d.mts +1 -3
  67. package/dist/apg/slider.bridge.mjs +1 -2
  68. package/dist/apg/spinbutton.bridge.d.mts +1 -3
  69. package/dist/apg/spinbutton.bridge.mjs +1 -2
  70. package/dist/apg/switch.bridge.d.mts +1 -3
  71. package/dist/apg/switch.bridge.mjs +1 -2
  72. package/dist/apg/tabs.bridge.d.mts +1 -3
  73. package/dist/apg/tabs.bridge.mjs +1 -2
  74. package/dist/apg/timeline.bridge.d.mts +1 -3
  75. package/dist/apg/timeline.bridge.mjs +1 -2
  76. package/dist/apg/toolbar.bridge.d.mts +1 -3
  77. package/dist/apg/toolbar.bridge.mjs +1 -2
  78. package/dist/apg/tooltip.bridge.d.mts +1 -3
  79. package/dist/apg/tooltip.bridge.mjs +1 -2
  80. package/dist/apg/treegrid.bridge.d.mts +1 -3
  81. package/dist/apg/treegrid.bridge.mjs +1 -2
  82. package/dist/apg/treeview.bridge.d.mts +1 -3
  83. package/dist/apg/treeview.bridge.mjs +1 -2
  84. package/dist/apg/windowsplitter.bridge.d.mts +1 -3
  85. package/dist/apg/windowsplitter.bridge.mjs +1 -2
  86. package/dist/canvas/canvas.bridge.d.mts +1 -2
  87. package/dist/canvas/canvas.bridge.mjs +1 -2
  88. package/dist/patterns/resizable-panels.bridge.d.mts +42 -0
  89. package/dist/patterns/resizable-panels.bridge.mjs +286 -0
  90. package/dist/patterns/resizable-panels.model.json +11221 -0
  91. package/dist/scenario.d.mts +0 -2
  92. package/dist/scenario.mjs +1 -2
  93. package/package.json +9 -2
  94. package/scenarios/patterns/README.md +12 -0
  95. package/scenarios/patterns/resizable-panels.spec.json +539 -0
  96. package/specs/patterns/resizable-panels.qnt +831 -0
@@ -0,0 +1,42 @@
1
+ import { ActionStep, Probe, ScenarioConfig, SpecBridge } from "../scenario.mjs";
2
+ export type PanelsOrientation = "vertical" | "horizontal";
3
+ /** The layouts the spec reaches by a host write (hostEqual / hostWide / ...). */
4
+ export declare const PANELS_LAYOUTS: {
5
+ readonly initial: readonly [20, 40, 40];
6
+ readonly equal: readonly [30, 30, 40];
7
+ readonly wide: readonly [50, 30, 20];
8
+ readonly narrow: readonly [10, 40, 50];
9
+ readonly collapsed: readonly [0, 50, 50];
10
+ };
11
+ export type PanelsLayout = keyof typeof PANELS_LAYOUTS;
12
+ export declare const PANEL_COUNT = 3;
13
+ export declare const PANEL_MIN = 10;
14
+ export declare const PANEL_MAX = 80;
15
+ /**
16
+ * Scenario `config` -> what both targets are mounted with. The spec is the
17
+ * three-pane instance (MIN 10, MAX 80, 10% steps, pane 0 collapsible at 0), so
18
+ * any other geometry is not modelled. `layout` only leaves the initial layout
19
+ * through a host write, which needs `controlled`; an uncontrolled scenario that
20
+ * starts elsewhere is `Unsupported` (it would have to be reached by keys, which
21
+ * no ported scenario needs).
22
+ */
23
+ export interface PanelsConfig {
24
+ readonly orientation: PanelsOrientation;
25
+ readonly layout: PanelsLayout;
26
+ readonly sizes: ReadonlyArray<number>;
27
+ readonly controlled: boolean;
28
+ readonly accepts: boolean;
29
+ }
30
+ export declare const resizablePanelsConfig: (config: ScenarioConfig) => PanelsConfig;
31
+ /**
32
+ * Resizable panels vocabulary <-> `specs/patterns/resizable-panels.qnt`. Parts:
33
+ * `separator:0` / `separator:1` and `pane:0..2`; keys only act on the focused
34
+ * separator (focus it first, as a user would). The drag is its own vocabulary
35
+ * (`dragStart` on a separator, `dragBy` with a ±10 delta, `dragEnd`), so the
36
+ * spec's snapshot + delta model and the DOM's real pointer events see the same
37
+ * steps. `hostLayout` is the host writing a whole layout (controlled only).
38
+ */
39
+ export declare const resizablePanelsBridge: SpecBridge;
40
+ /** The user-level steps a differential walk draws from. */
41
+ export declare const resizablePanelsSteps: ReadonlyArray<ActionStep>;
42
+ export declare const resizablePanelsProbe: Probe;
@@ -0,0 +1,286 @@
1
+ import { Unsupported } from "../scenario.mjs";
2
+ // src/patterns/resizable-panels.bridge.ts
3
+ const specEvent = (tag, params = {}) => ({
4
+ _tag: tag,
5
+ ...params
6
+ });
7
+ const num = (value) => value === void 0 ? 0 : Number(value);
8
+ /** The layouts the spec reaches by a host write (hostEqual / hostWide / ...). */
9
+ const PANELS_LAYOUTS = {
10
+ initial: [
11
+ 20,
12
+ 40,
13
+ 40
14
+ ],
15
+ equal: [
16
+ 30,
17
+ 30,
18
+ 40
19
+ ],
20
+ wide: [
21
+ 50,
22
+ 30,
23
+ 20
24
+ ],
25
+ narrow: [
26
+ 10,
27
+ 40,
28
+ 50
29
+ ],
30
+ collapsed: [
31
+ 0,
32
+ 50,
33
+ 50
34
+ ]
35
+ };
36
+ const PANEL_COUNT = 3;
37
+ const PANEL_MIN = 10;
38
+ const PANEL_MAX = 80;
39
+ const isLayout = (value) => typeof value === "string" && Object.hasOwn(PANELS_LAYOUTS, value);
40
+ const resizablePanelsConfig = (config) => {
41
+ let orientation = "vertical";
42
+ let layout = "initial";
43
+ let controlled = false;
44
+ let accepts = true;
45
+ for (const [key, raw] of Object.entries(config)) if (key === "orientation" && (raw === "vertical" || raw === "horizontal")) orientation = raw;
46
+ else if (key === "layout" && isLayout(raw)) layout = raw;
47
+ else if (key === "controlled" && typeof raw === "boolean") controlled = raw;
48
+ else if (key === "accepts" && typeof raw === "boolean") accepts = raw;
49
+ else if (key === "collapsible" && raw === true) continue;
50
+ else if (key === "panes" && raw === 3) continue;
51
+ else if (key === "min" && raw === 10) continue;
52
+ else if (key === "max" && raw === 80) continue;
53
+ else throw new Unsupported(`config ${key}=${JSON.stringify(raw)}`);
54
+ if (!controlled && layout !== "initial") throw new Unsupported(`layout ${layout} without a controlled host`);
55
+ return {
56
+ orientation,
57
+ layout,
58
+ sizes: PANELS_LAYOUTS[layout],
59
+ controlled,
60
+ accepts
61
+ };
62
+ };
63
+ const separatorIndex = (target) => {
64
+ const match = /^separator:(\d+)$/.exec(target ?? "separator:0");
65
+ if (match === null) throw new Unsupported(`part ${target ?? ""}`);
66
+ const index = Number(match[1]);
67
+ if (index < 0 || index >= 2) throw new Unsupported(`separator ${index}`);
68
+ return index;
69
+ };
70
+ const HOST_EVENTS = {
71
+ initial: "hostEqual",
72
+ equal: "hostEqual",
73
+ wide: "hostWide",
74
+ narrow: "hostNarrow",
75
+ collapsed: "hostCollapsed"
76
+ };
77
+ const KEY_EVENTS = {
78
+ ArrowLeft: "left",
79
+ ArrowRight: "right",
80
+ ArrowUp: "up",
81
+ ArrowDown: "down",
82
+ Home: "home",
83
+ End: "end",
84
+ Enter: "enter"
85
+ };
86
+ const isKeyEvent = (key) => key in KEY_EVENTS;
87
+ /**
88
+ * Resizable panels vocabulary <-> `specs/patterns/resizable-panels.qnt`. Parts:
89
+ * `separator:0` / `separator:1` and `pane:0..2`; keys only act on the focused
90
+ * separator (focus it first, as a user would). The drag is its own vocabulary
91
+ * (`dragStart` on a separator, `dragBy` with a ±10 delta, `dragEnd`), so the
92
+ * spec's snapshot + delta model and the DOM's real pointer events see the same
93
+ * steps. `hostLayout` is the host writing a whole layout (controlled only).
94
+ */
95
+ const resizablePanelsBridge = {
96
+ setup: (config) => {
97
+ const c = resizablePanelsConfig(config);
98
+ const events = [];
99
+ if (c.orientation === "horizontal") events.push(specEvent("orientHorizontal"));
100
+ if (c.controlled) {
101
+ events.push(specEvent("control"));
102
+ if (c.accepts) events.push(specEvent("acceptRequests"));
103
+ if (c.layout !== "initial") events.push(specEvent(HOST_EVENTS[c.layout]));
104
+ }
105
+ return events;
106
+ },
107
+ event: (step) => {
108
+ switch (step.event) {
109
+ case "focus": return [specEvent(`focusSep${separatorIndex(step.target)}`)];
110
+ case "blur": return [specEvent("blur")];
111
+ case "key": {
112
+ const key = step.key ?? "";
113
+ if (!isKeyEvent(key)) throw new Unsupported(`key ${key}`);
114
+ return [specEvent(KEY_EVENTS[key])];
115
+ }
116
+ case "dragStart": return [specEvent(`dragDown${separatorIndex(step.target)}`)];
117
+ case "dragBy": {
118
+ const delta = step.value;
119
+ if (delta === 10) return [specEvent("dragNext")];
120
+ if (delta === -10) return [specEvent("dragPrev")];
121
+ throw new Unsupported(`drag delta ${JSON.stringify(delta)}`);
122
+ }
123
+ case "dragEnd":
124
+ case "dragCancel": return [specEvent("dragUp")];
125
+ case "hover":
126
+ case "unhover": return [];
127
+ case "prop": switch (step.prop) {
128
+ case "orientation":
129
+ if (step.value === "horizontal") return [specEvent("orientHorizontal")];
130
+ if (step.value === "vertical") return [specEvent("orientVertical")];
131
+ throw new Unsupported(`orientation ${JSON.stringify(step.value)}`);
132
+ case "hostLayout":
133
+ if (!isLayout(step.value)) throw new Unsupported(`layout ${JSON.stringify(step.value)}`);
134
+ if (step.value === "initial") throw new Unsupported("layout initial is not a host write");
135
+ return [specEvent(HOST_EVENTS[step.value])];
136
+ default: throw new Unsupported(`prop ${step.prop ?? ""}`);
137
+ }
138
+ default: throw new Unsupported(`event ${step.event}`);
139
+ }
140
+ },
141
+ value: (state) => [
142
+ num(state["s0"]),
143
+ num(state["s1"]),
144
+ num(state["s2"])
145
+ ],
146
+ focus: (state) => {
147
+ switch (state["focus"]) {
148
+ case "sep0": return "separator:0";
149
+ case "sep1": return "separator:1";
150
+ default: return null;
151
+ }
152
+ },
153
+ attr: (state, target, name) => {
154
+ const index = separatorIndex(target);
155
+ switch (name) {
156
+ case "role": return "separator";
157
+ case "aria-orientation": return state["o"] === "horizontal" ? "horizontal" : "vertical";
158
+ case "aria-valuenow": return String(num(state[`s${index}`]));
159
+ case "aria-valuemin": return String(10);
160
+ case "aria-valuemax": return String(80);
161
+ case "tabindex": return "0";
162
+ default: throw new Unsupported(`attribute ${name} on ${target}`);
163
+ }
164
+ },
165
+ visible: () => [],
166
+ events: (before, after) => before["requests"] === after["requests"] ? [] : [["onSizesChange", [
167
+ num(after["rq0"]),
168
+ num(after["rq1"]),
169
+ num(after["rq2"])
170
+ ]]]
171
+ };
172
+ /** The user-level steps a differential walk draws from. */
173
+ const resizablePanelsSteps = [
174
+ {
175
+ event: "focus",
176
+ target: "separator:0"
177
+ },
178
+ {
179
+ event: "focus",
180
+ target: "separator:1"
181
+ },
182
+ {
183
+ event: "blur",
184
+ target: "separator:0"
185
+ },
186
+ {
187
+ event: "key",
188
+ key: "ArrowLeft",
189
+ target: "separator:0"
190
+ },
191
+ {
192
+ event: "key",
193
+ key: "ArrowRight",
194
+ target: "separator:1"
195
+ },
196
+ {
197
+ event: "key",
198
+ key: "ArrowUp",
199
+ target: "separator:0"
200
+ },
201
+ {
202
+ event: "key",
203
+ key: "ArrowDown",
204
+ target: "separator:1"
205
+ },
206
+ {
207
+ event: "key",
208
+ key: "Home",
209
+ target: "separator:0"
210
+ },
211
+ {
212
+ event: "key",
213
+ key: "End",
214
+ target: "separator:1"
215
+ },
216
+ {
217
+ event: "key",
218
+ key: "Enter",
219
+ target: "separator:0"
220
+ },
221
+ {
222
+ event: "key",
223
+ key: "Enter",
224
+ target: "separator:1"
225
+ },
226
+ {
227
+ event: "prop",
228
+ prop: "orientation",
229
+ value: "horizontal"
230
+ },
231
+ {
232
+ event: "prop",
233
+ prop: "orientation",
234
+ value: "vertical"
235
+ },
236
+ {
237
+ event: "prop",
238
+ prop: "hostLayout",
239
+ value: "wide"
240
+ },
241
+ {
242
+ event: "prop",
243
+ prop: "hostLayout",
244
+ value: "narrow"
245
+ },
246
+ {
247
+ event: "prop",
248
+ prop: "hostLayout",
249
+ value: "collapsed"
250
+ },
251
+ {
252
+ event: "prop",
253
+ prop: "hostLayout",
254
+ value: "equal"
255
+ },
256
+ {
257
+ event: "dragStart",
258
+ target: "separator:0"
259
+ },
260
+ {
261
+ event: "dragStart",
262
+ target: "separator:1"
263
+ },
264
+ {
265
+ event: "dragBy",
266
+ value: 10
267
+ },
268
+ {
269
+ event: "dragBy",
270
+ value: -10
271
+ },
272
+ { event: "dragEnd" }
273
+ ];
274
+ const resizablePanelsProbe = {
275
+ attrs: [
276
+ ["separator:0", "role"],
277
+ ["separator:0", "aria-orientation"],
278
+ ["separator:0", "aria-valuenow"],
279
+ ["separator:0", "aria-valuemin"],
280
+ ["separator:0", "aria-valuemax"],
281
+ ["separator:0", "tabindex"],
282
+ ["separator:1", "aria-valuenow"]
283
+ ],
284
+ events: ["onSizesChange"]
285
+ };
286
+ export { PANELS_LAYOUTS, PANEL_COUNT, PANEL_MAX, PANEL_MIN, resizablePanelsBridge, resizablePanelsConfig, resizablePanelsProbe, resizablePanelsSteps };