1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { LineChart } from 'layerchart';
export interface ChartSeries {
key: string;
label?: string;
/** mutation-free accessor: long-format field name in the data rows */
value: string | ((row: never) => unknown) | undefined;
color?: string;
dashed?: boolean;
area?: boolean;
opacity?: number;
}
interface Props {
// long-format rows: { i, date, [key]: value }
data: {
[key: string]: string | number | undefined;
i: number;
}[];
series: ChartSeries[];
height?: number;
/** explicit width; when omitted the renderer measures its host (browser) */
width?: number;
ariaLabel?: string;
legend?: boolean;
}
let { data, series, height = 340, width = $bindable(undefined), ariaLabel = '图表', legend = true }: Props = $props();
let host: HTMLDivElement;
let observer: ResizeObserver | null = null;
let measured: number | undefined = $state(undefined);
// Same resize semantics as the previous echarts wrapper (chartResize.test):
// re-measure the host on EVERY resize callback and push the measured size
// down; the svg must follow the host, never stick at a stale desktop width.
function applyMeasure() {
const w = host?.clientWidth;
if (w && w > 0) measured = w;
}
onMount(() => {
applyMeasure();
if (typeof ResizeObserver !== 'undefined') {
observer = new ResizeObserver(applyMeasure);
observer.observe(host);
}
});
onDestroy(() => observer?.disconnect());
const lcSeries = $derived(
series.map((s) => ({
key: s.key,
label: s.label ?? s.key,
value: s.value,
color: s.color
}))
);
// Resize semantics (chartResize.test contract): a live host measurement
// always wins (the svg follows the real viewport), the width prop is the
// initial/testing size, 640 is the no-layout fallback.
const resolvedWidth = $derived(measured ?? width ?? 640);
</script>
<div bind:this={host} style="height:{height}px; width:100%; min-width:0;" role="img" aria-label={ariaLabel}>
<LineChart
data={data as never[]}
x="i"
series={lcSeries as never}
width={resolvedWidth}
{height}
{legend}
axis
clip
/>
</div>
|