@reshape-biotech/design-system 1.2.4 → 1.2.6
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/button/Button.svelte +7 -7
- package/dist/components/collapsible/components/collapsible-trigger.svelte +2 -2
- package/dist/components/combobox/components/combobox-content.svelte +1 -1
- package/dist/components/datepicker/DatePicker.svelte +2 -2
- package/dist/components/graphs/bar-chart/BarChart.svelte +34 -21
- package/dist/components/graphs/bar-chart/StackedBarChart.stories.svelte +1 -1
- package/dist/components/graphs/bar-chart/StackedBarChart.svelte +36 -23
- package/dist/components/graphs/chart/Chart.svelte +0 -2
- package/dist/components/graphs/line/LineChart.stories.svelte +1 -1
- package/dist/components/graphs/line/LineChart.svelte +37 -19
- package/dist/components/graphs/multiline/MultiLineChart.svelte +33 -17
- package/dist/components/graphs/utils/tooltipFormatter.js +4 -4
- package/dist/components/icon-button/IconButton.svelte +3 -3
- package/dist/components/icons/index.d.ts +3 -2
- package/dist/components/icons/index.js +2 -0
- package/dist/components/manual-cfu-counter/ManualCFUCounter.svelte +14 -18
- package/dist/components/multi-cfu-counter/MultiCFUCounter.stories.svelte +215 -0
- package/dist/components/multi-cfu-counter/MultiCFUCounter.stories.svelte.d.ts +3 -0
- package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte +662 -0
- package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte.d.ts +32 -0
- package/dist/components/multi-cfu-counter/index.d.ts +1 -0
- package/dist/components/multi-cfu-counter/index.js +1 -0
- package/dist/components/multi-cfu-counter/test/MultiCFUCounterTestWrapper.svelte +28 -0
- package/dist/components/multi-cfu-counter/test/MultiCFUCounterTestWrapper.svelte.d.ts +20 -0
- package/dist/components/select-new/components/SelectItem.svelte +5 -7
- package/dist/components/select-new/components/SelectTrigger.svelte +2 -2
- package/dist/components/slider/Slider.svelte +1 -1
- package/dist/components/stat-card/StatCard.svelte +1 -1
- package/dist/components/status-badge/StatusBadge.svelte +1 -1
- package/dist/components/stepper/components/stepper-step.svelte +1 -1
- package/dist/components/tag/Tag.svelte +2 -1
- package/dist/components/tag/Tag.svelte.d.ts +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +5 -1
- package/dist/styles.d.ts +1 -0
- package/dist/styles.js +4 -0
- package/dist/tailwind-safelist.js +10 -2
- package/dist/tailwind.preset.d.ts +6 -0
- package/dist/tokens/colors.d.ts +246 -0
- package/dist/tokens/colors.js +139 -0
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/index.js +5 -0
- package/dist/tokens/typography.d.ts +48 -0
- package/dist/tokens/typography.js +48 -0
- package/dist/tokens.d.ts +49 -251
- package/dist/tokens.js +37 -147
- package/package.json +397 -78
|
@@ -34,6 +34,7 @@ import ClockCountdown from 'phosphor-svelte/lib/ClockCountdown';
|
|
|
34
34
|
import Copy from 'phosphor-svelte/lib/Copy';
|
|
35
35
|
import Crop from 'phosphor-svelte/lib/Crop';
|
|
36
36
|
import Cube from 'phosphor-svelte/lib/Cube';
|
|
37
|
+
import CursorClick from 'phosphor-svelte/lib/CursorClick';
|
|
37
38
|
import Database from 'phosphor-svelte/lib/Database';
|
|
38
39
|
import Dna from 'phosphor-svelte/lib/Dna';
|
|
39
40
|
import DotsThree from 'phosphor-svelte/lib/DotsThree';
|
|
@@ -151,6 +152,7 @@ export const iconMap = {
|
|
|
151
152
|
Copy,
|
|
152
153
|
Crop,
|
|
153
154
|
Cube,
|
|
155
|
+
CursorClick,
|
|
154
156
|
Database,
|
|
155
157
|
Dna,
|
|
156
158
|
DotsThree,
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
3
|
import { Button } from '../button';
|
|
4
|
-
import { textColor } from '../../tokens';
|
|
4
|
+
import { textColor, borderColor } from '../../tokens';
|
|
5
5
|
import { Icon } from '../icons';
|
|
6
6
|
import IconButton from '../icon-button/IconButton.svelte';
|
|
7
7
|
import Divider from '../divider/Divider.svelte';
|
|
8
8
|
|
|
9
9
|
// Constants for the component
|
|
10
10
|
const BASE_IMAGE_SIZE = 464; // Base size, will be overriden by container dimensions
|
|
11
|
-
const BASE_MARKER_SIZE =
|
|
12
|
-
const BASE_MARKER_FONT_SIZE =
|
|
11
|
+
const BASE_MARKER_SIZE = 8; // Base size at zoom level 1
|
|
12
|
+
const BASE_MARKER_FONT_SIZE = 8; // Base font size at zoom level 1
|
|
13
13
|
const MAX_ZOOM = 10;
|
|
14
14
|
const MIN_ZOOM = 1;
|
|
15
15
|
const ZOOM_STEP = 0.001;
|
|
16
16
|
const MARKER_COLOR = textColor['icon-blue'] || '#2563eb';
|
|
17
|
+
const MARKER_BORDER_COLOR = borderColor['interactive'] || '#12192A1A';
|
|
17
18
|
const TEXT_COLOR = textColor['primary-inverse'];
|
|
18
19
|
const DRAG_THRESHOLD = 5;
|
|
19
20
|
|
|
@@ -36,7 +37,6 @@
|
|
|
36
37
|
let svgElement: SVGSVGElement;
|
|
37
38
|
let viewport: SVGGraphicsElement;
|
|
38
39
|
let dotsGroup: SVGElement;
|
|
39
|
-
let marksHistory: Array<Array<{ x: number; y: number }>> = $state([]);
|
|
40
40
|
let container: HTMLDivElement;
|
|
41
41
|
|
|
42
42
|
let containerW = BASE_IMAGE_SIZE;
|
|
@@ -219,6 +219,9 @@
|
|
|
219
219
|
circle.setAttribute('r', String(adjustedMarkerSize));
|
|
220
220
|
circle.setAttribute('fill', MARKER_COLOR);
|
|
221
221
|
circle.setAttribute('class', 'drop-shadow-sm');
|
|
222
|
+
circle.setAttribute('opacity', '0.75');
|
|
223
|
+
circle.setAttribute('stroke', MARKER_BORDER_COLOR);
|
|
224
|
+
circle.setAttribute('stroke-width', String(1 / transform.scale));
|
|
222
225
|
group.appendChild(circle);
|
|
223
226
|
|
|
224
227
|
const text = document.createElementNS(svgns, 'text');
|
|
@@ -241,7 +244,6 @@
|
|
|
241
244
|
panningState = null;
|
|
242
245
|
return;
|
|
243
246
|
}
|
|
244
|
-
saveCurrentState();
|
|
245
247
|
const pt = getSvgPoint(event);
|
|
246
248
|
marks.push({ x: pt.x, y: pt.y });
|
|
247
249
|
|
|
@@ -368,23 +370,17 @@
|
|
|
368
370
|
updateTransform();
|
|
369
371
|
}
|
|
370
372
|
|
|
371
|
-
function
|
|
372
|
-
marksHistory.push([...marks]);
|
|
373
|
-
}
|
|
374
|
-
|
|
373
|
+
// Simplified undo function - just pop the last mark
|
|
375
374
|
function undo() {
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
renderMarkers();
|
|
382
|
-
}
|
|
375
|
+
if (marks.length === 0) return;
|
|
376
|
+
|
|
377
|
+
marks.pop();
|
|
378
|
+
marks = marks;
|
|
379
|
+
renderMarkers();
|
|
383
380
|
}
|
|
384
381
|
|
|
385
382
|
function reset() {
|
|
386
383
|
if (marks.length === 0) return;
|
|
387
|
-
saveCurrentState();
|
|
388
384
|
|
|
389
385
|
marks = [];
|
|
390
386
|
renderMarkers();
|
|
@@ -480,7 +476,7 @@
|
|
|
480
476
|
variant="transparent-inverse"
|
|
481
477
|
rounded={false}
|
|
482
478
|
onclick={undo}
|
|
483
|
-
disabled={
|
|
479
|
+
disabled={marks.length === 0 || disabled}
|
|
484
480
|
aria-label="Undo last mark"
|
|
485
481
|
>
|
|
486
482
|
<Icon iconName={'ArrowUUpLeft'} />
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
|
+
import MultiCFUCounter from './MultiCFUCounter.svelte';
|
|
4
|
+
const { Story } = defineMeta({
|
|
5
|
+
component: MultiCFUCounter,
|
|
6
|
+
title: 'Design System/MultiCFUCounter',
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
args: {
|
|
9
|
+
imageUrl: '/imgs/ris.jpg',
|
|
10
|
+
marks: [],
|
|
11
|
+
disabled: false,
|
|
12
|
+
hideMarkers: false
|
|
13
|
+
},
|
|
14
|
+
argTypes: {
|
|
15
|
+
imageUrl: {
|
|
16
|
+
control: 'text',
|
|
17
|
+
description: 'URL of the image to display'
|
|
18
|
+
},
|
|
19
|
+
marks: {
|
|
20
|
+
control: 'object',
|
|
21
|
+
description: 'Array of marker coordinates. Bindable for two-way data flow.'
|
|
22
|
+
},
|
|
23
|
+
onclick: {
|
|
24
|
+
action: 'clicked',
|
|
25
|
+
description: 'Event handler for when a marker is added'
|
|
26
|
+
},
|
|
27
|
+
disabled: {
|
|
28
|
+
control: 'boolean',
|
|
29
|
+
description: 'Disables interaction with the counter'
|
|
30
|
+
},
|
|
31
|
+
hideMarkers: {
|
|
32
|
+
control: 'boolean',
|
|
33
|
+
description: 'Hides the markers and prevents new ones from being added'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const largeImage = '/imgs/ris.jpg';
|
|
39
|
+
|
|
40
|
+
function handleUpdate(event: MouseEvent, marks: Array<{ x: number; y: number }>) {
|
|
41
|
+
console.log('Marker count:', marks.length);
|
|
42
|
+
console.log('Marker positions:', marks);
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<script lang="ts">
|
|
47
|
+
let activeConfigIndex = $state(0);
|
|
48
|
+
let allMarksMultiConfig = $state([
|
|
49
|
+
// Yeast Count (config 0) - blue markers
|
|
50
|
+
{ x: 120, y: 150, configIndex: 0, color: 'icon-blue', id: 'yeast-1' },
|
|
51
|
+
{ x: 180, y: 220, configIndex: 0, color: 'icon-blue', id: 'yeast-2' },
|
|
52
|
+
{ x: 250, y: 180, configIndex: 0, color: 'icon-blue', id: 'yeast-3' },
|
|
53
|
+
|
|
54
|
+
// Mold Count (config 1) - orange markers
|
|
55
|
+
{ x: 320, y: 160, configIndex: 1, color: 'icon-orange', id: 'mold-1' },
|
|
56
|
+
{ x: 380, y: 240, configIndex: 1, color: 'icon-orange', id: 'mold-2' },
|
|
57
|
+
|
|
58
|
+
// Colony Type (config 2) - lime markers
|
|
59
|
+
{ x: 400, y: 120, configIndex: 2, color: 'icon-lime', id: 'type-1' }
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const configurations = [
|
|
63
|
+
{ name: 'Yeast Count', color: 'icon-blue', configIndex: 0 },
|
|
64
|
+
{ name: 'Mold Count', color: 'icon-orange', configIndex: 1 },
|
|
65
|
+
{ name: 'Colony Type', color: 'icon-lime', configIndex: 2 }
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
function handleMultiConfigUpdate(event: MouseEvent, marks: Array<{ x: number; y: number }>) {
|
|
69
|
+
console.log(`Configuration ${activeConfigIndex} updated:`, marks.length, 'marks');
|
|
70
|
+
|
|
71
|
+
allMarksMultiConfig = allMarksMultiConfig.filter(
|
|
72
|
+
(mark) => mark.configIndex !== activeConfigIndex
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const newMarks = marks.map((mark, index) => ({
|
|
76
|
+
x: mark.x,
|
|
77
|
+
y: mark.y,
|
|
78
|
+
configIndex: activeConfigIndex,
|
|
79
|
+
color: configurations[activeConfigIndex].color,
|
|
80
|
+
id: `config-${activeConfigIndex}-${index + 1}`
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
allMarksMultiConfig = [...allMarksMultiConfig, ...newMarks];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function switchConfiguration(configIndex: number) {
|
|
87
|
+
activeConfigIndex = configIndex;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const activeConfigMarks = $derived(
|
|
91
|
+
allMarksMultiConfig
|
|
92
|
+
.filter((mark) => mark.configIndex === activeConfigIndex)
|
|
93
|
+
.map((mark) => ({ x: mark.x, y: mark.y }))
|
|
94
|
+
);
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<Story name="Default" asChild>
|
|
98
|
+
<div class="p-4">
|
|
99
|
+
<h3 class="mb-4 text-lg font-semibold">Manual CFU Counter</h3>
|
|
100
|
+
<p class="mb-4 text-sm text-secondary">
|
|
101
|
+
Click to add markers. Pan by clicking and dragging. Zoom with the mouse wheel.
|
|
102
|
+
</p>
|
|
103
|
+
<div class="h-[600px] rounded-lg border border-static p-4">
|
|
104
|
+
<MultiCFUCounter imageUrl={largeImage} onclick={handleUpdate} activeMarkerName="Yeast" />
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</Story>
|
|
108
|
+
|
|
109
|
+
<Story name="Multi-Configuration Mode" asChild>
|
|
110
|
+
<div class="p-4">
|
|
111
|
+
<h3 class="mb-4 text-lg font-semibold">Multi-Configuration CFU Counter</h3>
|
|
112
|
+
<p class="mb-4 text-sm text-secondary">
|
|
113
|
+
This example demonstrates the multi-configuration mode used in assay analysis validation.
|
|
114
|
+
Switch between different configurations to count different types of objects with color-coded
|
|
115
|
+
markers.
|
|
116
|
+
</p>
|
|
117
|
+
|
|
118
|
+
<div class="mb-4 flex gap-2">
|
|
119
|
+
{#each configurations as config, index}
|
|
120
|
+
<button
|
|
121
|
+
class="flex items-center gap-2 rounded-lg border px-3 py-2 text-sm transition-colors"
|
|
122
|
+
class:border-interactive={activeConfigIndex === index}
|
|
123
|
+
class:bg-surface-secondary={activeConfigIndex === index}
|
|
124
|
+
class:border-neutral={activeConfigIndex !== index}
|
|
125
|
+
onclick={() => switchConfiguration(index)}
|
|
126
|
+
>
|
|
127
|
+
<div
|
|
128
|
+
class="h-3 w-3 rounded-full"
|
|
129
|
+
style="background-color: var(--text-{config.color})"
|
|
130
|
+
></div>
|
|
131
|
+
{config.name}
|
|
132
|
+
<span class="text-xs text-tertiary">
|
|
133
|
+
({allMarksMultiConfig.filter((m) => m.configIndex === index).length})
|
|
134
|
+
</span>
|
|
135
|
+
</button>
|
|
136
|
+
{/each}
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div class="mb-4 rounded-lg border border-neutral bg-surface-secondary p-3">
|
|
140
|
+
<div class="mb-2 flex items-center gap-2">
|
|
141
|
+
<div
|
|
142
|
+
class="h-4 w-4 rounded-full"
|
|
143
|
+
style="background-color: var(--text-{configurations[activeConfigIndex].color})"
|
|
144
|
+
></div>
|
|
145
|
+
<span class="font-medium">Active: {configurations[activeConfigIndex].name}</span>
|
|
146
|
+
</div>
|
|
147
|
+
<p class="text-sm text-secondary">
|
|
148
|
+
Click on the image to add markers for {configurations[
|
|
149
|
+
activeConfigIndex
|
|
150
|
+
].name.toLowerCase()}. Each configuration maintains its own set of markers with distinct
|
|
151
|
+
colors.
|
|
152
|
+
</p>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<div class="h-[600px] rounded-lg border border-static p-4">
|
|
156
|
+
<MultiCFUCounter
|
|
157
|
+
imageUrl={largeImage}
|
|
158
|
+
marks={activeConfigMarks}
|
|
159
|
+
allMarks={allMarksMultiConfig}
|
|
160
|
+
showMultiConfig={true}
|
|
161
|
+
activeMarkerColor={configurations[activeConfigIndex].color}
|
|
162
|
+
activeMarkerName={configurations[activeConfigIndex].name}
|
|
163
|
+
editableConfigIndex={activeConfigIndex}
|
|
164
|
+
onclick={handleMultiConfigUpdate}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div class="mt-4 grid grid-cols-3 gap-4">
|
|
169
|
+
{#each configurations as config, index}
|
|
170
|
+
<div class="rounded-lg border border-neutral bg-surface p-3">
|
|
171
|
+
<div class="mb-1 flex items-center gap-2">
|
|
172
|
+
<div
|
|
173
|
+
class="h-3 w-3 rounded-full"
|
|
174
|
+
style="background-color: var(--text-{config.color})"
|
|
175
|
+
></div>
|
|
176
|
+
<span class="text-sm font-medium">{config.name}</span>
|
|
177
|
+
</div>
|
|
178
|
+
<p class="text-lg font-bold">
|
|
179
|
+
{allMarksMultiConfig.filter((m) => m.configIndex === index).length}
|
|
180
|
+
</p>
|
|
181
|
+
<p class="text-xs text-tertiary">objects counted</p>
|
|
182
|
+
</div>
|
|
183
|
+
{/each}
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<details class="mt-4 text-sm">
|
|
187
|
+
<summary>All Marks Data:</summary>
|
|
188
|
+
<pre class="mt-2 rounded bg-surface-secondary p-2 text-xs">{JSON.stringify(
|
|
189
|
+
allMarksMultiConfig,
|
|
190
|
+
null,
|
|
191
|
+
2
|
|
192
|
+
)}</pre>
|
|
193
|
+
</details>
|
|
194
|
+
</div>
|
|
195
|
+
</Story>
|
|
196
|
+
|
|
197
|
+
<Story name="Disabled" asChild>
|
|
198
|
+
<div class="p-4">
|
|
199
|
+
<h3 class="mb-4 text-lg font-semibold">Disabled Counter</h3>
|
|
200
|
+
<p class="mb-4 text-sm text-secondary">No interactions are possible in disabled mode.</p>
|
|
201
|
+
<div class="h-[600px] rounded-lg border border-static p-4">
|
|
202
|
+
<MultiCFUCounter
|
|
203
|
+
imageUrl={largeImage}
|
|
204
|
+
marks={activeConfigMarks}
|
|
205
|
+
allMarks={allMarksMultiConfig}
|
|
206
|
+
showMultiConfig={true}
|
|
207
|
+
activeMarkerColor={configurations[activeConfigIndex].color}
|
|
208
|
+
activeMarkerName={configurations[activeConfigIndex].name}
|
|
209
|
+
editableConfigIndex={activeConfigIndex}
|
|
210
|
+
onclick={handleMultiConfigUpdate}
|
|
211
|
+
disabled
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</Story>
|