@jxsuite/ui 0.0.0 → 0.1.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.
Files changed (105) hide show
  1. package/components/jx-accordion-item.json +281 -0
  2. package/components/jx-accordion.json +33 -0
  3. package/components/jx-action-button.json +261 -33
  4. package/components/jx-action-group.json +123 -0
  5. package/components/jx-button.json +133 -25
  6. package/components/jx-checkbox.json +215 -0
  7. package/components/jx-color-area.json +263 -0
  8. package/components/jx-color-field.json +565 -0
  9. package/components/jx-color-slider.json +216 -0
  10. package/components/jx-combobox.json +544 -0
  11. package/components/jx-dialog.json +416 -0
  12. package/components/jx-divider.json +52 -0
  13. package/components/jx-dot.json +38 -0
  14. package/components/jx-field.json +162 -0
  15. package/components/jx-kbd.json +24 -0
  16. package/components/jx-listbox.json +76 -0
  17. package/components/jx-menu-item.json +5 -12
  18. package/components/jx-menu.json +45 -6
  19. package/components/jx-number-field.json +441 -0
  20. package/components/jx-option.json +350 -0
  21. package/components/jx-popover.json +227 -0
  22. package/components/jx-select.json +637 -0
  23. package/components/jx-spinner.json +129 -0
  24. package/components/jx-split.json +221 -0
  25. package/components/jx-swatch-group.json +107 -0
  26. package/components/jx-swatch.json +262 -0
  27. package/components/jx-switch.json +253 -0
  28. package/components/jx-tab-panel.json +58 -0
  29. package/components/jx-tab.json +296 -0
  30. package/components/jx-tabs.json +124 -0
  31. package/components/jx-textfield.json +639 -0
  32. package/components/jx-toast-host.json +121 -0
  33. package/components/jx-toast.json +255 -0
  34. package/components/jx-toolbar.json +81 -0
  35. package/components/jx-tooltip.json +207 -0
  36. package/components/jx-tree-item.json +325 -0
  37. package/components/jx-tree.json +202 -0
  38. package/icons/list.json +4 -0
  39. package/icons/manifest.json +12 -0
  40. package/package.json +27 -5
  41. package/project.json +164 -153
  42. package/src/behaviors/action-group.ts +255 -0
  43. package/src/behaviors/color-area.ts +303 -0
  44. package/src/behaviors/color-field.ts +465 -0
  45. package/src/behaviors/color-slider.ts +154 -0
  46. package/src/behaviors/combobox.ts +474 -0
  47. package/src/behaviors/dialog.ts +137 -0
  48. package/src/behaviors/listbox.ts +118 -0
  49. package/src/behaviors/menu.ts +80 -48
  50. package/src/behaviors/number-field.ts +161 -0
  51. package/src/behaviors/popover.ts +399 -0
  52. package/src/behaviors/select.ts +154 -0
  53. package/src/behaviors/split.ts +478 -0
  54. package/src/behaviors/swatch-group.ts +297 -0
  55. package/src/behaviors/tabs.ts +319 -0
  56. package/src/behaviors/textfield.ts +164 -0
  57. package/src/behaviors/toast-host.ts +241 -0
  58. package/src/behaviors/toast.ts +261 -0
  59. package/src/behaviors/toolbar.ts +413 -0
  60. package/src/behaviors/tooltip.ts +715 -0
  61. package/src/behaviors/tree.ts +830 -0
  62. package/src/color.ts +433 -0
  63. package/src/documents.ts +91 -1
  64. package/src/index.ts +41 -1
  65. package/src/loaders.ts +47 -0
  66. package/src/testing/anchor-support.ts +28 -0
  67. package/src/testing/popover-shim.ts +39 -6
  68. package/src/theme.ts +30 -6
  69. package/stylebook/jx-accordion-item.json +195 -0
  70. package/stylebook/jx-accordion.json +220 -0
  71. package/stylebook/jx-action-button.json +191 -23
  72. package/stylebook/jx-action-group.json +335 -0
  73. package/stylebook/jx-button.json +129 -26
  74. package/stylebook/jx-checkbox.json +245 -0
  75. package/stylebook/jx-color-area.json +272 -0
  76. package/stylebook/jx-color-field.json +261 -0
  77. package/stylebook/jx-color-slider.json +220 -0
  78. package/stylebook/jx-combobox.json +332 -0
  79. package/stylebook/jx-dialog.json +186 -0
  80. package/stylebook/jx-divider.json +125 -0
  81. package/stylebook/jx-dot.json +92 -0
  82. package/stylebook/jx-field.json +313 -0
  83. package/stylebook/jx-icon.json +4 -0
  84. package/stylebook/jx-kbd.json +90 -0
  85. package/stylebook/jx-listbox.json +337 -0
  86. package/stylebook/jx-menu.json +0 -1
  87. package/stylebook/jx-number-field.json +203 -0
  88. package/stylebook/jx-option.json +285 -0
  89. package/stylebook/jx-popover.json +123 -0
  90. package/stylebook/jx-select.json +407 -0
  91. package/stylebook/jx-spinner.json +111 -0
  92. package/stylebook/jx-split.json +170 -0
  93. package/stylebook/jx-swatch-group.json +175 -0
  94. package/stylebook/jx-swatch.json +249 -0
  95. package/stylebook/jx-switch.json +240 -0
  96. package/stylebook/jx-tab-panel.json +147 -0
  97. package/stylebook/jx-tab.json +201 -0
  98. package/stylebook/jx-tabs.json +278 -0
  99. package/stylebook/jx-textfield.json +309 -0
  100. package/stylebook/jx-toast-host.json +157 -0
  101. package/stylebook/jx-toast.json +266 -0
  102. package/stylebook/jx-toolbar.json +435 -0
  103. package/stylebook/jx-tooltip.json +162 -0
  104. package/stylebook/jx-tree-item.json +258 -0
  105. package/stylebook/jx-tree.json +429 -0
@@ -21,11 +21,21 @@
21
21
  "stacked",
22
22
  "haspopup",
23
23
  "expanded",
24
- "badge"
24
+ "badge",
25
+ "loading",
26
+ "labelledby",
27
+ "describedby",
28
+ "checked"
25
29
  ],
26
30
  "$elements": [
27
31
  {
28
32
  "$ref": "./jx-icon.json"
33
+ },
34
+ {
35
+ "$ref": "./jx-spinner.json"
36
+ },
37
+ {
38
+ "$ref": "./jx-tooltip.json"
29
39
  }
30
40
  ],
31
41
  "state": {
@@ -33,13 +43,13 @@
33
43
  "type": "string",
34
44
  "default": "",
35
45
  "attribute": "label",
36
- "description": "The accessible name, required: an action button is icon-first, so its name is not on screen. Also its tooltip."
46
+ "description": "The accessible name, required: an action button is icon-first, so its name is not on screen. It is NOT also the tooltip — see hint."
37
47
  },
38
48
  "hint": {
39
49
  "type": "string",
40
50
  "default": "",
41
51
  "attribute": "hint",
42
- "description": "The tooltip when it is more than the name: the name with its chord, or with why the button is off. Empty means the label is the tooltip."
52
+ "description": "The tooltip, and the whole of it. While the button is ENABLED it is drawn as a jx-tooltip child of this element — shown on keyboard focus at once and on hover after the tip's delay, hoverable and dismissible with Escape (SC 1.4.13) — and the control names it in aria-describedby, so it is a description beside the name and never the name itself. While the button is DISABLED it is the control's native title instead, because a disabled control receives no focus and no pointer event and could never open a tip, while the platform still shows a title on hover: that is the one affordance a button whose hint says why it cannot act still has. Never both at once. It used to fall back to label, so EVERY button got a tooltip whether or not its name was already on screen and a reader heard the name twice. Now each call site decides: pass hint on an icon-only button or one whose visible label ellipses, where a tooltip carrying the name is the standard affordance and hint equal to label is the right answer; leave it empty wherever the text is already fully visible."
43
53
  },
44
54
  "icon": {
45
55
  "type": "string",
@@ -69,7 +79,18 @@
69
79
  "type": "boolean",
70
80
  "default": false,
71
81
  "attribute": "selected",
72
- "description": "The pressed state of a toggling button. A host may hold it: the property wins over the flip."
82
+ "description": "The pressed or chosen state, and the ONLY thing the selected drawing follows — whoever wrote it. A host that owns the selection sets it with toggles off and the button draws selected without flipping itself or emitting change; with toggles on the button also flips it on activation. What selected does NOT do is announce anything by itself: aria-pressed comes from toggles and aria-checked from checked, so a host-owned segment that must be announced is a checked radio (and setting checked also draws it selected, so the two never disagree)."
83
+ },
84
+ "checked": {
85
+ "type": "string",
86
+ "default": "",
87
+ "attribute": "checked",
88
+ "description": "Empty for a plain button; true or false for one segment of a radio group, which then carries role=radio and aria-checked on its control, is drawn selected while it is true, and puts role=none on the HOST so the radio is the radiogroup's own child rather than a grandchild behind a generic. It is the ALTERNATIVE to toggles, not a companion: a button that is one of several mutually exclusive choices is a radio, a button that is independently on or off is a toggle. Setting both is not a second opinion but a refusal — checked wins, so no aria-pressed is written on a radio (it is not a state of that role) and the button does not flip itself either."
89
+ },
90
+ "tabindex": {
91
+ "type": "string",
92
+ "default": "",
93
+ "description": "Forwarded to the inner control as its tabindex, which is what makes a roving caret possible: the focusable node is the button inside, not the host, so a group moving focus writes this rather than reaching into a child's internals. It is a PROPERTY ONLY (el.tabindex = \"0\") and deliberately not an observed attribute: written as an attribute it would land on the host as well, and a host carrying tabindex is itself focusable, so the one control would become two tab stops. Write tabindex on the host and you get the platform's meaning — the host is the tab stop and nothing reaches the control."
73
94
  },
74
95
  "quiet": {
75
96
  "type": "boolean",
@@ -93,7 +114,13 @@
93
114
  "type": "boolean",
94
115
  "default": false,
95
116
  "attribute": "disabled",
96
- "description": "Cannot act. Disabled, never absent."
117
+ "description": "Cannot act. Disabled, never absent, and the hint moves from the tip to the control's title while it is: a disabled control cannot open a tip, and the reason it cannot act is exactly what the reader hovers to find out."
118
+ },
119
+ "loading": {
120
+ "type": "boolean",
121
+ "default": false,
122
+ "attribute": "loading",
123
+ "description": "Working on the last activation: the click is swallowed, the glyph gives way to a spinner, and aria-busy says so. An action button draws its icon from a name and has no icon slot, so this is the only way a host can put a spinner where the glyph was."
97
124
  },
98
125
  "stacked": {
99
126
  "type": "boolean",
@@ -101,6 +128,18 @@
101
128
  "attribute": "stacked",
102
129
  "description": "Icon above the label, the label visible and small: the shape of a rail button. The label is then on screen, so it is the accessible name by itself."
103
130
  },
131
+ "labelledby": {
132
+ "type": "string",
133
+ "default": "",
134
+ "attribute": "labelledby",
135
+ "description": "Forwarded to the inner control as aria-labelledby: the id of the visible text that names this button, when something on screen already does."
136
+ },
137
+ "describedby": {
138
+ "type": "string",
139
+ "default": "",
140
+ "attribute": "describedby",
141
+ "description": "Forwarded to the inner control as aria-describedby: the id of a description beside the name, after the hint's own tip when there is one. A hint needs none of this — the element names its own tip — so it is for a description the host draws elsewhere."
142
+ },
104
143
  "haspopup": {
105
144
  "type": "string",
106
145
  "default": "",
@@ -143,6 +182,47 @@
143
182
  "attribute": "commandfor",
144
183
  "description": "The element the command is sent to; forwarded to the inner control."
145
184
  },
185
+ "uid": {
186
+ "type": "string",
187
+ "default": "",
188
+ "description": "This button's own id stem, minted on mount by the tooltip sidecar. The control's id, the tip's id and the three attributes that wire one to the other are all built from it, so a consumer writes none of them and two hinted buttons in one toolbar cannot collide. Empty until the mount microtask, which is why every binding that reads it guards on it."
189
+ },
190
+ "hasTip": {
191
+ "$expression": {
192
+ "operator": "&&",
193
+ "target": {
194
+ "operator": "!",
195
+ "target": {
196
+ "operator": "!",
197
+ "target": {
198
+ "$ref": "#/state/hint"
199
+ }
200
+ }
201
+ },
202
+ "value": {
203
+ "operator": "&&",
204
+ "target": {
205
+ "operator": "!",
206
+ "target": {
207
+ "$ref": "#/state/disabled"
208
+ }
209
+ },
210
+ "value": {
211
+ "operator": "!==",
212
+ "target": {
213
+ "$ref": "#/state/uid"
214
+ },
215
+ "value": ""
216
+ }
217
+ }
218
+ }
219
+ },
220
+ "onMount": {
221
+ "$prototype": "Function",
222
+ "$src": "jx-ui:/behaviors/tooltip.ts",
223
+ "$export": "mintHintId",
224
+ "description": "Mints the id stem the hint's tooltip is wired by. A document cannot: the closed operator set has no counter and no identity."
225
+ },
146
226
  "noIcon": {
147
227
  "$expression": {
148
228
  "operator": "!",
@@ -151,6 +231,59 @@
151
231
  }
152
232
  }
153
233
  },
234
+ "notLoading": {
235
+ "$expression": {
236
+ "operator": "!",
237
+ "target": {
238
+ "$ref": "#/state/loading"
239
+ }
240
+ }
241
+ },
242
+ "iconHidden": {
243
+ "$expression": {
244
+ "operator": "||",
245
+ "target": {
246
+ "$ref": "#/state/noIcon"
247
+ },
248
+ "value": {
249
+ "$ref": "#/state/loading"
250
+ }
251
+ }
252
+ },
253
+ "notRadio": {
254
+ "$expression": {
255
+ "operator": "!",
256
+ "target": {
257
+ "$ref": "#/state/checked"
258
+ }
259
+ }
260
+ },
261
+ "canToggle": {
262
+ "$expression": {
263
+ "operator": "&&",
264
+ "target": {
265
+ "$ref": "#/state/toggles"
266
+ },
267
+ "value": {
268
+ "operator": "&&",
269
+ "target": {
270
+ "$ref": "#/state/notRadio"
271
+ },
272
+ "value": {
273
+ "operator": "&&",
274
+ "target": {
275
+ "operator": "!",
276
+ "target": {
277
+ "$ref": "#/state/disabled"
278
+ }
279
+ },
280
+ "value": {
281
+ "$ref": "#/state/notLoading"
282
+ }
283
+ }
284
+ }
285
+ }
286
+ },
154
287
  "hasBadge": {
155
288
  "$expression": {
156
289
  "operator": "!==",
@@ -160,21 +293,44 @@
160
293
  "value": ""
161
294
  }
162
295
  },
296
+ "onSwallow": {
297
+ "$prototype": "Function",
298
+ "body": [
299
+ {
300
+ "if": {
301
+ "$ref": "#/state/loading"
302
+ },
303
+ "then": [
304
+ {
305
+ "stopPropagation": true
306
+ },
307
+ {
308
+ "preventDefault": true
309
+ }
310
+ ]
311
+ }
312
+ ],
313
+ "description": "The swallow, on the INNER CONTROL rather than the host: stopPropagation halts an event at the node it is called on and every node after it, not the other listeners on that same node — so a swallow on the host cancels the default and then runs the host's own click listeners anyway, which is the one path a consumer actually uses. Called one node earlier, the click never reaches the host at all."
314
+ },
163
315
  "onClick": {
164
316
  "$prototype": "Function",
165
317
  "body": [
166
318
  {
167
319
  "if": {
168
- "operator": "&&",
169
- "target": {
170
- "$ref": "#/state/toggles"
320
+ "$ref": "#/state/loading"
321
+ },
322
+ "then": [
323
+ {
324
+ "stopPropagation": true
171
325
  },
172
- "value": {
173
- "operator": "!",
174
- "target": {
175
- "$ref": "#/state/disabled"
176
- }
326
+ {
327
+ "preventDefault": true
177
328
  }
329
+ ]
330
+ },
331
+ {
332
+ "if": {
333
+ "$ref": "#/state/canToggle"
178
334
  },
179
335
  "then": [
180
336
  {
@@ -205,17 +361,18 @@
205
361
  "description": "A toggling button flipped; detail is the new selected state."
206
362
  }
207
363
  ],
208
- "description": "A toggling button flips selected and says so with change. A plain one leaves the click to the host, which listens for it on the element."
364
+ "description": "A toggling button flips selected and says so with change. A plain one leaves the click to the host, which listens for it on the element. The loading branch is the second half of the swallow: a click dispatched at the HOST never passes the inner control, so this is the only guard that sees it."
209
365
  }
210
366
  },
211
367
  "onclick": {
212
368
  "$ref": "#/state/onClick"
213
369
  },
214
370
  "attributes": {
371
+ "role": "${state.checked ? 'none' : null}",
215
372
  "data-size": "${state.size}",
216
373
  "data-quiet": "${state.quiet ? '' : null}",
217
374
  "data-emphasized": "${state.emphasized ? '' : null}",
218
- "data-selected": "${state.toggles && state.selected ? '' : null}",
375
+ "data-selected": "${state.selected || state.checked === 'true' ? '' : null}",
219
376
  "data-stacked": "${state.stacked ? '' : null}"
220
377
  },
221
378
  "style": {
@@ -257,6 +414,9 @@
257
414
  "opacity": "0.5",
258
415
  "cursor": "default"
259
416
  },
417
+ "&[aria-busy=\"true\"]": {
418
+ "cursor": "progress"
419
+ },
260
420
  "position": "relative"
261
421
  },
262
422
  "&[data-quiet] > [part=\"control\"]": {
@@ -277,11 +437,15 @@
277
437
  "lineHeight": "var(--jx-leading-xs)"
278
438
  },
279
439
  "&[data-stacked] > [part=\"control\"] > [part=\"label\"]": {
440
+ "display": "block",
280
441
  "maxWidth": "100%",
281
442
  "overflow": "hidden",
282
443
  "textOverflow": "ellipsis",
283
444
  "whiteSpace": "nowrap"
284
445
  },
446
+ "&[data-stacked] > [part=\"control\"] > [part=\"label\"]:empty, &[data-stacked] > [part=\"control\"] > [part=\"label\"]:has([part=\"label-slot\"]:empty)": {
447
+ "display": "none"
448
+ },
285
449
  "&[data-selected] > [part=\"control\"]": {
286
450
  "background": "var(--jx-accent-15)",
287
451
  "color": "var(--jx-fg)"
@@ -307,44 +471,74 @@
307
471
  "& [part=\"icon\"][hidden]": {
308
472
  "display": "none"
309
473
  },
310
- "& [part=\"label\"]:empty": {
311
- "display": "none"
474
+ "& [part=\"label\"]": {
475
+ "display": "contents"
476
+ },
477
+ "& [part=\"label-slot\"]": {
478
+ "display": "contents"
479
+ },
480
+ "& [part=\"badge-slot\"]": {
481
+ "display": "contents"
482
+ },
483
+ "& > [part=\"tooltip\"]": {
484
+ "display": "contents"
312
485
  },
313
486
  "& [part=\"badge\"]": {
314
- "position": "absolute",
315
- "top": "2px",
316
- "right": "2px",
487
+ "display": "inline-flex",
488
+ "alignItems": "center",
489
+ "justifyContent": "center",
317
490
  "minWidth": "16px",
318
- "height": "16px",
319
- "padding": "0 4px",
320
- "boxSizing": "border-box",
321
- "borderRadius": "8px",
491
+ "padding": "0 var(--jx-space-2)",
492
+ "borderRadius": "999px",
322
493
  "background": "var(--jx-accent-solid)",
323
494
  "color": "var(--jx-accent-fg)",
324
495
  "fontSize": "var(--jx-text-xs)",
325
496
  "lineHeight": "16px",
326
- "textAlign": "center",
497
+ "fontVariantNumeric": "tabular-nums",
498
+ "position": "absolute",
499
+ "top": "2px",
500
+ "right": "2px",
501
+ "boxSizing": "border-box",
327
502
  "pointerEvents": "none"
503
+ },
504
+ "& [part=\"spinner\"]": {
505
+ "display": "inline-flex",
506
+ "color": "inherit"
507
+ },
508
+ "& [part=\"spinner\"][hidden]": {
509
+ "display": "none"
328
510
  }
329
511
  },
330
512
  "children": [
331
513
  {
332
514
  "tagName": "button",
515
+ "onclick": {
516
+ "$ref": "#/state/onSwallow"
517
+ },
333
518
  "attributes": {
334
519
  "part": "control",
335
520
  "type": "button",
336
521
  "disabled": {
337
522
  "$ref": "#/state/disabled"
338
523
  },
524
+ "id": "${state.uid || null}",
339
525
  "aria-label": "${state.label || null}",
340
- "title": "${state.hint || state.label || null}",
341
- "aria-pressed": "${state.toggles ? (state.selected ? 'true' : 'false') : null}",
526
+ "title": "${state.disabled && state.hint ? state.hint : null}",
527
+ "interestfor": "${state.hasTip ? state.uid + '-tip' : null}",
528
+ "role": "${state.checked ? 'radio' : null}",
529
+ "aria-checked": "${state.checked || null}",
530
+ "aria-pressed": "${state.toggles && !state.checked ? (state.selected ? 'true' : 'false') : null}",
531
+ "tabindex": "${state.tabindex || null}",
342
532
  "popovertarget": "${state.popovertarget || null}",
343
533
  "popovertargetaction": "${state.popovertargetaction || null}",
344
534
  "command": "${state.command || null}",
345
535
  "commandfor": "${state.commandfor || null}",
346
536
  "aria-haspopup": "${state.haspopup || null}",
347
- "aria-expanded": "${state.haspopup ? (state.expanded ? 'true' : 'false') : null}"
537
+ "aria-expanded": "${state.haspopup ? (state.expanded ? 'true' : 'false') : null}",
538
+ "aria-labelledby": "${state.labelledby || null}",
539
+ "aria-describedby": "${[state.hasTip ? state.uid + '-tip' : null, state.describedby || null].filter(Boolean).join(' ') || null}",
540
+ "aria-busy": "${state.loading ? 'true' : null}",
541
+ "aria-disabled": "${state.loading ? 'true' : null}"
348
542
  },
349
543
  "children": [
350
544
  {
@@ -353,7 +547,7 @@
353
547
  "part": "icon"
354
548
  },
355
549
  "hidden": {
356
- "$ref": "#/state/noIcon"
550
+ "$ref": "#/state/iconHidden"
357
551
  },
358
552
  "children": [
359
553
  {
@@ -375,6 +569,15 @@
375
569
  }
376
570
  ]
377
571
  },
572
+ {
573
+ "tagName": "jx-spinner",
574
+ "attributes": {
575
+ "part": "spinner"
576
+ },
577
+ "hidden": {
578
+ "$ref": "#/state/notLoading"
579
+ }
580
+ },
378
581
  {
379
582
  "tagName": "span",
380
583
  "attributes": {
@@ -382,10 +585,7 @@
382
585
  },
383
586
  "children": [
384
587
  {
385
- "tagName": "slot",
386
- "attributes": {
387
- "part": "label-slot"
388
- }
588
+ "tagName": "slot"
389
589
  }
390
590
  ]
391
591
  },
@@ -409,6 +609,34 @@
409
609
  }
410
610
  }
411
611
  ]
612
+ },
613
+ {
614
+ "tagName": "span",
615
+ "attributes": {
616
+ "part": "tooltip"
617
+ },
618
+ "$switch": {
619
+ "$ref": "#/state/hasTip"
620
+ },
621
+ "cases": {
622
+ "true": {
623
+ "tagName": "jx-tooltip",
624
+ "attributes": {
625
+ "part": "tip",
626
+ "id": "${state.uid}-tip",
627
+ "for": "${state.uid}"
628
+ },
629
+ "children": [
630
+ {
631
+ "tagName": "span",
632
+ "attributes": {
633
+ "part": "tip-text"
634
+ },
635
+ "textContent": "${state.hint}"
636
+ }
637
+ ]
638
+ }
639
+ }
412
640
  }
413
641
  ]
414
642
  }
@@ -0,0 +1,123 @@
1
+ {
2
+ "$schema": "https://jxsuite.com/schema/v1",
3
+ "$id": "JxActionGroup",
4
+ "tagName": "jx-action-group",
5
+ "observedAttributes": ["selects", "compact", "label", "orientation"],
6
+ "state": {
7
+ "selects": {
8
+ "type": "string",
9
+ "default": "none",
10
+ "attribute": "selects",
11
+ "description": "Which ARIA pattern the row is: none is a toolbar of unrelated tools, single is a radiogroup of mutually exclusive choices, multiple is a plain group of independent toggles. It chooses the pattern and the keyboard; it does NOT own the selection. Every shipped call site keeps the selected value in the host, so in single the group clicks the child an arrow lands on and lets the host decide what that means. In single the host must also give every child a checked prop of \"true\" or \"false\": a radiogroup's required owned element is a radio, and jx-action-button becomes one only when checked is non-empty — a group of bare buttons is announced as a radiogroup with no checked member at all."
12
+ },
13
+ "compact": {
14
+ "type": "boolean",
15
+ "default": true,
16
+ "attribute": "compact",
17
+ "description": "Draw the children joined into one segmented control, sharing their inner borders and rounding only the outer corners. Off leaves them as separate buttons with a gap. This is the one thing no button can do about itself: joining N of them needs selectors about a child's position among its siblings. The seam is a DIRECT-CHILD rule: it reaches the buttons the group itself owns — a slot leaves no node, so a distributed child is one — and so a nested group keeps its own compact, and a button a surface wraps in a div of its own is roved but not joined."
18
+ },
19
+ "label": {
20
+ "type": "string",
21
+ "default": "",
22
+ "attribute": "label",
23
+ "description": "The accessible name of the group, and it is not optional in practice: a toolbar, a radiogroup and a group are all containers a reader is told they have entered, and an unnamed one is announced by its role alone. Nothing lints its absence — a bound role switches the naming rules off — so the element's own test pins it instead."
24
+ },
25
+ "orientation": {
26
+ "type": "string",
27
+ "default": "horizontal",
28
+ "attribute": "orientation",
29
+ "description": "horizontal or vertical. It chooses which arrow pair moves the caret, and it turns both the layout and the segmented seam through 90 degrees off data-orientation. It also sets aria-orientation, but only where ARIA permits one: role=group — what selects=\"multiple\" resolves to — supports neither aria-orientation nor its inheritance, and Chrome drops the attribute there, so writing it anyway would be an aria-allowed-attr violation in a consumer's own axe run."
30
+ },
31
+ "onKeydown": {
32
+ "$prototype": "Function",
33
+ "$src": "jx-ui:/behaviors/action-group.ts",
34
+ "$export": "onGroupKeydown",
35
+ "description": "The group keyboard contract: the arrow along its own axis moves the roving caret between enabled children with wrap, Home and End go to the ends, and in selects=single the child that takes the caret is clicked."
36
+ },
37
+ "onReady": {
38
+ "$prototype": "Function",
39
+ "$src": "jx-ui:/behaviors/action-group.ts",
40
+ "$export": "onGroupReady",
41
+ "description": "Collapses the group to one tab stop once it has rendered, and keeps it collapsed to one as children come and go. It listens for the element's own jx-ready because onMount is handed the reactive scope alone, and placing a caret needs the host."
42
+ },
43
+ "onMount": {
44
+ "$prototype": "Function",
45
+ "body": [
46
+ {
47
+ "dispatchEvent": "jx-ready",
48
+ "bubbles": true
49
+ }
50
+ ],
51
+ "emits": [
52
+ {
53
+ "name": "jx-ready",
54
+ "description": "The group has rendered and distributed its children, so its caret can be placed."
55
+ }
56
+ ],
57
+ "description": "Announces that the element has rendered. The group's own onReady listens for it, and a host may too."
58
+ }
59
+ },
60
+ "attributes": {
61
+ "role": "${state.selects === 'single' ? 'radiogroup' : (state.selects === 'multiple' ? 'group' : 'toolbar')}",
62
+ "aria-label": "${state.label || null}",
63
+ "aria-orientation": "${state.selects === 'multiple' ? null : state.orientation}",
64
+ "data-orientation": "${state.orientation}",
65
+ "data-compact": "${state.compact ? '' : null}"
66
+ },
67
+ "onkeydown": {
68
+ "$ref": "#/state/onKeydown"
69
+ },
70
+ "onjx-ready": {
71
+ "$ref": "#/state/onReady"
72
+ },
73
+ "style": {
74
+ "display": "inline-flex",
75
+ "&[hidden]": {
76
+ "display": "none"
77
+ },
78
+ "verticalAlign": "middle",
79
+ "alignItems": "center",
80
+ "gap": "var(--jx-space-1)",
81
+ "&[data-orientation=\"vertical\"]": {
82
+ "flexDirection": "column",
83
+ "alignItems": "stretch"
84
+ },
85
+ "&[data-compact]": {
86
+ "gap": "0"
87
+ },
88
+ "&[data-compact] > jx-action-button > [part=\"control\"]": {
89
+ "borderRadius": "0"
90
+ },
91
+ "&[data-compact] > jx-action-button + jx-action-button > [part=\"control\"]": {
92
+ "marginInlineStart": "-1px"
93
+ },
94
+ "&[data-compact] > jx-action-button:first-of-type > [part=\"control\"]": {
95
+ "borderStartStartRadius": "var(--jx-radius-sm)",
96
+ "borderEndStartRadius": "var(--jx-radius-sm)"
97
+ },
98
+ "&[data-compact] > jx-action-button:last-of-type > [part=\"control\"]": {
99
+ "borderStartEndRadius": "var(--jx-radius-sm)",
100
+ "borderEndEndRadius": "var(--jx-radius-sm)"
101
+ },
102
+ "&[data-compact][data-orientation=\"vertical\"] > jx-action-button + jx-action-button > [part=\"control\"]": {
103
+ "marginInlineStart": "0",
104
+ "marginBlockStart": "-1px"
105
+ },
106
+ "&[data-compact][data-orientation=\"vertical\"] > jx-action-button:first-of-type > [part=\"control\"]": {
107
+ "borderEndStartRadius": "0",
108
+ "borderStartEndRadius": "var(--jx-radius-sm)"
109
+ },
110
+ "&[data-compact][data-orientation=\"vertical\"] > jx-action-button:last-of-type > [part=\"control\"]": {
111
+ "borderStartEndRadius": "0",
112
+ "borderEndStartRadius": "var(--jx-radius-sm)"
113
+ },
114
+ "&[data-compact] > jx-action-button > [part=\"control\"]:focus-visible": {
115
+ "zIndex": "1"
116
+ }
117
+ },
118
+ "children": [
119
+ {
120
+ "tagName": "slot"
121
+ }
122
+ ]
123
+ }