@classic-homes/charts-svelte 0.2.0 → 0.3.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/dist/lib/components/base/ChartContainer.svelte +141 -14
- package/dist/lib/components/base/ChartContainer.svelte.d.ts +19 -1
- package/dist/lib/components/base/ChartDataTable.svelte +59 -0
- package/dist/lib/components/base/ChartDataTable.svelte.d.ts +15 -0
- package/dist/lib/components/base/ChartEmpty.svelte +1 -0
- package/dist/lib/components/base/ChartSkeleton.svelte +7 -2
- package/dist/lib/components/base/ChartSkeleton.svelte.d.ts +2 -0
- package/dist/lib/components/base/index.d.ts +3 -0
- package/dist/lib/components/base/index.js +1 -0
- package/dist/lib/components/core/AreaChart.svelte +115 -70
- package/dist/lib/components/core/BarChart.svelte +134 -62
- package/dist/lib/components/core/DonutChart.svelte +41 -22
- package/dist/lib/components/core/LineChart.svelte +104 -73
- package/dist/lib/components/core/PieChart.svelte +41 -22
- package/dist/lib/components/core/ScatterChart.svelte +72 -68
- package/dist/lib/components/extended/CandlestickChart.svelte +67 -44
- package/dist/lib/components/extended/FunnelChart.svelte +41 -27
- package/dist/lib/components/extended/GaugeChart.svelte +61 -10
- package/dist/lib/components/extended/HeatmapChart.svelte +62 -33
- package/dist/lib/components/extended/RadarChart.svelte +39 -19
- package/dist/lib/components/extended/SankeyChart.svelte +50 -20
- package/dist/lib/components/extended/TreemapChart.svelte +53 -21
- package/dist/lib/components/index.d.ts +2 -1
- package/dist/lib/components/index.js +3 -1
- package/dist/lib/components/report/BoxPlotChart.svelte +253 -0
- package/dist/lib/components/report/BoxPlotChart.svelte.d.ts +7 -0
- package/dist/lib/components/report/BulletChart.svelte +231 -0
- package/dist/lib/components/report/BulletChart.svelte.d.ts +7 -0
- package/dist/lib/components/report/ComboChart.svelte +327 -0
- package/dist/lib/components/report/ComboChart.svelte.d.ts +8 -0
- package/dist/lib/components/report/DotPlotChart.svelte +325 -0
- package/dist/lib/components/report/DotPlotChart.svelte.d.ts +8 -0
- package/dist/lib/components/report/HistogramChart.svelte +226 -0
- package/dist/lib/components/report/HistogramChart.svelte.d.ts +7 -0
- package/dist/lib/components/report/Sparkline.svelte +181 -0
- package/dist/lib/components/report/Sparkline.svelte.d.ts +4 -0
- package/dist/lib/components/report/StatCard.svelte +146 -0
- package/dist/lib/components/report/StatCard.svelte.d.ts +4 -0
- package/dist/lib/components/report/StatGroup.svelte +35 -0
- package/dist/lib/components/report/StatGroup.svelte.d.ts +12 -0
- package/dist/lib/components/report/WaterfallChart.svelte +271 -0
- package/dist/lib/components/report/WaterfallChart.svelte.d.ts +7 -0
- package/dist/lib/components/report/index.d.ts +12 -0
- package/dist/lib/components/report/index.js +9 -0
- package/dist/lib/composables/index.d.ts +1 -1
- package/dist/lib/composables/index.js +1 -1
- package/dist/lib/composables/useChart.svelte.d.ts +34 -0
- package/dist/lib/composables/useChart.svelte.js +121 -9
- package/dist/lib/composables/useChartTheme.svelte.js +14 -11
- package/dist/lib/events.d.ts +15 -0
- package/dist/lib/events.js +56 -0
- package/dist/lib/index.d.ts +4 -3
- package/dist/lib/index.js +7 -3
- package/package.json +10 -4
|
@@ -4,32 +4,32 @@
|
|
|
4
4
|
ScatterChart as EChartsScatterChart,
|
|
5
5
|
LineChart as EChartsLineChart,
|
|
6
6
|
} from 'echarts/charts';
|
|
7
|
-
import {
|
|
8
|
-
GridComponent,
|
|
9
|
-
TooltipComponent,
|
|
10
|
-
LegendComponent,
|
|
11
|
-
TitleComponent,
|
|
12
|
-
} from 'echarts/components';
|
|
13
|
-
import { CanvasRenderer } from 'echarts/renderers';
|
|
7
|
+
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components';
|
|
14
8
|
import type { EChartsOption } from 'echarts';
|
|
15
9
|
|
|
16
|
-
import type { ScatterChartProps
|
|
10
|
+
import type { ScatterChartProps } from '@classic-homes/charts-core';
|
|
17
11
|
import {
|
|
18
12
|
buildAxis,
|
|
19
13
|
buildDataLabel,
|
|
20
14
|
buildLegend,
|
|
21
15
|
buildTooltip,
|
|
16
|
+
chartFormatters,
|
|
22
17
|
escapeHtml,
|
|
23
18
|
formatTooltipValue,
|
|
24
19
|
generateScatterChartDescription,
|
|
25
20
|
getChartColors,
|
|
26
21
|
gridInset,
|
|
27
22
|
mergeChartOption,
|
|
23
|
+
resolveValueFormatter,
|
|
24
|
+
scatterTable,
|
|
28
25
|
seriesLabelLookup,
|
|
26
|
+
trendLine,
|
|
29
27
|
withPointLabels,
|
|
28
|
+
withReferenceMarks,
|
|
30
29
|
} from '@classic-homes/charts-core';
|
|
31
30
|
|
|
32
31
|
import { cn } from '../../utils.js';
|
|
32
|
+
import { chartEvents, toDataPointParams } from '../../events.js';
|
|
33
33
|
import { useChart } from '../../composables/useChart.svelte.js';
|
|
34
34
|
import {
|
|
35
35
|
useChartLegend,
|
|
@@ -45,8 +45,6 @@
|
|
|
45
45
|
GridComponent,
|
|
46
46
|
TooltipComponent,
|
|
47
47
|
LegendComponent,
|
|
48
|
-
TitleComponent,
|
|
49
|
-
CanvasRenderer,
|
|
50
48
|
]);
|
|
51
49
|
|
|
52
50
|
interface Props extends Omit<ScatterChartProps, 'class'>, CustomLegendProps {
|
|
@@ -58,8 +56,11 @@
|
|
|
58
56
|
description,
|
|
59
57
|
data,
|
|
60
58
|
height = 400,
|
|
59
|
+
minHeight,
|
|
60
|
+
maxHeight,
|
|
61
61
|
loading,
|
|
62
62
|
error,
|
|
63
|
+
onRetry,
|
|
63
64
|
emptyMessage,
|
|
64
65
|
theme = 'auto',
|
|
65
66
|
animation = true,
|
|
@@ -69,35 +70,53 @@
|
|
|
69
70
|
showTrendLine = false,
|
|
70
71
|
symbolSize = 10,
|
|
71
72
|
showLabels = false,
|
|
73
|
+
valueFormat,
|
|
74
|
+
locale,
|
|
72
75
|
labelFormatter,
|
|
73
76
|
xAxisConfig,
|
|
74
77
|
yAxisConfig,
|
|
78
|
+
referenceLines,
|
|
79
|
+
referenceBands,
|
|
75
80
|
tooltipFormatter,
|
|
76
81
|
tooltipValueFormatter,
|
|
77
82
|
legend: legendConfig,
|
|
78
83
|
renderLegend,
|
|
79
84
|
onLegendChange,
|
|
80
85
|
option: optionOverride,
|
|
86
|
+
onReady,
|
|
87
|
+
events,
|
|
88
|
+
showDataTable,
|
|
89
|
+
exportable,
|
|
90
|
+
patterns,
|
|
81
91
|
onClick,
|
|
92
|
+
onHover,
|
|
82
93
|
class: className,
|
|
83
94
|
}: Props = $props();
|
|
84
95
|
|
|
85
96
|
const isEmpty = $derived(!data?.series?.length || data.series.every((s) => !s.data?.length));
|
|
86
97
|
|
|
98
|
+
// `valueFormat` applies to y values; format x with `xAxisConfig.formatter`
|
|
99
|
+
const formatters = $derived(
|
|
100
|
+
chartFormatters({ valueFormat, locale, tooltipValueFormatter, labelFormatter })
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Per-element colours that follow the resolved chart theme
|
|
104
|
+
const chartColors = $derived.by(() => getChartColors(chart.theme));
|
|
105
|
+
|
|
87
106
|
const option: EChartsOption = $derived.by(() => {
|
|
88
107
|
if (isEmpty) return {};
|
|
89
108
|
|
|
90
109
|
const getLabel = seriesLabelLookup(data.series);
|
|
91
110
|
|
|
92
|
-
const series:
|
|
111
|
+
const series: Record<string, unknown>[] = data.series.map((s) => ({
|
|
93
112
|
name: s.name,
|
|
94
113
|
type: 'scatter',
|
|
95
|
-
data: withPointLabels(s.data, s.labels,
|
|
96
|
-
symbolSize
|
|
114
|
+
data: withPointLabels(s.data, s.labels, formatters.label, { position: 'top' }),
|
|
115
|
+
symbolSize,
|
|
97
116
|
itemStyle: s.color ? { color: s.color } : undefined,
|
|
98
117
|
label: buildDataLabel({ show: true, position: 'top' }, undefined, {
|
|
99
118
|
show: showLabels,
|
|
100
|
-
formatter:
|
|
119
|
+
formatter: formatters.label,
|
|
101
120
|
getLabel,
|
|
102
121
|
}),
|
|
103
122
|
emphasis: {
|
|
@@ -109,47 +128,24 @@
|
|
|
109
128
|
},
|
|
110
129
|
}));
|
|
111
130
|
|
|
112
|
-
//
|
|
131
|
+
// Trend lines share their series' name and colour, so the legend toggles
|
|
132
|
+
// both together; they ignore the pointer
|
|
113
133
|
if (showTrendLine) {
|
|
114
134
|
data.series.forEach((s, index) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
sumY = 0,
|
|
120
|
-
sumXY = 0,
|
|
121
|
-
sumX2 = 0;
|
|
122
|
-
|
|
123
|
-
s.data.forEach(([x, y]) => {
|
|
124
|
-
sumX += x;
|
|
125
|
-
sumY += y;
|
|
126
|
-
sumXY += x * y;
|
|
127
|
-
sumX2 += x * x;
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
|
|
131
|
-
const intercept = (sumY - slope * sumX) / n;
|
|
132
|
-
|
|
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
|
-
}
|
|
140
|
-
|
|
141
|
-
(series as unknown[]).push({
|
|
142
|
-
name: `${s.name} Trend`,
|
|
135
|
+
const ends = trendLine(s.data);
|
|
136
|
+
if (!ends) return;
|
|
137
|
+
series.push({
|
|
138
|
+
name: s.name,
|
|
143
139
|
type: 'line',
|
|
144
|
-
data:
|
|
145
|
-
[minX, slope * minX + intercept],
|
|
146
|
-
[maxX, slope * maxX + intercept],
|
|
147
|
-
],
|
|
140
|
+
data: ends,
|
|
148
141
|
smooth: false,
|
|
149
142
|
showSymbol: false,
|
|
143
|
+
silent: true,
|
|
144
|
+
tooltip: { show: false },
|
|
150
145
|
lineStyle: {
|
|
151
146
|
type: 'dashed',
|
|
152
147
|
opacity: 0.7,
|
|
148
|
+
color: s.color || chartColors.series[index % chartColors.series.length],
|
|
153
149
|
},
|
|
154
150
|
emphasis: {
|
|
155
151
|
disabled: true,
|
|
@@ -175,14 +171,14 @@
|
|
|
175
171
|
const p = params as { seriesName?: string; value?: number[] };
|
|
176
172
|
if (!p.value) return '';
|
|
177
173
|
const x = formatTooltipValue(p.value[0], tooltipValueFormatter);
|
|
178
|
-
const y = formatTooltipValue(p.value[1],
|
|
174
|
+
const y = formatTooltipValue(p.value[1], formatters.tooltip);
|
|
179
175
|
return `${escapeHtml(p.seriesName)}<br/>X: ${escapeHtml(x)}<br/>Y: ${escapeHtml(y)}`;
|
|
180
176
|
},
|
|
181
177
|
},
|
|
182
178
|
{
|
|
183
179
|
show: showTooltip,
|
|
184
180
|
formatter: tooltipFormatter,
|
|
185
|
-
valueFormatter:
|
|
181
|
+
valueFormatter: formatters.tooltip,
|
|
186
182
|
getLabel,
|
|
187
183
|
}
|
|
188
184
|
),
|
|
@@ -207,9 +203,14 @@
|
|
|
207
203
|
show: showGrid,
|
|
208
204
|
},
|
|
209
205
|
},
|
|
210
|
-
yAxisConfig
|
|
206
|
+
formatters.axis(yAxisConfig)
|
|
211
207
|
),
|
|
212
|
-
series,
|
|
208
|
+
series: withReferenceMarks(series, referenceLines, referenceBands, {
|
|
209
|
+
valueAxis: 'y',
|
|
210
|
+
format: formatters.format,
|
|
211
|
+
color: chartColors.reference,
|
|
212
|
+
bandColor: chartColors.referenceBand,
|
|
213
|
+
}),
|
|
213
214
|
} as EChartsOption,
|
|
214
215
|
optionOverride
|
|
215
216
|
);
|
|
@@ -219,6 +220,10 @@
|
|
|
219
220
|
description || (!isEmpty ? generateScatterChartDescription(title, data) : undefined)
|
|
220
221
|
);
|
|
221
222
|
|
|
223
|
+
const dataTable = $derived(
|
|
224
|
+
isEmpty ? undefined : scatterTable(data, xAxisConfig?.name, yAxisConfig?.name)
|
|
225
|
+
);
|
|
226
|
+
|
|
222
227
|
// Legend selection state, shared with a custom legend
|
|
223
228
|
const legend = useChartLegend({
|
|
224
229
|
names: () => data?.series?.map((s) => s.name) ?? [],
|
|
@@ -235,23 +240,14 @@
|
|
|
235
240
|
theme: () => theme,
|
|
236
241
|
animation: () => animation,
|
|
237
242
|
isEmpty: () => isEmpty,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
seriesName: params.seriesName || '',
|
|
247
|
-
dataIndex: params.dataIndex || 0,
|
|
248
|
-
name: params.name || '',
|
|
249
|
-
value: params.value as number[],
|
|
250
|
-
color: params.color as string,
|
|
251
|
-
} as DataPointEventParams);
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
},
|
|
243
|
+
patterns: () => patterns,
|
|
244
|
+
events: () =>
|
|
245
|
+
chartEvents(events, {
|
|
246
|
+
click: onClick && ((params) => onClick(toDataPointParams(params, 'click'))),
|
|
247
|
+
mouseover: onHover && ((params) => onHover(toDataPointParams(params, 'mouseover'))),
|
|
248
|
+
}),
|
|
249
|
+
onReady: (instance) => legend.attach(instance),
|
|
250
|
+
onCreated: () => onReady,
|
|
255
251
|
});
|
|
256
252
|
</script>
|
|
257
253
|
|
|
@@ -263,13 +259,21 @@
|
|
|
263
259
|
{title}
|
|
264
260
|
description={accessibilityDescription}
|
|
265
261
|
{height}
|
|
262
|
+
{minHeight}
|
|
263
|
+
{maxHeight}
|
|
266
264
|
{loading}
|
|
267
265
|
{error}
|
|
266
|
+
{onRetry}
|
|
268
267
|
empty={isEmpty}
|
|
269
268
|
{emptyMessage}
|
|
270
269
|
class={cn(className)}
|
|
271
270
|
legend={renderLegend ? customLegend : undefined}
|
|
272
271
|
legendPosition={legendConfig?.position}
|
|
272
|
+
{dataTable}
|
|
273
|
+
{showDataTable}
|
|
274
|
+
format={resolveValueFormatter(valueFormat, locale)}
|
|
275
|
+
{exportable}
|
|
276
|
+
exportImage={chart.exportImage}
|
|
273
277
|
>
|
|
274
278
|
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
275
279
|
</ChartContainer>
|
|
@@ -4,36 +4,25 @@
|
|
|
4
4
|
CandlestickChart as EChartsCandlestickChart,
|
|
5
5
|
BarChart as EChartsBarChart,
|
|
6
6
|
} from 'echarts/charts';
|
|
7
|
-
import {
|
|
8
|
-
GridComponent,
|
|
9
|
-
TooltipComponent,
|
|
10
|
-
TitleComponent,
|
|
11
|
-
DataZoomComponent,
|
|
12
|
-
} from 'echarts/components';
|
|
13
|
-
import { CanvasRenderer } from 'echarts/renderers';
|
|
7
|
+
import { GridComponent, TooltipComponent } from 'echarts/components';
|
|
14
8
|
import type { EChartsOption } from 'echarts';
|
|
15
9
|
|
|
16
10
|
import type { CandlestickChartProps, CandlestickEventParams } from '@classic-homes/charts-core';
|
|
17
11
|
import {
|
|
18
12
|
buildAxis,
|
|
19
13
|
buildTooltip,
|
|
14
|
+
candlestickTable,
|
|
20
15
|
getChartColors,
|
|
21
16
|
mergeChartOption,
|
|
17
|
+
resolveValueFormatter,
|
|
22
18
|
} from '@classic-homes/charts-core';
|
|
23
19
|
|
|
24
20
|
import { cn } from '../../utils.js';
|
|
21
|
+
import { chartEvents } from '../../events.js';
|
|
25
22
|
import { useChart } from '../../composables/useChart.svelte.js';
|
|
26
23
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
27
24
|
|
|
28
|
-
echarts.use([
|
|
29
|
-
EChartsCandlestickChart,
|
|
30
|
-
EChartsBarChart,
|
|
31
|
-
GridComponent,
|
|
32
|
-
TooltipComponent,
|
|
33
|
-
TitleComponent,
|
|
34
|
-
DataZoomComponent,
|
|
35
|
-
CanvasRenderer,
|
|
36
|
-
]);
|
|
25
|
+
echarts.use([EChartsCandlestickChart, EChartsBarChart, GridComponent, TooltipComponent]);
|
|
37
26
|
|
|
38
27
|
interface Props extends Omit<CandlestickChartProps, 'class'> {
|
|
39
28
|
class?: string;
|
|
@@ -44,22 +33,39 @@
|
|
|
44
33
|
description,
|
|
45
34
|
data,
|
|
46
35
|
height = 400,
|
|
36
|
+
minHeight,
|
|
37
|
+
maxHeight,
|
|
47
38
|
loading,
|
|
48
39
|
error,
|
|
40
|
+
onRetry,
|
|
49
41
|
emptyMessage,
|
|
50
42
|
theme = 'auto',
|
|
51
43
|
animation = true,
|
|
52
44
|
showTooltip = true,
|
|
53
45
|
showVolume = false,
|
|
46
|
+
showGrid = true,
|
|
47
|
+
zoomStart = 80,
|
|
54
48
|
xAxisConfig,
|
|
55
49
|
yAxisConfig,
|
|
56
50
|
tooltipFormatter,
|
|
57
51
|
tooltipValueFormatter,
|
|
58
52
|
option: optionOverride,
|
|
53
|
+
valueFormat,
|
|
54
|
+
locale,
|
|
55
|
+
onReady,
|
|
56
|
+
events,
|
|
57
|
+
showDataTable,
|
|
58
|
+
exportable,
|
|
59
|
+
patterns,
|
|
59
60
|
onClick,
|
|
60
61
|
class: className,
|
|
61
62
|
}: Props = $props();
|
|
62
63
|
|
|
64
|
+
// `valueFormat` writes values in tooltips and the data table
|
|
65
|
+
const valueFormatter = $derived(
|
|
66
|
+
tooltipValueFormatter ?? resolveValueFormatter(valueFormat, locale)
|
|
67
|
+
);
|
|
68
|
+
|
|
63
69
|
const isEmpty = $derived(!data?.dates?.length || !data?.data?.length);
|
|
64
70
|
|
|
65
71
|
// Per-element colours that follow the resolved chart theme
|
|
@@ -75,11 +81,12 @@
|
|
|
75
81
|
name: title,
|
|
76
82
|
type: 'candlestick',
|
|
77
83
|
data: candlestickData,
|
|
84
|
+
// ECharts draws rising candles with `color` and falling ones with `color0`
|
|
78
85
|
itemStyle: {
|
|
79
|
-
color: chartColors.
|
|
80
|
-
color0: chartColors.
|
|
81
|
-
borderColor: chartColors.
|
|
82
|
-
borderColor0: chartColors.
|
|
86
|
+
color: chartColors.success,
|
|
87
|
+
color0: chartColors.destructive,
|
|
88
|
+
borderColor: chartColors.success,
|
|
89
|
+
borderColor0: chartColors.destructive,
|
|
83
90
|
},
|
|
84
91
|
},
|
|
85
92
|
];
|
|
@@ -95,7 +102,7 @@
|
|
|
95
102
|
data: data.dates,
|
|
96
103
|
boundaryGap: false,
|
|
97
104
|
axisLine: { onZero: false },
|
|
98
|
-
splitLine: { show:
|
|
105
|
+
splitLine: { show: showGrid },
|
|
99
106
|
min: 'dataMin',
|
|
100
107
|
max: 'dataMax',
|
|
101
108
|
},
|
|
@@ -103,7 +110,10 @@
|
|
|
103
110
|
),
|
|
104
111
|
];
|
|
105
112
|
const yAxisArr: unknown[] = [
|
|
106
|
-
buildAxis(
|
|
113
|
+
buildAxis(
|
|
114
|
+
{ scale: true, splitArea: { show: showGrid }, splitLine: { show: showGrid } },
|
|
115
|
+
yAxisConfig
|
|
116
|
+
),
|
|
107
117
|
];
|
|
108
118
|
|
|
109
119
|
if (showVolume && data.volume) {
|
|
@@ -143,14 +153,14 @@
|
|
|
143
153
|
{
|
|
144
154
|
tooltip: buildTooltip(
|
|
145
155
|
{ trigger: 'axis', axisPointer: { type: 'cross' } },
|
|
146
|
-
{ show: showTooltip, formatter: tooltipFormatter, valueFormatter
|
|
156
|
+
{ show: showTooltip, formatter: tooltipFormatter, valueFormatter }
|
|
147
157
|
),
|
|
148
158
|
grid: gridArr,
|
|
149
159
|
xAxis: xAxisArr as EChartsOption['xAxis'],
|
|
150
160
|
yAxis: yAxisArr as EChartsOption['yAxis'],
|
|
151
161
|
dataZoom: [
|
|
152
|
-
{ type: 'inside', start:
|
|
153
|
-
{ show: true, type: 'slider', top: '90%', start:
|
|
162
|
+
{ type: 'inside', start: zoomStart, end: 100 },
|
|
163
|
+
{ show: true, type: 'slider', top: '90%', start: zoomStart, end: 100 },
|
|
154
164
|
],
|
|
155
165
|
series: seriesArr,
|
|
156
166
|
} as EChartsOption,
|
|
@@ -163,31 +173,36 @@
|
|
|
163
173
|
(!isEmpty ? `${title}. Candlestick chart with ${data.dates.length} data points.` : undefined)
|
|
164
174
|
);
|
|
165
175
|
|
|
176
|
+
const dataTable = $derived(isEmpty ? undefined : candlestickTable(data));
|
|
177
|
+
|
|
166
178
|
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
167
179
|
const chart = useChart({
|
|
168
180
|
option: () => option,
|
|
169
181
|
theme: () => theme,
|
|
170
182
|
animation: () => animation,
|
|
171
183
|
isEmpty: () => isEmpty,
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
patterns: () => patterns,
|
|
185
|
+
events: () =>
|
|
186
|
+
chartEvents(events, {
|
|
187
|
+
click:
|
|
188
|
+
onClick &&
|
|
189
|
+
((params) => {
|
|
190
|
+
const dataIndex = params.dataIndex || 0;
|
|
191
|
+
const item = data.data[dataIndex];
|
|
192
|
+
if (!item) return;
|
|
193
|
+
onClick({
|
|
194
|
+
type: params.type || 'click',
|
|
195
|
+
componentType: params.componentType || 'series',
|
|
196
|
+
date: data.dates[dataIndex],
|
|
197
|
+
open: item.open,
|
|
198
|
+
close: item.close,
|
|
199
|
+
high: item.high,
|
|
200
|
+
low: item.low,
|
|
201
|
+
volume: data.volume?.[dataIndex],
|
|
202
|
+
} as CandlestickEventParams);
|
|
203
|
+
}),
|
|
204
|
+
}),
|
|
205
|
+
onCreated: () => onReady,
|
|
191
206
|
});
|
|
192
207
|
</script>
|
|
193
208
|
|
|
@@ -195,11 +210,19 @@
|
|
|
195
210
|
{title}
|
|
196
211
|
description={accessibilityDescription}
|
|
197
212
|
{height}
|
|
213
|
+
{minHeight}
|
|
214
|
+
{maxHeight}
|
|
198
215
|
{loading}
|
|
199
216
|
{error}
|
|
217
|
+
{onRetry}
|
|
200
218
|
empty={isEmpty}
|
|
201
219
|
{emptyMessage}
|
|
202
220
|
class={cn(className)}
|
|
221
|
+
{dataTable}
|
|
222
|
+
{showDataTable}
|
|
223
|
+
format={valueFormatter}
|
|
224
|
+
{exportable}
|
|
225
|
+
exportImage={chart.exportImage}
|
|
203
226
|
>
|
|
204
227
|
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
205
228
|
</ChartContainer>
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as echarts from 'echarts/core';
|
|
3
3
|
import { FunnelChart as EChartsFunnelChart } from 'echarts/charts';
|
|
4
|
-
import { TooltipComponent, LegendComponent
|
|
5
|
-
import { CanvasRenderer } from 'echarts/renderers';
|
|
4
|
+
import { TooltipComponent, LegendComponent } from 'echarts/components';
|
|
6
5
|
import type { EChartsOption } from 'echarts';
|
|
7
6
|
|
|
8
|
-
import type { FunnelChartProps
|
|
7
|
+
import type { FunnelChartProps } from '@classic-homes/charts-core';
|
|
9
8
|
import {
|
|
10
9
|
buildDataLabel,
|
|
11
10
|
buildLegend,
|
|
@@ -16,9 +15,12 @@
|
|
|
16
15
|
itemLabelLookup,
|
|
17
16
|
mergeChartOption,
|
|
18
17
|
pointLabel,
|
|
18
|
+
resolveValueFormatter,
|
|
19
|
+
sliceTable,
|
|
19
20
|
} from '@classic-homes/charts-core';
|
|
20
21
|
|
|
21
22
|
import { cn } from '../../utils.js';
|
|
23
|
+
import { chartEvents, toSliceParams } from '../../events.js';
|
|
22
24
|
import { useChart } from '../../composables/useChart.svelte.js';
|
|
23
25
|
import {
|
|
24
26
|
useChartLegend,
|
|
@@ -26,13 +28,7 @@
|
|
|
26
28
|
} from '../../composables/useChartLegend.svelte.js';
|
|
27
29
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
28
30
|
|
|
29
|
-
echarts.use([
|
|
30
|
-
EChartsFunnelChart,
|
|
31
|
-
TooltipComponent,
|
|
32
|
-
LegendComponent,
|
|
33
|
-
TitleComponent,
|
|
34
|
-
CanvasRenderer,
|
|
35
|
-
]);
|
|
31
|
+
echarts.use([EChartsFunnelChart, TooltipComponent, LegendComponent]);
|
|
36
32
|
|
|
37
33
|
interface Props extends Omit<FunnelChartProps, 'class'>, CustomLegendProps {
|
|
38
34
|
class?: string;
|
|
@@ -43,8 +39,11 @@
|
|
|
43
39
|
description,
|
|
44
40
|
data,
|
|
45
41
|
height = 400,
|
|
42
|
+
minHeight,
|
|
43
|
+
maxHeight,
|
|
46
44
|
loading,
|
|
47
45
|
error,
|
|
46
|
+
onRetry,
|
|
48
47
|
emptyMessage,
|
|
49
48
|
theme = 'auto',
|
|
50
49
|
animation = true,
|
|
@@ -60,10 +59,22 @@
|
|
|
60
59
|
renderLegend,
|
|
61
60
|
onLegendChange,
|
|
62
61
|
option: optionOverride,
|
|
62
|
+
valueFormat,
|
|
63
|
+
locale,
|
|
64
|
+
onReady,
|
|
65
|
+
events,
|
|
66
|
+
showDataTable,
|
|
67
|
+
exportable,
|
|
68
|
+
patterns,
|
|
63
69
|
onClick,
|
|
64
70
|
class: className,
|
|
65
71
|
}: Props = $props();
|
|
66
72
|
|
|
73
|
+
// `valueFormat` writes values in tooltips and the data table
|
|
74
|
+
const valueFormatter = $derived(
|
|
75
|
+
tooltipValueFormatter ?? resolveValueFormatter(valueFormat, locale)
|
|
76
|
+
);
|
|
77
|
+
|
|
67
78
|
const isEmpty = $derived(!data?.length);
|
|
68
79
|
|
|
69
80
|
const option: EChartsOption = $derived.by(() => {
|
|
@@ -78,17 +89,17 @@
|
|
|
78
89
|
trigger: 'item',
|
|
79
90
|
// A string template ignores valueFormatter, so format the value here
|
|
80
91
|
formatter:
|
|
81
|
-
|
|
92
|
+
valueFormatter && !tooltipFormatter
|
|
82
93
|
? (params: unknown) => {
|
|
83
94
|
const p = params as { seriesName?: string; name?: string; value?: unknown };
|
|
84
|
-
return `${escapeHtml(p.seriesName)} <br/>${escapeHtml(p.name)}: ${escapeHtml(formatTooltipValue(p.value,
|
|
95
|
+
return `${escapeHtml(p.seriesName)} <br/>${escapeHtml(p.name)}: ${escapeHtml(formatTooltipValue(p.value, valueFormatter))}`;
|
|
85
96
|
}
|
|
86
97
|
: '{a} <br/>{b}: {c}',
|
|
87
98
|
},
|
|
88
99
|
{
|
|
89
100
|
show: showTooltip,
|
|
90
101
|
formatter: tooltipFormatter,
|
|
91
|
-
valueFormatter
|
|
102
|
+
valueFormatter,
|
|
92
103
|
getLabel,
|
|
93
104
|
}
|
|
94
105
|
),
|
|
@@ -147,26 +158,21 @@
|
|
|
147
158
|
onLegendChange: () => onLegendChange,
|
|
148
159
|
});
|
|
149
160
|
|
|
161
|
+
const dataTable = $derived(isEmpty ? undefined : sliceTable(data, 'Stage'));
|
|
162
|
+
|
|
150
163
|
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
151
164
|
const chart = useChart({
|
|
152
165
|
option: () => option,
|
|
153
166
|
theme: () => theme,
|
|
154
167
|
animation: () => animation,
|
|
155
168
|
isEmpty: () => isEmpty,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
name: params.name || '',
|
|
164
|
-
value: params.value as number,
|
|
165
|
-
color: params.color as string,
|
|
166
|
-
} as SliceEventParams);
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
},
|
|
169
|
+
patterns: () => patterns,
|
|
170
|
+
events: () =>
|
|
171
|
+
chartEvents(events, {
|
|
172
|
+
click: onClick && ((params) => onClick(toSliceParams(params, 'click'))),
|
|
173
|
+
}),
|
|
174
|
+
onReady: (instance) => legend.attach(instance),
|
|
175
|
+
onCreated: () => onReady,
|
|
170
176
|
});
|
|
171
177
|
</script>
|
|
172
178
|
|
|
@@ -178,13 +184,21 @@
|
|
|
178
184
|
{title}
|
|
179
185
|
description={accessibilityDescription}
|
|
180
186
|
{height}
|
|
187
|
+
{minHeight}
|
|
188
|
+
{maxHeight}
|
|
181
189
|
{loading}
|
|
182
190
|
{error}
|
|
191
|
+
{onRetry}
|
|
183
192
|
empty={isEmpty}
|
|
184
193
|
{emptyMessage}
|
|
185
194
|
class={cn(className)}
|
|
186
195
|
legend={renderLegend ? customLegend : undefined}
|
|
187
196
|
legendPosition={legendConfig?.position}
|
|
197
|
+
{dataTable}
|
|
198
|
+
{showDataTable}
|
|
199
|
+
format={valueFormatter}
|
|
200
|
+
{exportable}
|
|
201
|
+
exportImage={chart.exportImage}
|
|
188
202
|
>
|
|
189
203
|
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
190
204
|
</ChartContainer>
|