@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
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as echarts from 'echarts/core';
|
|
3
3
|
import { GaugeChart as EChartsGaugeChart } from 'echarts/charts';
|
|
4
|
-
import {
|
|
5
|
-
import { CanvasRenderer } from 'echarts/renderers';
|
|
4
|
+
import { TooltipComponent } from 'echarts/components';
|
|
6
5
|
import type { EChartsOption } from 'echarts';
|
|
7
6
|
|
|
8
7
|
import type { GaugeChartProps } from '@classic-homes/charts-core';
|
|
9
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
buildTooltip,
|
|
10
|
+
formatValue as formatChartValue,
|
|
11
|
+
getChartColors,
|
|
12
|
+
mergeChartOption,
|
|
13
|
+
resolveValueFormatter,
|
|
14
|
+
} from '@classic-homes/charts-core';
|
|
10
15
|
|
|
11
16
|
import { cn } from '../../utils.js';
|
|
17
|
+
import { chartEvents, toDataPointParams } from '../../events.js';
|
|
12
18
|
import { useChart } from '../../composables/useChart.svelte.js';
|
|
13
19
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
14
20
|
|
|
15
|
-
echarts.use([EChartsGaugeChart,
|
|
21
|
+
echarts.use([EChartsGaugeChart, TooltipComponent]);
|
|
16
22
|
|
|
17
23
|
interface Props extends Omit<GaugeChartProps, 'class'> {
|
|
18
24
|
class?: string;
|
|
@@ -25,33 +31,57 @@
|
|
|
25
31
|
min = 0,
|
|
26
32
|
max = 100,
|
|
27
33
|
height = 400,
|
|
34
|
+
minHeight,
|
|
35
|
+
maxHeight,
|
|
28
36
|
loading,
|
|
29
37
|
error,
|
|
38
|
+
onRetry,
|
|
30
39
|
emptyMessage,
|
|
31
40
|
theme = 'auto',
|
|
32
41
|
animation = true,
|
|
33
42
|
splitNumber = 10,
|
|
34
43
|
showProgress = true,
|
|
35
44
|
formatValue,
|
|
45
|
+
valueFormat,
|
|
46
|
+
locale,
|
|
36
47
|
showTooltip = true,
|
|
37
48
|
tooltipFormatter,
|
|
38
49
|
tooltipValueFormatter,
|
|
39
50
|
option: optionOverride,
|
|
51
|
+
onReady,
|
|
52
|
+
events,
|
|
53
|
+
showDataTable,
|
|
54
|
+
exportable,
|
|
55
|
+
patterns,
|
|
56
|
+
onClick,
|
|
40
57
|
class: className,
|
|
41
58
|
}: Props = $props();
|
|
42
59
|
|
|
60
|
+
// A missing or non-numeric value has nothing to point at
|
|
61
|
+
const isEmpty = $derived(typeof value !== 'number' || !Number.isFinite(value));
|
|
62
|
+
|
|
63
|
+
const valueFormatter = $derived(formatValue ?? resolveValueFormatter(valueFormat, locale));
|
|
64
|
+
|
|
43
65
|
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
44
66
|
const chart = useChart({
|
|
45
67
|
option: () => option,
|
|
46
68
|
theme: () => theme,
|
|
47
69
|
animation: () => animation,
|
|
70
|
+
isEmpty: () => isEmpty,
|
|
71
|
+
patterns: () => patterns,
|
|
72
|
+
events: () =>
|
|
73
|
+
chartEvents(events, {
|
|
74
|
+
click: onClick && ((params) => onClick(toDataPointParams(params, 'click'))),
|
|
75
|
+
}),
|
|
76
|
+
onCreated: () => onReady,
|
|
48
77
|
});
|
|
49
78
|
|
|
50
79
|
// Per-element colours that follow the resolved chart theme
|
|
51
80
|
const chartColors = $derived.by(() => getChartColors(chart.theme));
|
|
52
81
|
|
|
53
|
-
const option: EChartsOption = $derived.by(() =>
|
|
54
|
-
|
|
82
|
+
const option: EChartsOption = $derived.by(() => {
|
|
83
|
+
if (isEmpty) return {};
|
|
84
|
+
return mergeChartOption(
|
|
55
85
|
{
|
|
56
86
|
// No tooltip by default; one appears only when a formatter is set
|
|
57
87
|
tooltip:
|
|
@@ -94,18 +124,30 @@
|
|
|
94
124
|
fontSize: 28,
|
|
95
125
|
fontWeight: 'bold',
|
|
96
126
|
offsetCenter: [0, '40%'],
|
|
97
|
-
formatter:
|
|
127
|
+
formatter: valueFormatter || '{value}',
|
|
98
128
|
},
|
|
99
129
|
data: [{ value, name: title }],
|
|
100
130
|
},
|
|
101
131
|
],
|
|
102
132
|
} as EChartsOption,
|
|
103
133
|
optionOverride
|
|
104
|
-
)
|
|
105
|
-
);
|
|
134
|
+
);
|
|
135
|
+
});
|
|
106
136
|
|
|
107
137
|
const accessibilityDescription = $derived(
|
|
108
|
-
description ||
|
|
138
|
+
description ||
|
|
139
|
+
(isEmpty
|
|
140
|
+
? undefined
|
|
141
|
+
: `${title}. Gauge showing value ${formatChartValue(value, valueFormatter)} on scale from ${formatChartValue(min, valueFormatter)} to ${formatChartValue(max, valueFormatter)}.`)
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const dataTable = $derived(
|
|
145
|
+
isEmpty
|
|
146
|
+
? undefined
|
|
147
|
+
: {
|
|
148
|
+
headers: ['Metric', 'Value', 'Minimum', 'Maximum'],
|
|
149
|
+
rows: [[title, value, min, max]],
|
|
150
|
+
}
|
|
109
151
|
);
|
|
110
152
|
</script>
|
|
111
153
|
|
|
@@ -113,10 +155,19 @@
|
|
|
113
155
|
{title}
|
|
114
156
|
description={accessibilityDescription}
|
|
115
157
|
{height}
|
|
158
|
+
{minHeight}
|
|
159
|
+
{maxHeight}
|
|
116
160
|
{loading}
|
|
117
161
|
{error}
|
|
162
|
+
{onRetry}
|
|
163
|
+
empty={isEmpty}
|
|
118
164
|
{emptyMessage}
|
|
119
165
|
class={cn(className)}
|
|
166
|
+
{dataTable}
|
|
167
|
+
{showDataTable}
|
|
168
|
+
format={valueFormatter}
|
|
169
|
+
{exportable}
|
|
170
|
+
exportImage={chart.exportImage}
|
|
120
171
|
>
|
|
121
172
|
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
122
173
|
</ChartContainer>
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as echarts from 'echarts/core';
|
|
3
3
|
import { HeatmapChart as EChartsHeatmapChart } from 'echarts/charts';
|
|
4
|
-
import {
|
|
5
|
-
GridComponent,
|
|
6
|
-
TooltipComponent,
|
|
7
|
-
TitleComponent,
|
|
8
|
-
VisualMapComponent,
|
|
9
|
-
} from 'echarts/components';
|
|
10
|
-
import { CanvasRenderer } from 'echarts/renderers';
|
|
4
|
+
import { GridComponent, TooltipComponent, VisualMapComponent } from 'echarts/components';
|
|
11
5
|
import type { EChartsOption } from 'echarts';
|
|
12
6
|
|
|
13
7
|
import type { HeatmapChartProps, HeatmapEventParams } from '@classic-homes/charts-core';
|
|
@@ -15,23 +9,21 @@
|
|
|
15
9
|
buildAxis,
|
|
16
10
|
buildDataLabel,
|
|
17
11
|
buildTooltip,
|
|
12
|
+
colorWithAlpha,
|
|
18
13
|
escapeHtml,
|
|
19
14
|
formatTooltipValue,
|
|
15
|
+
getChartColors,
|
|
16
|
+
heatmapTable,
|
|
20
17
|
mergeChartOption,
|
|
18
|
+
resolveValueFormatter,
|
|
21
19
|
} from '@classic-homes/charts-core';
|
|
22
20
|
|
|
23
21
|
import { cn } from '../../utils.js';
|
|
22
|
+
import { chartEvents } from '../../events.js';
|
|
24
23
|
import { useChart } from '../../composables/useChart.svelte.js';
|
|
25
24
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
26
25
|
|
|
27
|
-
echarts.use([
|
|
28
|
-
EChartsHeatmapChart,
|
|
29
|
-
GridComponent,
|
|
30
|
-
TooltipComponent,
|
|
31
|
-
TitleComponent,
|
|
32
|
-
VisualMapComponent,
|
|
33
|
-
CanvasRenderer,
|
|
34
|
-
]);
|
|
26
|
+
echarts.use([EChartsHeatmapChart, GridComponent, TooltipComponent, VisualMapComponent]);
|
|
35
27
|
|
|
36
28
|
interface Props extends Omit<HeatmapChartProps, 'class'> {
|
|
37
29
|
class?: string;
|
|
@@ -44,8 +36,11 @@
|
|
|
44
36
|
xAxis,
|
|
45
37
|
yAxis,
|
|
46
38
|
height = 400,
|
|
39
|
+
minHeight,
|
|
40
|
+
maxHeight,
|
|
47
41
|
loading,
|
|
48
42
|
error,
|
|
43
|
+
onRetry,
|
|
49
44
|
emptyMessage,
|
|
50
45
|
theme = 'auto',
|
|
51
46
|
animation = true,
|
|
@@ -57,10 +52,25 @@
|
|
|
57
52
|
tooltipFormatter,
|
|
58
53
|
tooltipValueFormatter,
|
|
59
54
|
option: optionOverride,
|
|
55
|
+
valueFormat,
|
|
56
|
+
locale,
|
|
57
|
+
onReady,
|
|
58
|
+
events,
|
|
59
|
+
showDataTable,
|
|
60
|
+
exportable,
|
|
61
|
+
patterns,
|
|
60
62
|
onClick,
|
|
61
63
|
class: className,
|
|
62
64
|
}: Props = $props();
|
|
63
65
|
|
|
66
|
+
// `valueFormat` writes values in tooltips and the data table
|
|
67
|
+
const valueFormatter = $derived(
|
|
68
|
+
tooltipValueFormatter ?? resolveValueFormatter(valueFormat, locale)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Per-element colours that follow the resolved chart theme
|
|
72
|
+
const chartColors = $derived.by(() => getChartColors(chart.theme));
|
|
73
|
+
|
|
64
74
|
const isEmpty = $derived(!data?.data?.length || !xAxis?.length || !yAxis?.length);
|
|
65
75
|
|
|
66
76
|
// A loop: spreading large arrays into Math.min/max overflows the call stack
|
|
@@ -88,10 +98,10 @@
|
|
|
88
98
|
const p = params as { value?: [number, number, number] };
|
|
89
99
|
if (!p.value) return '';
|
|
90
100
|
const [xIdx, yIdx, value] = p.value;
|
|
91
|
-
return `${escapeHtml(xAxis[xIdx])} / ${escapeHtml(yAxis[yIdx])}: ${escapeHtml(formatTooltipValue(value,
|
|
101
|
+
return `${escapeHtml(xAxis[xIdx])} / ${escapeHtml(yAxis[yIdx])}: ${escapeHtml(formatTooltipValue(value, valueFormatter))}`;
|
|
92
102
|
},
|
|
93
103
|
},
|
|
94
|
-
{ show: showTooltip, formatter: tooltipFormatter, valueFormatter
|
|
104
|
+
{ show: showTooltip, formatter: tooltipFormatter, valueFormatter }
|
|
95
105
|
),
|
|
96
106
|
grid: { left: '3%', right: '10%', bottom: '3%', containLabel: true },
|
|
97
107
|
xAxis: buildAxis({ type: 'category', data: xAxis, splitArea: { show: true } }, xAxisConfig),
|
|
@@ -103,6 +113,12 @@
|
|
|
103
113
|
orient: 'vertical',
|
|
104
114
|
right: '0%',
|
|
105
115
|
top: 'center',
|
|
116
|
+
// Shade from a faint to a full first series colour, matching the theme
|
|
117
|
+
inRange: {
|
|
118
|
+
color: [colorWithAlpha(chartColors.series[0], 0.08), chartColors.series[0]],
|
|
119
|
+
},
|
|
120
|
+
textStyle: { color: chartColors.mutedForeground },
|
|
121
|
+
formatter: valueFormatter ? (value: number) => valueFormatter(value) : undefined,
|
|
106
122
|
},
|
|
107
123
|
series: [
|
|
108
124
|
{
|
|
@@ -128,28 +144,33 @@
|
|
|
128
144
|
: undefined)
|
|
129
145
|
);
|
|
130
146
|
|
|
147
|
+
const dataTable = $derived(isEmpty ? undefined : heatmapTable(data, xAxis, yAxis));
|
|
148
|
+
|
|
131
149
|
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
132
150
|
const chart = useChart({
|
|
133
151
|
option: () => option,
|
|
134
152
|
theme: () => theme,
|
|
135
153
|
animation: () => animation,
|
|
136
154
|
isEmpty: () => isEmpty,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
onClick
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
patterns: () => patterns,
|
|
156
|
+
events: () =>
|
|
157
|
+
chartEvents(events, {
|
|
158
|
+
click:
|
|
159
|
+
onClick &&
|
|
160
|
+
((params) => {
|
|
161
|
+
const [xIdx, yIdx, value] = (params.value as [number, number, number]) || [0, 0, 0];
|
|
162
|
+
onClick({
|
|
163
|
+
type: params.type || 'click',
|
|
164
|
+
componentType: params.componentType || 'series',
|
|
165
|
+
xIndex: xIdx,
|
|
166
|
+
yIndex: yIdx,
|
|
167
|
+
xLabel: xAxis[xIdx],
|
|
168
|
+
yLabel: yAxis[yIdx],
|
|
169
|
+
value,
|
|
170
|
+
} as HeatmapEventParams);
|
|
171
|
+
}),
|
|
172
|
+
}),
|
|
173
|
+
onCreated: () => onReady,
|
|
153
174
|
});
|
|
154
175
|
</script>
|
|
155
176
|
|
|
@@ -157,11 +178,19 @@
|
|
|
157
178
|
{title}
|
|
158
179
|
description={accessibilityDescription}
|
|
159
180
|
{height}
|
|
181
|
+
{minHeight}
|
|
182
|
+
{maxHeight}
|
|
160
183
|
{loading}
|
|
161
184
|
{error}
|
|
185
|
+
{onRetry}
|
|
162
186
|
empty={isEmpty}
|
|
163
187
|
{emptyMessage}
|
|
164
188
|
class={cn(className)}
|
|
189
|
+
{dataTable}
|
|
190
|
+
{showDataTable}
|
|
191
|
+
format={valueFormatter}
|
|
192
|
+
{exportable}
|
|
193
|
+
exportImage={chart.exportImage}
|
|
165
194
|
>
|
|
166
195
|
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
167
196
|
</ChartContainer>
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as echarts from 'echarts/core';
|
|
3
3
|
import { RadarChart as EChartsRadarChart } from 'echarts/charts';
|
|
4
|
-
import {
|
|
5
|
-
TooltipComponent,
|
|
6
|
-
LegendComponent,
|
|
7
|
-
TitleComponent,
|
|
8
|
-
RadarComponent,
|
|
9
|
-
} from 'echarts/components';
|
|
10
|
-
import { CanvasRenderer } from 'echarts/renderers';
|
|
4
|
+
import { TooltipComponent, LegendComponent, RadarComponent } from 'echarts/components';
|
|
11
5
|
import type { EChartsOption } from 'echarts';
|
|
12
6
|
|
|
13
7
|
import type { RadarChartProps } from '@classic-homes/charts-core';
|
|
@@ -18,9 +12,12 @@
|
|
|
18
12
|
getChartColors,
|
|
19
13
|
legendCenter,
|
|
20
14
|
mergeChartOption,
|
|
15
|
+
radarTable,
|
|
16
|
+
resolveValueFormatter,
|
|
21
17
|
} from '@classic-homes/charts-core';
|
|
22
18
|
|
|
23
19
|
import { cn } from '../../utils.js';
|
|
20
|
+
import { chartEvents, toDataPointParams } from '../../events.js';
|
|
24
21
|
import { useChart } from '../../composables/useChart.svelte.js';
|
|
25
22
|
import {
|
|
26
23
|
useChartLegend,
|
|
@@ -28,14 +25,7 @@
|
|
|
28
25
|
} from '../../composables/useChartLegend.svelte.js';
|
|
29
26
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
30
27
|
|
|
31
|
-
echarts.use([
|
|
32
|
-
EChartsRadarChart,
|
|
33
|
-
TooltipComponent,
|
|
34
|
-
LegendComponent,
|
|
35
|
-
TitleComponent,
|
|
36
|
-
RadarComponent,
|
|
37
|
-
CanvasRenderer,
|
|
38
|
-
]);
|
|
28
|
+
echarts.use([EChartsRadarChart, TooltipComponent, LegendComponent, RadarComponent]);
|
|
39
29
|
|
|
40
30
|
interface Props extends Omit<RadarChartProps, 'class'>, CustomLegendProps {
|
|
41
31
|
class?: string;
|
|
@@ -47,8 +37,11 @@
|
|
|
47
37
|
data,
|
|
48
38
|
indicators,
|
|
49
39
|
height = 400,
|
|
40
|
+
minHeight,
|
|
41
|
+
maxHeight,
|
|
50
42
|
loading,
|
|
51
43
|
error,
|
|
44
|
+
onRetry,
|
|
52
45
|
emptyMessage,
|
|
53
46
|
theme = 'auto',
|
|
54
47
|
animation = true,
|
|
@@ -63,9 +56,22 @@
|
|
|
63
56
|
renderLegend,
|
|
64
57
|
onLegendChange,
|
|
65
58
|
option: optionOverride,
|
|
59
|
+
valueFormat,
|
|
60
|
+
locale,
|
|
61
|
+
onReady,
|
|
62
|
+
events,
|
|
63
|
+
showDataTable,
|
|
64
|
+
exportable,
|
|
65
|
+
patterns,
|
|
66
|
+
onClick,
|
|
66
67
|
class: className,
|
|
67
68
|
}: Props = $props();
|
|
68
69
|
|
|
70
|
+
// `valueFormat` writes values in tooltips and the data table
|
|
71
|
+
const valueFormatter = $derived(
|
|
72
|
+
tooltipValueFormatter ?? resolveValueFormatter(valueFormat, locale)
|
|
73
|
+
);
|
|
74
|
+
|
|
69
75
|
const isEmpty = $derived(
|
|
70
76
|
!data?.series?.length || !indicators?.length || data.series.every((s) => !s.data?.length)
|
|
71
77
|
);
|
|
@@ -77,7 +83,7 @@
|
|
|
77
83
|
{
|
|
78
84
|
tooltip: buildTooltip(
|
|
79
85
|
{ trigger: 'item' },
|
|
80
|
-
{ show: showTooltip, formatter: tooltipFormatter, valueFormatter
|
|
86
|
+
{ show: showTooltip, formatter: tooltipFormatter, valueFormatter }
|
|
81
87
|
),
|
|
82
88
|
legend: buildLegend(
|
|
83
89
|
data.series.map((s) => s.name),
|
|
@@ -130,15 +136,21 @@
|
|
|
130
136
|
onLegendChange: () => onLegendChange,
|
|
131
137
|
});
|
|
132
138
|
|
|
139
|
+
const dataTable = $derived(isEmpty ? undefined : radarTable(data, indicators));
|
|
140
|
+
|
|
133
141
|
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
134
142
|
const chart = useChart({
|
|
135
143
|
option: () => option,
|
|
136
144
|
theme: () => theme,
|
|
137
145
|
animation: () => animation,
|
|
138
146
|
isEmpty: () => isEmpty,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
patterns: () => patterns,
|
|
148
|
+
events: () =>
|
|
149
|
+
chartEvents(events, {
|
|
150
|
+
click: onClick && ((params) => onClick(toDataPointParams(params, 'click'))),
|
|
151
|
+
}),
|
|
152
|
+
onReady: (instance) => legend.attach(instance),
|
|
153
|
+
onCreated: () => onReady,
|
|
142
154
|
});
|
|
143
155
|
</script>
|
|
144
156
|
|
|
@@ -150,13 +162,21 @@
|
|
|
150
162
|
{title}
|
|
151
163
|
description={accessibilityDescription}
|
|
152
164
|
{height}
|
|
165
|
+
{minHeight}
|
|
166
|
+
{maxHeight}
|
|
153
167
|
{loading}
|
|
154
168
|
{error}
|
|
169
|
+
{onRetry}
|
|
155
170
|
empty={isEmpty}
|
|
156
171
|
{emptyMessage}
|
|
157
172
|
class={cn(className)}
|
|
158
173
|
legend={renderLegend ? customLegend : undefined}
|
|
159
174
|
legendPosition={legendConfig?.position}
|
|
175
|
+
{dataTable}
|
|
176
|
+
{showDataTable}
|
|
177
|
+
format={valueFormatter}
|
|
178
|
+
{exportable}
|
|
179
|
+
exportImage={chart.exportImage}
|
|
160
180
|
>
|
|
161
181
|
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
162
182
|
</ChartContainer>
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import * as echarts from 'echarts/core';
|
|
3
3
|
import { SankeyChart as EChartsSankeyChart } from 'echarts/charts';
|
|
4
|
-
import { TooltipComponent
|
|
5
|
-
import { CanvasRenderer } from 'echarts/renderers';
|
|
4
|
+
import { TooltipComponent } from 'echarts/components';
|
|
6
5
|
import type { EChartsOption } from 'echarts';
|
|
7
6
|
|
|
8
7
|
import type { SankeyChartProps, SankeyEventParams } from '@classic-homes/charts-core';
|
|
@@ -12,13 +11,16 @@
|
|
|
12
11
|
mergeChartOption,
|
|
13
12
|
nameLabelLookup,
|
|
14
13
|
pointLabel,
|
|
14
|
+
resolveValueFormatter,
|
|
15
|
+
sankeyTable,
|
|
15
16
|
} from '@classic-homes/charts-core';
|
|
16
17
|
|
|
17
18
|
import { cn } from '../../utils.js';
|
|
19
|
+
import { chartEvents } from '../../events.js';
|
|
18
20
|
import { useChart } from '../../composables/useChart.svelte.js';
|
|
19
21
|
import ChartContainer from '../base/ChartContainer.svelte';
|
|
20
22
|
|
|
21
|
-
echarts.use([EChartsSankeyChart, TooltipComponent
|
|
23
|
+
echarts.use([EChartsSankeyChart, TooltipComponent]);
|
|
22
24
|
|
|
23
25
|
interface Props extends Omit<SankeyChartProps, 'class'> {
|
|
24
26
|
class?: string;
|
|
@@ -29,8 +31,11 @@
|
|
|
29
31
|
description,
|
|
30
32
|
data,
|
|
31
33
|
height = 400,
|
|
34
|
+
minHeight,
|
|
35
|
+
maxHeight,
|
|
32
36
|
loading,
|
|
33
37
|
error,
|
|
38
|
+
onRetry,
|
|
34
39
|
emptyMessage,
|
|
35
40
|
theme = 'auto',
|
|
36
41
|
animation = true,
|
|
@@ -40,10 +45,22 @@
|
|
|
40
45
|
tooltipFormatter,
|
|
41
46
|
tooltipValueFormatter,
|
|
42
47
|
option: optionOverride,
|
|
48
|
+
valueFormat,
|
|
49
|
+
locale,
|
|
50
|
+
onReady,
|
|
51
|
+
events,
|
|
52
|
+
showDataTable,
|
|
53
|
+
exportable,
|
|
54
|
+
patterns,
|
|
43
55
|
onClick,
|
|
44
56
|
class: className,
|
|
45
57
|
}: Props = $props();
|
|
46
58
|
|
|
59
|
+
// `valueFormat` writes values in tooltips and the data table
|
|
60
|
+
const valueFormatter = $derived(
|
|
61
|
+
tooltipValueFormatter ?? resolveValueFormatter(valueFormat, locale)
|
|
62
|
+
);
|
|
63
|
+
|
|
47
64
|
const isEmpty = $derived(!data?.nodes?.length || !data?.links?.length);
|
|
48
65
|
|
|
49
66
|
const option: EChartsOption = $derived.by(() => {
|
|
@@ -58,7 +75,7 @@
|
|
|
58
75
|
{
|
|
59
76
|
show: showTooltip,
|
|
60
77
|
formatter: tooltipFormatter,
|
|
61
|
-
valueFormatter
|
|
78
|
+
valueFormatter,
|
|
62
79
|
getLabel,
|
|
63
80
|
}
|
|
64
81
|
),
|
|
@@ -97,28 +114,33 @@
|
|
|
97
114
|
: undefined)
|
|
98
115
|
);
|
|
99
116
|
|
|
117
|
+
const dataTable = $derived(isEmpty ? undefined : sankeyTable(data));
|
|
118
|
+
|
|
100
119
|
// Manage ECharts instance lifecycle (init, theme, resize, option updates)
|
|
101
120
|
const chart = useChart({
|
|
102
121
|
option: () => option,
|
|
103
122
|
theme: () => theme,
|
|
104
123
|
animation: () => animation,
|
|
105
124
|
isEmpty: () => isEmpty,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
onClick
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
patterns: () => patterns,
|
|
126
|
+
events: () =>
|
|
127
|
+
chartEvents(events, {
|
|
128
|
+
click:
|
|
129
|
+
onClick &&
|
|
130
|
+
((params) => {
|
|
131
|
+
const isEdge = params.dataType === 'edge';
|
|
132
|
+
onClick({
|
|
133
|
+
type: params.type || 'click',
|
|
134
|
+
componentType: params.componentType || 'series',
|
|
135
|
+
dataType: isEdge ? 'edge' : 'node',
|
|
136
|
+
name: params.name || '',
|
|
137
|
+
value: isEdge ? (params.value as number) : undefined,
|
|
138
|
+
source: isEdge ? (params.data as { source?: string })?.source : undefined,
|
|
139
|
+
target: isEdge ? (params.data as { target?: string })?.target : undefined,
|
|
140
|
+
} as SankeyEventParams);
|
|
141
|
+
}),
|
|
142
|
+
}),
|
|
143
|
+
onCreated: () => onReady,
|
|
122
144
|
});
|
|
123
145
|
</script>
|
|
124
146
|
|
|
@@ -126,11 +148,19 @@
|
|
|
126
148
|
{title}
|
|
127
149
|
description={accessibilityDescription}
|
|
128
150
|
{height}
|
|
151
|
+
{minHeight}
|
|
152
|
+
{maxHeight}
|
|
129
153
|
{loading}
|
|
130
154
|
{error}
|
|
155
|
+
{onRetry}
|
|
131
156
|
empty={isEmpty}
|
|
132
157
|
{emptyMessage}
|
|
133
158
|
class={cn(className)}
|
|
159
|
+
{dataTable}
|
|
160
|
+
{showDataTable}
|
|
161
|
+
format={valueFormatter}
|
|
162
|
+
{exportable}
|
|
163
|
+
exportImage={chart.exportImage}
|
|
134
164
|
>
|
|
135
165
|
<div bind:this={chart.container} class="h-full w-full"></div>
|
|
136
166
|
</ChartContainer>
|