@adia-ai/web-components 0.0.13 → 0.0.14
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/README.md +2 -2
- package/components/card/card.css +29 -0
- package/components/chart/chart.a2ui.json +43 -6
- package/components/chart/chart.css +224 -0
- package/components/chart/chart.js +1049 -27
- package/components/chart/chart.yaml +62 -6
- package/components/chart-legend/chart-legend.a2ui.json +139 -0
- package/components/chart-legend/chart-legend.css +124 -0
- package/components/chart-legend/chart-legend.js +185 -0
- package/components/chart-legend/chart-legend.yaml +133 -0
- package/components/code/code-editor.js +58 -0
- package/components/code/code.a2ui.json +59 -0
- package/components/code/code.css +78 -2
- package/components/code/code.js +146 -8
- package/components/code/code.yaml +42 -0
- package/components/heatmap/heatmap.js +62 -13
- package/components/index.js +1 -0
- package/components/stat/stat.a2ui.json +3 -0
- package/components/stat/stat.css +32 -0
- package/components/stat/stat.yaml +6 -0
- package/components/tooltip/tooltip.a2ui.json +29 -4
- package/components/tooltip/tooltip.css +111 -0
- package/components/tooltip/tooltip.js +200 -12
- package/components/tooltip/tooltip.yaml +38 -4
- package/core/icons.js +35 -1
- package/core/provider.js +1 -1
- package/package.json +1 -1
- package/styles/colors/semantics.css +3 -2
- package/styles/components.css +1 -0
|
@@ -6,11 +6,13 @@ tag: chart-ui
|
|
|
6
6
|
component: Chart
|
|
7
7
|
category: agent
|
|
8
8
|
version: 1
|
|
9
|
-
description:
|
|
10
|
-
|
|
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.
|
|
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:
|
|
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:
|
|
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
|