@adia-ai/web-components 0.0.13 → 0.0.15

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.
@@ -6,11 +6,13 @@ tag: chart-ui
6
6
  component: Chart
7
7
  category: agent
8
8
  version: 1
9
- description: Declarative SVG chart supporting bar, line, pie, donut, radar, sparkline, stacked-bar,
10
- grouped-bar, and multi-line types.
9
+ description: >-
10
+ Declarative SVG chart supporting 18 types: bar, line, pie, donut, radar,
11
+ sparkline, segments, area, scatter, radial-bar, gauge, stacked-bar,
12
+ grouped-bar, multi-line, funnel, treemap, sankey, and composed.
11
13
  props:
12
14
  type:
13
- description: Chart type. Only 'bar' is built-in.
15
+ description: Chart type. All 18 enum values have dedicated render paths in chart.js.
14
16
  type: string
15
17
  default: bar
16
18
  enum:
@@ -20,11 +22,23 @@ props:
20
22
  - donut
21
23
  - radar
22
24
  - sparkline
25
+ - segments
26
+ - area
27
+ - scatter
28
+ - radial-bar
29
+ - gauge
23
30
  - stacked-bar
24
31
  - grouped-bar
25
32
  - multi-line
33
+ - funnel
34
+ - treemap
35
+ - sankey
36
+ - composed
26
37
  aspect:
27
- description: Aspect ratio
38
+ description: >-
39
+ DEPRECATED (OD-CHART-02). Parents should size the chart directly
40
+ (width/height on the enclosing card or container). Still honored for
41
+ back-compat; emits a one-shot console.warn per instance when set.
28
42
  type: string
29
43
  default: std
30
44
  enum:
@@ -32,6 +46,22 @@ props:
32
46
  - wide
33
47
  - square
34
48
  - tall
49
+ deprecated: true
50
+ format:
51
+ description: >-
52
+ Number-format mode applied to axis labels + value overlays + donut
53
+ total + gauge value + treemap value + funnel value + internal
54
+ tooltip. `abbr` is the legacy 1.2K / 3M format; `decimal` fixes 2
55
+ decimals; `currency` prefixes via `--chart-currency-prefix` token
56
+ (default "$"); `percent` multiplies × 100 and adds a % suffix.
57
+ type: string
58
+ default: abbr
59
+ enum:
60
+ - abbr
61
+ - decimal
62
+ - currency
63
+ - percent
64
+ reflect: true
35
65
  color:
36
66
  description: Color scheme
37
67
  type: string
@@ -74,10 +104,16 @@ props:
74
104
  type: number
75
105
  default: 0.4
76
106
  heading:
77
- description: Chart heading text rendered above the canvas.
107
+ description: >-
108
+ DEPRECATED (OD-CHART-02). Place chart titles in an enclosing
109
+ card-ui's `<header><span slot="heading">...</span></header>`
110
+ instead. Still honored for back-compat; emits a one-shot
111
+ console.warn per instance when set. Used as the chart's
112
+ `aria-label` when no explicit label is provided.
78
113
  type: string
79
114
  default: ""
80
115
  reflect: true
116
+ deprecated: true
81
117
  x:
82
118
  description: Data key for x-axis (category) values
83
119
  type: string
@@ -86,15 +122,35 @@ props:
86
122
  description: Y-axis key(s), comma-separated for multi-series
87
123
  type: string
88
124
  default: ""
89
- events: {}
125
+ events:
126
+ chart-hover:
127
+ description: >-
128
+ Fires when the pointer enters a datum (bar / dot / slice). Detail includes
129
+ {label, value, pct, series, slot, pointerX, pointerY}. Only re-fires when
130
+ the hovered datum changes — moving inside the same datum is quiet.
131
+ chart-leave:
132
+ description: Fires when the pointer leaves the plot area or a previously-hovered datum with no new one entering.
133
+ chart-select:
134
+ description: Fires on click of a datum with the same detail shape as chart-hover.
90
135
  slots:
91
136
  canvas:
92
137
  description: Chart rendering area
93
138
  title:
94
139
  description: Chart title element
140
+ empty:
141
+ description: >-
142
+ Shown in place of the SVG when `.data` is empty or missing. Typically an
143
+ `<empty-state-ui>` with icon + heading + description + optional action.
144
+ Visibility is CSS-toggled via `[data-has-data]` on the host — authors
145
+ don't need to manage it imperatively.
95
146
  states:
96
147
  - name: idle
97
148
  description: Default, ready for interaction.
149
+ - name: empty
150
+ description: >-
151
+ No data — the empty slot (if provided) is rendered; otherwise the chart
152
+ area renders nothing. Host carries [data-has-data] only when data
153
+ is present.
98
154
  traits: []
99
155
  tokens: {}
100
156
  a2ui:
@@ -0,0 +1,139 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/ChartLegend.json",
4
+ "title": "ChartLegend",
5
+ "description": "Standalone legend primitive for the AdiaUI chart family. Renders a row of badge-ui chips (swatch + label) that are keyboard-focusable and click-toggleable. Composes with chart-ui via [for] id-ref (auto-mirror) or via explicit [items] JSON.",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "items": {
17
+ "description": "JSON array of {key, label, slot?, pct?} legend items. Takes precedence over [for] when both are provided.",
18
+ "type": "string",
19
+ "default": ""
20
+ },
21
+ "component": {
22
+ "const": "ChartLegend"
23
+ },
24
+ "for": {
25
+ "description": "id-ref of a chart-ui / heatmap-ui to mirror series from.",
26
+ "type": "string",
27
+ "default": ""
28
+ },
29
+ "onToggle": {
30
+ "description": "Series-toggle mode emitted via legend-toggle event. `hide` removes the series from the render; `opacity` fades it. Wired via [for] on chart-ui.",
31
+ "type": "string",
32
+ "enum": [
33
+ "hide",
34
+ "opacity"
35
+ ],
36
+ "default": "hide"
37
+ },
38
+ "position": {
39
+ "description": "Layout hint — drives flex-direction. Actual placement follows DOM order.",
40
+ "type": "string",
41
+ "enum": [
42
+ "top",
43
+ "bottom",
44
+ "left",
45
+ "right"
46
+ ],
47
+ "default": "bottom"
48
+ },
49
+ "shape": {
50
+ "description": "Swatch shape. Maps to badge-ui's icon for dot/square variants.",
51
+ "type": "string",
52
+ "enum": [
53
+ "dot",
54
+ "square",
55
+ "line",
56
+ "dashed"
57
+ ],
58
+ "default": "dot"
59
+ },
60
+ "static": {
61
+ "description": "When set, rows are non-interactive <span>s (no click, no toggle).",
62
+ "type": "boolean",
63
+ "default": false
64
+ }
65
+ },
66
+ "required": [
67
+ "component"
68
+ ],
69
+ "unevaluatedProperties": false,
70
+ "x-adiaui": {
71
+ "anti_patterns": [],
72
+ "category": "agent",
73
+ "events": {
74
+ "legend-toggle": {
75
+ "description": "Fires on row click (non-static). Detail: {key, active, mode}. `active` is the new state (true=visible). Consumers (or chart-ui via [for]) wire this to series visibility."
76
+ }
77
+ },
78
+ "examples": [
79
+ {
80
+ "description": "Standalone legend with explicit items array.",
81
+ "a2ui": "[\n {\n \"id\": \"legend\", \"component\": \"ChartLegend\",\n \"shape\": \"dot\",\n \"items\": \"[{\\\"key\\\":\\\"revenue\\\",\\\"label\\\":\\\"Revenue\\\",\\\"slot\\\":0},{\\\"key\\\":\\\"users\\\",\\\"label\\\":\\\"Users\\\",\\\"slot\\\":1}]\"\n }\n]",
82
+ "name": "standalone-with-items"
83
+ },
84
+ {
85
+ "description": "Legend mirrors a named chart's series data.",
86
+ "a2ui": "[\n {\n \"id\": \"root\", \"component\": \"Column\",\n \"children\": [\"chart\", \"legend\"]\n },\n {\n \"id\": \"chart\", \"component\": \"Chart\",\n \"type\": \"multi-line\", \"x\": \"month\", \"y\": \"revenue,users\"\n },\n {\n \"id\": \"legend\", \"component\": \"ChartLegend\",\n \"for\": \"chart\",\n \"shape\": \"line\",\n \"position\": \"bottom\"\n }\n]",
87
+ "name": "mirror-chart-by-id"
88
+ }
89
+ ],
90
+ "keywords": [
91
+ "legend",
92
+ "chart-legend",
93
+ "series",
94
+ "key",
95
+ "swatch",
96
+ "chart",
97
+ "graph",
98
+ "visualization"
99
+ ],
100
+ "name": "AdiaChartLegend",
101
+ "related": [
102
+ "chart",
103
+ "badge",
104
+ "heatmap"
105
+ ],
106
+ "slots": {
107
+ "default": {
108
+ "description": "Normally empty — the primitive renders its own rows. Authors who want fully custom markup can provide their own children with [data-row] and [data-key] attributes; the primitive won't wipe them if [items] is empty and [for] is unset."
109
+ }
110
+ },
111
+ "states": [
112
+ {
113
+ "description": "Default, ready for interaction.",
114
+ "name": "idle"
115
+ },
116
+ {
117
+ "description": "Rows are <button>s. Default unless [static].",
118
+ "name": "interactive"
119
+ },
120
+ {
121
+ "description": "Rows are <span>s. No click handler, no toggle affordance.",
122
+ "name": "static"
123
+ }
124
+ ],
125
+ "synonyms": {
126
+ "chart": [
127
+ "chart-legend",
128
+ "legend"
129
+ ],
130
+ "legend": [
131
+ "chart-legend"
132
+ ]
133
+ },
134
+ "tag": "chart-legend-ui",
135
+ "tokens": {},
136
+ "traits": [],
137
+ "version": 1
138
+ }
139
+ }
@@ -0,0 +1,124 @@
1
+ @scope (chart-legend-ui) {
2
+ :where(:scope) {
3
+ /* ── Layout ── */
4
+ --chart-legend-gap: var(--a-space-2);
5
+ --chart-legend-row-gap: var(--a-space-1);
6
+ --chart-legend-py: var(--a-space-1);
7
+ --chart-legend-px: 0;
8
+
9
+ /* ── Swatch ── */
10
+ --chart-legend-swatch-size: var(--a-space-2-5);
11
+ --chart-legend-line-w: 2px;
12
+
13
+ /* ── Typography ── */
14
+ --chart-legend-font-size: var(--a-ui-tiny);
15
+ --chart-legend-fg: var(--a-fg-subtle);
16
+ --chart-legend-fg-inactive: var(--a-fg-muted);
17
+
18
+ /* ── Interaction ── */
19
+ --chart-legend-row-radius: var(--a-radius-sm);
20
+ --chart-legend-row-hover: var(--a-canvas-2);
21
+ --chart-legend-row-fg-hover: var(--a-fg);
22
+
23
+ /* ── Focus ── */
24
+ --chart-legend-focus-ring: var(--a-focus-ring);
25
+ }
26
+
27
+ :scope {
28
+ box-sizing: border-box;
29
+ display: flex;
30
+ flex-wrap: wrap;
31
+ gap: var(--chart-legend-gap);
32
+ padding: var(--chart-legend-py) var(--chart-legend-px);
33
+ font-size: var(--chart-legend-font-size);
34
+ color: var(--chart-legend-fg);
35
+ }
36
+
37
+ /* Position variants — drive flex-direction. DOM placement still wins for
38
+ actual layout; these tune the internal row direction. */
39
+ :scope[position="top"],
40
+ :scope[position="bottom"] {
41
+ flex-direction: row;
42
+ justify-content: center;
43
+ }
44
+
45
+ :scope[position="left"],
46
+ :scope[position="right"] {
47
+ flex-direction: column;
48
+ align-items: flex-start;
49
+ }
50
+
51
+ /* Rows — both <button> (interactive) and <span> (static) variants */
52
+ [data-row] {
53
+ all: unset;
54
+ box-sizing: border-box;
55
+ display: inline-flex;
56
+ align-items: center;
57
+ gap: var(--chart-legend-row-gap);
58
+ padding: var(--chart-legend-py) var(--a-space-1);
59
+ border-radius: var(--chart-legend-row-radius);
60
+ cursor: pointer;
61
+ color: inherit;
62
+ line-height: 1.2;
63
+ transition: background var(--a-duration-fast) var(--a-easing),
64
+ color var(--a-duration-fast) var(--a-easing);
65
+ }
66
+
67
+ /* Static rows — no interaction */
68
+ :scope[static] [data-row] {
69
+ cursor: default;
70
+ }
71
+
72
+ button[data-row]:hover {
73
+ background: var(--chart-legend-row-hover);
74
+ color: var(--chart-legend-row-fg-hover);
75
+ }
76
+
77
+ button[data-row]:focus-visible {
78
+ outline: none;
79
+ box-shadow: var(--chart-legend-focus-ring);
80
+ }
81
+
82
+ /* Inactive (toggled-off) rows — dim the swatch + fade label */
83
+ [data-row]:not([data-active]) {
84
+ color: var(--chart-legend-fg-inactive);
85
+ }
86
+ [data-row]:not([data-active]) [data-swatch] {
87
+ opacity: 0.4;
88
+ }
89
+
90
+ /* Swatch — dedicated span per row, styled by the legend's [shape] attr.
91
+ --swatch-color is set inline per-row from --color-{key} with a
92
+ --chart-{slot} fallback; overrides cascade through atomically. */
93
+ [data-swatch] {
94
+ display: inline-block;
95
+ flex-shrink: 0;
96
+ background: var(--swatch-color, var(--chart-0));
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
+ }
124
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * <chart-legend-ui shape="dot" position="bottom" items='[{"key":"revenue","label":"Revenue"},{"key":"users","label":"Users"}]'></chart-legend-ui>
3
+ *
4
+ * Standalone legend primitive for the AdiaUI chart family. Renders a row
5
+ * of badge-ui chips (swatch + label) that are keyboard-focusable and
6
+ * click-toggleable — clicking emits a `legend-toggle` event that consumers
7
+ * (or chart-ui via [for]) wire to series visibility.
8
+ *
9
+ * Attributes:
10
+ * for — id-ref of a chart-ui / heatmap-ui to mirror. When set, the
11
+ * legend reads series data from that element's `.legendData`
12
+ * property and subscribes to its `legend-update` event.
13
+ * (Auto-mirror ships behind [for] — explicit items still
14
+ * override when both are provided.)
15
+ * items — JSON array of {key, label, slot?, pct?} legend items.
16
+ * Takes precedence over [for] if both are provided.
17
+ * shape — dot | square | line | dashed. Default: dot. Maps to
18
+ * badge-ui's icon slot (dot = phosphor 'dot', square =
19
+ * 'square-fill', line/dashed = custom CSS swatches).
20
+ * position — top | bottom | left | right. Layout hint; actual placement
21
+ * is where the element is placed in the DOM. Drives
22
+ * flex-direction.
23
+ * static — when set, rows render as non-interactive <span>s. Default
24
+ * is interactive <button>s that toggle.
25
+ * on-toggle — hide | opacity. Default hide. Escape hatch per OD-CHART-09.
26
+ * Reported via legend-toggle event detail; chart-ui reads it
27
+ * when wired via [for].
28
+ *
29
+ * Events:
30
+ * legend-toggle — detail: {key, active, mode}. Fires on row click when
31
+ * not [static]. `active` is the new state (true=visible).
32
+ */
33
+
34
+ import { AdiaElement } from '../../core/element.js';
35
+
36
+ class AdiaChartLegend extends AdiaElement {
37
+ static properties = {
38
+ for: { type: String, default: '', reflect: true },
39
+ items: { type: String, default: '', reflect: false },
40
+ shape: { type: String, default: 'dot', reflect: true },
41
+ position: { type: String, default: 'bottom', reflect: true },
42
+ static: { type: Boolean, default: false, reflect: true },
43
+ onToggle: { type: String, default: 'hide', reflect: true, attribute: 'on-toggle' },
44
+ };
45
+
46
+ static template = () => null;
47
+
48
+ #target = null;
49
+ #targetListener = null;
50
+ #hiddenKeys = new Set();
51
+
52
+ connected() {
53
+ this.setAttribute('role', 'list');
54
+ this.#resolveTarget();
55
+ this.#paint();
56
+ }
57
+
58
+ disconnected() {
59
+ this.#detachTarget();
60
+ }
61
+
62
+ render() {
63
+ this.#resolveTarget();
64
+ this.#paint();
65
+ }
66
+
67
+ /* ── Target resolution ──────────────────────────────────────────── */
68
+
69
+ #resolveTarget() {
70
+ this.#detachTarget();
71
+ if (!this.for) return;
72
+ const el = this.getRootNode()?.getElementById?.(this.for)
73
+ || document.getElementById(this.for);
74
+ if (!el) {
75
+ // Warn once per (element × id) pair
76
+ if (!AdiaChartLegend._warned) AdiaChartLegend._warned = new WeakSet();
77
+ const marker = { el: this, id: this.for };
78
+ if (!AdiaChartLegend._warned.has(this)) {
79
+ console.warn(`[chart-legend-ui] [for="${this.for}"] did not resolve to an element.`);
80
+ AdiaChartLegend._warned.add(this);
81
+ }
82
+ return;
83
+ }
84
+ this.#target = el;
85
+ this.#targetListener = () => this.#paint();
86
+ el.addEventListener('legend-update', this.#targetListener);
87
+ }
88
+
89
+ #detachTarget() {
90
+ if (this.#target && this.#targetListener) {
91
+ this.#target.removeEventListener('legend-update', this.#targetListener);
92
+ }
93
+ this.#target = null;
94
+ this.#targetListener = null;
95
+ }
96
+
97
+ /* ── Item source ────────────────────────────────────────────────── */
98
+
99
+ #resolveItems() {
100
+ /* Explicit items prop wins. */
101
+ if (this.items) {
102
+ try {
103
+ const parsed = JSON.parse(this.items);
104
+ if (Array.isArray(parsed)) return parsed;
105
+ } catch (_) { /* fall through */ }
106
+ }
107
+ /* Mirror from [for] target when present. */
108
+ if (this.#target && Array.isArray(this.#target.legendData)) {
109
+ return this.#target.legendData;
110
+ }
111
+ return [];
112
+ }
113
+
114
+ /* ── Render ─────────────────────────────────────────────────────── */
115
+
116
+ #paint() {
117
+ const items = this.#resolveItems();
118
+ this.innerHTML = '';
119
+ if (!items.length) return;
120
+
121
+ for (const item of items) {
122
+ const key = item.key ?? item.label ?? '';
123
+ const label = item.label ?? key;
124
+ const slot = item.slot != null ? item.slot : 0;
125
+ const active = !this.#hiddenKeys.has(key);
126
+
127
+ const row = this.static
128
+ ? document.createElement('span')
129
+ : document.createElement('button');
130
+
131
+ row.setAttribute('data-row', '');
132
+ row.setAttribute('role', 'listitem');
133
+ if (key) row.setAttribute('data-key', key);
134
+ if (active) row.setAttribute('data-active', '');
135
+ if (!this.static) row.setAttribute('type', 'button');
136
+
137
+ /* Swatch + label. A dedicated `[data-swatch]` span lets shape-specific
138
+ CSS handle dot/square/line/dashed variants without fighting badge-
139
+ ui's icon slot (which is shadow-DOM-less and doesn't expose an
140
+ `icon` part — previous badge-ui compose produced visual artifacts
141
+ for line/dashed shapes). Color resolves via --color-{key} with a
142
+ chart-{slot} fallback, mirroring chart-ui's per-series CSS var
143
+ injection so ancestor overrides cascade through atomically. */
144
+ const swatch = document.createElement('span');
145
+ swatch.setAttribute('data-swatch', '');
146
+ const swatchColor = key
147
+ ? `var(--color-${key}, var(--chart-${slot}))`
148
+ : `var(--chart-${slot})`;
149
+ swatch.style.setProperty('--swatch-color', swatchColor);
150
+ row.appendChild(swatch);
151
+
152
+ const labelEl = document.createElement('span');
153
+ labelEl.setAttribute('data-label', '');
154
+ labelEl.textContent = label;
155
+ row.appendChild(labelEl);
156
+
157
+ if (!this.static) {
158
+ row.addEventListener('click', (e) => this.#onRowClick(e, key));
159
+ }
160
+
161
+ this.appendChild(row);
162
+ }
163
+ }
164
+
165
+ #onRowClick(e, key) {
166
+ if (!key) return;
167
+ const currentlyActive = !this.#hiddenKeys.has(key);
168
+ const newActive = !currentlyActive;
169
+ if (newActive) this.#hiddenKeys.delete(key);
170
+ else this.#hiddenKeys.add(key);
171
+
172
+ const row = e.currentTarget;
173
+ if (newActive) row.setAttribute('data-active', '');
174
+ else row.removeAttribute('data-active');
175
+
176
+ this.dispatchEvent(new CustomEvent('legend-toggle', {
177
+ bubbles: true,
178
+ detail: { key, active: newActive, mode: this.onToggle || 'hide' },
179
+ }));
180
+ }
181
+ }
182
+
183
+ customElements.define('chart-legend-ui', AdiaChartLegend);
184
+
185
+ export { AdiaChartLegend };
@@ -0,0 +1,133 @@
1
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
2
+ name: AdiaChartLegend
3
+ tag: chart-legend-ui
4
+ component: ChartLegend
5
+ category: agent
6
+ version: 1
7
+ description: >-
8
+ Standalone legend primitive for the AdiaUI chart family. Renders a row of
9
+ badge-ui chips (swatch + label) that are keyboard-focusable and
10
+ click-toggleable. Composes with chart-ui via [for] id-ref (auto-mirror) or
11
+ via explicit [items] JSON.
12
+ props:
13
+ for:
14
+ description: id-ref of a chart-ui / heatmap-ui to mirror series from.
15
+ type: string
16
+ default: ''
17
+ reflect: true
18
+ items:
19
+ description: >-
20
+ JSON array of {key, label, slot?, pct?} legend items. Takes precedence
21
+ over [for] when both are provided.
22
+ type: string
23
+ default: ''
24
+ shape:
25
+ description: Swatch shape. Maps to badge-ui's icon for dot/square variants.
26
+ type: string
27
+ default: dot
28
+ enum:
29
+ - dot
30
+ - square
31
+ - line
32
+ - dashed
33
+ reflect: true
34
+ position:
35
+ description: Layout hint — drives flex-direction. Actual placement follows DOM order.
36
+ type: string
37
+ default: bottom
38
+ enum:
39
+ - top
40
+ - bottom
41
+ - left
42
+ - right
43
+ reflect: true
44
+ static:
45
+ description: When set, rows are non-interactive <span>s (no click, no toggle).
46
+ type: boolean
47
+ default: false
48
+ reflect: true
49
+ onToggle:
50
+ description: >-
51
+ Series-toggle mode emitted via legend-toggle event. `hide` removes the
52
+ series from the render; `opacity` fades it. Wired via [for] on chart-ui.
53
+ type: string
54
+ default: hide
55
+ enum:
56
+ - hide
57
+ - opacity
58
+ reflect: true
59
+ attribute: on-toggle
60
+ events:
61
+ legend-toggle:
62
+ description: >-
63
+ Fires on row click (non-static). Detail: {key, active, mode}. `active` is
64
+ the new state (true=visible). Consumers (or chart-ui via [for]) wire
65
+ this to series visibility.
66
+ slots:
67
+ default:
68
+ description: >-
69
+ Normally empty — the primitive renders its own rows. Authors who want
70
+ fully custom markup can provide their own children with [data-row] and
71
+ [data-key] attributes; the primitive won't wipe them if [items] is empty
72
+ and [for] is unset.
73
+ states:
74
+ - name: idle
75
+ description: Default, ready for interaction.
76
+ - name: interactive
77
+ description: Rows are <button>s. Default unless [static].
78
+ - name: static
79
+ description: Rows are <span>s. No click handler, no toggle affordance.
80
+ traits: []
81
+ tokens: {}
82
+ a2ui:
83
+ rules: []
84
+ anti_patterns: []
85
+ examples:
86
+ - name: standalone-with-items
87
+ description: Standalone legend with explicit items array.
88
+ a2ui: >-
89
+ [
90
+ {
91
+ "id": "legend", "component": "ChartLegend",
92
+ "shape": "dot",
93
+ "items": "[{\"key\":\"revenue\",\"label\":\"Revenue\",\"slot\":0},{\"key\":\"users\",\"label\":\"Users\",\"slot\":1}]"
94
+ }
95
+ ]
96
+ - name: mirror-chart-by-id
97
+ description: Legend mirrors a named chart's series data.
98
+ a2ui: >-
99
+ [
100
+ {
101
+ "id": "root", "component": "Column",
102
+ "children": ["chart", "legend"]
103
+ },
104
+ {
105
+ "id": "chart", "component": "Chart",
106
+ "type": "multi-line", "x": "month", "y": "revenue,users"
107
+ },
108
+ {
109
+ "id": "legend", "component": "ChartLegend",
110
+ "for": "chart",
111
+ "shape": "line",
112
+ "position": "bottom"
113
+ }
114
+ ]
115
+ keywords:
116
+ - legend
117
+ - chart-legend
118
+ - series
119
+ - key
120
+ - swatch
121
+ - chart
122
+ - graph
123
+ - visualization
124
+ synonyms:
125
+ chart:
126
+ - chart-legend
127
+ - legend
128
+ legend:
129
+ - chart-legend
130
+ related:
131
+ - chart
132
+ - badge
133
+ - heatmap