@aiaiai-pt/design-system 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/DateRangePicker.svelte.d.ts +1 -1
- package/components/EChart.svelte +123 -158
- package/components/EChart.svelte.d.ts +45 -24
- package/components/NotificationPrefs.svelte.d.ts +12 -9
- package/components/ResultsChart.svelte +62 -32
- package/components/ResultsChart.svelte.d.ts +22 -15
- package/components/SiteHeader.svelte.d.ts +40 -5
- package/components/index.d.ts +2 -0
- package/components/map-utils.d.ts +17 -0
- package/components/renderer/EChartWidget.svelte +47 -22
- package/components/renderer/EChartWidget.svelte.d.ts +4 -1
- package/components/renderer/ResultsChartWidget.svelte +35 -18
- package/components/renderer/aggregate.d.ts +63 -0
- package/components/renderer/aggregate.ts +123 -0
- package/components/renderer/chart-option.d.ts +54 -0
- package/components/renderer/chart-option.ts +256 -0
- package/components/renderer/chart-spec.d.ts +21 -0
- package/components/renderer/chart-spec.ts +109 -0
- package/components/renderer/types.d.ts +27 -0
- package/package.json +2 -2
|
@@ -35,7 +35,7 @@ declare const DateRangePicker: import("svelte").Component<{
|
|
|
35
35
|
onchange?: any;
|
|
36
36
|
id?: any;
|
|
37
37
|
class?: string;
|
|
38
|
-
} & Record<string, any>, {}, "
|
|
38
|
+
} & Record<string, any>, {}, "start" | "end">;
|
|
39
39
|
type $$ComponentProps = {
|
|
40
40
|
start?: any;
|
|
41
41
|
end?: any;
|
package/components/EChart.svelte
CHANGED
|
@@ -1,64 +1,81 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
@component EChart
|
|
3
3
|
|
|
4
|
-
A
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
A categorical chart over a public aggregate VIEW, rendered with Apache ECharts,
|
|
5
|
+
that SHIPS ITS DATA TABLE — same a11y contract as `ResultsChart`. The ECharts
|
|
6
|
+
canvas is decorative (`aria-hidden`); the accompanying `<table>` is the
|
|
7
|
+
accessible source of truth, always rendered server-side and client-side, so the
|
|
8
|
+
data is reachable with JS off, CSS off, at any zoom, and to assistive tech. This
|
|
9
|
+
is a hard requirement (WCAG / ARTE: a chart must not be the only encoding of its
|
|
10
|
+
data), not a toggle.
|
|
11
|
+
|
|
12
|
+
Declarative + multi-series (#176 follow-on): the data contract is the
|
|
13
|
+
column-aligned `{ category, series[] }` (`ChartData`) produced by `toSeriesData`
|
|
14
|
+
— a single series is the degenerate case, ≥2 series is multi-series. Each series
|
|
15
|
+
carries its own `type` (bar/line/area/scatter/pie), optional `stack`, and
|
|
16
|
+
optional secondary `axis`; the ECharts `option` is built by the PURE
|
|
17
|
+
`buildChartOption` (unit-tested separately). Back-compat: the legacy
|
|
18
|
+
`items={[{label,value}]}` + `kind` props synthesise a single series.
|
|
19
|
+
|
|
20
|
+
ECharts is LAZY-loaded inside the mount effect, so the heavy library lands in
|
|
21
|
+
its OWN async chunk — a page that never mounts a chart never pays for it.
|
|
22
|
+
Effects are client-only in Svelte 5, so the canvas is never touched during SSR.
|
|
23
|
+
|
|
24
|
+
Themed from the live `--color-*` semantic tokens read off the container, and
|
|
25
|
+
re-themed on `data-theme` / `prefers-color-scheme` change (#244). The per-series
|
|
26
|
+
palette is an alpha ramp off `--color-accent` — no hardcoded colour list.
|
|
27
|
+
Soft-empty: no series/category → renders nothing.
|
|
24
28
|
|
|
25
29
|
@example
|
|
26
30
|
<EChart
|
|
27
|
-
caption="
|
|
28
|
-
labelHeader="
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
{
|
|
31
|
+
caption="Reports by status"
|
|
32
|
+
labelHeader="Status"
|
|
33
|
+
category={["Open", "In progress", "Resolved"]}
|
|
34
|
+
series={[
|
|
35
|
+
{ name: "Open", type: "bar", data: [12, 0, 0] },
|
|
36
|
+
{ name: "Resolved", type: "bar", data: [0, 0, 31] },
|
|
33
37
|
]}
|
|
38
|
+
legend
|
|
34
39
|
locale="pt"
|
|
35
40
|
/>
|
|
36
41
|
-->
|
|
37
42
|
<script module>
|
|
38
43
|
/**
|
|
39
44
|
* @typedef {{ label: string, value: number }} ChartItem
|
|
45
|
+
* @typedef {import('./renderer/aggregate').ChartData} ChartData
|
|
46
|
+
* @typedef {import('./renderer/aggregate').SeriesData} SeriesData
|
|
40
47
|
*/
|
|
41
48
|
</script>
|
|
42
49
|
|
|
43
50
|
<script>
|
|
44
51
|
import { watchTheme } from "./map-utils.js";
|
|
52
|
+
import { buildChartOption } from "./renderer/chart-option";
|
|
45
53
|
|
|
46
54
|
let {
|
|
47
|
-
/** @type {
|
|
55
|
+
/** @type {string[]} The category (x / pie-angle) ticks. */
|
|
56
|
+
category = [],
|
|
57
|
+
/** @type {SeriesData[]} The series to plot (≥1; ≥2 = multi-series). */
|
|
58
|
+
series = [],
|
|
59
|
+
/** @type {boolean} Render a legend (needed once there are ≥2 series). */
|
|
60
|
+
legend = false,
|
|
61
|
+
/** @type {"horizontal" | "vertical" | undefined} Cartesian orientation. */
|
|
62
|
+
orientation = undefined,
|
|
63
|
+
/** @type {boolean} Enable a 2nd value axis for `axis: "secondary"` series. */
|
|
64
|
+
ySecondary = false,
|
|
65
|
+
/** @type {number} Pie/donut hole as a fraction of the radius (0 = full pie). */
|
|
66
|
+
innerRadius = 0,
|
|
67
|
+
/** @type {ChartItem[]} BACK-COMPAT: single-series `{label,value}` rows. */
|
|
48
68
|
items = [],
|
|
69
|
+
/** @type {"bar" | "line" | "donut"} BACK-COMPAT: legacy single-series kind. */
|
|
70
|
+
kind = "bar",
|
|
49
71
|
/** @type {string} Accessible caption / heading for the chart + table. */
|
|
50
72
|
caption = "Results",
|
|
51
73
|
/** @type {string} Column header for the category (localize it). */
|
|
52
74
|
labelHeader = "Item",
|
|
53
|
-
/** @type {string}
|
|
75
|
+
/** @type {string} Header for the (legacy single) value column (localize it). */
|
|
54
76
|
valueHeader = "Value",
|
|
55
77
|
/** @type {string} BCP-47 locale for number formatting. */
|
|
56
78
|
locale = "en",
|
|
57
|
-
/** @type {"bar" | "line" | "donut"} Chart kind (#176 Tier 1). `bar` is the
|
|
58
|
-
* horizontal categorical bar; `line` plots the same series as a value line
|
|
59
|
-
* over the category axis; `donut` is a ring pie. The data table below is
|
|
60
|
-
* the accessible source of truth for every kind. */
|
|
61
|
-
kind = "bar",
|
|
62
79
|
/** @type {string} CSS block-size for the chart canvas. */
|
|
63
80
|
height = "20rem",
|
|
64
81
|
/** @type {string} */
|
|
@@ -71,17 +88,49 @@
|
|
|
71
88
|
return new Intl.NumberFormat(locale).format(value);
|
|
72
89
|
}
|
|
73
90
|
|
|
91
|
+
// Normalise to the declarative `{ category, series }` contract. When `series`
|
|
92
|
+
// is given we render it directly; otherwise we synthesise a single series from
|
|
93
|
+
// the legacy `items` + `kind` (donut → a pie series).
|
|
94
|
+
/** @type {ChartData} */
|
|
95
|
+
const chartData = $derived.by(() => {
|
|
96
|
+
if (series.length > 0) return { category, series };
|
|
97
|
+
if (items.length === 0) return { category: [], series: [] };
|
|
98
|
+
const t = kind === "donut" ? "pie" : kind;
|
|
99
|
+
return {
|
|
100
|
+
category: items.map((it) => it.label),
|
|
101
|
+
series: [
|
|
102
|
+
{
|
|
103
|
+
name: valueHeader,
|
|
104
|
+
type: /** @type {SeriesData["type"]} */ (t),
|
|
105
|
+
data: items.map((it) => it.value),
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Legacy donut gets the ring hole even though the caller passes no innerRadius.
|
|
112
|
+
const effInnerRadius = $derived(
|
|
113
|
+
series.length === 0 && kind === "donut" ? 0.45 : innerRadius,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// The legacy single-series `kind="bar"` rendered a HORIZONTAL bar (category on
|
|
117
|
+
// the y-axis). Preserve that look for back-compat callers; the declarative
|
|
118
|
+
// path defaults to vertical (category on x) unless `orientation` says otherwise.
|
|
119
|
+
const effOrientation = $derived(
|
|
120
|
+
orientation ?? (series.length === 0 && kind === "bar" ? "horizontal" : undefined),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const hasData = $derived(
|
|
124
|
+
chartData.series.length > 0 && chartData.category.length > 0,
|
|
125
|
+
);
|
|
126
|
+
|
|
74
127
|
/** @type {HTMLElement | undefined} */
|
|
75
128
|
let container = $state();
|
|
76
129
|
|
|
77
|
-
// The ECharts instance + the canvas-renderer chart option are owned by the
|
|
78
|
-
// mount effect; this effect (re-)builds the option whenever `items`, the
|
|
79
|
-
// locale, or the theme change. `_chart` is hoisted so the data effect can
|
|
80
|
-
// re-setOption without re-initialising the canvas.
|
|
81
130
|
/** @type {import('echarts/core').EChartsType | undefined} */
|
|
82
131
|
let _chart = $state();
|
|
83
132
|
|
|
84
|
-
/** Build the ECharts option from current
|
|
133
|
+
/** Build the ECharts option from current data + the container's tokens. */
|
|
85
134
|
function buildOption() {
|
|
86
135
|
const el = container;
|
|
87
136
|
if (!el) return {};
|
|
@@ -90,110 +139,19 @@
|
|
|
90
139
|
const v = cs.getPropertyValue(name).trim();
|
|
91
140
|
return v || fallback;
|
|
92
141
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// (alpha ramp) so it stays on-theme without a hardcoded colour list.
|
|
100
|
-
if (kind === "donut") {
|
|
101
|
-
const palette = items.map((_, i) => {
|
|
102
|
-
const alpha = Math.max(0.35, 1 - i * 0.12);
|
|
103
|
-
return `color-mix(in srgb, ${accent} ${Math.round(alpha * 100)}%, transparent)`;
|
|
104
|
-
});
|
|
105
|
-
return {
|
|
106
|
-
animation: false,
|
|
107
|
-
tooltip: {
|
|
108
|
-
trigger: "item",
|
|
109
|
-
/** @param {{ name: string, value: number, percent: number }} p */
|
|
110
|
-
formatter: (p) => `${p.name}: ${fmt(p.value)} (${p.percent}%)`,
|
|
111
|
-
},
|
|
112
|
-
color: palette,
|
|
113
|
-
series: [
|
|
114
|
-
{
|
|
115
|
-
type: "pie",
|
|
116
|
-
radius: ["45%", "72%"],
|
|
117
|
-
data: items.map((it) => ({ name: it.label, value: it.value })),
|
|
118
|
-
label: { color: textPrimary },
|
|
119
|
-
labelLine: { lineStyle: { color: border } },
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Line: value over the category axis, in the consumer's order (desc by
|
|
126
|
-
// value). Shares the same accent + token theming as the bar.
|
|
127
|
-
if (kind === "line") {
|
|
128
|
-
return {
|
|
129
|
-
animation: false,
|
|
130
|
-
grid: { left: 8, right: 16, top: 8, bottom: 8, containLabel: true },
|
|
131
|
-
tooltip: {
|
|
132
|
-
trigger: "axis",
|
|
133
|
-
/** @param {{ name: string, value: number }[]} p */
|
|
134
|
-
formatter: (p) => `${p[0]?.name}: ${fmt(p[0]?.value)}`,
|
|
135
|
-
},
|
|
136
|
-
xAxis: {
|
|
137
|
-
type: "category",
|
|
138
|
-
data: items.map((it) => it.label),
|
|
139
|
-
axisLabel: { color: textPrimary },
|
|
140
|
-
axisLine: { lineStyle: { color: border } },
|
|
141
|
-
axisTick: { show: false },
|
|
142
|
-
},
|
|
143
|
-
yAxis: {
|
|
144
|
-
type: "value",
|
|
145
|
-
axisLabel: { color: textSecondary, formatter: (/** @type {number} */ v) => fmt(v) },
|
|
146
|
-
axisLine: { lineStyle: { color: border } },
|
|
147
|
-
splitLine: { lineStyle: { color: border } },
|
|
148
|
-
},
|
|
149
|
-
series: [
|
|
150
|
-
{
|
|
151
|
-
type: "line",
|
|
152
|
-
data: items.map((it) => it.value),
|
|
153
|
-
itemStyle: { color: accent },
|
|
154
|
-
lineStyle: { color: accent },
|
|
155
|
-
symbolSize: 7,
|
|
156
|
-
smooth: false,
|
|
157
|
-
},
|
|
158
|
-
],
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Bar (default). ECharts plots the category axis bottom-up; reverse so the
|
|
163
|
-
// highest value sits at the top (matching ResultsChart's top-down order).
|
|
164
|
-
// `items` is already sorted desc by the consumer.
|
|
165
|
-
const ordered = [...items].reverse();
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
animation: false,
|
|
169
|
-
grid: { left: 8, right: 16, top: 8, bottom: 8, containLabel: true },
|
|
170
|
-
tooltip: {
|
|
171
|
-
trigger: "item",
|
|
172
|
-
/** @param {{ name: string, value: number }} p */
|
|
173
|
-
formatter: (p) => `${p.name}: ${fmt(p.value)}`,
|
|
174
|
-
},
|
|
175
|
-
xAxis: {
|
|
176
|
-
type: "value",
|
|
177
|
-
axisLabel: { color: textSecondary, formatter: (/** @type {number} */ v) => fmt(v) },
|
|
178
|
-
axisLine: { lineStyle: { color: border } },
|
|
179
|
-
splitLine: { lineStyle: { color: border } },
|
|
142
|
+
return buildChartOption(chartData, {
|
|
143
|
+
tokens: {
|
|
144
|
+
accent: token("--color-accent", "#2563eb"),
|
|
145
|
+
textPrimary: token("--color-text-primary", "#1f2937"),
|
|
146
|
+
textSecondary: token("--color-text-secondary", "#6b7280"),
|
|
147
|
+
border: token("--color-border", "#e5e7eb"),
|
|
180
148
|
},
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
},
|
|
188
|
-
series: [
|
|
189
|
-
{
|
|
190
|
-
type: "bar",
|
|
191
|
-
data: ordered.map((it) => it.value),
|
|
192
|
-
itemStyle: { color: accent, borderRadius: [0, 4, 4, 0] },
|
|
193
|
-
barMaxWidth: 28,
|
|
194
|
-
},
|
|
195
|
-
],
|
|
196
|
-
};
|
|
149
|
+
locale,
|
|
150
|
+
legend,
|
|
151
|
+
orientation: effOrientation,
|
|
152
|
+
ySecondary,
|
|
153
|
+
innerRadius: effInnerRadius,
|
|
154
|
+
});
|
|
197
155
|
}
|
|
198
156
|
|
|
199
157
|
$effect(() => {
|
|
@@ -221,8 +179,10 @@
|
|
|
221
179
|
charts.BarChart,
|
|
222
180
|
charts.LineChart,
|
|
223
181
|
charts.PieChart,
|
|
182
|
+
charts.ScatterChart,
|
|
224
183
|
components.GridComponent,
|
|
225
184
|
components.TooltipComponent,
|
|
185
|
+
components.LegendComponent,
|
|
226
186
|
renderers.CanvasRenderer,
|
|
227
187
|
]);
|
|
228
188
|
|
|
@@ -233,7 +193,7 @@
|
|
|
233
193
|
ro = new ResizeObserver(() => chart?.resize());
|
|
234
194
|
ro.observe(container);
|
|
235
195
|
|
|
236
|
-
disposeTheme = watchTheme(() => chart?.setOption(buildOption()));
|
|
196
|
+
disposeTheme = watchTheme(() => chart?.setOption(buildOption(), true));
|
|
237
197
|
} catch (err) {
|
|
238
198
|
// Fail soft: the <table> below is the accessible source of truth, so a
|
|
239
199
|
// failed chart load degrades to data-only, never to a broken page.
|
|
@@ -251,21 +211,21 @@
|
|
|
251
211
|
};
|
|
252
212
|
});
|
|
253
213
|
|
|
254
|
-
// Re-apply the option when
|
|
255
|
-
// persists; only the
|
|
256
|
-
//
|
|
214
|
+
// Re-apply the option when the data / locale / options change after mount (the
|
|
215
|
+
// canvas persists; only the option is swapped). `true` replaces (not merges)
|
|
216
|
+
// so stale axis/series components from a previous shape are cleared.
|
|
257
217
|
$effect(() => {
|
|
258
|
-
|
|
259
|
-
void items;
|
|
218
|
+
void chartData;
|
|
260
219
|
void locale;
|
|
261
|
-
void
|
|
262
|
-
|
|
263
|
-
|
|
220
|
+
void legend;
|
|
221
|
+
void effOrientation;
|
|
222
|
+
void ySecondary;
|
|
223
|
+
void effInnerRadius;
|
|
264
224
|
if (_chart) _chart.setOption(buildOption(), true);
|
|
265
225
|
});
|
|
266
226
|
</script>
|
|
267
227
|
|
|
268
|
-
{#if
|
|
228
|
+
{#if hasData}
|
|
269
229
|
<figure class="echart {className}" aria-label={caption} {...rest}>
|
|
270
230
|
<!-- Decorative visual: the data lives in the table below. -->
|
|
271
231
|
<div
|
|
@@ -275,21 +235,26 @@
|
|
|
275
235
|
style="block-size: {height}"
|
|
276
236
|
></div>
|
|
277
237
|
|
|
278
|
-
<!-- Accessible source of truth — always rendered (ARTE #3/#6)
|
|
279
|
-
aria-label, not a <caption>:
|
|
280
|
-
display:table-caption element
|
|
238
|
+
<!-- Accessible source of truth — always rendered (ARTE #3/#6), one value
|
|
239
|
+
column per series. Named via aria-label, not a <caption>:
|
|
240
|
+
position:absolute/clip is unreliable on a display:table-caption element
|
|
241
|
+
(it leaks visibly in some engines). -->
|
|
281
242
|
<table class="echart-table" aria-label={caption}>
|
|
282
243
|
<thead>
|
|
283
244
|
<tr>
|
|
284
245
|
<th scope="col">{labelHeader}</th>
|
|
285
|
-
|
|
246
|
+
{#each chartData.series as s (s.name)}
|
|
247
|
+
<th scope="col">{s.name}</th>
|
|
248
|
+
{/each}
|
|
286
249
|
</tr>
|
|
287
250
|
</thead>
|
|
288
251
|
<tbody>
|
|
289
|
-
{#each
|
|
252
|
+
{#each chartData.category as cat, r (cat + r)}
|
|
290
253
|
<tr>
|
|
291
|
-
<th scope="row">{
|
|
292
|
-
|
|
254
|
+
<th scope="row">{cat}</th>
|
|
255
|
+
{#each chartData.series as s (s.name)}
|
|
256
|
+
<td>{fmt(s.data[r] ?? 0)}</td>
|
|
257
|
+
{/each}
|
|
293
258
|
</tr>
|
|
294
259
|
{/each}
|
|
295
260
|
</tbody>
|
|
@@ -3,6 +3,8 @@ export type ChartItem = {
|
|
|
3
3
|
label: string;
|
|
4
4
|
value: number;
|
|
5
5
|
};
|
|
6
|
+
export type ChartData = import("./renderer/aggregate").ChartData;
|
|
7
|
+
export type SeriesData = import("./renderer/aggregate").SeriesData;
|
|
6
8
|
type EChart = {
|
|
7
9
|
$on?(type: string, callback: (e: any) => void): () => void;
|
|
8
10
|
$set?(props: Partial<$$ComponentProps>): void;
|
|
@@ -10,41 +12,53 @@ type EChart = {
|
|
|
10
12
|
/**
|
|
11
13
|
* EChart
|
|
12
14
|
*
|
|
13
|
-
* A
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* not a toggle.
|
|
15
|
+
* A categorical chart over a public aggregate VIEW, rendered with Apache ECharts,
|
|
16
|
+
* that SHIPS ITS DATA TABLE — same a11y contract as `ResultsChart`. The ECharts
|
|
17
|
+
* canvas is decorative (`aria-hidden`); the accompanying `<table>` is the
|
|
18
|
+
* accessible source of truth, always rendered server-side and client-side, so the
|
|
19
|
+
* data is reachable with JS off, CSS off, at any zoom, and to assistive tech. This
|
|
20
|
+
* is a hard requirement (WCAG / ARTE: a chart must not be the only encoding of its
|
|
21
|
+
* data), not a toggle.
|
|
21
22
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* Declarative + multi-series (#176 follow-on): the data contract is the
|
|
24
|
+
* column-aligned `{ category, series[] }` (`ChartData`) produced by `toSeriesData`
|
|
25
|
+
* — a single series is the degenerate case, ≥2 series is multi-series. Each series
|
|
26
|
+
* carries its own `type` (bar/line/area/scatter/pie), optional `stack`, and
|
|
27
|
+
* optional secondary `axis`; the ECharts `option` is built by the PURE
|
|
28
|
+
* `buildChartOption` (unit-tested separately). Back-compat: the legacy
|
|
29
|
+
* `items={[{label,value}]}` + `kind` props synthesise a single series.
|
|
27
30
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* ECharts is LAZY-loaded inside the mount effect, so the heavy library lands in
|
|
32
|
+
* its OWN async chunk — a page that never mounts a chart never pays for it.
|
|
33
|
+
* Effects are client-only in Svelte 5, so the canvas is never touched during SSR.
|
|
34
|
+
*
|
|
35
|
+
* Themed from the live `--color-*` semantic tokens read off the container, and
|
|
36
|
+
* re-themed on `data-theme` / `prefers-color-scheme` change (#244). The per-series
|
|
37
|
+
* palette is an alpha ramp off `--color-accent` — no hardcoded colour list.
|
|
38
|
+
* Soft-empty: no series/category → renders nothing.
|
|
33
39
|
*
|
|
34
40
|
* @example
|
|
35
41
|
* <EChart
|
|
36
|
-
* caption="
|
|
37
|
-
* labelHeader="
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* {
|
|
41
|
-
* {
|
|
42
|
+
* caption="Reports by status"
|
|
43
|
+
* labelHeader="Status"
|
|
44
|
+
* category={["Open", "In progress", "Resolved"]}
|
|
45
|
+
* series={[
|
|
46
|
+
* { name: "Open", type: "bar", data: [12, 0, 0] },
|
|
47
|
+
* { name: "Resolved", type: "bar", data: [0, 0, 31] },
|
|
42
48
|
* ]}
|
|
49
|
+
* legend
|
|
43
50
|
* locale="pt"
|
|
44
51
|
* />
|
|
45
52
|
*/
|
|
46
53
|
declare const EChart: import("svelte").Component<{
|
|
54
|
+
category?: any[];
|
|
55
|
+
series?: any[];
|
|
56
|
+
legend?: boolean;
|
|
57
|
+
orientation?: any;
|
|
58
|
+
ySecondary?: boolean;
|
|
59
|
+
innerRadius?: number;
|
|
47
60
|
items?: any[];
|
|
61
|
+
kind?: string;
|
|
48
62
|
caption?: string;
|
|
49
63
|
labelHeader?: string;
|
|
50
64
|
valueHeader?: string;
|
|
@@ -53,7 +67,14 @@ declare const EChart: import("svelte").Component<{
|
|
|
53
67
|
class?: string;
|
|
54
68
|
} & Record<string, any>, {}, "">;
|
|
55
69
|
type $$ComponentProps = {
|
|
70
|
+
category?: any[];
|
|
71
|
+
series?: any[];
|
|
72
|
+
legend?: boolean;
|
|
73
|
+
orientation?: any;
|
|
74
|
+
ySecondary?: boolean;
|
|
75
|
+
innerRadius?: number;
|
|
56
76
|
items?: any[];
|
|
77
|
+
kind?: string;
|
|
57
78
|
caption?: string;
|
|
58
79
|
labelHeader?: string;
|
|
59
80
|
valueHeader?: string;
|
|
@@ -4,6 +4,7 @@ export type PrefItem = {
|
|
|
4
4
|
label: string;
|
|
5
5
|
description?: string;
|
|
6
6
|
active: boolean;
|
|
7
|
+
group?: string;
|
|
7
8
|
};
|
|
8
9
|
type NotificationPrefs = {
|
|
9
10
|
$on?(type: string, callback: (e: any) => void): () => void;
|
|
@@ -19,16 +20,18 @@ type NotificationPrefs = {
|
|
|
19
20
|
* Toggling a row calls `onToggle(id, next)` — the consumer performs the write
|
|
20
21
|
* and re-feeds `items` (or sets `busyId` while it's in flight).
|
|
21
22
|
*
|
|
22
|
-
* Accessibility-first: a bordered semantic list
|
|
23
|
-
* label (
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* Accessibility-first: a bordered semantic list inside a `<section>` region
|
|
24
|
+
* named by `label` (the visible page heading is the consumer's job — same split
|
|
25
|
+
* as RankingBoard, so one widget can own the page `<h1>`). Each row pairs a real
|
|
26
|
+
* text label (and optional description) with a `Toggle` (`role="switch"`,
|
|
27
|
+
* `aria-checked`); the switch carries its own accessible name via `aria-label`.
|
|
28
|
+
* A row that's saving is `disabled` (no double-submit). Consumes semantic tokens
|
|
29
|
+
* so dark / high-contrast schemes (#244) ride through. Soft-empty: no items →
|
|
30
|
+
* renders the `emptyText` note (never an empty shell).
|
|
28
31
|
*
|
|
29
32
|
* @example
|
|
30
33
|
* <NotificationPrefs
|
|
31
|
-
*
|
|
34
|
+
* label="Notifications"
|
|
32
35
|
* items={[
|
|
33
36
|
* { id: "all", label: "All updates", description: "Every proposal and phase change", active: true },
|
|
34
37
|
* { id: "sub-2", label: "Cycling lane", active: false },
|
|
@@ -39,7 +42,7 @@ type NotificationPrefs = {
|
|
|
39
42
|
*/
|
|
40
43
|
declare const NotificationPrefs: import("svelte").Component<{
|
|
41
44
|
items?: any[];
|
|
42
|
-
|
|
45
|
+
label?: string;
|
|
43
46
|
emptyText?: string;
|
|
44
47
|
onToggle?: any;
|
|
45
48
|
busyId?: any;
|
|
@@ -47,7 +50,7 @@ declare const NotificationPrefs: import("svelte").Component<{
|
|
|
47
50
|
} & Record<string, any>, {}, "">;
|
|
48
51
|
type $$ComponentProps = {
|
|
49
52
|
items?: any[];
|
|
50
|
-
|
|
53
|
+
label?: string;
|
|
51
54
|
emptyText?: string;
|
|
52
55
|
onToggle?: any;
|
|
53
56
|
busyId?: any;
|