@reshape-biotech/design-system 2.2.3 → 2.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/components/graphs/bar-chart/BarChart.svelte +23 -16
- package/dist/components/graphs/bar-chart/BarChart.svelte.d.ts +1 -0
- package/dist/components/graphs/bar-chart/StackedBarChart.svelte +8 -2
- package/dist/components/graphs/bar-chart/StackedBarChart.svelte.d.ts +1 -0
- package/dist/components/graphs/chart/Chart.svelte +8 -1
- package/dist/components/graphs/chart/Chart.svelte.d.ts +3 -2
- package/dist/components/graphs/line/LineChart.svelte +42 -37
- package/dist/components/graphs/line/LineChart.svelte.d.ts +1 -9
- package/dist/components/graphs/line/types.d.ts +13 -0
- package/dist/components/graphs/line/types.js +1 -0
- package/dist/components/graphs/multiline/MultiLineChart.svelte +92 -70
- package/dist/components/graphs/multiline/MultiLineChart.svelte.d.ts +4 -0
- package/dist/components/graphs/multiline/types.d.ts +20 -0
- package/dist/components/graphs/multiline/types.js +1 -0
- package/dist/components/graphs/utils/tooltipFormatter.d.ts +5 -1
- package/dist/components/graphs/utils/tooltipFormatter.js +6 -2
- package/dist/components/manual-cfu-counter/ManualCFUCounter.svelte +7 -0
- package/dist/components/manual-cfu-counter/ManualCFUCounter.svelte.d.ts +5 -1
- package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte +7 -0
- package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte.d.ts +5 -1
- package/dist/components/stat-card/StatCard.svelte +1 -1
- package/dist/tokens.d.ts +7 -1
- package/dist/tokens.js +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import Chart, { type GenericChartProps } from '../chart/Chart.svelte';
|
|
4
4
|
import type { EChartsOption, BarSeriesOption } from 'echarts';
|
|
5
5
|
import { createTooltipFormatter } from '../utils/tooltipFormatter';
|
|
6
|
-
import { Duration } from 'luxon';
|
|
7
6
|
import { formattedDurationCompact } from '../utils/duration';
|
|
8
7
|
|
|
9
8
|
type DataItem = {
|
|
@@ -17,6 +16,7 @@
|
|
|
17
16
|
rotateXAxisLabels?: boolean;
|
|
18
17
|
grid?: EChartsOption['grid'];
|
|
19
18
|
captureInterval?: number;
|
|
19
|
+
shouldDim?: (key: string) => boolean;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
let {
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
xAxisName,
|
|
27
27
|
yAxisName,
|
|
28
28
|
captureInterval,
|
|
29
|
+
shouldDim = () => false,
|
|
30
|
+
|
|
29
31
|
...props
|
|
30
32
|
}: BarChartProps = $props();
|
|
31
33
|
|
|
@@ -42,7 +44,7 @@
|
|
|
42
44
|
};
|
|
43
45
|
const defaultColorRotationArray = $derived(Object.values(defaultColors));
|
|
44
46
|
|
|
45
|
-
const xAxisData = $derived(data.map((item) => item[xAxisName]));
|
|
47
|
+
const xAxisData = $derived(data.map((item) => item[xAxisName!]));
|
|
46
48
|
|
|
47
49
|
const captureIntervals = captureInterval
|
|
48
50
|
? Array(data.length)
|
|
@@ -53,24 +55,27 @@
|
|
|
53
55
|
})
|
|
54
56
|
: undefined;
|
|
55
57
|
|
|
56
|
-
const createBarDataItem = (value: number | null, color: string) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
const createBarDataItem = (value: number | null, color: string, key: string) => {
|
|
59
|
+
return {
|
|
60
|
+
value,
|
|
61
|
+
itemStyle: {
|
|
62
|
+
color,
|
|
63
|
+
borderRadius: 2,
|
|
64
|
+
borderWidth: 0.5,
|
|
65
|
+
borderColor: borderColor['white'],
|
|
66
|
+
opacity: shouldDim(key) ? 0.3 : 1,
|
|
67
|
+
},
|
|
68
|
+
emphasis: {
|
|
69
|
+
disabled: true,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
};
|
|
68
73
|
|
|
69
74
|
const chartSeries: BarSeriesOption[] = $derived.by(() => {
|
|
70
75
|
const seriesData = data.map((item) => item.value);
|
|
71
76
|
const seriesColors = data.map(
|
|
72
77
|
(item, index) =>
|
|
73
|
-
colors?.[item[xAxisName] as string] ??
|
|
78
|
+
colors?.[item[xAxisName!] as string] ??
|
|
74
79
|
defaultColorRotationArray[index % defaultColorRotationArray.length]
|
|
75
80
|
);
|
|
76
81
|
|
|
@@ -78,7 +83,9 @@
|
|
|
78
83
|
{
|
|
79
84
|
name: yAxisName,
|
|
80
85
|
type: 'bar',
|
|
81
|
-
data: seriesData.map((value, index) =>
|
|
86
|
+
data: seriesData.map((value, index) =>
|
|
87
|
+
createBarDataItem(value, seriesColors[index], String(data[index][xAxisName!]))
|
|
88
|
+
),
|
|
82
89
|
emphasis: { disabled: true },
|
|
83
90
|
},
|
|
84
91
|
];
|
|
@@ -10,6 +10,7 @@ interface BarChartProps extends Omit<GenericChartProps, 'timeIndex'> {
|
|
|
10
10
|
rotateXAxisLabels?: boolean;
|
|
11
11
|
grid?: EChartsOption['grid'];
|
|
12
12
|
captureInterval?: number;
|
|
13
|
+
shouldDim?: (key: string) => boolean;
|
|
13
14
|
}
|
|
14
15
|
declare const BarChart: import("svelte").Component<BarChartProps, {}, "">;
|
|
15
16
|
type BarChart = ReturnType<typeof BarChart>;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
rotateXAxisLabels?: boolean;
|
|
18
18
|
grid?: EChartsOption['grid'];
|
|
19
19
|
captureInterval?: number;
|
|
20
|
+
shouldDim?: (key: string) => boolean;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
let {
|
|
@@ -25,9 +26,13 @@
|
|
|
25
26
|
colors,
|
|
26
27
|
rotateXAxisLabels = false,
|
|
27
28
|
captureInterval,
|
|
29
|
+
shouldDim = () => false,
|
|
28
30
|
...props
|
|
29
31
|
}: StackedBarChartProps = $props();
|
|
30
32
|
|
|
33
|
+
let focusedSeries = $state<string | undefined>(undefined);
|
|
34
|
+
let focusedMetric = $state<string | undefined>(undefined);
|
|
35
|
+
|
|
31
36
|
const defaultColors: Record<string, string> = {
|
|
32
37
|
accent: chartColor['accent'],
|
|
33
38
|
blue: chartColor['blue'],
|
|
@@ -52,13 +57,14 @@
|
|
|
52
57
|
})
|
|
53
58
|
: undefined;
|
|
54
59
|
|
|
55
|
-
const createBarDataItem = (value: number | null, color: string) => ({
|
|
60
|
+
const createBarDataItem = (value: number | null, color: string, key: string) => ({
|
|
56
61
|
value,
|
|
57
62
|
itemStyle: {
|
|
58
63
|
color,
|
|
59
64
|
borderRadius: 2,
|
|
60
65
|
borderWidth: 0.5,
|
|
61
66
|
borderColor: borderColor['white'],
|
|
67
|
+
opacity: shouldDim(key) ? 0.3 : 1,
|
|
62
68
|
},
|
|
63
69
|
emphasis: {
|
|
64
70
|
disabled: true,
|
|
@@ -79,7 +85,7 @@
|
|
|
79
85
|
type: 'bar',
|
|
80
86
|
stack: 'total',
|
|
81
87
|
emphasis: { focus: 'none', disabled: true },
|
|
82
|
-
data: seriesItem.data.map((val) => createBarDataItem(val, seriesColor)),
|
|
88
|
+
data: seriesItem.data.map((val) => createBarDataItem(val, seriesColor, seriesItem.name)),
|
|
83
89
|
};
|
|
84
90
|
});
|
|
85
91
|
});
|
|
@@ -11,6 +11,7 @@ interface StackedBarChartProps extends GenericChartProps {
|
|
|
11
11
|
rotateXAxisLabels?: boolean;
|
|
12
12
|
grid?: EChartsOption['grid'];
|
|
13
13
|
captureInterval?: number;
|
|
14
|
+
shouldDim?: (key: string) => boolean;
|
|
14
15
|
}
|
|
15
16
|
declare const StackedBarChart: import("svelte").Component<StackedBarChartProps, {}, "">;
|
|
16
17
|
type StackedBarChart = ReturnType<typeof StackedBarChart>;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
xAxisOptions?: EChartsOption['xAxis'];
|
|
17
17
|
yAxisOptions?: EChartsOption['yAxis'];
|
|
18
18
|
// Common event handlers
|
|
19
|
-
onitemclick?: (params: ECElementEvent) => void;
|
|
19
|
+
onitemclick?: (params: ECElementEvent, ids?: (string | number)[], metricName?: string) => void;
|
|
20
20
|
onmouseover?: (params: ECElementEvent) => void;
|
|
21
21
|
onmouseout?: () => void;
|
|
22
22
|
// Additional options
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
loading?: boolean;
|
|
25
25
|
timeIndex?: number;
|
|
26
26
|
children?: Snippet;
|
|
27
|
+
onChartReady?: (chart: ECharts) => void;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
export type GenericChartProps = Omit<ChartProps, 'options'>;
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
loading = false,
|
|
42
43
|
timeIndex,
|
|
43
44
|
children,
|
|
45
|
+
onChartReady,
|
|
44
46
|
}: ChartProps = $props();
|
|
45
47
|
|
|
46
48
|
let chart = $state<ECharts | null>(null);
|
|
@@ -99,6 +101,9 @@
|
|
|
99
101
|
color: textColor.secondary,
|
|
100
102
|
},
|
|
101
103
|
},
|
|
104
|
+
tooltip: {
|
|
105
|
+
appendTo: document.body,
|
|
106
|
+
},
|
|
102
107
|
} as const;
|
|
103
108
|
|
|
104
109
|
function initChart() {
|
|
@@ -111,6 +116,8 @@
|
|
|
111
116
|
if (onmouseout) chart.on('mouseout', onmouseout);
|
|
112
117
|
|
|
113
118
|
if (loading) chart.showLoading();
|
|
119
|
+
|
|
120
|
+
onChartReady?.(chart);
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EChartsOption, ECElementEvent } from 'echarts';
|
|
1
|
+
import type { EChartsOption, ECElementEvent, ECharts } from 'echarts';
|
|
2
2
|
import { type Snippet } from 'svelte';
|
|
3
3
|
type ChartProps = {
|
|
4
4
|
options: EChartsOption;
|
|
@@ -11,13 +11,14 @@ type ChartProps = {
|
|
|
11
11
|
yAxisUnit?: string;
|
|
12
12
|
xAxisOptions?: EChartsOption['xAxis'];
|
|
13
13
|
yAxisOptions?: EChartsOption['yAxis'];
|
|
14
|
-
onitemclick?: (params: ECElementEvent) => void;
|
|
14
|
+
onitemclick?: (params: ECElementEvent, ids?: (string | number)[], metricName?: string) => void;
|
|
15
15
|
onmouseover?: (params: ECElementEvent) => void;
|
|
16
16
|
onmouseout?: () => void;
|
|
17
17
|
autoResize?: boolean;
|
|
18
18
|
loading?: boolean;
|
|
19
19
|
timeIndex?: number;
|
|
20
20
|
children?: Snippet;
|
|
21
|
+
onChartReady?: (chart: ECharts) => void;
|
|
21
22
|
};
|
|
22
23
|
export type GenericChartProps = Omit<ChartProps, 'options'>;
|
|
23
24
|
declare const Chart: import("svelte").Component<ChartProps, {
|
|
@@ -3,17 +3,9 @@
|
|
|
3
3
|
import Chart from '../chart/Chart.svelte';
|
|
4
4
|
import type { EChartsOption } from 'echarts';
|
|
5
5
|
import * as echarts from 'echarts/core';
|
|
6
|
-
import type { GenericChartProps } from '../chart/Chart.svelte';
|
|
7
6
|
import { createTooltipFormatter } from '../utils/tooltipFormatter';
|
|
8
7
|
import { formattedDurationCompact } from '../utils/duration';
|
|
9
|
-
|
|
10
|
-
interface LineChartProps extends GenericChartProps {
|
|
11
|
-
data: (number | null)[];
|
|
12
|
-
withArea?: boolean;
|
|
13
|
-
color?: string;
|
|
14
|
-
grid?: EChartsOption['grid'];
|
|
15
|
-
captureInterval?: number;
|
|
16
|
-
}
|
|
8
|
+
import type { LineChartProps } from './types';
|
|
17
9
|
|
|
18
10
|
let {
|
|
19
11
|
data,
|
|
@@ -22,6 +14,10 @@
|
|
|
22
14
|
withArea = false,
|
|
23
15
|
color = chartColor['accent'],
|
|
24
16
|
captureInterval,
|
|
17
|
+
additionalSeries,
|
|
18
|
+
graphic,
|
|
19
|
+
tooltipFormatter,
|
|
20
|
+
shouldDim = () => false,
|
|
25
21
|
...props
|
|
26
22
|
}: LineChartProps = $props();
|
|
27
23
|
|
|
@@ -49,6 +45,40 @@
|
|
|
49
45
|
})
|
|
50
46
|
: undefined;
|
|
51
47
|
|
|
48
|
+
const seriesData = $derived.by(() => {
|
|
49
|
+
const mainSeries = {
|
|
50
|
+
type: 'line',
|
|
51
|
+
symbol: 'circle',
|
|
52
|
+
name: yAxisName,
|
|
53
|
+
symbolSize: 1,
|
|
54
|
+
data: data,
|
|
55
|
+
triggerLineEvent: true,
|
|
56
|
+
lineStyle: {
|
|
57
|
+
color: color,
|
|
58
|
+
opacity: shouldDim() ? 0.3 : 1,
|
|
59
|
+
},
|
|
60
|
+
itemStyle: {
|
|
61
|
+
color: color,
|
|
62
|
+
opacity: shouldDim() ? 0.3 : 1,
|
|
63
|
+
},
|
|
64
|
+
areaStyle: withArea
|
|
65
|
+
? {
|
|
66
|
+
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
67
|
+
{
|
|
68
|
+
offset: 0,
|
|
69
|
+
color: echarts.color.modifyAlpha(color, 0.2),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
offset: 1,
|
|
73
|
+
color: echarts.color.modifyAlpha(color, 0),
|
|
74
|
+
},
|
|
75
|
+
]),
|
|
76
|
+
}
|
|
77
|
+
: undefined,
|
|
78
|
+
} as any;
|
|
79
|
+
return additionalSeries ? [mainSeries, ...additionalSeries] : mainSeries;
|
|
80
|
+
});
|
|
81
|
+
|
|
52
82
|
let options: EChartsOption = $derived({
|
|
53
83
|
grid: {
|
|
54
84
|
containLabel: true,
|
|
@@ -96,33 +126,7 @@
|
|
|
96
126
|
...props.yAxisOptions,
|
|
97
127
|
},
|
|
98
128
|
selectedMode: 'multiple',
|
|
99
|
-
series:
|
|
100
|
-
type: 'line',
|
|
101
|
-
symbol: 'circle',
|
|
102
|
-
name: yAxisName,
|
|
103
|
-
symbolSize: 1,
|
|
104
|
-
data: data,
|
|
105
|
-
lineStyle: {
|
|
106
|
-
color: color,
|
|
107
|
-
},
|
|
108
|
-
itemStyle: {
|
|
109
|
-
color: color,
|
|
110
|
-
},
|
|
111
|
-
areaStyle: withArea
|
|
112
|
-
? {
|
|
113
|
-
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
114
|
-
{
|
|
115
|
-
offset: 0,
|
|
116
|
-
color: echarts.color.modifyAlpha(color, 0.2),
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
offset: 1,
|
|
120
|
-
color: echarts.color.modifyAlpha(color, 0),
|
|
121
|
-
},
|
|
122
|
-
]),
|
|
123
|
-
}
|
|
124
|
-
: undefined,
|
|
125
|
-
},
|
|
129
|
+
series: seriesData,
|
|
126
130
|
tooltip: {
|
|
127
131
|
axisPointer: {
|
|
128
132
|
type: 'line',
|
|
@@ -136,8 +140,9 @@
|
|
|
136
140
|
borderWidth: 0,
|
|
137
141
|
borderRadius: 6,
|
|
138
142
|
extraCssText: `box-shadow: ${boxShadow.menu}`,
|
|
139
|
-
formatter: formatTooltip,
|
|
143
|
+
formatter: tooltipFormatter || formatTooltip,
|
|
140
144
|
},
|
|
145
|
+
...(graphic ? { graphic } : {}),
|
|
141
146
|
});
|
|
142
147
|
</script>
|
|
143
148
|
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { GenericChartProps } from '../chart/Chart.svelte';
|
|
3
|
-
interface LineChartProps extends GenericChartProps {
|
|
4
|
-
data: (number | null)[];
|
|
5
|
-
withArea?: boolean;
|
|
6
|
-
color?: string;
|
|
7
|
-
grid?: EChartsOption['grid'];
|
|
8
|
-
captureInterval?: number;
|
|
9
|
-
}
|
|
1
|
+
import type { LineChartProps } from './types';
|
|
10
2
|
declare const LineChart: import("svelte").Component<LineChartProps, {}, "">;
|
|
11
3
|
type LineChart = ReturnType<typeof LineChart>;
|
|
12
4
|
export default LineChart;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EChartsOption } from 'echarts';
|
|
2
|
+
import type { GenericChartProps } from '../chart/Chart.svelte';
|
|
3
|
+
export interface LineChartProps extends GenericChartProps {
|
|
4
|
+
data: (number | null)[];
|
|
5
|
+
withArea?: boolean;
|
|
6
|
+
color?: string;
|
|
7
|
+
grid?: EChartsOption['grid'];
|
|
8
|
+
captureInterval?: number;
|
|
9
|
+
additionalSeries?: EChartsOption['series'][];
|
|
10
|
+
graphic?: EChartsOption['graphic'];
|
|
11
|
+
tooltipFormatter?: (params: any) => string;
|
|
12
|
+
shouldDim?: () => boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import type { ECElementEvent, EChartsOption, LineSeriesOption } from 'echarts';
|
|
5
5
|
import * as echarts from 'echarts/core';
|
|
6
6
|
import { createTooltipFormatter } from '../utils/tooltipFormatter';
|
|
7
|
-
import { Duration } from 'luxon';
|
|
8
7
|
import { formattedDurationCompact } from '../utils/duration';
|
|
9
8
|
|
|
10
9
|
interface Groups {
|
|
@@ -21,6 +20,10 @@
|
|
|
21
20
|
sumYAxis?: boolean;
|
|
22
21
|
captureInterval?: number;
|
|
23
22
|
grid?: EChartsOption['grid'];
|
|
23
|
+
additionalSeries?: EChartsOption['series'][];
|
|
24
|
+
graphic?: EChartsOption['graphic'];
|
|
25
|
+
tooltipFormatter?: (params: any) => string;
|
|
26
|
+
shouldDim?: (selected: string) => boolean;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
let {
|
|
@@ -33,6 +36,10 @@
|
|
|
33
36
|
focusTargetKey,
|
|
34
37
|
sumYAxis = true,
|
|
35
38
|
captureInterval,
|
|
39
|
+
additionalSeries,
|
|
40
|
+
graphic,
|
|
41
|
+
tooltipFormatter,
|
|
42
|
+
shouldDim = () => false,
|
|
36
43
|
...props
|
|
37
44
|
}: MultiLineChartProps = $props();
|
|
38
45
|
|
|
@@ -99,6 +106,79 @@
|
|
|
99
106
|
maximumFractionDigits: fractionDigits,
|
|
100
107
|
}) ?? '';
|
|
101
108
|
|
|
109
|
+
const seriesData = $derived.by(() => {
|
|
110
|
+
const mainSeries = data.map((group, index): LineSeriesOption => {
|
|
111
|
+
const color =
|
|
112
|
+
colors?.[group.key] ?? defaultColorRotationArray[index % defaultColorRotationArray.length];
|
|
113
|
+
|
|
114
|
+
// Determine emphasis based on focusTargetKey or withFocus
|
|
115
|
+
let emphasisConfig: LineSeriesOption['emphasis'];
|
|
116
|
+
if (focusTargetKey) {
|
|
117
|
+
if (group.key === focusTargetKey) {
|
|
118
|
+
emphasisConfig = { focus: 'series' };
|
|
119
|
+
} else {
|
|
120
|
+
emphasisConfig = undefined;
|
|
121
|
+
}
|
|
122
|
+
} else if (withFocus) {
|
|
123
|
+
emphasisConfig = { focus: 'series' };
|
|
124
|
+
} else {
|
|
125
|
+
emphasisConfig = undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Determine visual style based on focusTargetKey
|
|
129
|
+
const isTargeted = focusTargetKey && group.key === focusTargetKey;
|
|
130
|
+
const isBlurred = focusTargetKey && !isTargeted;
|
|
131
|
+
const lineOpacity = shouldDim(group.key) ? 0.3 : isBlurred ? 0.25 : 1;
|
|
132
|
+
const areaOpacityBase = shouldDim(group.key) || isBlurred ? 0.1 : 0.2; // Lower base opacity for blurred area
|
|
133
|
+
const areaOpacityEnd = 0;
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
animation: false,
|
|
137
|
+
...(emphasisConfig && { emphasis: emphasisConfig }),
|
|
138
|
+
type: 'line',
|
|
139
|
+
stack: sumYAxis ? 'total' : undefined,
|
|
140
|
+
symbol: 'circle',
|
|
141
|
+
progressive: 300,
|
|
142
|
+
progressiveThreshold: 1000,
|
|
143
|
+
symbolSize: 1,
|
|
144
|
+
data: group.series,
|
|
145
|
+
triggerLineEvent: true,
|
|
146
|
+
name: group.key,
|
|
147
|
+
lineStyle: {
|
|
148
|
+
color: color,
|
|
149
|
+
opacity: lineOpacity,
|
|
150
|
+
},
|
|
151
|
+
itemStyle: {
|
|
152
|
+
color: color,
|
|
153
|
+
opacity: lineOpacity, // Also apply opacity to item markers if needed
|
|
154
|
+
},
|
|
155
|
+
areaStyle: withArea
|
|
156
|
+
? {
|
|
157
|
+
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
158
|
+
{
|
|
159
|
+
offset: 0,
|
|
160
|
+
color: echarts.color.modifyAlpha(color, areaOpacityBase),
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
offset: 1,
|
|
164
|
+
color: echarts.color.modifyAlpha(color, areaOpacityEnd),
|
|
165
|
+
},
|
|
166
|
+
]),
|
|
167
|
+
}
|
|
168
|
+
: undefined,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
if (additionalSeries) {
|
|
173
|
+
const flattenedAdditional = additionalSeries
|
|
174
|
+
.flat()
|
|
175
|
+
.filter((series): series is LineSeriesOption => series != null);
|
|
176
|
+
return [...mainSeries, ...flattenedAdditional];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return mainSeries;
|
|
180
|
+
});
|
|
181
|
+
|
|
102
182
|
let options: EChartsOption = $derived.by(() => ({
|
|
103
183
|
grid: {
|
|
104
184
|
left: 0,
|
|
@@ -156,75 +236,18 @@
|
|
|
156
236
|
},
|
|
157
237
|
},
|
|
158
238
|
extraCssText: `box-shadow: ${boxShadow.menu}`,
|
|
159
|
-
formatter:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
239
|
+
formatter:
|
|
240
|
+
tooltipFormatter ||
|
|
241
|
+
createTooltipFormatter({
|
|
242
|
+
yAxisName,
|
|
243
|
+
yAxisUnit: props.yAxisUnit ?? undefined,
|
|
244
|
+
focusedSeriesAccessor: () => focusedSeries,
|
|
245
|
+
getSeriesColor,
|
|
246
|
+
maxSeriesToShow: 6,
|
|
247
|
+
}),
|
|
166
248
|
},
|
|
167
|
-
series:
|
|
168
|
-
|
|
169
|
-
colors?.[group.key] ?? defaultColorRotationArray[index % defaultColorRotationArray.length];
|
|
170
|
-
|
|
171
|
-
// Determine emphasis based on focusTargetKey or withFocus
|
|
172
|
-
let emphasisConfig: LineSeriesOption['emphasis'];
|
|
173
|
-
if (focusTargetKey) {
|
|
174
|
-
if (group.key === focusTargetKey) {
|
|
175
|
-
emphasisConfig = { focus: 'series' };
|
|
176
|
-
} else {
|
|
177
|
-
emphasisConfig = undefined;
|
|
178
|
-
}
|
|
179
|
-
} else if (withFocus) {
|
|
180
|
-
emphasisConfig = { focus: 'series' };
|
|
181
|
-
} else {
|
|
182
|
-
emphasisConfig = undefined;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Determine visual style based on focusTargetKey
|
|
186
|
-
const isTargeted = focusTargetKey && group.key === focusTargetKey;
|
|
187
|
-
const isBlurred = focusTargetKey && !isTargeted;
|
|
188
|
-
const lineOpacity = isBlurred ? 0.25 : 1;
|
|
189
|
-
const areaOpacityBase = isBlurred ? 0.1 : 0.2; // Lower base opacity for blurred area
|
|
190
|
-
const areaOpacityEnd = 0;
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
animation: false,
|
|
194
|
-
...(emphasisConfig && { emphasis: emphasisConfig }),
|
|
195
|
-
type: 'line',
|
|
196
|
-
stack: sumYAxis ? 'total' : undefined,
|
|
197
|
-
symbol: 'circle',
|
|
198
|
-
progressive: 300,
|
|
199
|
-
progressiveThreshold: 1000,
|
|
200
|
-
symbolSize: 1,
|
|
201
|
-
data: group.series,
|
|
202
|
-
triggerLineEvent: true,
|
|
203
|
-
name: group.key,
|
|
204
|
-
lineStyle: {
|
|
205
|
-
color: color,
|
|
206
|
-
opacity: lineOpacity,
|
|
207
|
-
},
|
|
208
|
-
itemStyle: {
|
|
209
|
-
color: color,
|
|
210
|
-
opacity: lineOpacity, // Also apply opacity to item markers if needed
|
|
211
|
-
},
|
|
212
|
-
areaStyle: withArea
|
|
213
|
-
? {
|
|
214
|
-
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
215
|
-
{
|
|
216
|
-
offset: 0,
|
|
217
|
-
color: echarts.color.modifyAlpha(color, areaOpacityBase),
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
offset: 1,
|
|
221
|
-
color: echarts.color.modifyAlpha(color, areaOpacityEnd),
|
|
222
|
-
},
|
|
223
|
-
]),
|
|
224
|
-
}
|
|
225
|
-
: undefined,
|
|
226
|
-
};
|
|
227
|
-
}),
|
|
249
|
+
series: seriesData,
|
|
250
|
+
...(graphic ? { graphic } : {}),
|
|
228
251
|
}));
|
|
229
252
|
</script>
|
|
230
253
|
|
|
@@ -232,7 +255,6 @@
|
|
|
232
255
|
{options}
|
|
233
256
|
{...{
|
|
234
257
|
...props,
|
|
235
|
-
|
|
236
258
|
xAxisName,
|
|
237
259
|
yAxisName,
|
|
238
260
|
onmouseover: handleMouseOver,
|
|
@@ -13,6 +13,10 @@ interface MultiLineChartProps extends GenericChartProps {
|
|
|
13
13
|
sumYAxis?: boolean;
|
|
14
14
|
captureInterval?: number;
|
|
15
15
|
grid?: EChartsOption['grid'];
|
|
16
|
+
additionalSeries?: EChartsOption['series'][];
|
|
17
|
+
graphic?: EChartsOption['graphic'];
|
|
18
|
+
tooltipFormatter?: (params: any) => string;
|
|
19
|
+
shouldDim?: (selected: string) => boolean;
|
|
16
20
|
}
|
|
17
21
|
declare const MultiLineChart: import("svelte").Component<MultiLineChartProps, {}, "">;
|
|
18
22
|
type MultiLineChart = ReturnType<typeof MultiLineChart>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EChartsOption } from 'echarts';
|
|
2
|
+
import type { GenericChartProps } from '../chart/Chart.svelte';
|
|
3
|
+
interface Groups {
|
|
4
|
+
key: string;
|
|
5
|
+
series: (number | null)[];
|
|
6
|
+
}
|
|
7
|
+
export interface MultiLineChartProps extends GenericChartProps {
|
|
8
|
+
data: Groups[];
|
|
9
|
+
withArea?: boolean;
|
|
10
|
+
colors?: Record<string, string>;
|
|
11
|
+
withFocus?: boolean;
|
|
12
|
+
focusTargetKey?: string;
|
|
13
|
+
sumYAxis?: boolean;
|
|
14
|
+
captureInterval?: number;
|
|
15
|
+
grid?: EChartsOption['grid'];
|
|
16
|
+
additionalSeries?: EChartsOption['series'][];
|
|
17
|
+
graphic?: EChartsOption['graphic'];
|
|
18
|
+
tooltipFormatter?: (params: any) => string;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { CallbackDataParams } from 'echarts/types/dist/shared';
|
|
2
|
+
interface ExtendedCallbackDataParams extends CallbackDataParams {
|
|
3
|
+
prefix?: string;
|
|
4
|
+
textColor?: string;
|
|
5
|
+
}
|
|
2
6
|
interface TooltipFormatterConfig {
|
|
3
7
|
yAxisName?: string;
|
|
4
8
|
yAxisUnit?: string;
|
|
@@ -6,5 +10,5 @@ interface TooltipFormatterConfig {
|
|
|
6
10
|
getSeriesColor?: (seriesName: string, seriesIndex: number) => string;
|
|
7
11
|
maxSeriesToShow?: number;
|
|
8
12
|
}
|
|
9
|
-
export declare const createTooltipFormatter: (config: TooltipFormatterConfig) => (params:
|
|
13
|
+
export declare const createTooltipFormatter: (config: TooltipFormatterConfig) => (params: ExtendedCallbackDataParams | ExtendedCallbackDataParams[]) => string;
|
|
10
14
|
export {};
|
|
@@ -20,7 +20,7 @@ export const createTooltipFormatter = (config) => {
|
|
|
20
20
|
}
|
|
21
21
|
const firstParam = paramArray[0];
|
|
22
22
|
const categoryName = firstParam.name ?? ''; // X-axis value (category)
|
|
23
|
-
const unit = config.yAxisUnit
|
|
23
|
+
const unit = config.yAxisUnit ? ` (${config.yAxisUnit})` : '';
|
|
24
24
|
let tooltipContent = `<div class="font-medium mb-1 text-primary">${categoryName}</div>`;
|
|
25
25
|
const originalSeriesCount = paramArray.length;
|
|
26
26
|
let seriesToShow = paramArray;
|
|
@@ -33,6 +33,8 @@ export const createTooltipFormatter = (config) => {
|
|
|
33
33
|
const seriesName = p.seriesName ?? config.yAxisName ?? ''; // Use series name or configured yAxisName
|
|
34
34
|
const value = p.value; // Assume number after null check
|
|
35
35
|
let color = p.color;
|
|
36
|
+
const prefix = p.prefix ?? '';
|
|
37
|
+
const textColorValue = p.textColor;
|
|
36
38
|
// Resolve color for multiline if needed and getter provided
|
|
37
39
|
if (!color && config.getSeriesColor && p.seriesName) {
|
|
38
40
|
color = config.getSeriesColor(p.seriesName, p.seriesIndex ?? index);
|
|
@@ -40,7 +42,9 @@ export const createTooltipFormatter = (config) => {
|
|
|
40
42
|
// Fallback color if still unresolved (should ideally not happen if configured correctly)
|
|
41
43
|
color = color || textColor['icon-blue'];
|
|
42
44
|
const marker = `<span class="size-2 rounded-sm" style="background-color:${color}"></span>`;
|
|
43
|
-
const
|
|
45
|
+
const formattedValue = toFixedLocaleString(value, 2);
|
|
46
|
+
const textColorClass = textColorValue ? `color: ${textColorValue}` : 'text-primary';
|
|
47
|
+
const valueString = `<span class="${textColorClass}">${prefix}${formattedValue}${unit}</span>`;
|
|
44
48
|
tooltipContent += `<div class="flex items-center justify-between gap-4"><span class="flex items-center gap-2">${marker}${seriesName}</span><span>${valueString}</span></div>`;
|
|
45
49
|
});
|
|
46
50
|
if (originalSeriesCount > maxSeriesToShow) {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
onclick?: (event: MouseEvent, marks: Array<{ x: number; y: number }>) => void;
|
|
25
25
|
disabled?: boolean;
|
|
26
26
|
hideMarkers?: boolean;
|
|
27
|
+
imageDimensions?: { width: number; height: number };
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
let {
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
disabled = false,
|
|
33
34
|
hideMarkers = false,
|
|
34
35
|
marks = $bindable([]),
|
|
36
|
+
imageDimensions = $bindable({ width: 0, height: 0 }),
|
|
35
37
|
}: Props = $props();
|
|
36
38
|
|
|
37
39
|
let svgElement: SVGSVGElement;
|
|
@@ -465,6 +467,11 @@
|
|
|
465
467
|
$effect(() => {
|
|
466
468
|
renderMarkers();
|
|
467
469
|
});
|
|
470
|
+
|
|
471
|
+
$effect(() => {
|
|
472
|
+
imageDimensions.width = imageDisplayWidth;
|
|
473
|
+
imageDimensions.height = imageDisplayHeight;
|
|
474
|
+
});
|
|
468
475
|
</script>
|
|
469
476
|
|
|
470
477
|
{#snippet TopLeftActions()}
|
|
@@ -10,7 +10,11 @@ interface Props {
|
|
|
10
10
|
}>) => void;
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
hideMarkers?: boolean;
|
|
13
|
+
imageDimensions?: {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
};
|
|
13
17
|
}
|
|
14
|
-
declare const ManualCfuCounter: import("svelte").Component<Props, {}, "marks">;
|
|
18
|
+
declare const ManualCfuCounter: import("svelte").Component<Props, {}, "marks" | "imageDimensions">;
|
|
15
19
|
type ManualCfuCounter = ReturnType<typeof ManualCfuCounter>;
|
|
16
20
|
export default ManualCfuCounter;
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
activeMarkerName?: string;
|
|
52
52
|
editableConfigIndex?: number | null;
|
|
53
53
|
showMultiConfig?: boolean;
|
|
54
|
+
imageDimensions?: { width: number; height: number };
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
let {
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
activeMarkerName = '',
|
|
66
67
|
editableConfigIndex = null,
|
|
67
68
|
showMultiConfig = false,
|
|
69
|
+
imageDimensions = $bindable({ width: 0, height: 0 }),
|
|
68
70
|
}: Props = $props();
|
|
69
71
|
|
|
70
72
|
let previousConfigIndex = $state<number | null>(null);
|
|
@@ -531,6 +533,11 @@
|
|
|
531
533
|
$effect(() => {
|
|
532
534
|
renderMarkers();
|
|
533
535
|
});
|
|
536
|
+
|
|
537
|
+
$effect(() => {
|
|
538
|
+
imageDimensions.width = imageDisplayWidth;
|
|
539
|
+
imageDimensions.height = imageDisplayHeight;
|
|
540
|
+
});
|
|
534
541
|
</script>
|
|
535
542
|
|
|
536
543
|
{#snippet UndoResetControls()}
|
|
@@ -26,7 +26,11 @@ interface Props {
|
|
|
26
26
|
activeMarkerName?: string;
|
|
27
27
|
editableConfigIndex?: number | null;
|
|
28
28
|
showMultiConfig?: boolean;
|
|
29
|
+
imageDimensions?: {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
29
33
|
}
|
|
30
|
-
declare const MultiCfuCounter: import("svelte").Component<Props, {}, "marks">;
|
|
34
|
+
declare const MultiCfuCounter: import("svelte").Component<Props, {}, "marks" | "imageDimensions">;
|
|
31
35
|
type MultiCfuCounter = ReturnType<typeof MultiCfuCounter>;
|
|
32
36
|
export default MultiCfuCounter;
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
class="flex w-full flex-shrink-0 flex-grow basis-0 flex-col items-start gap-2 overflow-clip rounded-lg bg-neutral p-4 text-left transition-colors"
|
|
96
96
|
class:hover:bg-neutral-hover={editable && !isEditing && value !== null}
|
|
97
97
|
class:cursor-pointer={editable && !isEditing && value !== null}
|
|
98
|
-
onclick={
|
|
98
|
+
onclick={handleCardClick}
|
|
99
99
|
onkeydown={editable && !isEditing && value !== null ? handleInputKeydown : undefined}
|
|
100
100
|
aria-label={editable && !isEditing && value !== null ? `Edit ${title}` : undefined}
|
|
101
101
|
>
|
package/dist/tokens.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { colors } from './tokens/colors';
|
|
2
2
|
export type OutputName = 'cfu' | 'halo' | 'seed' | 'seedling' | 'leaf' | 'insect' | 'egg' | 'food' | 'positive' | 'negative' | 'review' | 'tntc' | 'contaminated' | 'countable' | 'full_growth' | 'reduced_growth' | 'dotted_growth';
|
|
3
|
+
declare const annotationOutputFadedFillColors: {
|
|
4
|
+
[K in OutputName]: string;
|
|
5
|
+
};
|
|
6
|
+
declare const annotationOutputFadedStrokeColors: {
|
|
7
|
+
[K in OutputName]: string;
|
|
8
|
+
};
|
|
3
9
|
declare const borderColor: {
|
|
4
10
|
'dark-static': string;
|
|
5
11
|
'dark-input': string;
|
|
@@ -212,7 +218,7 @@ declare const boxShadow: {
|
|
|
212
218
|
container: string;
|
|
213
219
|
menu: string;
|
|
214
220
|
};
|
|
215
|
-
export { colors, borderColor, textColor, backgroundColor, boxShadow, outlineColor, chartColor };
|
|
221
|
+
export { colors, borderColor, textColor, backgroundColor, boxShadow, outlineColor, chartColor, annotationOutputFadedFillColors, annotationOutputFadedStrokeColors, };
|
|
216
222
|
export declare const tokens: {
|
|
217
223
|
colors: {
|
|
218
224
|
base: {
|
package/dist/tokens.js
CHANGED
|
@@ -358,7 +358,7 @@ const boxShadow = {
|
|
|
358
358
|
menu: `0 4px 20px 0 ${colors.shadow[6]}, 0 0 0 1px ${colors.shadow[8]}`,
|
|
359
359
|
};
|
|
360
360
|
// Export individual token categories for tree-shaking
|
|
361
|
-
export { colors, borderColor, textColor, backgroundColor, boxShadow, outlineColor, chartColor };
|
|
361
|
+
export { colors, borderColor, textColor, backgroundColor, boxShadow, outlineColor, chartColor, annotationOutputFadedFillColors, annotationOutputFadedStrokeColors, };
|
|
362
362
|
// Export tokens object for backward compatibility
|
|
363
363
|
export const tokens = {
|
|
364
364
|
colors,
|