@adia-ai/web-components 0.6.50 → 0.7.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/CHANGELOG.md +134 -0
- package/components/action-list/action-list.css +1 -1
- package/components/agent-artifact/agent-artifact.class.js +10 -10
- package/components/agent-artifact/agent-artifact.css +1 -1
- package/components/agent-reasoning/agent-reasoning.class.js +51 -0
- package/components/agent-reasoning/agent-reasoning.css +49 -22
- package/components/alert/alert.class.js +8 -1
- package/components/alert/alert.css +13 -1
- package/components/avatar/avatar.a2ui.json +2 -14
- package/components/avatar/avatar.class.js +3 -15
- package/components/avatar/avatar.d.ts +2 -4
- package/components/avatar/avatar.yaml +1 -18
- package/components/breadcrumb/breadcrumb.css +4 -1
- package/components/button/button.a2ui.json +3 -0
- package/components/button/button.css +14 -3
- package/components/button/button.yaml +5 -0
- package/components/calendar-grid/calendar-grid.css +1 -1
- package/components/calendar-picker/calendar-picker.css +5 -2
- package/components/chart/chart.a2ui.json +0 -18
- package/components/chart/chart.class.js +8 -50
- package/components/chart/chart.css +1 -15
- package/components/chart/chart.d.ts +0 -4
- package/components/chart/chart.yaml +0 -24
- package/components/color-input/color-input.css +4 -1
- package/components/combobox/combobox.class.js +11 -0
- package/components/combobox/combobox.css +8 -0
- package/components/date-range-picker/date-range-picker.class.js +5 -1
- package/components/date-range-picker/date-range-picker.css +12 -2
- package/components/datetime-picker/datetime-picker.class.js +3 -0
- package/components/datetime-picker/datetime-picker.css +16 -2
- package/components/empty-state/empty-state.css +11 -4
- package/components/field/field.css +17 -6
- package/components/grid/grid.a2ui.json +5 -0
- package/components/grid/grid.class.js +16 -6
- package/components/grid/grid.css +17 -3
- package/components/grid/grid.d.ts +2 -0
- package/components/grid/grid.yaml +9 -0
- package/components/heatmap/heatmap.class.js +9 -3
- package/components/heatmap/heatmap.css +19 -2
- package/components/image/image.css +4 -1
- package/components/input/input.class.js +38 -0
- package/components/input/input.css +9 -5
- package/components/input/input.test.js +57 -0
- package/components/integration-card/integration-card.class.js +31 -7
- package/components/integration-card/integration-card.test.js +12 -1
- package/components/kbd/kbd.a2ui.json +3 -2
- package/components/kbd/kbd.css +7 -4
- package/components/kbd/kbd.d.ts +2 -2
- package/components/kbd/kbd.yaml +2 -1
- package/components/list/list.class.js +8 -1
- package/components/menu/menu.class.js +12 -3
- package/components/menu/menu.css +4 -1
- package/components/menu/menu.test.js +130 -0
- package/components/modal/modal.class.js +10 -1
- package/components/modal/modal.css +9 -0
- package/components/option-card/option-card.a2ui.json +3 -0
- package/components/option-card/option-card.css +44 -19
- package/components/option-card/option-card.yaml +5 -0
- package/components/otp-input/otp-input.css +25 -10
- package/components/page/page.css +64 -11
- package/components/pagination/pagination.class.js +1 -1
- package/components/pagination/pagination.css +9 -1
- package/components/pipeline-status/pipeline-status.css +6 -0
- package/components/popover/popover.css +12 -1
- package/components/preview/preview.css +30 -3
- package/components/progress-row/progress-row.css +3 -1
- package/components/qr-code/qr-code.css +4 -1
- package/components/segmented/segmented.css +4 -1
- package/components/select/select.a2ui.json +1 -1
- package/components/select/select.class.js +63 -7
- package/components/select/select.css +18 -0
- package/components/select/select.yaml +9 -2
- package/components/stack/stack.a2ui.json +12 -1
- package/components/stack/stack.d.ts +2 -2
- package/components/stack/stack.yaml +13 -1
- package/components/stat/stat.a2ui.json +5 -0
- package/components/stat/stat.css +55 -0
- package/components/stat/stat.d.ts +2 -0
- package/components/stat/stat.js +4 -0
- package/components/stat/stat.yaml +9 -0
- package/components/swiper/swiper.class.js +14 -6
- package/components/switch/switch.css +13 -0
- package/components/table/table.a2ui.json +2 -2
- package/components/table/table.css +13 -1
- package/components/table/table.yaml +2 -2
- package/components/time-picker/time-picker.css +4 -1
- package/components/timeline/timeline.class.js +3 -3
- package/components/timeline/timeline.css +23 -5
- package/components/toggle-group/toggle-group.css +4 -1
- package/components/toggle-scheme/toggle-scheme.css +4 -1
- package/components/tree/tree.class.js +24 -4
- package/components/tree/tree.test.js +108 -0
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +83 -83
- package/package.json +3 -3
- package/styles/api/layout.css +7 -0
- package/styles/api/text.css +9 -5
- package/styles/index.css +11 -2
- package/styles/prose.css +8 -0
- package/styles/resets.css +5 -5
- package/styles/themes.css +8 -1
- package/styles/tokens.css +3 -3
- package/styles/type/elements.css +73 -0
- package/styles/type/roles.css +14 -49
- package/styles/type/scale.css +0 -5
- package/styles/typography.css +3 -3
|
@@ -71,6 +71,7 @@ export class UISelect extends UIFormElement {
|
|
|
71
71
|
static template = () => null;
|
|
72
72
|
|
|
73
73
|
#options = [];
|
|
74
|
+
#ownTrigger = false; // true when WE stamped the trigger (vs a consumer-custom one)
|
|
74
75
|
#listbox = null;
|
|
75
76
|
#anchorCleanup = null;
|
|
76
77
|
#query = '';
|
|
@@ -386,14 +387,18 @@ export class UISelect extends UIFormElement {
|
|
|
386
387
|
|
|
387
388
|
// Stamp default trigger if none provided
|
|
388
389
|
if (!this.querySelector('[slot="trigger"]')) {
|
|
390
|
+
this.#ownTrigger = true;
|
|
389
391
|
// Detach listbox before innerHTML wipe so it isn't destroyed
|
|
390
392
|
const lb = this.#listbox;
|
|
391
393
|
if (lb?.parentNode === this) this.removeChild(lb);
|
|
392
394
|
|
|
395
|
+
// Initial leading reflects the host [avatar]/[icon]; #syncLeading() then
|
|
396
|
+
// reconciles it to the SELECTED option's icon/avatar on every render. The
|
|
397
|
+
// `data-select-leading` marker scopes that reconciliation to our element.
|
|
393
398
|
const leading = this.avatar
|
|
394
|
-
? `<img slot="leading" src="${this.avatar}" alt="" />`
|
|
399
|
+
? `<img slot="leading" data-select-leading src="${escapeHTML(this.avatar)}" alt="" />`
|
|
395
400
|
: this.icon
|
|
396
|
-
? `<icon-ui slot="leading" name="${this.icon}"></icon-ui>`
|
|
401
|
+
? `<icon-ui slot="leading" data-select-leading name="${escapeHTML(this.icon)}"></icon-ui>`
|
|
397
402
|
: '';
|
|
398
403
|
const displayMarkup = this.searchable
|
|
399
404
|
? `<input slot="display" type="text" role="combobox" aria-autocomplete="list" autocomplete="off" placeholder="${escapeHTML(this.placeholder || '')}" value="${escapeHTML(this.#displayText() === this.placeholder ? '' : this.#displayText())}" />`
|
|
@@ -470,6 +475,9 @@ export class UISelect extends UIFormElement {
|
|
|
470
475
|
}
|
|
471
476
|
}
|
|
472
477
|
|
|
478
|
+
// Reflect the selected option's icon/avatar in the trigger leading.
|
|
479
|
+
this.#syncLeading();
|
|
480
|
+
|
|
473
481
|
// SPEC-040 — stamp / reconcile chips + "+N more" pill on every render.
|
|
474
482
|
if (this.multiple) this.#stampChips();
|
|
475
483
|
// Show clear-all only in multi-select mode when [clearable] + chips present.
|
|
@@ -548,12 +556,12 @@ export class UISelect extends UIFormElement {
|
|
|
548
556
|
if (child.tagName === 'OPTGROUP') {
|
|
549
557
|
const group = { label: child.label || child.getAttribute('label') || '', options: [] };
|
|
550
558
|
for (const opt of child.querySelectorAll('option')) {
|
|
551
|
-
group.options.push({ value: opt.value, label: opt.textContent.trim(), disabled: opt.disabled });
|
|
559
|
+
group.options.push({ value: opt.value, label: opt.textContent.trim(), disabled: opt.disabled, icon: opt.getAttribute('icon') || '', avatar: opt.getAttribute('avatar') || '' });
|
|
552
560
|
if (opt.hasAttribute('selected')) preSelectedArr.push(opt.value);
|
|
553
561
|
}
|
|
554
562
|
this.#options.push(group);
|
|
555
563
|
} else if (child.tagName === 'OPTION') {
|
|
556
|
-
this.#options.push({ value: child.value, label: child.textContent.trim(), disabled: child.disabled });
|
|
564
|
+
this.#options.push({ value: child.value, label: child.textContent.trim(), disabled: child.disabled, icon: child.getAttribute('icon') || '', avatar: child.getAttribute('avatar') || '' });
|
|
557
565
|
if (child.hasAttribute('selected')) preSelectedArr.push(child.value);
|
|
558
566
|
} else if (
|
|
559
567
|
// §225: skip [slot="display"] / [slot="listbox"] / [slot="action"] etc. — these are
|
|
@@ -614,6 +622,52 @@ export class UISelect extends UIFormElement {
|
|
|
614
622
|
|
|
615
623
|
get options() { return this.#options; }
|
|
616
624
|
|
|
625
|
+
// Per-option leading markup: avatar (img) beats icon (icon-ui). Shared by
|
|
626
|
+
// the listbox rows AND the trigger (resolved against the selected option).
|
|
627
|
+
static #optionLeadHTML(opt) {
|
|
628
|
+
if (!opt) return '';
|
|
629
|
+
if (opt.avatar) return `<img data-option-avatar src="${escapeHTML(opt.avatar)}" alt="" />`;
|
|
630
|
+
if (opt.icon) return `<icon-ui name="${escapeHTML(opt.icon)}"></icon-ui>`;
|
|
631
|
+
return '';
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Reflect the SELECTED option's icon/avatar in the trigger's leading slot.
|
|
636
|
+
* Single-select only (multi-select shows chips). Falls back to the host
|
|
637
|
+
* [avatar]/[icon] when the selected option carries neither. Only manages the
|
|
638
|
+
* leading WE stamped (`[data-select-leading]`) — a consumer-custom trigger
|
|
639
|
+
* owns its own leading.
|
|
640
|
+
*/
|
|
641
|
+
#syncLeading() {
|
|
642
|
+
if (this.multiple || !this.#ownTrigger) return;
|
|
643
|
+
const trigger = this.querySelector('[slot="trigger"]');
|
|
644
|
+
if (!trigger) return;
|
|
645
|
+
const flat = this.#options.flatMap((o) => o.options || [o]);
|
|
646
|
+
const sel = flat.find((o) => !o.header && !o.separator && o.value === this.value);
|
|
647
|
+
const avatar = (sel && sel.avatar) || this.avatar || '';
|
|
648
|
+
const icon = (sel && sel.icon) || this.icon || '';
|
|
649
|
+
const existing = trigger.querySelector(':scope > [data-select-leading]');
|
|
650
|
+
let html = '';
|
|
651
|
+
if (avatar) html = `<img slot="leading" data-select-leading src="${escapeHTML(avatar)}" alt="" />`;
|
|
652
|
+
else if (icon) html = `<icon-ui slot="leading" data-select-leading name="${escapeHTML(icon)}"></icon-ui>`;
|
|
653
|
+
if (!html) { existing?.remove(); return; }
|
|
654
|
+
const tmp = document.createElement('template');
|
|
655
|
+
tmp.innerHTML = html;
|
|
656
|
+
const next = tmp.content.firstElementChild;
|
|
657
|
+
if (!existing) {
|
|
658
|
+
trigger.insertBefore(next, trigger.firstChild);
|
|
659
|
+
} else if (existing.tagName === next.tagName) {
|
|
660
|
+
// Same element type — update the changing attribute in place.
|
|
661
|
+
if (next.tagName === 'IMG') {
|
|
662
|
+
if (existing.getAttribute('src') !== next.getAttribute('src')) existing.setAttribute('src', next.getAttribute('src'));
|
|
663
|
+
} else if (existing.getAttribute('name') !== next.getAttribute('name')) {
|
|
664
|
+
existing.setAttribute('name', next.getAttribute('name'));
|
|
665
|
+
}
|
|
666
|
+
} else {
|
|
667
|
+
existing.replaceWith(next); // icon ↔ avatar switch
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
617
671
|
#renderOptions() {
|
|
618
672
|
if (!this.#listbox) return;
|
|
619
673
|
this.#listbox.innerHTML = '';
|
|
@@ -653,6 +707,8 @@ export class UISelect extends UIFormElement {
|
|
|
653
707
|
// SPEC-040 — multi-select option rows render a leading checkbox
|
|
654
708
|
// indicator (CSS-driven via [data-multi-option]); the `check` icon
|
|
655
709
|
// shows when aria-selected="true".
|
|
710
|
+
// Per-option leading glyph — avatar (img) wins over icon (icon-ui).
|
|
711
|
+
const lead = UISelect.#optionLeadHTML(opt);
|
|
656
712
|
if (this.multiple) {
|
|
657
713
|
el.setAttribute('data-multi-option', '');
|
|
658
714
|
const box = document.createElement('span');
|
|
@@ -661,11 +717,11 @@ export class UISelect extends UIFormElement {
|
|
|
661
717
|
el.appendChild(box);
|
|
662
718
|
const label = document.createElement('span');
|
|
663
719
|
label.setAttribute('data-option-label', '');
|
|
664
|
-
if (
|
|
720
|
+
if (lead) label.innerHTML = `${lead}${escapeHTML(opt.label)}`;
|
|
665
721
|
else label.textContent = opt.label;
|
|
666
722
|
el.appendChild(label);
|
|
667
|
-
} else if (
|
|
668
|
-
el.innerHTML =
|
|
723
|
+
} else if (lead) {
|
|
724
|
+
el.innerHTML = `${lead}${escapeHTML(opt.label)}`;
|
|
669
725
|
} else {
|
|
670
726
|
el.textContent = opt.label;
|
|
671
727
|
}
|
|
@@ -191,6 +191,11 @@
|
|
|
191
191
|
:scope[data-multi-chips] [slot="trigger"] {
|
|
192
192
|
flex-wrap: wrap;
|
|
193
193
|
align-items: center;
|
|
194
|
+
/* Pack chips left with a small gap — override the base trigger's
|
|
195
|
+
`space-between` (which is for the single-select content↔caret split),
|
|
196
|
+
otherwise the chips spread across the full trigger width. The display
|
|
197
|
+
slot flex-grows (below) to push the caret to the trailing edge. */
|
|
198
|
+
justify-content: flex-start;
|
|
194
199
|
gap: var(--a-space-1);
|
|
195
200
|
/* min-height tracks single-row height when empty; flex-wrap allows
|
|
196
201
|
it to grow as chips overflow. */
|
|
@@ -290,6 +295,8 @@ select-ui [slot="listbox"]:popover-open {
|
|
|
290
295
|
}
|
|
291
296
|
|
|
292
297
|
select-ui [role="option"] {
|
|
298
|
+
display: flex;
|
|
299
|
+
align-items: center;
|
|
293
300
|
padding: var(--a-space-1) var(--a-ui-px);
|
|
294
301
|
border-radius: var(--a-radius-sm);
|
|
295
302
|
white-space: nowrap;
|
|
@@ -325,6 +332,17 @@ select-ui [role="option"] icon-ui {
|
|
|
325
332
|
vertical-align: -0.125em;
|
|
326
333
|
}
|
|
327
334
|
|
|
335
|
+
/* Option with avatar — small inline image (matches the trigger leading
|
|
336
|
+
avatar radius), sized to the option line-height. */
|
|
337
|
+
select-ui [role="option"] img[data-option-avatar] {
|
|
338
|
+
width: var(--a-ui-size);
|
|
339
|
+
height: var(--a-ui-size);
|
|
340
|
+
border-radius: var(--select-leading-radius, var(--select-leading-radius-default));
|
|
341
|
+
object-fit: cover;
|
|
342
|
+
margin-inline-end: var(--a-space-1);
|
|
343
|
+
vertical-align: -0.2em;
|
|
344
|
+
}
|
|
345
|
+
|
|
328
346
|
/* Separator */
|
|
329
347
|
select-ui [data-separator] {
|
|
330
348
|
height: 1px;
|
|
@@ -23,7 +23,7 @@ description: |
|
|
|
23
23
|
# Per ADR-0027 — primitives that programmatically create other primitives
|
|
24
24
|
# do NOT auto-import them. Consumer (or demo shell) must explicitly import.
|
|
25
25
|
composes:
|
|
26
|
-
- icon-ui #
|
|
26
|
+
- icon-ui # caret + option-row affixes (created in render)
|
|
27
27
|
- tag-ui # multi-select chip per selected option in the trigger
|
|
28
28
|
props:
|
|
29
29
|
name:
|
|
@@ -151,7 +151,7 @@ props:
|
|
|
151
151
|
default: false
|
|
152
152
|
reflect: true
|
|
153
153
|
options:
|
|
154
|
-
description: "Option list. Array of {value, label, disabled?} or grouped {label, options: [...]}. Alternative to declarative <option> / <optgroup> children."
|
|
154
|
+
description: "Option list. Array of {value, label, disabled?, icon?, avatar?} or grouped {label, options: [...]}. Alternative to declarative <option> / <optgroup> children. Per-option icon/avatar render in the list AND reflect in the trigger's selected state."
|
|
155
155
|
type: array
|
|
156
156
|
default: []
|
|
157
157
|
pattern:
|
|
@@ -249,6 +249,13 @@ a2ui:
|
|
|
249
249
|
tag names are silently ignored (per §225 v0.5.9) and warned once
|
|
250
250
|
at runtime. Or set `.options` programmatically as an array of
|
|
251
251
|
`{value, label, disabled?}` (grouped form: `{label, options:[…]}`).
|
|
252
|
+
- >-
|
|
253
|
+
Per-option visuals: give each <option> an `icon` (Phosphor name) or
|
|
254
|
+
`avatar` (image URL) — `<option value="light" icon="sun">`. Each row
|
|
255
|
+
renders its glyph in the list AND the trigger reflects the SELECTED
|
|
256
|
+
option's icon/avatar (theme pickers, assignee/account switchers).
|
|
257
|
+
`avatar` wins over `icon`; a host-level [icon]/[avatar] is the
|
|
258
|
+
fallback when the selected option carries neither.
|
|
252
259
|
- >-
|
|
253
260
|
For dynamic option lists rendered inside <editor-shell>, set the
|
|
254
261
|
JSON via the [data-options] attribute — <editor-shell>'s
|
|
@@ -14,8 +14,19 @@
|
|
|
14
14
|
],
|
|
15
15
|
"properties": {
|
|
16
16
|
"align": {
|
|
17
|
-
"description": "Alignment of
|
|
17
|
+
"description": "Alignment of the layered children within the shared cell (maps to grid place-items). `center` (default) plus eight directional keywords.",
|
|
18
18
|
"type": "string",
|
|
19
|
+
"enum": [
|
|
20
|
+
"center",
|
|
21
|
+
"top-left",
|
|
22
|
+
"top",
|
|
23
|
+
"top-right",
|
|
24
|
+
"left",
|
|
25
|
+
"right",
|
|
26
|
+
"bottom-left",
|
|
27
|
+
"bottom",
|
|
28
|
+
"bottom-right"
|
|
29
|
+
],
|
|
19
30
|
"default": "center"
|
|
20
31
|
},
|
|
21
32
|
"component": {
|
|
@@ -21,6 +21,6 @@ cell.
|
|
|
21
21
|
import { UIElement } from '../../core/element.js';
|
|
22
22
|
|
|
23
23
|
export class UIStack extends UIElement {
|
|
24
|
-
/** Alignment of
|
|
25
|
-
align:
|
|
24
|
+
/** Alignment of the layered children within the shared cell (maps to grid place-items). `center` (default) plus eight directional keywords. */
|
|
25
|
+
align: 'center' | 'top-left' | 'top' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right';
|
|
26
26
|
}
|
|
@@ -18,9 +18,21 @@ description: |
|
|
|
18
18
|
cell.
|
|
19
19
|
props:
|
|
20
20
|
align:
|
|
21
|
-
description:
|
|
21
|
+
description: >-
|
|
22
|
+
Alignment of the layered children within the shared cell (maps to
|
|
23
|
+
grid place-items). `center` (default) plus eight directional keywords.
|
|
22
24
|
type: string
|
|
23
25
|
default: center
|
|
26
|
+
enum:
|
|
27
|
+
- center
|
|
28
|
+
- top-left
|
|
29
|
+
- top
|
|
30
|
+
- top-right
|
|
31
|
+
- left
|
|
32
|
+
- right
|
|
33
|
+
- bottom-left
|
|
34
|
+
- bottom
|
|
35
|
+
- bottom-right
|
|
24
36
|
events: {}
|
|
25
37
|
slots:
|
|
26
38
|
default:
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"properties": {
|
|
16
|
+
"bleed": {
|
|
17
|
+
"description": "Horizontal-bleed KPI layout. With a `slot=\"chart\"` child, the value / label / change stack on the left while the chart fills the right column at full height and bleeds to the card's top / right / bottom edges (the horizontal counterpart to a chart in a card `<section bleed>`). No effect without a chart slot.",
|
|
18
|
+
"type": "boolean",
|
|
19
|
+
"default": false
|
|
20
|
+
},
|
|
16
21
|
"change": {
|
|
17
22
|
"description": "Change indicator text (e.g. '+12%', '-3%')",
|
|
18
23
|
"type": "string",
|
package/components/stat/stat.css
CHANGED
|
@@ -72,6 +72,61 @@
|
|
|
72
72
|
grid-column: 1;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/* ── Horizontal-bleed KPI tile ──
|
|
76
|
+
value / label / change stack on the LEFT; the chart fills the RIGHT
|
|
77
|
+
column at full height and bleeds to the card's top / right / bottom
|
|
78
|
+
edges (cancels the card-section inset via negative margins keyed to
|
|
79
|
+
the inherited --card-inset). The compact `slot="chart"` reflow above
|
|
80
|
+
keeps the chart resting on the value baseline; this opt-in spreads it
|
|
81
|
+
into a full-height trajectory panel. Compose inside a card section:
|
|
82
|
+
<card-ui><section>
|
|
83
|
+
<stat-ui bleed value=… label=… change=… trend=…>
|
|
84
|
+
<chart-ui slot="chart" type="area" …></chart-ui>
|
|
85
|
+
</stat-ui>
|
|
86
|
+
</section></card-ui> */
|
|
87
|
+
:scope[bleed]:has([slot="chart"]) {
|
|
88
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, var(--stat-bleed-col, 46%));
|
|
89
|
+
grid-template-areas:
|
|
90
|
+
"label chart"
|
|
91
|
+
"value chart"
|
|
92
|
+
"change chart";
|
|
93
|
+
align-items: start;
|
|
94
|
+
}
|
|
95
|
+
:scope[bleed] [slot="value"],
|
|
96
|
+
:scope[bleed] [slot="change"] {
|
|
97
|
+
grid-column: 1;
|
|
98
|
+
}
|
|
99
|
+
/* Icon shares the label row, pinned to the text column's inner edge. */
|
|
100
|
+
:scope[bleed] [slot="label"] {
|
|
101
|
+
padding-inline-end: var(--a-space-5);
|
|
102
|
+
}
|
|
103
|
+
:scope[bleed] [slot="icon"] {
|
|
104
|
+
grid-area: label;
|
|
105
|
+
justify-self: end;
|
|
106
|
+
align-self: start;
|
|
107
|
+
}
|
|
108
|
+
:scope[bleed] [slot="chart"] {
|
|
109
|
+
grid-area: chart;
|
|
110
|
+
/* VERTICAL bleed via stretch: a stretched grid item's height is
|
|
111
|
+
`track − margins`, so a negative margin-block overflows the track by
|
|
112
|
+
that amount top + bottom. `height:auto` overrides the base
|
|
113
|
+
chart-ui[slot="chart"]{height:100%} so the stretch governs. */
|
|
114
|
+
align-self: stretch;
|
|
115
|
+
height: auto;
|
|
116
|
+
margin-block: calc(-1 * var(--card-inset, var(--card-inset-default, 0px)));
|
|
117
|
+
/* HORIZONTAL bleed via explicit width: chart-ui pins its own
|
|
118
|
+
`width:100%` (chart.css), which beats justify-self:stretch and would
|
|
119
|
+
freeze the box to the track — so a negative margin-inline-end can't
|
|
120
|
+
widen it. Instead size the box to `track + inset` and left-anchor it,
|
|
121
|
+
so the right edge overflows to the card edge while the left keeps the
|
|
122
|
+
column gap. The percentage resolves against the grid track. */
|
|
123
|
+
justify-self: start;
|
|
124
|
+
width: calc(100% + var(--card-inset, var(--card-inset-default, 0px)));
|
|
125
|
+
min-width: 0;
|
|
126
|
+
/* override the inline-chart 4:3 — fill the row span instead */
|
|
127
|
+
aspect-ratio: auto;
|
|
128
|
+
}
|
|
129
|
+
|
|
75
130
|
/* ── Label (eyebrow) ── */
|
|
76
131
|
[slot="label"] {
|
|
77
132
|
grid-area: label;
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
import { UIElement } from '../../core/element.js';
|
|
14
14
|
|
|
15
15
|
export class UIStat extends UIElement {
|
|
16
|
+
/** Horizontal-bleed KPI layout. With a `slot="chart"` child, the value / label / change stack on the left while the chart fills the right column at full height and bleeds to the card's top / right / bottom edges (the horizontal counterpart to a chart in a card `<section bleed>`). No effect without a chart slot. */
|
|
17
|
+
bleed: boolean;
|
|
16
18
|
/** Change indicator text (e.g. '+12%', '-3%') */
|
|
17
19
|
change: string;
|
|
18
20
|
/** Icon name displayed in the icon slot */
|
package/components/stat/stat.js
CHANGED
|
@@ -18,6 +18,10 @@ class UIStat extends UIElement {
|
|
|
18
18
|
trend: { type: String, default: '', reflect: true },
|
|
19
19
|
icon: { type: String, default: '', reflect: true },
|
|
20
20
|
loading: { type: Boolean, default: false, reflect: true },
|
|
21
|
+
// Horizontal-bleed KPI layout: value/label/change stack on the left, the
|
|
22
|
+
// slot="chart" child fills the right column and bleeds to the card edges.
|
|
23
|
+
// Reflected so the [bleed] CSS selector gates the layout.
|
|
24
|
+
bleed: { type: Boolean, default: false, reflect: true },
|
|
21
25
|
};
|
|
22
26
|
|
|
23
27
|
static template = () => null;
|
|
@@ -49,6 +49,15 @@ props:
|
|
|
49
49
|
required: true
|
|
50
50
|
type: string
|
|
51
51
|
default: ""
|
|
52
|
+
bleed:
|
|
53
|
+
description: >-
|
|
54
|
+
Horizontal-bleed KPI layout. With a `slot="chart"` child, the value /
|
|
55
|
+
label / change stack on the left while the chart fills the right column
|
|
56
|
+
at full height and bleeds to the card's top / right / bottom edges (the
|
|
57
|
+
horizontal counterpart to a chart in a card `<section bleed>`). No effect
|
|
58
|
+
without a chart slot.
|
|
59
|
+
type: boolean
|
|
60
|
+
default: false
|
|
52
61
|
events: {}
|
|
53
62
|
slots:
|
|
54
63
|
change:
|
|
@@ -249,14 +249,22 @@ export class UISwiper extends UIElement {
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
// ── Snap & page helpers ──
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
|
|
252
|
+
// The column count drives the page/dot math. `[slides-per-view]` (and the
|
|
253
|
+
// responsive @container rules) set `--swiper-columns-default` on the host /
|
|
254
|
+
// track per the component-token contract; consumers may override via the
|
|
255
|
+
// public `--swiper-columns`. We must read the SAME fallback chain the CSS
|
|
256
|
+
// width calc uses — `var(--swiper-columns, var(--swiper-columns-default))` —
|
|
257
|
+
// not just the public token. Reading only `--swiper-columns` (unset unless a
|
|
258
|
+
// consumer overrides) made #getColumns() always fall back to 1, so the dot
|
|
259
|
+
// count was `total - 1 + 1 = total slides` instead of the real page count
|
|
260
|
+
// (bug-29: 5 slides at slides-per-view=3 stamped 5 dots, not 3). Regressed by
|
|
261
|
+
// the 2026-05-24 OD-5 `-default` token sweep, which renamed the CSS side but
|
|
262
|
+
// not this read site.
|
|
257
263
|
#getColumns() {
|
|
258
264
|
if (!this.#track) return 1;
|
|
259
|
-
const
|
|
265
|
+
const cs = getComputedStyle(this.#track);
|
|
266
|
+
const v = cs.getPropertyValue('--swiper-columns').trim()
|
|
267
|
+
|| cs.getPropertyValue('--swiper-columns-default').trim();
|
|
260
268
|
const n = parseInt(v, 10);
|
|
261
269
|
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
262
270
|
}
|
|
@@ -115,6 +115,19 @@ switch-ui[checked] [slot="thumb"] {
|
|
|
115
115
|
color: var(--switch-hint-fg, var(--a-fg-muted));
|
|
116
116
|
line-height: var(--switch-hint-lh, 1.4);
|
|
117
117
|
}
|
|
118
|
+
/* The hint is documented (yaml + props table) as rendering BELOW the label,
|
|
119
|
+
but the base `inline-flex` row laid the stamped `[slot="hint"]` out as a
|
|
120
|
+
third inline item (beside the label). When a hint is present, let the row
|
|
121
|
+
wrap and force the hint onto its own full-width line, indented under the
|
|
122
|
+
label (past the track + gap). `:has()`-scoped so hint-less switches keep
|
|
123
|
+
the unchanged single-row layout (no long-label side effects); no display-
|
|
124
|
+
model change (stays inline-flex). The hint span is stamped only when
|
|
125
|
+
[hint] is set (switch.class.js), so this never adds a phantom line. */
|
|
126
|
+
:scope:has([slot="hint"]) { flex-wrap: wrap; }
|
|
127
|
+
:scope:has([slot="hint"]) [slot="hint"] {
|
|
128
|
+
flex-basis: 100%;
|
|
129
|
+
padding-inline-start: calc(var(--switch-track-width, var(--switch-track-width-default)) + var(--switch-gap, var(--switch-gap-default)));
|
|
130
|
+
}
|
|
118
131
|
|
|
119
132
|
:scope:focus-visible { outline: none; }
|
|
120
133
|
:scope:focus-visible [slot="track"] { box-shadow: var(--switch-focus-ring, var(--switch-focus-ring-default)); }
|
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
}
|
|
170
170
|
},
|
|
171
171
|
"row-collapse": {
|
|
172
|
-
"description": "Fired when an already-expanded row's
|
|
172
|
+
"description": "Fired when an already-expanded row's caret is activated (collapses the row). Mirror of row-expand.",
|
|
173
173
|
"detail": {
|
|
174
174
|
"index": {
|
|
175
175
|
"description": "Row index in the underlying data array.",
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
}
|
|
183
183
|
},
|
|
184
184
|
"row-expand": {
|
|
185
|
-
"description": "Fired when an expandable row's
|
|
185
|
+
"description": "Fired when an expandable row's caret is activated. detail.index is the row position; detail.row is the row data.",
|
|
186
186
|
"detail": {
|
|
187
187
|
"index": {
|
|
188
188
|
"description": "Row index in the underlying data array.",
|
|
@@ -323,7 +323,12 @@
|
|
|
323
323
|
color: var(--table-fg-hover, var(--table-fg-hover-default));
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
[data-
|
|
326
|
+
/* Selected row — must out-specify the base row rule `[data-body] >
|
|
327
|
+
[role="row"]` (0,2,0) and the hover rule (0,2,1), or the highlight never
|
|
328
|
+
paints. Scoping to `[data-body] > [role="row"][data-selected]` lifts it to
|
|
329
|
+
(0,4,0). (The striped-even override below handles selected+striped, which
|
|
330
|
+
is higher still.) */
|
|
331
|
+
[data-body] > [role="row"][data-selected] {
|
|
327
332
|
background: var(--table-row-bg-selected, var(--table-row-bg-selected-default));
|
|
328
333
|
}
|
|
329
334
|
|
|
@@ -341,6 +346,13 @@
|
|
|
341
346
|
background: var(--table-row-bg-hover, var(--table-row-bg-hover-default));
|
|
342
347
|
}
|
|
343
348
|
|
|
349
|
+
/* Selected wins over the stripe — same (0,5,0) specificity as the striped-
|
|
350
|
+
even rule above, placed AFTER it so source order resolves the tie in
|
|
351
|
+
favor of selection (a selected row reads as selected regardless of stripe). */
|
|
352
|
+
:scope[striped] [data-body] > [role="row"][data-selected] {
|
|
353
|
+
background: var(--table-row-bg-selected, var(--table-row-bg-selected-default));
|
|
354
|
+
}
|
|
355
|
+
|
|
344
356
|
/* ═══════ Selection checkboxes ═══════ */
|
|
345
357
|
|
|
346
358
|
[data-check-col] {
|
|
@@ -146,7 +146,7 @@ events:
|
|
|
146
146
|
type: number
|
|
147
147
|
description: New column width in pixels.
|
|
148
148
|
row-collapse:
|
|
149
|
-
description: Fired when an already-expanded row's
|
|
149
|
+
description: Fired when an already-expanded row's caret is activated (collapses the row). Mirror of row-expand.
|
|
150
150
|
detail:
|
|
151
151
|
index:
|
|
152
152
|
type: number
|
|
@@ -155,7 +155,7 @@ events:
|
|
|
155
155
|
type: object
|
|
156
156
|
description: Row data object.
|
|
157
157
|
row-expand:
|
|
158
|
-
description: Fired when an expandable row's
|
|
158
|
+
description: Fired when an expandable row's caret is activated. detail.index is the row position; detail.row is the row data.
|
|
159
159
|
detail:
|
|
160
160
|
index:
|
|
161
161
|
type: number
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
:scope {
|
|
31
31
|
/* ── Base ── */
|
|
32
32
|
box-sizing: border-box;
|
|
33
|
-
display:
|
|
33
|
+
display: flex;
|
|
34
34
|
align-items: center;
|
|
35
35
|
gap: var(--time-picker-gap, var(--time-picker-gap-default));
|
|
36
36
|
min-height: var(--time-picker-height, var(--time-picker-height-default));
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
box-shadow var(--time-picker-duration, var(--time-picker-duration-default)) var(--time-picker-easing, var(--time-picker-easing-default));
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
|
|
54
|
+
:scope[inline] { display: inline-flex; }
|
|
55
|
+
|
|
53
56
|
/* ── Segments ── */
|
|
54
57
|
:scope [data-segment] {
|
|
55
58
|
display: inline-block;
|
|
@@ -87,7 +87,7 @@ export class UITimelineItem extends UIElement {
|
|
|
87
87
|
spinner: { type: Boolean, default: false, reflect: true },
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
// §205 (v0.5.7): dynamic
|
|
90
|
+
// §205 (v0.5.7): dynamic caret icons stamped on expanded-state ternary
|
|
91
91
|
// (timeline.class.js:167). Per FEEDBACK-16 §1 + §209 slot-11 ternary-walker discovery.
|
|
92
92
|
// Note: `this.icon` consumer-supplied — not declared here.
|
|
93
93
|
static requiredIcons = ['caret-down', 'caret-right'];
|
|
@@ -134,7 +134,7 @@ export class UITimelineItem extends UIElement {
|
|
|
134
134
|
this.querySelector(':scope > [slot="icon"]')?.remove();
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
// Outcomes sub-list + toggle
|
|
137
|
+
// Outcomes sub-list + toggle caret
|
|
138
138
|
let body = this.querySelector(':scope > [slot="outcomes"]');
|
|
139
139
|
if (this.#outcomes.length > 0) {
|
|
140
140
|
if (!body) {
|
|
@@ -150,7 +150,7 @@ export class UITimelineItem extends UIElement {
|
|
|
150
150
|
}
|
|
151
151
|
body.hidden = !this.#expanded;
|
|
152
152
|
|
|
153
|
-
// Toggle
|
|
153
|
+
// Toggle caret lives on the row — we stamp a button once
|
|
154
154
|
let toggle = this.querySelector(':scope > [data-timeline-toggle]');
|
|
155
155
|
if (!toggle) {
|
|
156
156
|
toggle = document.createElement('button');
|
|
@@ -39,10 +39,18 @@
|
|
|
39
39
|
--timeline-line-fg-default: var(--a-border-subtle);
|
|
40
40
|
--timeline-line-fg-done-default: var(--a-border);
|
|
41
41
|
|
|
42
|
+
/* Marker box — the shared circular footprint the icon marker fills and
|
|
43
|
+
the dot / spinner center within (the column track --timeline-marker-w
|
|
44
|
+
is a touch wider for breathing room). One token sizes the whole marker
|
|
45
|
+
system; consumers (agent-reasoning-ui, chat surfaces) override it to
|
|
46
|
+
scale every marker type coherently. */
|
|
47
|
+
--timeline-marker-box-size-default: 1rem;
|
|
48
|
+
|
|
42
49
|
/* Icon (replaces dot) */
|
|
43
|
-
--timeline-icon-size-default:
|
|
50
|
+
--timeline-icon-size-default: var(--timeline-marker-box-size, var(--timeline-marker-box-size-default));
|
|
44
51
|
--timeline-icon-bg-default: var(--a-bg-muted);
|
|
45
52
|
--timeline-icon-fg-default: var(--a-fg-subtle);
|
|
53
|
+
--timeline-icon-border-default: var(--a-border-subtle);
|
|
46
54
|
--timeline-icon-bg-success-default: var(--a-success-muted);
|
|
47
55
|
--timeline-icon-fg-success-default: var(--a-success-strong);
|
|
48
56
|
--timeline-icon-bg-accent-default: var(--a-accent-muted);
|
|
@@ -85,6 +93,7 @@
|
|
|
85
93
|
/* Compact variant (agent-reasoning + chat-adjacent) */
|
|
86
94
|
:scope[size="sm"] {
|
|
87
95
|
--timeline-marker-w-default: 1rem;
|
|
96
|
+
--timeline-marker-box-size-default: 0.875rem;
|
|
88
97
|
--timeline-dot-size-default: 6px;
|
|
89
98
|
--timeline-gap-row-default: var(--a-space-2);
|
|
90
99
|
--timeline-label-size-default: var(--a-ui-sm);
|
|
@@ -133,9 +142,14 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
|
|
|
133
142
|
--timeline-item-line-fg: var(--timeline-line-fg, var(--a-border-subtle));
|
|
134
143
|
--timeline-item-line-fg-done: var(--timeline-line-fg-done, var(--a-border));
|
|
135
144
|
|
|
136
|
-
|
|
145
|
+
/* Reads the override THEN the inherited -default (set on timeline-ui,
|
|
146
|
+
and shrunk by [size="sm"]) THEN a literal — so the marker box scales
|
|
147
|
+
with both consumer overrides AND the sm compact variant. */
|
|
148
|
+
--timeline-item-marker-box-size: var(--timeline-marker-box-size, var(--timeline-marker-box-size-default, 1rem));
|
|
149
|
+
--timeline-item-icon-size: var(--timeline-icon-size, var(--timeline-item-marker-box-size));
|
|
137
150
|
--timeline-item-icon-bg: var(--timeline-icon-bg, var(--a-bg-muted));
|
|
138
151
|
--timeline-item-icon-fg: var(--timeline-icon-fg, var(--a-fg-subtle));
|
|
152
|
+
--timeline-item-icon-border: var(--timeline-icon-border, var(--a-border-subtle));
|
|
139
153
|
--timeline-item-icon-bg-success: var(--timeline-icon-bg-success, var(--a-success-muted));
|
|
140
154
|
--timeline-item-icon-fg-success: var(--timeline-icon-fg-success, var(--a-success-strong));
|
|
141
155
|
--timeline-item-icon-bg-accent: var(--timeline-icon-bg-accent, var(--a-accent-muted));
|
|
@@ -245,6 +259,10 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
|
|
|
245
259
|
border-radius: 50%;
|
|
246
260
|
background: var(--timeline-item-icon-bg);
|
|
247
261
|
color: var(--timeline-item-icon-fg);
|
|
262
|
+
/* Subtle ring so the default (no-variant) chip reads as a marker even
|
|
263
|
+
when its muted fill blends with the surrounding surface; the colored
|
|
264
|
+
variants below restate background only, keeping this ring. */
|
|
265
|
+
border: 1px solid var(--timeline-item-icon-border);
|
|
248
266
|
display: inline-flex;
|
|
249
267
|
align-items: center;
|
|
250
268
|
justify-content: center;
|
|
@@ -342,12 +360,12 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
|
|
|
342
360
|
background: var(--timeline-item-subdot-bg);
|
|
343
361
|
}
|
|
344
362
|
|
|
345
|
-
/* ═══════ Toggle
|
|
363
|
+
/* ═══════ Toggle caret (outcomes) ═══════ */
|
|
346
364
|
|
|
347
365
|
:scope > [data-timeline-toggle] {
|
|
348
366
|
position: absolute;
|
|
349
367
|
inset-inline-end: 0;
|
|
350
|
-
/* Place the
|
|
368
|
+
/* Place the caret's icon-center on row 1's optical center.
|
|
351
369
|
The button has --a-space-0-5 vertical padding, so the icon
|
|
352
370
|
sits that much lower inside the box; we subtract that so the
|
|
353
371
|
icon (not the box) lines up with the time-slot text center. */
|
|
@@ -372,7 +390,7 @@ agent-reasoning-ui timeline-ui:not([orientation="horizontal"]),
|
|
|
372
390
|
color: var(--timeline-item-label-fg, var(--a-fg));
|
|
373
391
|
}
|
|
374
392
|
|
|
375
|
-
/* Reserve room so the time isn't overlapped by the
|
|
393
|
+
/* Reserve room so the time isn't overlapped by the caret */
|
|
376
394
|
:scope:has(> [data-timeline-toggle]) [slot="time"] {
|
|
377
395
|
margin-inline-end: var(--timeline-item-toggle-time-margin);
|
|
378
396
|
}
|
|
@@ -19,11 +19,14 @@ toggle-option-ui:not([disabled]):hover {
|
|
|
19
19
|
|
|
20
20
|
:scope {
|
|
21
21
|
box-sizing: border-box;
|
|
22
|
-
display:
|
|
22
|
+
display: flex;
|
|
23
23
|
border: var(--toggle-group-border-width, var(--toggle-group-border-width-default)) solid var(--toggle-group-border-color, var(--toggle-group-border-color-default));
|
|
24
24
|
border-radius: var(--toggle-group-radius, var(--toggle-group-radius-default));
|
|
25
25
|
overflow: hidden;
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
/* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
|
|
29
|
+
:scope[inline] { display: inline-flex; }
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
@scope (toggle-option-ui) {
|
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
:scope {
|
|
7
|
-
display:
|
|
7
|
+
display: flex;
|
|
8
8
|
align-items: center;
|
|
9
9
|
justify-content: center;
|
|
10
10
|
line-height: 0;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/* Display convention (ADR-0037): block-level by default; [inline] opts back to inline-level. */
|
|
14
|
+
:scope[inline] { display: inline-flex; }
|
|
15
|
+
|
|
13
16
|
:scope[disabled] {
|
|
14
17
|
pointer-events: none;
|
|
15
18
|
}
|