@reshape-biotech/design-system 2.2.2 → 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.
Files changed (28) hide show
  1. package/dist/components/graphs/bar-chart/BarChart.svelte +23 -16
  2. package/dist/components/graphs/bar-chart/BarChart.svelte.d.ts +1 -0
  3. package/dist/components/graphs/bar-chart/StackedBarChart.svelte +8 -2
  4. package/dist/components/graphs/bar-chart/StackedBarChart.svelte.d.ts +1 -0
  5. package/dist/components/graphs/chart/Chart.svelte +8 -1
  6. package/dist/components/graphs/chart/Chart.svelte.d.ts +3 -2
  7. package/dist/components/graphs/line/LineChart.svelte +42 -37
  8. package/dist/components/graphs/line/LineChart.svelte.d.ts +1 -9
  9. package/dist/components/graphs/line/types.d.ts +13 -0
  10. package/dist/components/graphs/line/types.js +1 -0
  11. package/dist/components/graphs/multiline/MultiLineChart.svelte +92 -70
  12. package/dist/components/graphs/multiline/MultiLineChart.svelte.d.ts +4 -0
  13. package/dist/components/graphs/multiline/types.d.ts +20 -0
  14. package/dist/components/graphs/multiline/types.js +1 -0
  15. package/dist/components/graphs/utils/tooltipFormatter.d.ts +5 -1
  16. package/dist/components/graphs/utils/tooltipFormatter.js +6 -2
  17. package/dist/components/manual-cfu-counter/ManualCFUCounter.svelte +7 -0
  18. package/dist/components/manual-cfu-counter/ManualCFUCounter.svelte.d.ts +5 -1
  19. package/dist/components/modal/Modal.stories.svelte +1 -1
  20. package/dist/components/modal/components/modal-bottom.svelte +2 -1
  21. package/dist/components/modal/components/modal-content.svelte +2 -1
  22. package/dist/components/modal/components/modal-title.svelte +2 -1
  23. package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte +7 -0
  24. package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte.d.ts +5 -1
  25. package/dist/components/stat-card/StatCard.svelte +1 -1
  26. package/dist/tokens.d.ts +7 -1
  27. package/dist/tokens.js +1 -1
  28. package/package.json +3 -2
@@ -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
- value,
58
- itemStyle: {
59
- color,
60
- borderRadius: 2,
61
- borderWidth: 0.5,
62
- borderColor: borderColor['white'],
63
- },
64
- emphasis: {
65
- disabled: true,
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) => createBarDataItem(value, seriesColors[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 { EChartsOption } from 'echarts';
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: createTooltipFormatter({
160
- yAxisName,
161
- yAxisUnit: props.yAxisUnit ?? undefined,
162
- focusedSeriesAccessor: () => focusedSeries,
163
- getSeriesColor,
164
- maxSeriesToShow: 6,
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: data.map((group, index): LineSeriesOption => {
168
- const color =
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: CallbackDataParams | CallbackDataParams[]) => string;
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 valueString = `<span class="text-primary">${toFixedLocaleString(value, 2)} ${unit}</span>`;
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;
@@ -150,7 +150,7 @@
150
150
  <Modal.Trigger>
151
151
  <Button variant="primary">Open Custom Modal</Button>
152
152
  </Modal.Trigger>
153
- <Modal.Content class="min-w-full !bg-accent p-8">
153
+ <Modal.Content class="min-w-full bg-accent p-8">
154
154
  <Modal.Title class="text-xl text-primary-inverse">Custom Styled Modal</Modal.Title>
155
155
  <div class="space-y-4 pt-4">
156
156
  <p class="text-sm text-secondary-inverse">
@@ -1,10 +1,11 @@
1
1
  <script lang="ts">
2
+ import { twMerge } from 'tailwind-merge';
2
3
  import type { ModalBottomProps } from '../types';
3
4
 
4
5
  let { children, class: className = '', ...restProps }: ModalBottomProps = $props();
5
6
 
6
7
  const baseClasses = 'border-t border-static -mx-5';
7
- const finalClasses = `${baseClasses} ${className}`;
8
+ const finalClasses = twMerge(baseClasses, className);
8
9
  </script>
9
10
 
10
11
  <div class={finalClasses} {...restProps}>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { Dialog } from 'bits-ui';
3
3
  import { scale } from 'svelte/transition';
4
+ import { twMerge } from 'tailwind-merge';
4
5
  import { Icon } from '../../icons';
5
6
  import type { ModalContentProps } from '../types';
6
7
  import Overlay from './modal-overlay.svelte';
@@ -16,7 +17,7 @@
16
17
 
17
18
  const baseClasses =
18
19
  'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 bg-surface p-5 shadow-lg sm:rounded-lg';
19
- const finalClasses = `${baseClasses} ${className}`;
20
+ const finalClasses = twMerge(baseClasses, className);
20
21
  </script>
21
22
 
22
23
  {#snippet withCloseButton()}
@@ -1,11 +1,12 @@
1
1
  <script lang="ts">
2
2
  import { Dialog } from 'bits-ui';
3
3
  import type { ModalTitleProps } from '../types';
4
+ import { twMerge } from 'tailwind-merge';
4
5
 
5
6
  let { children, class: className = '', icon, ...restProps }: ModalTitleProps = $props();
6
7
 
7
8
  const baseClasses = 'text-xl font-semibold tracking-tight text-primary';
8
- const finalClasses = `${baseClasses} ${className}`;
9
+ const finalClasses = twMerge(baseClasses, className);
9
10
  </script>
10
11
 
11
12
  <div class="space-y-6">
@@ -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={editable && !isEditing && value !== null ? handleCardClick : undefined}
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reshape-biotech/design-system",
3
- "version": "2.2.2",
3
+ "version": "2.3.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build",
@@ -389,6 +389,7 @@
389
389
  },
390
390
  "dependencies": {
391
391
  "bits-ui": "^1.8.0",
392
- "echarts": "^5.6.0"
392
+ "echarts": "^5.6.0",
393
+ "tailwind-merge": "^3.3.1"
393
394
  }
394
395
  }