@classic-homes/charts-svelte 0.1.37 → 0.2.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.
Files changed (40) hide show
  1. package/dist/lib/components/base/ChartContainer.svelte +51 -11
  2. package/dist/lib/components/base/ChartContainer.svelte.d.ts +8 -0
  3. package/dist/lib/components/base/ChartError.svelte +3 -1
  4. package/dist/lib/components/base/ChartLegend.svelte +59 -0
  5. package/dist/lib/components/base/ChartLegend.svelte.d.ts +10 -0
  6. package/dist/lib/components/base/index.d.ts +12 -0
  7. package/dist/lib/components/base/index.js +1 -0
  8. package/dist/lib/components/core/AreaChart.svelte +126 -61
  9. package/dist/lib/components/core/AreaChart.svelte.d.ts +2 -1
  10. package/dist/lib/components/core/BarChart.svelte +91 -35
  11. package/dist/lib/components/core/BarChart.svelte.d.ts +2 -1
  12. package/dist/lib/components/core/DonutChart.svelte +155 -82
  13. package/dist/lib/components/core/DonutChart.svelte.d.ts +2 -1
  14. package/dist/lib/components/core/LineChart.svelte +118 -48
  15. package/dist/lib/components/core/LineChart.svelte.d.ts +2 -1
  16. package/dist/lib/components/core/PieChart.svelte +100 -38
  17. package/dist/lib/components/core/PieChart.svelte.d.ts +2 -1
  18. package/dist/lib/components/core/ScatterChart.svelte +115 -38
  19. package/dist/lib/components/core/ScatterChart.svelte.d.ts +2 -1
  20. package/dist/lib/components/extended/CandlestickChart.svelte +53 -26
  21. package/dist/lib/components/extended/FunnelChart.svelte +102 -32
  22. package/dist/lib/components/extended/FunnelChart.svelte.d.ts +2 -1
  23. package/dist/lib/components/extended/GaugeChart.svelte +62 -29
  24. package/dist/lib/components/extended/HeatmapChart.svelte +59 -29
  25. package/dist/lib/components/extended/RadarChart.svelte +76 -22
  26. package/dist/lib/components/extended/RadarChart.svelte.d.ts +2 -1
  27. package/dist/lib/components/extended/SankeyChart.svelte +50 -21
  28. package/dist/lib/components/extended/TreemapChart.svelte +71 -26
  29. package/dist/lib/components/index.d.ts +1 -1
  30. package/dist/lib/components/index.js +1 -1
  31. package/dist/lib/composables/index.d.ts +1 -0
  32. package/dist/lib/composables/index.js +1 -0
  33. package/dist/lib/composables/useChart.svelte.js +20 -3
  34. package/dist/lib/composables/useChartLegend.svelte.d.ts +37 -0
  35. package/dist/lib/composables/useChartLegend.svelte.js +54 -0
  36. package/dist/lib/composables/useChartTheme.svelte.d.ts +2 -1
  37. package/dist/lib/composables/useChartTheme.svelte.js +2 -6
  38. package/dist/lib/index.d.ts +3 -3
  39. package/dist/lib/index.js +2 -2
  40. package/package.json +1 -1
@@ -6,16 +6,32 @@
6
6
  import type { EChartsOption } from 'echarts';
7
7
 
8
8
  import type { PieChartProps, SliceEventParams } from '@classic-homes/charts-core';
9
- import { generatePieChartDescription } from '@classic-homes/charts-core';
9
+ import {
10
+ buildDataLabel,
11
+ buildLegend,
12
+ buildTooltip,
13
+ escapeHtml,
14
+ formatTooltipValue,
15
+ generatePieChartDescription,
16
+ getChartColors,
17
+ itemLabelLookup,
18
+ legendCenter,
19
+ mergeChartOption,
20
+ pointLabel,
21
+ } from '@classic-homes/charts-core';
10
22
 
11
23
  import { cn } from '../../utils.js';
12
24
  import { useChart } from '../../composables/useChart.svelte.js';
25
+ import {
26
+ useChartLegend,
27
+ type CustomLegendProps,
28
+ } from '../../composables/useChartLegend.svelte.js';
13
29
  import ChartContainer from '../base/ChartContainer.svelte';
14
30
 
15
31
  // Register required ECharts modules
16
32
  echarts.use([EChartsPieChart, TooltipComponent, LegendComponent, TitleComponent, CanvasRenderer]);
17
33
 
18
- interface Props extends Omit<PieChartProps, 'class'> {
34
+ interface Props extends Omit<PieChartProps, 'class'>, CustomLegendProps {
19
35
  class?: string;
20
36
  }
21
37
 
@@ -34,6 +50,13 @@
34
50
  showLabels = true,
35
51
  labelPosition = 'outside',
36
52
  innerRadius = 0,
53
+ labelFormatter,
54
+ tooltipFormatter,
55
+ tooltipValueFormatter,
56
+ legend: legendConfig,
57
+ renderLegend,
58
+ onLegendChange,
59
+ option: optionOverride,
37
60
  onClick,
38
61
  class: className,
39
62
  }: Props = $props();
@@ -43,56 +66,88 @@
43
66
  const option: EChartsOption = $derived.by(() => {
44
67
  if (isEmpty) return {};
45
68
 
46
- return {
47
- tooltip: showTooltip
48
- ? {
69
+ const getLabel = itemLabelLookup(data);
70
+
71
+ return mergeChartOption(
72
+ {
73
+ tooltip: buildTooltip(
74
+ {
49
75
  trigger: 'item',
50
- formatter: '{a} <br/>{b}: {c} ({d}%)',
51
- }
52
- : undefined,
53
- legend: showLegend
54
- ? {
55
- orient: 'horizontal',
56
- bottom: 0,
57
- data: data.map((d) => d.name),
76
+ // A string template ignores valueFormatter, so format the value here
77
+ formatter:
78
+ tooltipValueFormatter && !tooltipFormatter
79
+ ? (params: unknown) => {
80
+ const p = params as {
81
+ seriesName?: string;
82
+ name?: string;
83
+ value?: unknown;
84
+ percent?: number;
85
+ };
86
+ return `${escapeHtml(p.seriesName)} <br/>${escapeHtml(p.name)}: ${escapeHtml(formatTooltipValue(p.value, tooltipValueFormatter))} (${p.percent}%)`;
87
+ }
88
+ : '{a} <br/>{b}: {c} ({d}%)',
89
+ },
90
+ {
91
+ show: showTooltip,
92
+ formatter: tooltipFormatter,
93
+ valueFormatter: tooltipValueFormatter,
94
+ getLabel,
58
95
  }
59
- : undefined,
60
- series: [
61
- {
62
- name: title,
63
- type: 'pie',
64
- radius: innerRadius === 0 ? '70%' : ['50%', '70%'],
65
- center: ['50%', '45%'],
66
- data: data.map((item) => ({
67
- name: item.name,
68
- value: item.value,
69
- itemStyle: item.color ? { color: item.color } : undefined,
70
- })),
71
- label: showLabels
72
- ? {
96
+ ),
97
+ legend: buildLegend(
98
+ data.map((d) => d.name),
99
+ legendConfig,
100
+ { show: showLegend, custom: !!renderLegend, defaults: { orient: 'horizontal' } }
101
+ ),
102
+ series: [
103
+ {
104
+ name: title,
105
+ type: 'pie',
106
+ radius: innerRadius ? [innerRadius, '70%'] : '70%',
107
+ center: legendCenter(legendConfig, showLegend && !renderLegend, ['50%', '45%']),
108
+ data: data.map((item) => ({
109
+ name: item.name,
110
+ value: item.value,
111
+ itemStyle: item.color ? { color: item.color } : undefined,
112
+ label: pointLabel(item.label, labelFormatter, { position: labelPosition }),
113
+ })),
114
+ label: buildDataLabel(
115
+ {
73
116
  show: true,
74
117
  position: labelPosition,
75
118
  formatter: labelPosition === 'inside' ? '{d}%' : '{b}: {d}%',
76
- }
77
- : {
78
- show: false,
79
119
  },
80
- emphasis: {
81
- itemStyle: {
82
- shadowBlur: 10,
83
- shadowOffsetX: 0,
84
- shadowColor: 'rgba(0, 0, 0, 0.5)',
120
+ { show: false },
121
+ { show: showLabels, formatter: labelFormatter, getLabel }
122
+ ),
123
+ emphasis: {
124
+ itemStyle: {
125
+ shadowBlur: 10,
126
+ shadowOffsetX: 0,
127
+ shadowColor: 'rgba(0, 0, 0, 0.5)',
128
+ },
85
129
  },
86
130
  },
87
- },
88
- ],
89
- };
131
+ ],
132
+ } as EChartsOption,
133
+ optionOverride
134
+ );
90
135
  });
91
136
 
92
137
  const accessibilityDescription = $derived(
93
138
  description || (!isEmpty ? generatePieChartDescription(title, data, false) : undefined)
94
139
  );
95
140
 
141
+ // Legend selection state, shared with a custom legend
142
+ const legend = useChartLegend({
143
+ names: () => data?.map((d) => d.name) ?? [],
144
+ colors: () => data?.map((d) => d.color) ?? [],
145
+ palette: () => getChartColors(chart.theme).series,
146
+ config: () => legendConfig,
147
+ title: () => title,
148
+ onLegendChange: () => onLegendChange,
149
+ });
150
+
96
151
  // Manage ECharts instance lifecycle (init, theme, resize, option updates)
97
152
  const chart = useChart({
98
153
  option: () => option,
@@ -100,6 +155,7 @@
100
155
  animation: () => animation,
101
156
  isEmpty: () => isEmpty,
102
157
  onReady: (instance) => {
158
+ legend.attach(instance);
103
159
  if (onClick) {
104
160
  instance.on('click', (params) => {
105
161
  onClick({
@@ -116,6 +172,10 @@
116
172
  });
117
173
  </script>
118
174
 
175
+ {#snippet customLegend()}
176
+ {@render renderLegend?.(legend.renderProps)}
177
+ {/snippet}
178
+
119
179
  <ChartContainer
120
180
  {title}
121
181
  description={accessibilityDescription}
@@ -125,6 +185,8 @@
125
185
  empty={isEmpty}
126
186
  {emptyMessage}
127
187
  class={cn(className)}
188
+ legend={renderLegend ? customLegend : undefined}
189
+ legendPosition={legendConfig?.position}
128
190
  >
129
191
  <div bind:this={chart.container} class="h-full w-full"></div>
130
192
  </ChartContainer>
@@ -1,5 +1,6 @@
1
1
  import type { PieChartProps } from '@classic-homes/charts-core';
2
- interface Props extends Omit<PieChartProps, 'class'> {
2
+ import { type CustomLegendProps } from '../../composables/useChartLegend.svelte.js';
3
+ interface Props extends Omit<PieChartProps, 'class'>, CustomLegendProps {
3
4
  class?: string;
4
5
  }
5
6
  declare const PieChart: import("svelte").Component<Props, {}, "">;
@@ -1,6 +1,9 @@
1
1
  <script lang="ts">
2
2
  import * as echarts from 'echarts/core';
3
- import { ScatterChart as EChartsScatterChart } from 'echarts/charts';
3
+ import {
4
+ ScatterChart as EChartsScatterChart,
5
+ LineChart as EChartsLineChart,
6
+ } from 'echarts/charts';
4
7
  import {
5
8
  GridComponent,
6
9
  TooltipComponent,
@@ -11,15 +14,34 @@
11
14
  import type { EChartsOption } from 'echarts';
12
15
 
13
16
  import type { ScatterChartProps, DataPointEventParams } from '@classic-homes/charts-core';
14
- import { generateScatterChartDescription } from '@classic-homes/charts-core';
17
+ import {
18
+ buildAxis,
19
+ buildDataLabel,
20
+ buildLegend,
21
+ buildTooltip,
22
+ escapeHtml,
23
+ formatTooltipValue,
24
+ generateScatterChartDescription,
25
+ getChartColors,
26
+ gridInset,
27
+ mergeChartOption,
28
+ seriesLabelLookup,
29
+ withPointLabels,
30
+ } from '@classic-homes/charts-core';
15
31
 
16
32
  import { cn } from '../../utils.js';
17
33
  import { useChart } from '../../composables/useChart.svelte.js';
34
+ import {
35
+ useChartLegend,
36
+ type CustomLegendProps,
37
+ } from '../../composables/useChartLegend.svelte.js';
18
38
  import ChartContainer from '../base/ChartContainer.svelte';
19
39
 
20
40
  // Register required ECharts modules
21
41
  echarts.use([
22
42
  EChartsScatterChart,
43
+ // Trend lines are line series
44
+ EChartsLineChart,
23
45
  GridComponent,
24
46
  TooltipComponent,
25
47
  LegendComponent,
@@ -27,7 +49,7 @@
27
49
  CanvasRenderer,
28
50
  ]);
29
51
 
30
- interface Props extends Omit<ScatterChartProps, 'class'> {
52
+ interface Props extends Omit<ScatterChartProps, 'class'>, CustomLegendProps {
31
53
  class?: string;
32
54
  }
33
55
 
@@ -46,6 +68,16 @@
46
68
  showGrid = true,
47
69
  showTrendLine = false,
48
70
  symbolSize = 10,
71
+ showLabels = false,
72
+ labelFormatter,
73
+ xAxisConfig,
74
+ yAxisConfig,
75
+ tooltipFormatter,
76
+ tooltipValueFormatter,
77
+ legend: legendConfig,
78
+ renderLegend,
79
+ onLegendChange,
80
+ option: optionOverride,
49
81
  onClick,
50
82
  class: className,
51
83
  }: Props = $props();
@@ -55,12 +87,19 @@
55
87
  const option: EChartsOption = $derived.by(() => {
56
88
  if (isEmpty) return {};
57
89
 
90
+ const getLabel = seriesLabelLookup(data.series);
91
+
58
92
  const series: EChartsOption['series'] = data.series.map((s) => ({
59
93
  name: s.name,
60
94
  type: 'scatter',
61
- data: s.data,
95
+ data: withPointLabels(s.data, s.labels, labelFormatter, { position: 'top' }),
62
96
  symbolSize: typeof symbolSize === 'function' ? symbolSize : symbolSize,
63
97
  itemStyle: s.color ? { color: s.color } : undefined,
98
+ label: buildDataLabel({ show: true, position: 'top' }, undefined, {
99
+ show: showLabels,
100
+ formatter: labelFormatter,
101
+ getLabel,
102
+ }),
64
103
  emphasis: {
65
104
  focus: 'series',
66
105
  itemStyle: {
@@ -91,9 +130,13 @@
91
130
  const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
92
131
  const intercept = (sumY - slope * sumX) / n;
93
132
 
94
- const xValues = s.data.map(([x]) => x);
95
- const minX = Math.min(...xValues);
96
- const maxX = Math.max(...xValues);
133
+ // A loop: spreading large arrays into Math.min/max overflows the stack
134
+ let minX = Infinity;
135
+ let maxX = -Infinity;
136
+ for (const [x] of s.data) {
137
+ if (x < minX) minX = x;
138
+ if (x > maxX) maxX = x;
139
+ }
97
140
 
98
141
  (series as unknown[]).push({
99
142
  name: `${s.name} Trend`,
@@ -116,49 +159,76 @@
116
159
  });
117
160
  }
118
161
 
119
- return {
120
- grid: {
121
- left: '3%',
122
- right: '4%',
123
- bottom: '3%',
124
- containLabel: true,
125
- },
126
- tooltip: showTooltip
127
- ? {
162
+ return mergeChartOption(
163
+ {
164
+ grid: {
165
+ left: '3%',
166
+ right: '4%',
167
+ bottom: '3%',
168
+ containLabel: true,
169
+ ...gridInset(legendConfig, showLegend && !renderLegend, xAxisConfig, yAxisConfig),
170
+ },
171
+ tooltip: buildTooltip(
172
+ {
128
173
  trigger: 'item' as const,
129
174
  formatter: (params: unknown) => {
130
175
  const p = params as { seriesName?: string; value?: number[] };
131
176
  if (!p.value) return '';
132
- return `${p.seriesName}<br/>X: ${p.value[0]}<br/>Y: ${p.value[1]}`;
177
+ const x = formatTooltipValue(p.value[0], tooltipValueFormatter);
178
+ const y = formatTooltipValue(p.value[1], tooltipValueFormatter);
179
+ return `${escapeHtml(p.seriesName)}<br/>X: ${escapeHtml(x)}<br/>Y: ${escapeHtml(y)}`;
133
180
  },
181
+ },
182
+ {
183
+ show: showTooltip,
184
+ formatter: tooltipFormatter,
185
+ valueFormatter: tooltipValueFormatter,
186
+ getLabel,
134
187
  }
135
- : undefined,
136
- legend: showLegend
137
- ? {
138
- data: data.series.map((s) => s.name),
139
- bottom: 0,
140
- }
141
- : undefined,
142
- xAxis: {
143
- type: 'value',
144
- splitLine: {
145
- show: showGrid,
146
- },
147
- },
148
- yAxis: {
149
- type: 'value',
150
- splitLine: {
151
- show: showGrid,
152
- },
153
- },
154
- series,
155
- };
188
+ ),
189
+ legend: buildLegend(
190
+ data.series.map((s) => s.name),
191
+ legendConfig,
192
+ { show: showLegend, custom: !!renderLegend }
193
+ ),
194
+ xAxis: buildAxis(
195
+ {
196
+ type: 'value',
197
+ splitLine: {
198
+ show: showGrid,
199
+ },
200
+ },
201
+ xAxisConfig
202
+ ),
203
+ yAxis: buildAxis(
204
+ {
205
+ type: 'value',
206
+ splitLine: {
207
+ show: showGrid,
208
+ },
209
+ },
210
+ yAxisConfig
211
+ ),
212
+ series,
213
+ } as EChartsOption,
214
+ optionOverride
215
+ );
156
216
  });
157
217
 
158
218
  const accessibilityDescription = $derived(
159
219
  description || (!isEmpty ? generateScatterChartDescription(title, data) : undefined)
160
220
  );
161
221
 
222
+ // Legend selection state, shared with a custom legend
223
+ const legend = useChartLegend({
224
+ names: () => data?.series?.map((s) => s.name) ?? [],
225
+ colors: () => data?.series?.map((s) => s.color) ?? [],
226
+ palette: () => getChartColors(chart.theme).series,
227
+ config: () => legendConfig,
228
+ title: () => title,
229
+ onLegendChange: () => onLegendChange,
230
+ });
231
+
162
232
  // Manage ECharts instance lifecycle (init, theme, resize, option updates)
163
233
  const chart = useChart({
164
234
  option: () => option,
@@ -166,6 +236,7 @@
166
236
  animation: () => animation,
167
237
  isEmpty: () => isEmpty,
168
238
  onReady: (instance) => {
239
+ legend.attach(instance);
169
240
  if (onClick) {
170
241
  instance.on('click', (params) => {
171
242
  onClick({
@@ -184,6 +255,10 @@
184
255
  });
185
256
  </script>
186
257
 
258
+ {#snippet customLegend()}
259
+ {@render renderLegend?.(legend.renderProps)}
260
+ {/snippet}
261
+
187
262
  <ChartContainer
188
263
  {title}
189
264
  description={accessibilityDescription}
@@ -193,6 +268,8 @@
193
268
  empty={isEmpty}
194
269
  {emptyMessage}
195
270
  class={cn(className)}
271
+ legend={renderLegend ? customLegend : undefined}
272
+ legendPosition={legendConfig?.position}
196
273
  >
197
274
  <div bind:this={chart.container} class="h-full w-full"></div>
198
275
  </ChartContainer>
@@ -1,5 +1,6 @@
1
1
  import type { ScatterChartProps } from '@classic-homes/charts-core';
2
- interface Props extends Omit<ScatterChartProps, 'class'> {
2
+ import { type CustomLegendProps } from '../../composables/useChartLegend.svelte.js';
3
+ interface Props extends Omit<ScatterChartProps, 'class'>, CustomLegendProps {
3
4
  class?: string;
4
5
  }
5
6
  declare const ScatterChart: import("svelte").Component<Props, {}, "">;
@@ -14,6 +14,12 @@
14
14
  import type { EChartsOption } from 'echarts';
15
15
 
16
16
  import type { CandlestickChartProps, CandlestickEventParams } from '@classic-homes/charts-core';
17
+ import {
18
+ buildAxis,
19
+ buildTooltip,
20
+ getChartColors,
21
+ mergeChartOption,
22
+ } from '@classic-homes/charts-core';
17
23
 
18
24
  import { cn } from '../../utils.js';
19
25
  import { useChart } from '../../composables/useChart.svelte.js';
@@ -45,12 +51,20 @@
45
51
  animation = true,
46
52
  showTooltip = true,
47
53
  showVolume = false,
54
+ xAxisConfig,
55
+ yAxisConfig,
56
+ tooltipFormatter,
57
+ tooltipValueFormatter,
58
+ option: optionOverride,
48
59
  onClick,
49
60
  class: className,
50
61
  }: Props = $props();
51
62
 
52
63
  const isEmpty = $derived(!data?.dates?.length || !data?.data?.length);
53
64
 
65
+ // Per-element colours that follow the resolved chart theme
66
+ const chartColors = $derived.by(() => getChartColors(chart.theme));
67
+
54
68
  const option: EChartsOption = $derived.by(() => {
55
69
  if (isEmpty) return {};
56
70
 
@@ -62,10 +76,10 @@
62
76
  type: 'candlestick',
63
77
  data: candlestickData,
64
78
  itemStyle: {
65
- color: '#ef4444',
66
- color0: '#22c55e',
67
- borderColor: '#ef4444',
68
- borderColor0: '#22c55e',
79
+ color: chartColors.destructive,
80
+ color0: chartColors.success,
81
+ borderColor: chartColors.destructive,
82
+ borderColor0: chartColors.success,
69
83
  },
70
84
  },
71
85
  ];
@@ -73,18 +87,24 @@
73
87
  const gridArr: { left: string; right: string; top: number | string; height: string }[] = [
74
88
  { left: '10%', right: '10%', top: 60, height: showVolume ? '50%' : '70%' },
75
89
  ];
90
+ // Axis customisation applies to the main (price) axes only
76
91
  const xAxisArr: unknown[] = [
77
- {
78
- type: 'category',
79
- data: data.dates,
80
- boundaryGap: false,
81
- axisLine: { onZero: false },
82
- splitLine: { show: false },
83
- min: 'dataMin',
84
- max: 'dataMax',
85
- },
92
+ buildAxis(
93
+ {
94
+ type: 'category',
95
+ data: data.dates,
96
+ boundaryGap: false,
97
+ axisLine: { onZero: false },
98
+ splitLine: { show: false },
99
+ min: 'dataMin',
100
+ max: 'dataMax',
101
+ },
102
+ xAxisConfig
103
+ ),
104
+ ];
105
+ const yAxisArr: unknown[] = [
106
+ buildAxis({ scale: true, splitArea: { show: true } }, yAxisConfig),
86
107
  ];
87
- const yAxisArr: unknown[] = [{ scale: true, splitArea: { show: true } }];
88
108
 
89
109
  if (showVolume && data.volume) {
90
110
  gridArr.push({ left: '10%', right: '10%', top: '73%', height: '12%' });
@@ -115,21 +135,27 @@
115
135
  xAxisIndex: 1,
116
136
  yAxisIndex: 1,
117
137
  data: data.volume,
118
- itemStyle: { color: '#6b7280' },
138
+ itemStyle: { color: chartColors.mutedForeground },
119
139
  });
120
140
  }
121
141
 
122
- return {
123
- tooltip: showTooltip ? { trigger: 'axis', axisPointer: { type: 'cross' } } : undefined,
124
- grid: gridArr,
125
- xAxis: xAxisArr as EChartsOption['xAxis'],
126
- yAxis: yAxisArr as EChartsOption['yAxis'],
127
- dataZoom: [
128
- { type: 'inside', start: 80, end: 100 },
129
- { show: true, type: 'slider', top: '90%', start: 80, end: 100 },
130
- ],
131
- series: seriesArr,
132
- };
142
+ return mergeChartOption(
143
+ {
144
+ tooltip: buildTooltip(
145
+ { trigger: 'axis', axisPointer: { type: 'cross' } },
146
+ { show: showTooltip, formatter: tooltipFormatter, valueFormatter: tooltipValueFormatter }
147
+ ),
148
+ grid: gridArr,
149
+ xAxis: xAxisArr as EChartsOption['xAxis'],
150
+ yAxis: yAxisArr as EChartsOption['yAxis'],
151
+ dataZoom: [
152
+ { type: 'inside', start: 80, end: 100 },
153
+ { show: true, type: 'slider', top: '90%', start: 80, end: 100 },
154
+ ],
155
+ series: seriesArr,
156
+ } as EChartsOption,
157
+ optionOverride
158
+ );
133
159
  });
134
160
 
135
161
  const accessibilityDescription = $derived(
@@ -148,6 +174,7 @@
148
174
  instance.on('click', (params) => {
149
175
  const dataIndex = params.dataIndex || 0;
150
176
  const item = data.data[dataIndex];
177
+ if (!item) return;
151
178
  onClick({
152
179
  type: params.type || 'click',
153
180
  componentType: params.componentType || 'series',