@jxsuite/ui 0.0.0 → 0.2.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/components/jx-accordion-item.json +281 -0
- package/components/jx-accordion.json +33 -0
- package/components/jx-action-button.json +268 -34
- package/components/jx-action-group.json +131 -0
- package/components/jx-button.json +141 -26
- package/components/jx-checkbox.json +222 -0
- package/components/jx-color-area.json +263 -0
- package/components/jx-color-field.json +565 -0
- package/components/jx-color-slider.json +216 -0
- package/components/jx-combobox.json +551 -0
- package/components/jx-dialog.json +416 -0
- package/components/jx-divider.json +52 -0
- package/components/jx-dot.json +38 -0
- package/components/jx-field.json +162 -0
- package/components/jx-kbd.json +24 -0
- package/components/jx-listbox.json +76 -0
- package/components/jx-menu-item.json +6 -13
- package/components/jx-menu.json +45 -6
- package/components/jx-number-field.json +441 -0
- package/components/jx-option.json +350 -0
- package/components/jx-popover.json +227 -0
- package/components/jx-select.json +637 -0
- package/components/jx-spinner.json +129 -0
- package/components/jx-split.json +221 -0
- package/components/jx-swatch-group.json +107 -0
- package/components/jx-swatch.json +262 -0
- package/components/jx-switch.json +331 -0
- package/components/jx-tab-panel.json +58 -0
- package/components/jx-tab.json +296 -0
- package/components/jx-tabs.json +124 -0
- package/components/jx-textfield.json +645 -0
- package/components/jx-toast-host.json +121 -0
- package/components/jx-toast.json +255 -0
- package/components/jx-toolbar.json +81 -0
- package/components/jx-tooltip.json +207 -0
- package/components/jx-tree-item.json +325 -0
- package/components/jx-tree.json +202 -0
- package/icons/list.json +4 -0
- package/icons/manifest.json +12 -0
- package/package.json +27 -5
- package/project.json +164 -153
- package/src/behaviors/action-group.ts +255 -0
- package/src/behaviors/color-area.ts +303 -0
- package/src/behaviors/color-field.ts +465 -0
- package/src/behaviors/color-slider.ts +154 -0
- package/src/behaviors/combobox.ts +474 -0
- package/src/behaviors/dialog.ts +137 -0
- package/src/behaviors/listbox.ts +118 -0
- package/src/behaviors/menu.ts +80 -48
- package/src/behaviors/number-field.ts +161 -0
- package/src/behaviors/popover.ts +399 -0
- package/src/behaviors/select.ts +154 -0
- package/src/behaviors/split.ts +478 -0
- package/src/behaviors/swatch-group.ts +297 -0
- package/src/behaviors/tabs.ts +319 -0
- package/src/behaviors/textfield.ts +164 -0
- package/src/behaviors/toast-host.ts +241 -0
- package/src/behaviors/toast.ts +261 -0
- package/src/behaviors/toolbar.ts +413 -0
- package/src/behaviors/tooltip.ts +719 -0
- package/src/behaviors/tree.ts +830 -0
- package/src/color.ts +433 -0
- package/src/documents.ts +91 -1
- package/src/index.ts +41 -1
- package/src/loaders.ts +47 -0
- package/src/testing/anchor-support.ts +28 -0
- package/src/testing/popover-shim.ts +39 -6
- package/src/theme.ts +30 -6
- package/stylebook/jx-accordion-item.json +195 -0
- package/stylebook/jx-accordion.json +220 -0
- package/stylebook/jx-action-button.json +191 -23
- package/stylebook/jx-action-group.json +335 -0
- package/stylebook/jx-button.json +129 -26
- package/stylebook/jx-checkbox.json +245 -0
- package/stylebook/jx-color-area.json +272 -0
- package/stylebook/jx-color-field.json +261 -0
- package/stylebook/jx-color-slider.json +220 -0
- package/stylebook/jx-combobox.json +332 -0
- package/stylebook/jx-dialog.json +186 -0
- package/stylebook/jx-divider.json +125 -0
- package/stylebook/jx-dot.json +92 -0
- package/stylebook/jx-field.json +313 -0
- package/stylebook/jx-icon.json +4 -0
- package/stylebook/jx-kbd.json +90 -0
- package/stylebook/jx-listbox.json +337 -0
- package/stylebook/jx-menu.json +0 -1
- package/stylebook/jx-number-field.json +203 -0
- package/stylebook/jx-option.json +285 -0
- package/stylebook/jx-popover.json +123 -0
- package/stylebook/jx-select.json +407 -0
- package/stylebook/jx-spinner.json +111 -0
- package/stylebook/jx-split.json +170 -0
- package/stylebook/jx-swatch-group.json +175 -0
- package/stylebook/jx-swatch.json +249 -0
- package/stylebook/jx-switch.json +240 -0
- package/stylebook/jx-tab-panel.json +147 -0
- package/stylebook/jx-tab.json +201 -0
- package/stylebook/jx-tabs.json +278 -0
- package/stylebook/jx-textfield.json +309 -0
- package/stylebook/jx-toast-host.json +157 -0
- package/stylebook/jx-toast.json +266 -0
- package/stylebook/jx-toolbar.json +435 -0
- package/stylebook/jx-tooltip.json +162 -0
- package/stylebook/jx-tree-item.json +258 -0
- 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.
|
|
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
|
|
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
|
|
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
|
-
"
|
|
169
|
-
|
|
170
|
-
|
|
320
|
+
"$ref": "#/state/loading"
|
|
321
|
+
},
|
|
322
|
+
"then": [
|
|
323
|
+
{
|
|
324
|
+
"stopPropagation": true
|
|
171
325
|
},
|
|
172
|
-
|
|
173
|
-
"
|
|
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.
|
|
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)"
|
|
@@ -291,9 +455,15 @@
|
|
|
291
455
|
"color": "var(--jx-accent-fg)"
|
|
292
456
|
},
|
|
293
457
|
"&[data-size=\"sm\"] > [part=\"control\"]": {
|
|
458
|
+
"$description": "The small action button is the kit's most packed control — Studio's toolbars, block action bar and inspector rows declare it some four hundred times — and it draws 4px under the kit's control height, which is the WCAG 2.2 SC 2.5.8 floor (ui.md §4.3). The `::before` is the hit area, jx-tab's close-button technique: an empty pseudo-element that reaches 2px past the drawn box on every side, so the region that accepts the pointer is exactly `--jx-control-h` square while the picture stays 20px. The inset is -3px and not -2px because an absolutely positioned box is offset from its containing block's PADDING edge, and this control draws a 1px border: the border width comes first, then half of the 4px the box is under (jx-tab's close button is borderless, so -4px was exact there). The control is already `position: relative` for the badge, so the pseudo-element needs nothing more. A 4px gap (jx-toolbar's) lets two extended neighbours abut; a compact jx-action-group packs its members touching, and its sheet zeroes the extension on that axis, because an extension over a neighbour's box would take the neighbour's clicks, and the criterion gives an overlapped strip to the target on top rather than to both; a default group's 2px gap leaves every member but the last at 22px on that axis, which ui.md §11 names as a non-claim. Held by target-size.test.ts and the conformance gate, both of which read the border and subtract it.",
|
|
294
459
|
"minWidth": "calc(var(--jx-control-h) - 4px)",
|
|
295
460
|
"minHeight": "calc(var(--jx-control-h) - 4px)",
|
|
296
|
-
"padding": "0 var(--jx-space-1)"
|
|
461
|
+
"padding": "0 var(--jx-space-1)",
|
|
462
|
+
"::before": {
|
|
463
|
+
"content": "\"\"",
|
|
464
|
+
"position": "absolute",
|
|
465
|
+
"inset": "-3px"
|
|
466
|
+
}
|
|
297
467
|
},
|
|
298
468
|
"&[data-size=\"lg\"] > [part=\"control\"]": {
|
|
299
469
|
"minWidth": "calc(var(--jx-control-h) + 8px)",
|
|
@@ -307,44 +477,74 @@
|
|
|
307
477
|
"& [part=\"icon\"][hidden]": {
|
|
308
478
|
"display": "none"
|
|
309
479
|
},
|
|
310
|
-
"& [part=\"label\"]
|
|
311
|
-
"display": "
|
|
480
|
+
"& [part=\"label\"]": {
|
|
481
|
+
"display": "contents"
|
|
482
|
+
},
|
|
483
|
+
"& [part=\"label-slot\"]": {
|
|
484
|
+
"display": "contents"
|
|
485
|
+
},
|
|
486
|
+
"& [part=\"badge-slot\"]": {
|
|
487
|
+
"display": "contents"
|
|
488
|
+
},
|
|
489
|
+
"& > [part=\"tooltip\"]": {
|
|
490
|
+
"display": "contents"
|
|
312
491
|
},
|
|
313
492
|
"& [part=\"badge\"]": {
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
493
|
+
"display": "inline-flex",
|
|
494
|
+
"alignItems": "center",
|
|
495
|
+
"justifyContent": "center",
|
|
317
496
|
"minWidth": "16px",
|
|
318
|
-
"
|
|
319
|
-
"
|
|
320
|
-
"boxSizing": "border-box",
|
|
321
|
-
"borderRadius": "8px",
|
|
497
|
+
"padding": "0 var(--jx-space-2)",
|
|
498
|
+
"borderRadius": "999px",
|
|
322
499
|
"background": "var(--jx-accent-solid)",
|
|
323
500
|
"color": "var(--jx-accent-fg)",
|
|
324
501
|
"fontSize": "var(--jx-text-xs)",
|
|
325
502
|
"lineHeight": "16px",
|
|
326
|
-
"
|
|
503
|
+
"fontVariantNumeric": "tabular-nums",
|
|
504
|
+
"position": "absolute",
|
|
505
|
+
"top": "2px",
|
|
506
|
+
"right": "2px",
|
|
507
|
+
"boxSizing": "border-box",
|
|
327
508
|
"pointerEvents": "none"
|
|
509
|
+
},
|
|
510
|
+
"& [part=\"spinner\"]": {
|
|
511
|
+
"display": "inline-flex",
|
|
512
|
+
"color": "inherit"
|
|
513
|
+
},
|
|
514
|
+
"& [part=\"spinner\"][hidden]": {
|
|
515
|
+
"display": "none"
|
|
328
516
|
}
|
|
329
517
|
},
|
|
330
518
|
"children": [
|
|
331
519
|
{
|
|
332
520
|
"tagName": "button",
|
|
521
|
+
"onclick": {
|
|
522
|
+
"$ref": "#/state/onSwallow"
|
|
523
|
+
},
|
|
333
524
|
"attributes": {
|
|
334
525
|
"part": "control",
|
|
335
526
|
"type": "button",
|
|
336
527
|
"disabled": {
|
|
337
528
|
"$ref": "#/state/disabled"
|
|
338
529
|
},
|
|
530
|
+
"id": "${state.uid || null}",
|
|
339
531
|
"aria-label": "${state.label || null}",
|
|
340
|
-
"title": "${state.hint
|
|
341
|
-
"
|
|
532
|
+
"title": "${state.disabled && state.hint ? state.hint : null}",
|
|
533
|
+
"interestfor": "${state.hasTip ? state.uid + '-tip' : null}",
|
|
534
|
+
"role": "${state.checked ? 'radio' : null}",
|
|
535
|
+
"aria-checked": "${state.checked || null}",
|
|
536
|
+
"aria-pressed": "${state.toggles && !state.checked ? (state.selected ? 'true' : 'false') : null}",
|
|
537
|
+
"tabindex": "${state.tabindex || null}",
|
|
342
538
|
"popovertarget": "${state.popovertarget || null}",
|
|
343
539
|
"popovertargetaction": "${state.popovertargetaction || null}",
|
|
344
540
|
"command": "${state.command || null}",
|
|
345
541
|
"commandfor": "${state.commandfor || null}",
|
|
346
542
|
"aria-haspopup": "${state.haspopup || null}",
|
|
347
|
-
"aria-expanded": "${state.haspopup ? (state.expanded ? 'true' : 'false') : null}"
|
|
543
|
+
"aria-expanded": "${state.haspopup ? (state.expanded ? 'true' : 'false') : null}",
|
|
544
|
+
"aria-labelledby": "${state.labelledby || null}",
|
|
545
|
+
"aria-describedby": "${[state.hasTip ? state.uid + '-tip' : null, state.describedby || null].filter(Boolean).join(' ') || null}",
|
|
546
|
+
"aria-busy": "${state.loading ? 'true' : null}",
|
|
547
|
+
"aria-disabled": "${state.loading ? 'true' : null}"
|
|
348
548
|
},
|
|
349
549
|
"children": [
|
|
350
550
|
{
|
|
@@ -353,7 +553,7 @@
|
|
|
353
553
|
"part": "icon"
|
|
354
554
|
},
|
|
355
555
|
"hidden": {
|
|
356
|
-
"$ref": "#/state/
|
|
556
|
+
"$ref": "#/state/iconHidden"
|
|
357
557
|
},
|
|
358
558
|
"children": [
|
|
359
559
|
{
|
|
@@ -375,6 +575,15 @@
|
|
|
375
575
|
}
|
|
376
576
|
]
|
|
377
577
|
},
|
|
578
|
+
{
|
|
579
|
+
"tagName": "jx-spinner",
|
|
580
|
+
"attributes": {
|
|
581
|
+
"part": "spinner"
|
|
582
|
+
},
|
|
583
|
+
"hidden": {
|
|
584
|
+
"$ref": "#/state/notLoading"
|
|
585
|
+
}
|
|
586
|
+
},
|
|
378
587
|
{
|
|
379
588
|
"tagName": "span",
|
|
380
589
|
"attributes": {
|
|
@@ -382,10 +591,7 @@
|
|
|
382
591
|
},
|
|
383
592
|
"children": [
|
|
384
593
|
{
|
|
385
|
-
"tagName": "slot"
|
|
386
|
-
"attributes": {
|
|
387
|
-
"part": "label-slot"
|
|
388
|
-
}
|
|
594
|
+
"tagName": "slot"
|
|
389
595
|
}
|
|
390
596
|
]
|
|
391
597
|
},
|
|
@@ -409,6 +615,34 @@
|
|
|
409
615
|
}
|
|
410
616
|
}
|
|
411
617
|
]
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
"tagName": "span",
|
|
621
|
+
"attributes": {
|
|
622
|
+
"part": "tooltip"
|
|
623
|
+
},
|
|
624
|
+
"$switch": {
|
|
625
|
+
"$ref": "#/state/hasTip"
|
|
626
|
+
},
|
|
627
|
+
"cases": {
|
|
628
|
+
"true": {
|
|
629
|
+
"tagName": "jx-tooltip",
|
|
630
|
+
"attributes": {
|
|
631
|
+
"part": "tip",
|
|
632
|
+
"id": "${state.uid}-tip",
|
|
633
|
+
"for": "${state.uid}"
|
|
634
|
+
},
|
|
635
|
+
"children": [
|
|
636
|
+
{
|
|
637
|
+
"tagName": "span",
|
|
638
|
+
"attributes": {
|
|
639
|
+
"part": "tip-text"
|
|
640
|
+
},
|
|
641
|
+
"textContent": "${state.hint}"
|
|
642
|
+
}
|
|
643
|
+
]
|
|
644
|
+
}
|
|
645
|
+
}
|
|
412
646
|
}
|
|
413
647
|
]
|
|
414
648
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
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
|
+
"&[data-compact] > jx-action-button[data-size=\"sm\"] > [part=\"control\"]::before": {
|
|
118
|
+
"$description": "A small action button carries a `::before` that extends its hit area 2px past its drawn box on every side (an inset of -3px: the button's 1px border, then half the 4px it is under, because an absolutely positioned box is offset from the padding edge), so that a 20px picture accepts the pointer over the kit's 24px control height (WCAG 2.2 SC 2.5.8, ui.md §4.3). In a compact group the boxes TOUCH on the packed axis — no gap, and each border drawn over the last — so an extension there could reach nothing but the neighbour's own box, and the later sibling paints above the earlier one: the last 2px of every button but the last would activate the button after it. The criterion gives an overlapped area to the target on TOP, so the extension would buy the earlier button nothing and cost it a stolen strip. The default 2px gap is the other case: two extensions overlap by 2px there, the later member keeps the strip, and every small member but the last counts 22px on the packed axis, which is under the floor and which ui.md §11 names beside this one. The packed axis is therefore zeroed here — `0` is the padding edge, 1px inside the drawn border, and the button's own box accepts the pointer over that pixel — and the cross axis keeps its 2px past the drawn box, which reaches only the gap around the group: a compact group's small members are 24px tall and as wide as they draw, which ui.md §11 records as the one thing the technique cannot reach. The vertical variant swaps the axes, and its inline value is the button's own -3px for the same reason.",
|
|
119
|
+
"insetInline": "0"
|
|
120
|
+
},
|
|
121
|
+
"&[data-compact][data-orientation=\"vertical\"] > jx-action-button[data-size=\"sm\"] > [part=\"control\"]::before": {
|
|
122
|
+
"insetInline": "-3px",
|
|
123
|
+
"insetBlock": "0"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"children": [
|
|
127
|
+
{
|
|
128
|
+
"tagName": "slot"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|