@classic-homes/charts-svelte 0.1.36 → 0.1.38
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/dist/lib/components/base/ChartContainer.svelte +4 -3
- package/dist/lib/components/base/ChartError.svelte +3 -1
- package/dist/lib/components/base/index.d.ts +9 -0
- package/dist/lib/components/core/AreaChart.svelte +54 -84
- package/dist/lib/components/core/BarChart.svelte +24 -52
- package/dist/lib/components/core/DonutChart.svelte +28 -53
- package/dist/lib/components/core/LineChart.svelte +44 -72
- package/dist/lib/components/core/PieChart.svelte +23 -51
- package/dist/lib/components/core/ScatterChart.svelte +39 -58
- package/dist/lib/components/extended/CandlestickChart.svelte +36 -55
- package/dist/lib/components/extended/FunnelChart.svelte +23 -46
- package/dist/lib/components/extended/GaugeChart.svelte +23 -37
- package/dist/lib/components/extended/HeatmapChart.svelte +38 -52
- package/dist/lib/components/extended/RadarChart.svelte +8 -33
- package/dist/lib/components/extended/SankeyChart.svelte +24 -48
- package/dist/lib/components/extended/TreemapChart.svelte +29 -49
- package/dist/lib/components/index.d.ts +1 -1
- package/dist/lib/components/index.js +1 -1
- package/dist/lib/composables/index.d.ts +1 -0
- package/dist/lib/composables/index.js +1 -0
- package/dist/lib/composables/useChart.svelte.d.ts +35 -0
- package/dist/lib/composables/useChart.svelte.js +94 -0
- package/dist/lib/composables/useChartResize.svelte.js +3 -1
- package/dist/lib/composables/useChartTheme.svelte.d.ts +2 -1
- package/dist/lib/composables/useChartTheme.svelte.js +2 -6
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +1 -1
- package/package.json +1 -1
|
@@ -33,10 +33,11 @@
|
|
|
33
33
|
children,
|
|
34
34
|
}: Props = $props();
|
|
35
35
|
|
|
36
|
+
// Unique per instance: two charts may share a title
|
|
37
|
+
const uid = $props.id();
|
|
38
|
+
|
|
36
39
|
const heightStyle = $derived(typeof height === 'number' ? `${height}px` : height);
|
|
37
|
-
const descriptionId = $derived(
|
|
38
|
-
description ? `${title.replace(/\s+/g, '-').toLowerCase()}-desc` : undefined
|
|
39
|
-
);
|
|
40
|
+
const descriptionId = $derived(description ? `${uid}-desc` : undefined);
|
|
40
41
|
</script>
|
|
41
42
|
|
|
42
43
|
{#if loading}
|
|
@@ -37,9 +37,11 @@
|
|
|
37
37
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
38
38
|
/>
|
|
39
39
|
</svg>
|
|
40
|
-
|
|
40
|
+
<!-- text-destructive on the destructive tint fails AA; the icon and border carry the colour -->
|
|
41
|
+
<p class="mb-2 text-sm font-medium text-foreground">{message}</p>
|
|
41
42
|
{#if onRetry}
|
|
42
43
|
<button
|
|
44
|
+
type="button"
|
|
43
45
|
onclick={onRetry}
|
|
44
46
|
class="rounded-md bg-destructive px-3 py-1.5 text-xs font-medium text-destructive-foreground transition-colors hover:bg-destructive/90"
|
|
45
47
|
>
|
|
@@ -2,3 +2,12 @@ export { default as ChartContainer } from './ChartContainer.svelte';
|
|
|
2
2
|
export { default as ChartSkeleton } from './ChartSkeleton.svelte';
|
|
3
3
|
export { default as ChartError } from './ChartError.svelte';
|
|
4
4
|
export { default as ChartEmpty } from './ChartEmpty.svelte';
|
|
5
|
+
import type { ComponentProps } from 'svelte';
|
|
6
|
+
import type ChartContainer from './ChartContainer.svelte';
|
|
7
|
+
import type ChartSkeleton from './ChartSkeleton.svelte';
|
|
8
|
+
import type ChartError from './ChartError.svelte';
|
|
9
|
+
import type ChartEmpty from './ChartEmpty.svelte';
|
|
10
|
+
export type ChartContainerProps = ComponentProps<typeof ChartContainer>;
|
|
11
|
+
export type ChartSkeletonProps = ComponentProps<typeof ChartSkeleton>;
|
|
12
|
+
export type ChartErrorProps = ComponentProps<typeof ChartError>;
|
|
13
|
+
export type ChartEmptyProps = ComponentProps<typeof ChartEmpty>;
|
|
@@ -11,11 +11,10 @@
|
|
|
11
11
|
import type { EChartsOption } from 'echarts';
|
|
12
12
|
|
|
13
13
|
import type { AreaChartProps, DataPointEventParams } from '@classic-homes/charts-core';
|
|
14
|
+
import { colorWithAlpha, getChartColors } from '@classic-homes/charts-core';
|
|
14
15
|
|
|
15
16
|
import { cn } from '../../utils.js';
|
|
16
|
-
import {
|
|
17
|
-
import { useReducedMotion } from '../../composables/useReducedMotion.svelte.js';
|
|
18
|
-
import { useChartResize } from '../../composables/useChartResize.svelte.js';
|
|
17
|
+
import { useChart } from '../../composables/useChart.svelte.js';
|
|
19
18
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
20
19
|
|
|
21
20
|
// Register required ECharts modules
|
|
@@ -52,17 +51,13 @@
|
|
|
52
51
|
class: className,
|
|
53
52
|
}: Props = $props();
|
|
54
53
|
|
|
55
|
-
let containerEl: HTMLDivElement | null = $state(null);
|
|
56
|
-
let chart: echarts.ECharts | null = null;
|
|
57
|
-
|
|
58
|
-
const chartTheme = useChartTheme(() => theme);
|
|
59
|
-
const reducedMotion = useReducedMotion();
|
|
60
|
-
const chartResize = useChartResize();
|
|
61
|
-
|
|
62
54
|
const isEmpty = $derived(
|
|
63
55
|
!data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length)
|
|
64
56
|
);
|
|
65
57
|
|
|
58
|
+
// Per-element colours that follow the resolved chart theme
|
|
59
|
+
const chartColors = $derived.by(() => getChartColors(chart.theme));
|
|
60
|
+
|
|
66
61
|
const option: EChartsOption = $derived.by(() => {
|
|
67
62
|
if (isEmpty) return {};
|
|
68
63
|
|
|
@@ -79,7 +74,7 @@
|
|
|
79
74
|
axisPointer: {
|
|
80
75
|
type: 'cross',
|
|
81
76
|
label: {
|
|
82
|
-
backgroundColor:
|
|
77
|
+
backgroundColor: chartColors.axisPointerLabel,
|
|
83
78
|
},
|
|
84
79
|
},
|
|
85
80
|
}
|
|
@@ -101,38 +96,31 @@
|
|
|
101
96
|
show: showGrid,
|
|
102
97
|
},
|
|
103
98
|
},
|
|
104
|
-
series: data.series.map((series, index) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
itemStyle: series.color ? { color: series.color } : undefined,
|
|
130
|
-
emphasis: {
|
|
131
|
-
focus: 'series',
|
|
132
|
-
},
|
|
133
|
-
showSymbol: false,
|
|
134
|
-
})),
|
|
135
|
-
animation: animation && !reducedMotion.value,
|
|
99
|
+
series: data.series.map((series, index) => {
|
|
100
|
+
// Fade the series colour (theme palette unless overridden) downwards
|
|
101
|
+
const base = series.color || chartColors.series[index % chartColors.series.length];
|
|
102
|
+
return {
|
|
103
|
+
name: series.name,
|
|
104
|
+
type: 'line',
|
|
105
|
+
data: series.data,
|
|
106
|
+
smooth,
|
|
107
|
+
stack: stacked ? 'Total' : undefined,
|
|
108
|
+
areaStyle: gradient
|
|
109
|
+
? {
|
|
110
|
+
opacity: 0.8,
|
|
111
|
+
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
112
|
+
{ offset: 0, color: colorWithAlpha(base, 0.8) },
|
|
113
|
+
{ offset: 1, color: colorWithAlpha(base, 0.1) },
|
|
114
|
+
]),
|
|
115
|
+
}
|
|
116
|
+
: { opacity: 0.5 },
|
|
117
|
+
itemStyle: series.color ? { color: series.color } : undefined,
|
|
118
|
+
emphasis: {
|
|
119
|
+
focus: 'series',
|
|
120
|
+
},
|
|
121
|
+
showSymbol: false,
|
|
122
|
+
};
|
|
123
|
+
}),
|
|
136
124
|
};
|
|
137
125
|
});
|
|
138
126
|
|
|
@@ -143,46 +131,28 @@
|
|
|
143
131
|
: undefined)
|
|
144
132
|
);
|
|
145
133
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Set up ResizeObserver for container-aware resizing
|
|
172
|
-
chartResize.observe(containerEl, chart);
|
|
173
|
-
|
|
174
|
-
return () => {
|
|
175
|
-
chartResize.cleanup();
|
|
176
|
-
chart?.dispose();
|
|
177
|
-
chart = null;
|
|
178
|
-
};
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// Update options when they change (handles both initial and subsequent updates)
|
|
182
|
-
$effect(() => {
|
|
183
|
-
if (chart && !isEmpty) {
|
|
184
|
-
chart.setOption(option, { notMerge: true, lazyUpdate: true });
|
|
185
|
-
}
|
|
134
|
+
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
135
|
+
const chart = useChart({
|
|
136
|
+
option: () => option,
|
|
137
|
+
theme: () => theme,
|
|
138
|
+
animation: () => animation,
|
|
139
|
+
isEmpty: () => isEmpty,
|
|
140
|
+
onReady: (instance) => {
|
|
141
|
+
if (onClick) {
|
|
142
|
+
instance.on('click', (params) => {
|
|
143
|
+
onClick({
|
|
144
|
+
type: params.type || 'click',
|
|
145
|
+
componentType: params.componentType || 'series',
|
|
146
|
+
seriesIndex: params.seriesIndex,
|
|
147
|
+
seriesName: params.seriesName || '',
|
|
148
|
+
dataIndex: params.dataIndex || 0,
|
|
149
|
+
name: params.name || '',
|
|
150
|
+
value: params.value as number,
|
|
151
|
+
color: params.color as string,
|
|
152
|
+
} as DataPointEventParams);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
},
|
|
186
156
|
});
|
|
187
157
|
</script>
|
|
188
158
|
|
|
@@ -196,5 +166,5 @@
|
|
|
196
166
|
{emptyMessage}
|
|
197
167
|
class={cn(className)}
|
|
198
168
|
>
|
|
199
|
-
<div bind:this={
|
|
169
|
+
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
200
170
|
</ChartContainer>
|
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
import { generateBarChartDescription } from '@classic-homes/charts-core';
|
|
15
15
|
|
|
16
16
|
import { cn } from '../../utils.js';
|
|
17
|
-
import {
|
|
18
|
-
import { useReducedMotion } from '../../composables/useReducedMotion.svelte.js';
|
|
19
|
-
import { useChartResize } from '../../composables/useChartResize.svelte.js';
|
|
17
|
+
import { useChart } from '../../composables/useChart.svelte.js';
|
|
20
18
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
21
19
|
|
|
22
20
|
// Register required ECharts modules
|
|
@@ -53,13 +51,6 @@
|
|
|
53
51
|
class: className,
|
|
54
52
|
}: Props = $props();
|
|
55
53
|
|
|
56
|
-
let containerEl: HTMLDivElement | null = $state(null);
|
|
57
|
-
let chart: echarts.ECharts | null = null;
|
|
58
|
-
|
|
59
|
-
const chartTheme = useChartTheme(() => theme);
|
|
60
|
-
const reducedMotion = useReducedMotion();
|
|
61
|
-
const chartResize = useChartResize();
|
|
62
|
-
|
|
63
54
|
const isEmpty = $derived(
|
|
64
55
|
!data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length)
|
|
65
56
|
);
|
|
@@ -123,7 +114,6 @@
|
|
|
123
114
|
focus: 'series',
|
|
124
115
|
},
|
|
125
116
|
})),
|
|
126
|
-
animation: animation && !reducedMotion.value,
|
|
127
117
|
};
|
|
128
118
|
});
|
|
129
119
|
|
|
@@ -131,46 +121,28 @@
|
|
|
131
121
|
description || (!isEmpty ? generateBarChartDescription(title, data) : undefined)
|
|
132
122
|
);
|
|
133
123
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Set up ResizeObserver for container-aware resizing
|
|
160
|
-
chartResize.observe(containerEl, chart);
|
|
161
|
-
|
|
162
|
-
return () => {
|
|
163
|
-
chartResize.cleanup();
|
|
164
|
-
chart?.dispose();
|
|
165
|
-
chart = null;
|
|
166
|
-
};
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Update options when they change (handles both initial and subsequent updates)
|
|
170
|
-
$effect(() => {
|
|
171
|
-
if (chart && !isEmpty) {
|
|
172
|
-
chart.setOption(option, { notMerge: true, lazyUpdate: true });
|
|
173
|
-
}
|
|
124
|
+
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
125
|
+
const chart = useChart({
|
|
126
|
+
option: () => option,
|
|
127
|
+
theme: () => theme,
|
|
128
|
+
animation: () => animation,
|
|
129
|
+
isEmpty: () => isEmpty,
|
|
130
|
+
onReady: (instance) => {
|
|
131
|
+
if (onClick) {
|
|
132
|
+
instance.on('click', (params) => {
|
|
133
|
+
onClick({
|
|
134
|
+
type: params.type || 'click',
|
|
135
|
+
componentType: params.componentType || 'series',
|
|
136
|
+
seriesIndex: params.seriesIndex,
|
|
137
|
+
seriesName: params.seriesName || '',
|
|
138
|
+
dataIndex: params.dataIndex || 0,
|
|
139
|
+
name: params.name || '',
|
|
140
|
+
value: params.value as number,
|
|
141
|
+
color: params.color as string,
|
|
142
|
+
} as DataPointEventParams);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
},
|
|
174
146
|
});
|
|
175
147
|
</script>
|
|
176
148
|
|
|
@@ -184,5 +156,5 @@
|
|
|
184
156
|
{emptyMessage}
|
|
185
157
|
class={cn(className)}
|
|
186
158
|
>
|
|
187
|
-
<div bind:this={
|
|
159
|
+
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
188
160
|
</ChartContainer>
|
|
@@ -6,12 +6,10 @@
|
|
|
6
6
|
import type { EChartsOption } from 'echarts';
|
|
7
7
|
|
|
8
8
|
import type { DonutChartProps, SliceEventParams } from '@classic-homes/charts-core';
|
|
9
|
-
import { generatePieChartDescription } from '@classic-homes/charts-core';
|
|
9
|
+
import { generatePieChartDescription, getChartColors } from '@classic-homes/charts-core';
|
|
10
10
|
|
|
11
11
|
import { cn } from '../../utils.js';
|
|
12
|
-
import {
|
|
13
|
-
import { useReducedMotion } from '../../composables/useReducedMotion.svelte.js';
|
|
14
|
-
import { useChartResize } from '../../composables/useChartResize.svelte.js';
|
|
12
|
+
import { useChart } from '../../composables/useChart.svelte.js';
|
|
15
13
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
16
14
|
|
|
17
15
|
// Register required ECharts modules
|
|
@@ -42,15 +40,11 @@
|
|
|
42
40
|
class: className,
|
|
43
41
|
}: Props = $props();
|
|
44
42
|
|
|
45
|
-
let containerEl: HTMLDivElement | null = $state(null);
|
|
46
|
-
let chart: echarts.ECharts | null = null;
|
|
47
|
-
|
|
48
|
-
const chartTheme = useChartTheme(() => theme);
|
|
49
|
-
const reducedMotion = useReducedMotion();
|
|
50
|
-
const chartResize = useChartResize();
|
|
51
|
-
|
|
52
43
|
const isEmpty = $derived(!data?.length);
|
|
53
44
|
|
|
45
|
+
// Per-element colours that follow the resolved chart theme
|
|
46
|
+
const chartColors = $derived.by(() => getChartColors(chart.theme));
|
|
47
|
+
|
|
54
48
|
const option: EChartsOption = $derived.by(() => {
|
|
55
49
|
if (isEmpty) return {};
|
|
56
50
|
|
|
@@ -70,7 +64,7 @@
|
|
|
70
64
|
text: String(centerValue),
|
|
71
65
|
fontSize: 28,
|
|
72
66
|
fontWeight: 'bold',
|
|
73
|
-
fill:
|
|
67
|
+
fill: chartColors.foreground,
|
|
74
68
|
align: 'center',
|
|
75
69
|
},
|
|
76
70
|
top: 0,
|
|
@@ -85,7 +79,7 @@
|
|
|
85
79
|
style: {
|
|
86
80
|
text: centerLabel,
|
|
87
81
|
fontSize: 14,
|
|
88
|
-
fill:
|
|
82
|
+
fill: chartColors.mutedForeground,
|
|
89
83
|
align: 'center',
|
|
90
84
|
},
|
|
91
85
|
top: centerValue !== undefined ? 35 : 0,
|
|
@@ -146,7 +140,6 @@
|
|
|
146
140
|
},
|
|
147
141
|
},
|
|
148
142
|
],
|
|
149
|
-
animation: animation && !reducedMotion.value,
|
|
150
143
|
};
|
|
151
144
|
});
|
|
152
145
|
|
|
@@ -154,44 +147,26 @@
|
|
|
154
147
|
description || (!isEmpty ? generatePieChartDescription(title, data, true) : undefined)
|
|
155
148
|
);
|
|
156
149
|
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Set up ResizeObserver for container-aware resizing
|
|
181
|
-
chartResize.observe(containerEl, chart);
|
|
182
|
-
|
|
183
|
-
return () => {
|
|
184
|
-
chartResize.cleanup();
|
|
185
|
-
chart?.dispose();
|
|
186
|
-
chart = null;
|
|
187
|
-
};
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Update options when they change (handles both initial and subsequent updates)
|
|
191
|
-
$effect(() => {
|
|
192
|
-
if (chart && !isEmpty) {
|
|
193
|
-
chart.setOption(option, { notMerge: true, lazyUpdate: true });
|
|
194
|
-
}
|
|
150
|
+
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
151
|
+
const chart = useChart({
|
|
152
|
+
option: () => option,
|
|
153
|
+
theme: () => theme,
|
|
154
|
+
animation: () => animation,
|
|
155
|
+
isEmpty: () => isEmpty,
|
|
156
|
+
onReady: (instance) => {
|
|
157
|
+
if (onClick) {
|
|
158
|
+
instance.on('click', (params) => {
|
|
159
|
+
onClick({
|
|
160
|
+
type: params.type || 'click',
|
|
161
|
+
componentType: params.componentType || 'series',
|
|
162
|
+
name: params.name || '',
|
|
163
|
+
value: params.value as number,
|
|
164
|
+
percent: params.percent,
|
|
165
|
+
color: params.color as string,
|
|
166
|
+
} as SliceEventParams);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
},
|
|
195
170
|
});
|
|
196
171
|
</script>
|
|
197
172
|
|
|
@@ -205,5 +180,5 @@
|
|
|
205
180
|
{emptyMessage}
|
|
206
181
|
class={cn(className)}
|
|
207
182
|
>
|
|
208
|
-
<div bind:this={
|
|
183
|
+
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
209
184
|
</ChartContainer>
|
|
@@ -11,12 +11,10 @@
|
|
|
11
11
|
import type { EChartsOption } from 'echarts';
|
|
12
12
|
|
|
13
13
|
import type { LineChartProps, DataPointEventParams } from '@classic-homes/charts-core';
|
|
14
|
-
import { generateLineChartDescription } from '@classic-homes/charts-core';
|
|
14
|
+
import { generateLineChartDescription, getChartColors } from '@classic-homes/charts-core';
|
|
15
15
|
|
|
16
16
|
import { cn } from '../../utils.js';
|
|
17
|
-
import {
|
|
18
|
-
import { useReducedMotion } from '../../composables/useReducedMotion.svelte.js';
|
|
19
|
-
import { useChartResize } from '../../composables/useChartResize.svelte.js';
|
|
17
|
+
import { useChart } from '../../composables/useChart.svelte.js';
|
|
20
18
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
21
19
|
|
|
22
20
|
// Register required ECharts modules
|
|
@@ -55,17 +53,13 @@
|
|
|
55
53
|
class: className,
|
|
56
54
|
}: Props = $props();
|
|
57
55
|
|
|
58
|
-
let containerEl: HTMLDivElement | null = $state(null);
|
|
59
|
-
let chart: echarts.ECharts | null = null;
|
|
60
|
-
|
|
61
|
-
const chartTheme = useChartTheme(() => theme);
|
|
62
|
-
const reducedMotion = useReducedMotion();
|
|
63
|
-
const chartResize = useChartResize();
|
|
64
|
-
|
|
65
56
|
const isEmpty = $derived(
|
|
66
57
|
!data?.categories?.length || !data?.series?.length || data.series.every((s) => !s.data?.length)
|
|
67
58
|
);
|
|
68
59
|
|
|
60
|
+
// Per-element colours that follow the resolved chart theme
|
|
61
|
+
const chartColors = $derived.by(() => getChartColors(chart.theme));
|
|
62
|
+
|
|
69
63
|
const option: EChartsOption = $derived.by(() => {
|
|
70
64
|
if (isEmpty) return {};
|
|
71
65
|
|
|
@@ -82,7 +76,7 @@
|
|
|
82
76
|
axisPointer: {
|
|
83
77
|
type: 'cross',
|
|
84
78
|
label: {
|
|
85
|
-
backgroundColor:
|
|
79
|
+
backgroundColor: chartColors.axisPointerLabel,
|
|
86
80
|
},
|
|
87
81
|
},
|
|
88
82
|
}
|
|
@@ -121,7 +115,6 @@
|
|
|
121
115
|
focus: 'series',
|
|
122
116
|
},
|
|
123
117
|
})),
|
|
124
|
-
animation: animation && !reducedMotion.value,
|
|
125
118
|
};
|
|
126
119
|
});
|
|
127
120
|
|
|
@@ -129,64 +122,43 @@
|
|
|
129
122
|
description || (!isEmpty ? generateLineChartDescription(title, data) : undefined)
|
|
130
123
|
);
|
|
131
124
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
value: params.value as number,
|
|
170
|
-
color: params.color as string,
|
|
171
|
-
} as DataPointEventParams);
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Set up ResizeObserver for container-aware resizing
|
|
176
|
-
chartResize.observe(containerEl, chart);
|
|
177
|
-
|
|
178
|
-
return () => {
|
|
179
|
-
chartResize.cleanup();
|
|
180
|
-
chart?.dispose();
|
|
181
|
-
chart = null;
|
|
182
|
-
};
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// Update options when they change (handles both initial and subsequent updates)
|
|
186
|
-
$effect(() => {
|
|
187
|
-
if (chart && !isEmpty) {
|
|
188
|
-
chart.setOption(option, { notMerge: true, lazyUpdate: true });
|
|
189
|
-
}
|
|
125
|
+
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
126
|
+
const chart = useChart({
|
|
127
|
+
option: () => option,
|
|
128
|
+
theme: () => theme,
|
|
129
|
+
animation: () => animation,
|
|
130
|
+
isEmpty: () => isEmpty,
|
|
131
|
+
onReady: (instance) => {
|
|
132
|
+
if (onClick) {
|
|
133
|
+
instance.on('click', (params) => {
|
|
134
|
+
onClick({
|
|
135
|
+
type: params.type || 'click',
|
|
136
|
+
componentType: params.componentType || 'series',
|
|
137
|
+
seriesIndex: params.seriesIndex,
|
|
138
|
+
seriesName: params.seriesName || '',
|
|
139
|
+
dataIndex: params.dataIndex || 0,
|
|
140
|
+
name: params.name || '',
|
|
141
|
+
value: params.value as number,
|
|
142
|
+
color: params.color as string,
|
|
143
|
+
} as DataPointEventParams);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (onHover) {
|
|
148
|
+
instance.on('mouseover', (params) => {
|
|
149
|
+
onHover({
|
|
150
|
+
type: params.type || 'mouseover',
|
|
151
|
+
componentType: params.componentType || 'series',
|
|
152
|
+
seriesIndex: params.seriesIndex,
|
|
153
|
+
seriesName: params.seriesName || '',
|
|
154
|
+
dataIndex: params.dataIndex || 0,
|
|
155
|
+
name: params.name || '',
|
|
156
|
+
value: params.value as number,
|
|
157
|
+
color: params.color as string,
|
|
158
|
+
} as DataPointEventParams);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
},
|
|
190
162
|
});
|
|
191
163
|
</script>
|
|
192
164
|
|
|
@@ -200,5 +172,5 @@
|
|
|
200
172
|
{emptyMessage}
|
|
201
173
|
class={cn(className)}
|
|
202
174
|
>
|
|
203
|
-
<div bind:this={
|
|
175
|
+
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
204
176
|
</ChartContainer>
|