@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.
Files changed (47) hide show
  1. package/dist/components/button/Button.svelte +7 -7
  2. package/dist/components/collapsible/components/collapsible-trigger.svelte +2 -2
  3. package/dist/components/combobox/components/combobox-content.svelte +1 -1
  4. package/dist/components/datepicker/DatePicker.svelte +2 -2
  5. package/dist/components/graphs/bar-chart/BarChart.svelte +34 -21
  6. package/dist/components/graphs/bar-chart/StackedBarChart.stories.svelte +1 -1
  7. package/dist/components/graphs/bar-chart/StackedBarChart.svelte +36 -23
  8. package/dist/components/graphs/chart/Chart.svelte +0 -2
  9. package/dist/components/graphs/line/LineChart.stories.svelte +1 -1
  10. package/dist/components/graphs/line/LineChart.svelte +37 -19
  11. package/dist/components/graphs/multiline/MultiLineChart.svelte +33 -17
  12. package/dist/components/graphs/utils/tooltipFormatter.js +4 -4
  13. package/dist/components/icon-button/IconButton.svelte +3 -3
  14. package/dist/components/icons/index.d.ts +3 -2
  15. package/dist/components/icons/index.js +2 -0
  16. package/dist/components/manual-cfu-counter/ManualCFUCounter.svelte +14 -18
  17. package/dist/components/multi-cfu-counter/MultiCFUCounter.stories.svelte +215 -0
  18. package/dist/components/multi-cfu-counter/MultiCFUCounter.stories.svelte.d.ts +3 -0
  19. package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte +662 -0
  20. package/dist/components/multi-cfu-counter/MultiCFUCounter.svelte.d.ts +32 -0
  21. package/dist/components/multi-cfu-counter/index.d.ts +1 -0
  22. package/dist/components/multi-cfu-counter/index.js +1 -0
  23. package/dist/components/multi-cfu-counter/test/MultiCFUCounterTestWrapper.svelte +28 -0
  24. package/dist/components/multi-cfu-counter/test/MultiCFUCounterTestWrapper.svelte.d.ts +20 -0
  25. package/dist/components/select-new/components/SelectItem.svelte +5 -7
  26. package/dist/components/select-new/components/SelectTrigger.svelte +2 -2
  27. package/dist/components/slider/Slider.svelte +1 -1
  28. package/dist/components/stat-card/StatCard.svelte +1 -1
  29. package/dist/components/status-badge/StatusBadge.svelte +1 -1
  30. package/dist/components/stepper/components/stepper-step.svelte +1 -1
  31. package/dist/components/tag/Tag.svelte +2 -1
  32. package/dist/components/tag/Tag.svelte.d.ts +1 -1
  33. package/dist/index.d.ts +4 -1
  34. package/dist/index.js +5 -1
  35. package/dist/styles.d.ts +1 -0
  36. package/dist/styles.js +4 -0
  37. package/dist/tailwind-safelist.js +10 -2
  38. package/dist/tailwind.preset.d.ts +6 -0
  39. package/dist/tokens/colors.d.ts +246 -0
  40. package/dist/tokens/colors.js +139 -0
  41. package/dist/tokens/index.d.ts +3 -0
  42. package/dist/tokens/index.js +5 -0
  43. package/dist/tokens/typography.d.ts +48 -0
  44. package/dist/tokens/typography.js +48 -0
  45. package/dist/tokens.d.ts +49 -251
  46. package/dist/tokens.js +37 -147
  47. 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; // Base size at zoom level 1
12
- const BASE_MARKER_FONT_SIZE = 10; // Base font size at zoom level 1
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 saveCurrentState() {
372
- marksHistory.push([...marks]);
373
- }
374
-
373
+ // Simplified undo function - just pop the last mark
375
374
  function undo() {
376
- if (marksHistory.length === 0) return;
377
- const previousState = marksHistory.pop();
378
- if (previousState) {
379
- marks.splice(0, marks.length, ...previousState);
380
- marks = marks;
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={marksHistory.length === 0 || 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>
@@ -0,0 +1,3 @@
1
+ declare const MultiCfuCounter: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type MultiCfuCounter = ReturnType<typeof MultiCfuCounter>;
3
+ export default MultiCfuCounter;