@adia-ai/web-components 0.0.26 → 0.0.28
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/agent-artifact/agent-artifact.a2ui.json +1 -1
- package/components/agent-artifact/agent-artifact.css +11 -0
- package/components/agent-artifact/agent-artifact.js +23 -2
- package/components/agent-artifact/agent-artifact.yaml +1 -1
- package/components/agent-questions/agent-questions.css +20 -1
- package/components/agent-reasoning/agent-reasoning.css +11 -0
- package/components/agent-reasoning/agent-reasoning.js +16 -0
- package/components/agent-trace/agent-trace.css +36 -12
- package/components/alert/alert.a2ui.json +10 -4
- package/components/alert/alert.css +13 -0
- package/components/alert/alert.js +1 -1
- package/components/alert/alert.yaml +21 -4
- package/components/badge/badge.a2ui.json +0 -2
- package/components/badge/badge.css +20 -0
- package/components/badge/badge.js +10 -2
- package/components/badge/badge.yaml +0 -2
- package/components/breadcrumb/breadcrumb.a2ui.json +16 -1
- package/components/breadcrumb/breadcrumb.css +27 -0
- package/components/breadcrumb/breadcrumb.js +95 -17
- package/components/breadcrumb/breadcrumb.yaml +15 -1
- package/components/calendar-picker/calendar-picker.css +17 -0
- package/components/chart/chart.css +20 -13
- package/components/chart/chart.js +49 -17
- package/components/chart-legend/chart-legend.css +30 -54
- package/components/chart-legend/chart-legend.js +48 -30
- package/components/code/code.css +41 -0
- package/components/code/code.js +44 -3
- package/components/command/command.js +52 -1
- package/components/empty-state/empty-state.js +32 -21
- package/components/feed/feed-item.yaml +50 -0
- package/components/feed/feed.a2ui.json +59 -0
- package/components/feed/feed.css +141 -0
- package/components/feed/feed.js +276 -0
- package/components/feed/feed.yaml +33 -0
- package/components/index.js +2 -0
- package/components/list/list.js +20 -16
- package/components/menu/menu.css +18 -0
- package/components/menu/menu.js +24 -10
- package/components/pane/pane.css +5 -0
- package/components/pipeline-status/pipeline-status.css +15 -1
- package/components/popover/popover.css +17 -0
- package/components/select/select.css +18 -0
- package/components/swatch/swatch.a2ui.json +116 -0
- package/components/swatch/swatch.css +141 -0
- package/components/swatch/swatch.js +121 -0
- package/components/swatch/swatch.yaml +101 -0
- package/components/swiper/swiper.css +9 -0
- package/components/table/table.css +5 -0
- package/components/table/table.js +45 -1
- package/components/table-toolbar/table-toolbar.css +13 -0
- package/components/tag/tag.css +10 -0
- package/components/timeline/timeline.css +15 -4
- package/components/toast/toast.css +93 -48
- package/components/toast/toast.js +101 -22
- package/components/toolbar/toolbar.css +13 -0
- package/components/tooltip/tooltip.css +11 -3
- package/core/provider.js +1 -0
- package/package.json +1 -1
- package/styles/colors/semantics.css +1 -1
- package/styles/components.css +1 -0
|
@@ -6,12 +6,26 @@ tag: breadcrumb-ui
|
|
|
6
6
|
component: Breadcrumb
|
|
7
7
|
category: navigation
|
|
8
8
|
version: 1
|
|
9
|
-
description: Breadcrumb trail with auto-inserted separators.
|
|
9
|
+
description: Breadcrumb trail with auto-inserted separators. Supports a leading icon (first child) and an overflow popover that collapses middle crumbs into a `…` menu.
|
|
10
10
|
props:
|
|
11
11
|
separator:
|
|
12
12
|
description: Character or string rendered between breadcrumb items via CSS ::before.
|
|
13
13
|
type: string
|
|
14
14
|
default: /
|
|
15
|
+
collapse:
|
|
16
|
+
description: Collapse middle crumbs into a `…` overflow popover when there are 4+ items.
|
|
17
|
+
type: boolean
|
|
18
|
+
default: false
|
|
19
|
+
collapseKeepLeading:
|
|
20
|
+
description: Number of leading items to keep visible when [collapse] is active. The first item (often a home/icon link) sits before the overflow popover.
|
|
21
|
+
type: number
|
|
22
|
+
default: 1
|
|
23
|
+
attribute: collapse-keep-leading
|
|
24
|
+
collapseKeepTrailing:
|
|
25
|
+
description: Number of trailing items to keep visible when [collapse] is active. The last item is always the current page.
|
|
26
|
+
type: number
|
|
27
|
+
default: 2
|
|
28
|
+
attribute: collapse-keep-trailing
|
|
15
29
|
events: {}
|
|
16
30
|
slots: {}
|
|
17
31
|
states:
|
|
@@ -180,6 +180,23 @@ calendar-picker-ui [slot="popover"] {
|
|
|
180
180
|
font-family: inherit;
|
|
181
181
|
font-size: var(--calendar-picker-font-size);
|
|
182
182
|
color: var(--calendar-picker-popover-fg);
|
|
183
|
+
/* Fade + lift in on first paint via @starting-style. Same pattern
|
|
184
|
+
as menu/select/popover surfaces — see journal 2026-04-29 §18. */
|
|
185
|
+
opacity: 1;
|
|
186
|
+
translate: 0 0;
|
|
187
|
+
transition: opacity var(--a-duration-fast) var(--a-easing-out),
|
|
188
|
+
translate var(--a-duration-fast) var(--a-easing-out);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
calendar-picker-ui [slot="popover"]:popover-open {
|
|
192
|
+
@starting-style {
|
|
193
|
+
opacity: 0;
|
|
194
|
+
translate: 0 -4px;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@media (prefers-reduced-motion: reduce) {
|
|
199
|
+
calendar-picker-ui [slot="popover"] { transition: none; }
|
|
183
200
|
}
|
|
184
201
|
|
|
185
202
|
/* Header row */
|
|
@@ -463,24 +463,31 @@
|
|
|
463
463
|
/* ── Radial bar ──
|
|
464
464
|
Concentric rings — each ring stroked with bandwidth. Track is the
|
|
465
465
|
faint backing, filled arc is accent. Stroke-linecap:round in the path
|
|
466
|
-
gives rounded end-caps on the arcs.
|
|
467
|
-
|
|
466
|
+
gives rounded end-caps on the arcs.
|
|
467
|
+
|
|
468
|
+
Element-qualified `circle[data-radial-*]` selectors (specificity 0,1,1)
|
|
469
|
+
are required to override the generic `circle[data-slice="N"] { fill }`
|
|
470
|
+
rule above, which would otherwise fill the ring circles solid and
|
|
471
|
+
produce a solid-disc render. The inline `fill="none"` attribute on the
|
|
472
|
+
SVG element loses to any CSS rule that names `fill`. */
|
|
473
|
+
circle[data-radial-track] {
|
|
468
474
|
stroke: var(--a-border-subtle);
|
|
469
475
|
fill: none;
|
|
470
476
|
}
|
|
471
|
-
[data-radial-bar] {
|
|
477
|
+
circle[data-radial-bar] {
|
|
472
478
|
stroke: var(--chart-bar);
|
|
479
|
+
fill: none;
|
|
473
480
|
}
|
|
474
|
-
[data-radial-bar][data-slice="0"] { stroke: var(--chart-0); }
|
|
475
|
-
[data-radial-bar][data-slice="1"] { stroke: var(--chart-1); }
|
|
476
|
-
[data-radial-bar][data-slice="2"] { stroke: var(--chart-2); }
|
|
477
|
-
[data-radial-bar][data-slice="3"] { stroke: var(--chart-3); }
|
|
478
|
-
[data-radial-bar][data-slice="4"] { stroke: var(--chart-4); }
|
|
479
|
-
[data-radial-bar][data-slice="5"] { stroke: var(--chart-5); }
|
|
480
|
-
[data-radial-bar][data-slice="6"] { stroke: var(--chart-6); }
|
|
481
|
-
[data-radial-bar][data-slice="7"] { stroke: var(--chart-7); }
|
|
482
|
-
[data-radial-bar][data-slice="8"] { stroke: var(--chart-8); }
|
|
483
|
-
[data-radial-bar][data-slice="9"] { stroke: var(--chart-9); }
|
|
481
|
+
circle[data-radial-bar][data-slice="0"] { stroke: var(--chart-0); }
|
|
482
|
+
circle[data-radial-bar][data-slice="1"] { stroke: var(--chart-1); }
|
|
483
|
+
circle[data-radial-bar][data-slice="2"] { stroke: var(--chart-2); }
|
|
484
|
+
circle[data-radial-bar][data-slice="3"] { stroke: var(--chart-3); }
|
|
485
|
+
circle[data-radial-bar][data-slice="4"] { stroke: var(--chart-4); }
|
|
486
|
+
circle[data-radial-bar][data-slice="5"] { stroke: var(--chart-5); }
|
|
487
|
+
circle[data-radial-bar][data-slice="6"] { stroke: var(--chart-6); }
|
|
488
|
+
circle[data-radial-bar][data-slice="7"] { stroke: var(--chart-7); }
|
|
489
|
+
circle[data-radial-bar][data-slice="8"] { stroke: var(--chart-8); }
|
|
490
|
+
circle[data-radial-bar][data-slice="9"] { stroke: var(--chart-9); }
|
|
484
491
|
|
|
485
492
|
/* ── Gauge ──
|
|
486
493
|
Half-donut — track is faint backing, fill reads as primary accent. */
|
|
@@ -173,6 +173,20 @@ function smoothAreaPath(points, baselineY, t = 0.4) {
|
|
|
173
173
|
return `${line} L${last.x},${baselineY} L${first.x},${baselineY} Z`;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/** Build a column-bar path with only the TOP corners rounded so the bar
|
|
177
|
+
* sits flush on its baseline axis. Used for bar / grouped-bar / composed
|
|
178
|
+
* bar series / stacked-bar single + top segments — the value end gets a
|
|
179
|
+
* cap, the axis end stays square. r is clamped to (w/2, h) to handle
|
|
180
|
+
* short bars without the arcs overlapping or escaping the rect. */
|
|
181
|
+
function topRoundedBarPath(x, y, w, h, r = 0) {
|
|
182
|
+
if (h <= 0 || w <= 0) return '';
|
|
183
|
+
const rr = Math.max(0, Math.min(r, w / 2, h));
|
|
184
|
+
if (rr === 0) {
|
|
185
|
+
return `M${x},${y} H${x + w} V${y + h} H${x} Z`;
|
|
186
|
+
}
|
|
187
|
+
return `M${x},${y + h} V${y + rr} Q${x},${y} ${x + rr},${y} H${x + w - rr} Q${x + w},${y} ${x + w},${y + rr} V${y + h} Z`;
|
|
188
|
+
}
|
|
189
|
+
|
|
176
190
|
/* ── Aspect ratios ─────────────────────────────────────────────── */
|
|
177
191
|
|
|
178
192
|
const ASPECTS = {
|
|
@@ -263,6 +277,22 @@ class AdiaChart extends AdiaElement {
|
|
|
263
277
|
if (!this.hasAttribute('role')) this.setAttribute('role', 'img');
|
|
264
278
|
if (!this.hasAttribute('aria-label')) this.setAttribute('aria-label', this.heading || `${this.type} chart`);
|
|
265
279
|
|
|
280
|
+
/* Hydrate from inline `data="[…]"` HTML attribute. The canonical
|
|
281
|
+
entry point is the `.data` property (set programmatically), but
|
|
282
|
+
consumers commonly try the same declarative attribute shape that
|
|
283
|
+
every other chart prop accepts — `<chart-ui data='[…]' x="m"
|
|
284
|
+
y="v">`. AdiaElement's property system doesn't deserialize JSON
|
|
285
|
+
array attributes, so a static-HTML chart authored that way would
|
|
286
|
+
otherwise render empty. JSON-parse once at connect; malformed
|
|
287
|
+
payloads are ignored silently and a property assignment later
|
|
288
|
+
still wins. */
|
|
289
|
+
if (this.#data.length === 0 && this.hasAttribute('data')) {
|
|
290
|
+
try {
|
|
291
|
+
const parsed = JSON.parse(this.getAttribute('data'));
|
|
292
|
+
if (Array.isArray(parsed)) this.data = parsed;
|
|
293
|
+
} catch (_) { /* malformed JSON — leave empty, render() bails on no data */ }
|
|
294
|
+
}
|
|
295
|
+
|
|
266
296
|
/* Listen for canonical `toggle` events bubbled from external
|
|
267
297
|
chart-legend-ui[for=self] descendants. The handler filters by
|
|
268
298
|
target so other components dispatching `toggle` (accordion-ui,
|
|
@@ -1004,7 +1034,7 @@ class AdiaChart extends AdiaElement {
|
|
|
1004
1034
|
const bx = pad.left + barW * i + barGap;
|
|
1005
1035
|
const by = pad.top + plotH - barH;
|
|
1006
1036
|
|
|
1007
|
-
svg += `<
|
|
1037
|
+
svg += `<path data-bar${tip({ label: labels[i], value: v })} d="${topRoundedBarPath(bx, by, barInner, barH, this.#resolveRadius())}"/>`;
|
|
1008
1038
|
|
|
1009
1039
|
if (showValues) {
|
|
1010
1040
|
svg += `<text data-value x="${bx + barInner / 2}" y="${by - 4}" text-anchor="middle" font-size="${dims.valueSize}">${this.#fmtValue(v)}</text>`;
|
|
@@ -1450,6 +1480,12 @@ class AdiaChart extends AdiaElement {
|
|
|
1450
1480
|
const cy = height * 0.68;
|
|
1451
1481
|
const outerR = Math.max(40, Math.min(width * 0.45, height * 0.6));
|
|
1452
1482
|
const innerR = outerR * 0.72;
|
|
1483
|
+
/* End-cap radius — fully-rounded pill ends matching the
|
|
1484
|
+
progress-ui convention (--progress-radius: var(--a-radius-full)).
|
|
1485
|
+
donutArcPath clamps the corner radius to (outerR - innerR) / 2,
|
|
1486
|
+
so passing the ring half-thickness gives full pill caps on both
|
|
1487
|
+
ends of the arc. */
|
|
1488
|
+
const capR = (outerR - innerR) / 2;
|
|
1453
1489
|
|
|
1454
1490
|
let svg = '';
|
|
1455
1491
|
|
|
@@ -1457,14 +1493,14 @@ class AdiaChart extends AdiaElement {
|
|
|
1457
1493
|
clockwise through 12 o'clock (3π/2) to 3 o'clock (2π). donutArcPath
|
|
1458
1494
|
produces a filled ring wedge; with start=π, end=2π the sliceAngle
|
|
1459
1495
|
equals π so large-arc-flag=0 and the arc passes over the top. */
|
|
1460
|
-
svg += `<path data-radial-track d="${donutArcPath(cx, cy, outerR, innerR, Math.PI, 2 * Math.PI,
|
|
1496
|
+
svg += `<path data-radial-track d="${donutArcPath(cx, cy, outerR, innerR, Math.PI, 2 * Math.PI, capR)}"/>`;
|
|
1461
1497
|
|
|
1462
1498
|
/* Filled portion — 0..180° proportional to pct. pct=0 draws nothing;
|
|
1463
1499
|
pct=1 overlays the full backing arc. */
|
|
1464
1500
|
if (pct > 0) {
|
|
1465
1501
|
const fillEnd = Math.PI + Math.PI * pct;
|
|
1466
1502
|
const tipAttrs = tip({ label: data[0]?.[this.x] ?? 'Value', value: primary, pct: (pct * 100).toFixed(1) });
|
|
1467
|
-
svg += `<path data-slice="0"${tipAttrs} data-gauge-fill d="${donutArcPath(cx, cy, outerR, innerR, Math.PI, fillEnd,
|
|
1503
|
+
svg += `<path data-slice="0"${tipAttrs} data-gauge-fill d="${donutArcPath(cx, cy, outerR, innerR, Math.PI, fillEnd, capR)}"/>`;
|
|
1468
1504
|
}
|
|
1469
1505
|
|
|
1470
1506
|
/* Center value label */
|
|
@@ -1807,7 +1843,7 @@ class AdiaChart extends AdiaElement {
|
|
|
1807
1843
|
const barH = maxVal ? (v / maxVal) * plotH : 0;
|
|
1808
1844
|
const bx = pad.left + barW * i + barGap;
|
|
1809
1845
|
const by = pad.top + plotH - barH;
|
|
1810
|
-
svg += `<
|
|
1846
|
+
svg += `<path${this.#seriesFill(0, barKey)}${tip({ label: labels[i], value: v, series: barKey })} d="${topRoundedBarPath(bx, by, barInner, barH, this.#resolveRadius())}"/>`;
|
|
1811
1847
|
}
|
|
1812
1848
|
}
|
|
1813
1849
|
|
|
@@ -1943,22 +1979,18 @@ class AdiaChart extends AdiaElement {
|
|
|
1943
1979
|
const by = stackY;
|
|
1944
1980
|
const bh = segH;
|
|
1945
1981
|
const isTop = k === segCount - 1;
|
|
1946
|
-
const
|
|
1947
|
-
const r = Math.min(this.#resolveRadius(), barInner / 2, bh / 2);
|
|
1982
|
+
const r = this.#resolveRadius();
|
|
1948
1983
|
|
|
1949
1984
|
const attrs = `${this.#seriesFill(k % 10, keys[k])}${tip({ label: labels[i], value: v, series: keys[k] })}`;
|
|
1950
1985
|
|
|
1951
|
-
if (isTop
|
|
1952
|
-
//
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
svg += `<path${attrs} d="M${bx},${by + bh} V${by + r} Q${bx},${by} ${bx + r},${by} H${bx + barInner - r} Q${bx + barInner},${by} ${bx + barInner},${by + r} V${by + bh} Z"/>`;
|
|
1957
|
-
} else if (isBottom) {
|
|
1958
|
-
// Bottom segment — round bottom corners only
|
|
1959
|
-
svg += `<path${attrs} d="M${bx},${by} H${bx + barInner} V${by + bh - r} Q${bx + barInner},${by + bh} ${bx + barInner - r},${by + bh} H${bx + r} Q${bx},${by + bh} ${bx},${by + bh - r} Z"/>`;
|
|
1986
|
+
if (isTop) {
|
|
1987
|
+
// Top segment (or single segment) — round top corners only.
|
|
1988
|
+
// Bottom edge sits on the next segment OR the axis baseline; either
|
|
1989
|
+
// way it should be flush, so we never round the bottom corners.
|
|
1990
|
+
svg += `<path${attrs} d="${topRoundedBarPath(bx, by, barInner, bh, r)}"/>`;
|
|
1960
1991
|
} else {
|
|
1961
|
-
// Middle
|
|
1992
|
+
// Middle and bottom segments — flush on both ends. Bottom sits on
|
|
1993
|
+
// the axis baseline; middle sits between segments.
|
|
1962
1994
|
svg += `<rect${attrs} x="${bx}" y="${by}" width="${barInner}" height="${bh}"/>`;
|
|
1963
1995
|
}
|
|
1964
1996
|
}
|
|
@@ -1999,7 +2031,7 @@ class AdiaChart extends AdiaElement {
|
|
|
1999
2031
|
const barH = maxVal ? (v / maxVal) * plotH : 0;
|
|
2000
2032
|
const bx = pad.left + groupW * i + groupPad + (subBarW + barGap) * k;
|
|
2001
2033
|
const by = pad.top + plotH - barH;
|
|
2002
|
-
svg += `<
|
|
2034
|
+
svg += `<path${this.#seriesFill(k % 10, keys[k])}${tip({ label: labels[i], value: v, series: keys[k] })} d="${topRoundedBarPath(bx, by, subBarW, barH, this.#resolveRadius())}"/>`;
|
|
2003
2035
|
|
|
2004
2036
|
if (!this.hideValues) {
|
|
2005
2037
|
svg += `<text data-value x="${bx + subBarW / 2}" y="${by - 4}" text-anchor="middle" font-size="${dims.valueSize}">${this.#fmtValue(v)}</text>`;
|
|
@@ -48,77 +48,53 @@
|
|
|
48
48
|
align-items: flex-start;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
/* Rows
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
/* Rows are <badge-ui> chips. We override badge-ui's default tokens
|
|
52
|
+
to give them a quieter, click-to-toggle appearance — transparent
|
|
53
|
+
bg by default (so they read as inline text not chips), highlighted
|
|
54
|
+
on hover when interactive. The pill chrome (radius, padding,
|
|
55
|
+
font-size, line-height) comes from badge-ui itself. */
|
|
56
|
+
badge-ui[data-row] {
|
|
57
|
+
--badge-bg: transparent;
|
|
58
|
+
--badge-fg: var(--chart-legend-fg);
|
|
59
|
+
--badge-radius: var(--chart-legend-row-radius);
|
|
60
|
+
--badge-px: var(--a-space-1);
|
|
61
|
+
--badge-py: var(--chart-legend-py);
|
|
62
|
+
--badge-gap: var(--chart-legend-row-gap);
|
|
63
|
+
--badge-font-size: var(--chart-legend-font-size);
|
|
60
64
|
cursor: pointer;
|
|
61
|
-
color: inherit;
|
|
62
|
-
line-height: 1.2;
|
|
63
65
|
transition: background var(--a-duration-fast) var(--a-easing),
|
|
64
66
|
color var(--a-duration-fast) var(--a-easing);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/* Static rows — no interaction */
|
|
68
|
-
:scope[static] [data-row] {
|
|
70
|
+
:scope[static] badge-ui[data-row] {
|
|
69
71
|
cursor: default;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
badge-ui[data-row][role="button"]:hover {
|
|
75
|
+
--badge-bg: var(--chart-legend-row-hover);
|
|
76
|
+
--badge-fg: var(--chart-legend-row-fg-hover);
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
badge-ui[data-row][role="button"]:focus-visible {
|
|
78
80
|
outline: none;
|
|
79
81
|
box-shadow: var(--chart-legend-focus-ring);
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
/* Inactive (toggled-off) rows — dim the swatch + fade label
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
/* Inactive (toggled-off) rows — dim the swatch + fade label. Drives
|
|
85
|
+
badge-ui's --badge-fg so the label fades alongside the swatch. */
|
|
86
|
+
badge-ui[data-row]:not([data-active]) {
|
|
87
|
+
--badge-fg: var(--chart-legend-fg-inactive);
|
|
85
88
|
}
|
|
86
|
-
[data-row]:not([data-active])
|
|
89
|
+
badge-ui[data-row]:not([data-active]) swatch-ui {
|
|
87
90
|
opacity: 0.4;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
/* Swatch
|
|
91
|
-
|
|
92
|
-
--
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
line-height: 0;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
:scope[shape="dot"] [data-swatch] {
|
|
101
|
-
width: var(--chart-legend-swatch-size);
|
|
102
|
-
height: var(--chart-legend-swatch-size);
|
|
103
|
-
border-radius: 50%;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
:scope[shape="square"] [data-swatch] {
|
|
107
|
-
width: var(--chart-legend-swatch-size);
|
|
108
|
-
height: var(--chart-legend-swatch-size);
|
|
109
|
-
border-radius: var(--a-radius-sm);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
:scope[shape="line"] [data-swatch] {
|
|
113
|
-
width: calc(var(--chart-legend-swatch-size) * 1.75);
|
|
114
|
-
height: var(--chart-legend-line-w);
|
|
115
|
-
border-radius: var(--chart-legend-line-w);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
:scope[shape="dashed"] [data-swatch] {
|
|
119
|
-
width: calc(var(--chart-legend-swatch-size) * 1.75);
|
|
120
|
-
height: 0;
|
|
121
|
-
background: transparent;
|
|
122
|
-
border-top: var(--chart-legend-line-w) dashed var(--swatch-color, var(--chart-0));
|
|
123
|
-
}
|
|
93
|
+
/* Swatch is composed via <swatch-ui shape="…">; per-shape styling
|
|
94
|
+
lives in swatch.css. The legend just hands its [shape] attr through
|
|
95
|
+
and writes --swatch-color inline. Pre-2026-05-01 this file owned a
|
|
96
|
+
`<span data-swatch>` element + dot/square/line/dashed CSS rules,
|
|
97
|
+
which collided with the docs-shell's `[data-swatch]` token-demo
|
|
98
|
+
rule (cascade leak). Composing the primitive moves the styling to
|
|
99
|
+
one place. */
|
|
124
100
|
}
|
|
@@ -14,14 +14,17 @@
|
|
|
14
14
|
* override when both are provided.)
|
|
15
15
|
* items — JSON array of {key, label, slot?, pct?} legend items.
|
|
16
16
|
* Takes precedence over [for] if both are provided.
|
|
17
|
-
* shape — dot | square | line | dashed. Default: dot.
|
|
18
|
-
*
|
|
19
|
-
* '
|
|
17
|
+
* shape — dot | square | line | dashed. Default: dot. The swatch is
|
|
18
|
+
* a `[data-swatch]` span inside each badge-ui row, styled by
|
|
19
|
+
* the legend's [shape] attr. Custom span (rather than badge-
|
|
20
|
+
* ui's [icon] slot) is required because line/dashed shapes
|
|
21
|
+
* aren't representable as icon glyphs.
|
|
20
22
|
* position — top | bottom | left | right. Layout hint; actual placement
|
|
21
23
|
* is where the element is placed in the DOM. Drives
|
|
22
24
|
* flex-direction.
|
|
23
|
-
* static — when set, rows render as non-interactive
|
|
24
|
-
* is interactive
|
|
25
|
+
* static — when set, rows render as non-interactive badges (no
|
|
26
|
+
* role/tabindex/handlers). Default is interactive
|
|
27
|
+
* `role="button"` badges that toggle on click + Enter/Space.
|
|
25
28
|
* on-toggle — hide | opacity. Default hide. Escape hatch per OD-CHART-09.
|
|
26
29
|
* Reported via the `toggle` event detail; chart-ui reads it
|
|
27
30
|
* when wired via [for].
|
|
@@ -127,45 +130,59 @@ class AdiaChartLegend extends AdiaElement {
|
|
|
127
130
|
const slot = item.slot != null ? item.slot : 0;
|
|
128
131
|
const active = !this.#hiddenKeys.has(key);
|
|
129
132
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
/* Each row is a <badge-ui> chip — the canonical AdiaUI primitive for
|
|
134
|
+
legend pills (per badge.yaml's documented "chart-legend" example).
|
|
135
|
+
badge-ui carries the pill chrome (radius, padding, font-size, hover
|
|
136
|
+
affordance), the legend layers shape-specific swatch CSS + the
|
|
137
|
+
click-to-toggle behavior on top. Interactive rows get
|
|
138
|
+
role="button" + tabindex="0" + Enter/Space handlers so they're
|
|
139
|
+
keyboard-operable without a wrapping <button>. */
|
|
140
|
+
const row = document.createElement('badge-ui');
|
|
134
141
|
row.setAttribute('data-row', '');
|
|
135
|
-
row.setAttribute('role', 'listitem');
|
|
142
|
+
row.setAttribute('role', this.static ? 'listitem' : 'button');
|
|
143
|
+
row.setAttribute('text', label);
|
|
136
144
|
if (key) row.setAttribute('data-key', key);
|
|
137
145
|
if (active) row.setAttribute('data-active', '');
|
|
138
|
-
if (!this.static)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
if (!this.static) {
|
|
147
|
+
row.setAttribute('tabindex', '0');
|
|
148
|
+
row.setAttribute('aria-pressed', active ? 'true' : 'false');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Swatch — composed via `<swatch-ui>` with the legend's [shape] attr
|
|
152
|
+
passed through. Pre-2026-05-01 this was a hand-rolled `<span
|
|
153
|
+
data-swatch>` plus per-shape CSS in this file; that drift caused
|
|
154
|
+
a cascade-leak collision with the docs-shell's `[data-swatch]`
|
|
155
|
+
demo rule (token-colors page). Compose the primitive instead and
|
|
156
|
+
the swatch's dot / square / line / dashed variants live in one
|
|
157
|
+
place. Color resolves via --color-{key} with a --a-data-{slot}
|
|
158
|
+
fallback; the legend can render standalone (without a chart-ui
|
|
159
|
+
ancestor) so the fallback must reference the global data palette
|
|
160
|
+
directly, not chart-ui's scoped --chart-{N} alias. */
|
|
161
|
+
const swatchShape = this.shape || 'dot';
|
|
162
|
+
const swatch = document.createElement('swatch-ui');
|
|
163
|
+
swatch.setAttribute('shape', swatchShape);
|
|
164
|
+
swatch.setAttribute('size', 'sm');
|
|
149
165
|
const swatchColor = key
|
|
150
|
-
? `var(--color-${key}, var(--
|
|
151
|
-
: `var(--
|
|
166
|
+
? `var(--color-${key}, var(--a-data-${slot}))`
|
|
167
|
+
: `var(--a-data-${slot})`;
|
|
152
168
|
swatch.style.setProperty('--swatch-color', swatchColor);
|
|
153
169
|
row.appendChild(swatch);
|
|
154
170
|
|
|
155
|
-
const labelEl = document.createElement('span');
|
|
156
|
-
labelEl.setAttribute('data-label', '');
|
|
157
|
-
labelEl.textContent = label;
|
|
158
|
-
row.appendChild(labelEl);
|
|
159
|
-
|
|
160
171
|
if (!this.static) {
|
|
161
|
-
row.addEventListener('click', (e) => this.#
|
|
172
|
+
row.addEventListener('click', (e) => this.#onRowToggle(e, key));
|
|
173
|
+
row.addEventListener('keydown', (e) => {
|
|
174
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
175
|
+
e.preventDefault();
|
|
176
|
+
this.#onRowToggle(e, key);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
162
179
|
}
|
|
163
180
|
|
|
164
181
|
this.appendChild(row);
|
|
165
182
|
}
|
|
166
183
|
}
|
|
167
184
|
|
|
168
|
-
#
|
|
185
|
+
#onRowToggle(e, key) {
|
|
169
186
|
if (!key) return;
|
|
170
187
|
const currentlyActive = !this.#hiddenKeys.has(key);
|
|
171
188
|
const newActive = !currentlyActive;
|
|
@@ -175,6 +192,7 @@ class AdiaChartLegend extends AdiaElement {
|
|
|
175
192
|
const row = e.currentTarget;
|
|
176
193
|
if (newActive) row.setAttribute('data-active', '');
|
|
177
194
|
else row.removeAttribute('data-active');
|
|
195
|
+
row.setAttribute('aria-pressed', newActive ? 'true' : 'false');
|
|
178
196
|
|
|
179
197
|
this.dispatchEvent(new CustomEvent('toggle', {
|
|
180
198
|
bubbles: true,
|
package/components/code/code.css
CHANGED
|
@@ -156,6 +156,47 @@
|
|
|
156
156
|
color: inherit;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/* ── Diff line states (static path) ──
|
|
160
|
+
Active when the block uses `language="diff"` (auto-parse +/- prefix)
|
|
161
|
+
or carries `data-line-states="..."` (explicit per-line). Each line is
|
|
162
|
+
a `[data-line-state]` row whose bg picks up the state token. The
|
|
163
|
+
CodeMirror path doesn't use these markers; line decorations there go
|
|
164
|
+
through CM extensions instead. */
|
|
165
|
+
> pre > code[data-line-state-mode] {
|
|
166
|
+
display: block;
|
|
167
|
+
}
|
|
168
|
+
> pre > code[data-line-state-mode] > [data-line-state] {
|
|
169
|
+
display: grid;
|
|
170
|
+
grid-template-columns: 1fr;
|
|
171
|
+
/* Bleed the row tint to the pre's padding edges so it reads as a
|
|
172
|
+
full-width line, not a gap-margined pill. */
|
|
173
|
+
margin-inline: calc(-1 * var(--code-px));
|
|
174
|
+
padding-inline: var(--code-px);
|
|
175
|
+
}
|
|
176
|
+
> pre > code[data-line-numbers] > [data-line-state] {
|
|
177
|
+
grid-template-columns: auto 1fr;
|
|
178
|
+
column-gap: var(--a-space-3);
|
|
179
|
+
}
|
|
180
|
+
> pre > code[data-line-state-mode] [data-line-num] {
|
|
181
|
+
color: var(--a-fg-subtle);
|
|
182
|
+
text-align: end;
|
|
183
|
+
user-select: none;
|
|
184
|
+
min-width: 1.5ch;
|
|
185
|
+
}
|
|
186
|
+
> pre > code[data-line-state-mode] [data-line-body] {
|
|
187
|
+
white-space: pre;
|
|
188
|
+
}
|
|
189
|
+
/* Empty lines still need height so the diff column counts line up. */
|
|
190
|
+
> pre > code[data-line-state-mode] [data-line-body]:empty::before {
|
|
191
|
+
content: " ";
|
|
192
|
+
}
|
|
193
|
+
> pre > code [data-line-state="added"] {
|
|
194
|
+
background: var(--a-success-muted);
|
|
195
|
+
}
|
|
196
|
+
> pre > code [data-line-state="removed"] {
|
|
197
|
+
background: var(--a-danger-muted);
|
|
198
|
+
}
|
|
199
|
+
|
|
159
200
|
/* Footer — optional chrome band below the code block
|
|
160
201
|
(line counts, byte size, language family, etc.) */
|
|
161
202
|
> footer {
|
package/components/code/code.js
CHANGED
|
@@ -90,9 +90,12 @@ class AdiaCode extends AdiaElement {
|
|
|
90
90
|
// Mount CodeMirror when the language is supported OR the element is
|
|
91
91
|
// editable (editable plain-text is still useful). Inline instances
|
|
92
92
|
// stay on the static <code> path. Mount failures leave the static
|
|
93
|
-
// fallback in place — it's already visible.
|
|
93
|
+
// fallback in place — it's already visible. Diff line-state mode
|
|
94
|
+
// (auto via `language="diff"` or explicit `data-line-states`) also
|
|
95
|
+
// stays on the static path — bg tinting lives on the wrapper spans.
|
|
94
96
|
const lang = canonicalLanguage(this.language);
|
|
95
|
-
const
|
|
97
|
+
const useLineState = this.hasAttribute('data-line-states') || lang === 'diff';
|
|
98
|
+
const shouldMount = !this.inline && !useLineState && (SUPPORTED_LANGUAGES.has(lang) || this.editable);
|
|
96
99
|
if (shouldMount) {
|
|
97
100
|
this.#mountEditor();
|
|
98
101
|
}
|
|
@@ -145,7 +148,45 @@ class AdiaCode extends AdiaElement {
|
|
|
145
148
|
// Pre > Code — direct semantic elements, no slot attr
|
|
146
149
|
const pre = document.createElement('pre');
|
|
147
150
|
const code = document.createElement('code');
|
|
148
|
-
|
|
151
|
+
|
|
152
|
+
// Diff line-state mode — wrap each line so it can carry a state-driven
|
|
153
|
+
// bg tint and (when [line-numbers]) a leading line-number gutter.
|
|
154
|
+
// Triggered by `language="diff"` (auto-parses +/- prefix) or by an
|
|
155
|
+
// explicit `data-line-states` CSV that maps positionally onto lines.
|
|
156
|
+
const lang = canonicalLanguage(this.language);
|
|
157
|
+
const explicit = this.getAttribute('data-line-states');
|
|
158
|
+
const useLineState = explicit != null || lang === 'diff';
|
|
159
|
+
if (useLineState) {
|
|
160
|
+
code.setAttribute('data-line-state-mode', '');
|
|
161
|
+
if (this.lineNumbers) code.setAttribute('data-line-numbers', '');
|
|
162
|
+
const states = explicit?.split(',').map((s) => s.trim()) ?? null;
|
|
163
|
+
const lines = raw.split('\n');
|
|
164
|
+
lines.forEach((line, i) => {
|
|
165
|
+
let state = 'unchanged';
|
|
166
|
+
if (states && states[i]) {
|
|
167
|
+
state = states[i];
|
|
168
|
+
} else if (lang === 'diff') {
|
|
169
|
+
const head = line.charAt(0);
|
|
170
|
+
if (head === '+') state = 'added';
|
|
171
|
+
else if (head === '-') state = 'removed';
|
|
172
|
+
}
|
|
173
|
+
const row = document.createElement('span');
|
|
174
|
+
row.setAttribute('data-line-state', state);
|
|
175
|
+
if (this.lineNumbers) {
|
|
176
|
+
const num = document.createElement('span');
|
|
177
|
+
num.setAttribute('data-line-num', '');
|
|
178
|
+
num.textContent = String(i + 1);
|
|
179
|
+
row.appendChild(num);
|
|
180
|
+
}
|
|
181
|
+
const body = document.createElement('span');
|
|
182
|
+
body.setAttribute('data-line-body', '');
|
|
183
|
+
body.textContent = line;
|
|
184
|
+
row.appendChild(body);
|
|
185
|
+
code.appendChild(row);
|
|
186
|
+
});
|
|
187
|
+
} else {
|
|
188
|
+
code.textContent = raw;
|
|
189
|
+
}
|
|
149
190
|
pre.appendChild(code);
|
|
150
191
|
|
|
151
192
|
this.appendChild(pre);
|