@reshape-biotech/design-system 1.1.2 → 1.2.1

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 (30) hide show
  1. package/dist/components/graphs/bar-chart/BarChart.stories.svelte +38 -0
  2. package/dist/components/graphs/bar-chart/BarChart.svelte +14 -1
  3. package/dist/components/graphs/bar-chart/BarChart.svelte.d.ts +1 -0
  4. package/dist/components/graphs/bar-chart/StackedBarChart.stories.svelte +32 -0
  5. package/dist/components/graphs/bar-chart/StackedBarChart.svelte +16 -3
  6. package/dist/components/graphs/bar-chart/StackedBarChart.svelte.d.ts +3 -2
  7. package/dist/components/graphs/line/LineChart.stories.svelte +20 -0
  8. package/dist/components/graphs/line/LineChart.svelte +14 -20
  9. package/dist/components/graphs/line/LineChart.svelte.d.ts +1 -0
  10. package/dist/components/graphs/multiline/MultiLineChart.svelte +0 -20
  11. package/dist/components/icons/index.d.ts +1 -1
  12. package/dist/components/icons/index.js +2 -0
  13. package/dist/components/notifications/Notifications.stories.svelte +107 -0
  14. package/dist/components/notifications/Notifications.stories.svelte.d.ts +19 -0
  15. package/dist/components/notifications/Notifications.svelte +32 -0
  16. package/dist/components/notifications/Notifications.svelte.d.ts +18 -0
  17. package/dist/components/notifications/index.d.ts +1 -0
  18. package/dist/components/notifications/index.js +1 -0
  19. package/dist/components/select-new/index.d.ts +1 -1
  20. package/dist/components/toast/Toast.stories.svelte +209 -0
  21. package/dist/components/toast/Toast.stories.svelte.d.ts +19 -0
  22. package/dist/components/toast/Toast.svelte +62 -0
  23. package/dist/components/toast/Toast.svelte.d.ts +7 -0
  24. package/dist/components/toast/index.d.ts +1 -0
  25. package/dist/components/toast/index.js +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.js +3 -0
  28. package/dist/notifications.d.ts +22 -0
  29. package/dist/notifications.js +27 -0
  30. package/package.json +2 -1
@@ -79,3 +79,41 @@
79
79
  name="Empty Data"
80
80
  args={{ data: [], xAxisName: 'category', yAxisName: 'Value', height: '400px' }}
81
81
  />
82
+
83
+ <Story
84
+ name="With Capture Interval"
85
+ args={{
86
+ data: [
87
+ { category: '0', value: 10 },
88
+ { category: '1', value: 20 },
89
+ { category: '2', value: 15 },
90
+ { category: '3', value: 25 },
91
+ { category: '4', value: 30 },
92
+ { category: '5', value: 18 },
93
+ { category: '6', value: 22 }
94
+ ],
95
+ xAxisName: 'category',
96
+ yAxisName: 'Value',
97
+ height: '400px',
98
+ captureInterval: 3600
99
+ }}
100
+ />
101
+
102
+ <Story
103
+ name="With Capture Interval - Short Duration"
104
+ args={{
105
+ data: [
106
+ { category: '0', value: 10 },
107
+ { category: '1', value: 20 },
108
+ { category: '2', value: 15 },
109
+ { category: '3', value: 25 },
110
+ { category: '4', value: 30 },
111
+ { category: '5', value: 18 },
112
+ { category: '6', value: 22 }
113
+ ],
114
+ xAxisName: 'category',
115
+ yAxisName: 'Value',
116
+ height: '400px',
117
+ captureInterval: 600
118
+ }}
119
+ />
@@ -3,6 +3,7 @@
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';
6
7
 
7
8
  type DataItem = {
8
9
  value: number;
@@ -14,6 +15,7 @@
14
15
  colors?: Record<string, string>; // Optional: Color mapping per x-axis category
15
16
  rotateXAxisLabels?: boolean;
16
17
  grid?: EChartsOption['grid'];
18
+ captureInterval?: number;
17
19
  }
18
20
 
19
21
  let {
@@ -22,6 +24,7 @@
22
24
  rotateXAxisLabels = false,
23
25
  xAxisName,
24
26
  yAxisName,
27
+ captureInterval,
25
28
  ...props
26
29
  }: BarChartProps = $props();
27
30
 
@@ -38,6 +41,16 @@
38
41
 
39
42
  const xAxisData = $derived(data.map((item) => item[xAxisName]));
40
43
 
44
+ const captureIntervals = captureInterval
45
+ ? Array(data.length)
46
+ .fill(null)
47
+ .map((_, timeIndex) => {
48
+ const seconds = timeIndex * captureInterval;
49
+ const duration = Duration.fromObject({ seconds }).shiftTo('days', 'hours', 'minutes');
50
+ return duration.days >= 1 ? duration.toFormat('dd:hh:mm') : duration.toFormat('hh:mm');
51
+ })
52
+ : undefined;
53
+
41
54
  const createBarDataItem = (value: number | null, color: string) => ({
42
55
  value,
43
56
  itemStyle: {
@@ -94,7 +107,7 @@
94
107
  },
95
108
  xAxis: {
96
109
  type: 'category',
97
- data: xAxisData,
110
+ data: captureIntervals || xAxisData,
98
111
  axisTick: {
99
112
  show: false,
100
113
  alignWithLabel: true
@@ -9,6 +9,7 @@ interface BarChartProps extends Omit<GenericChartProps, 'timeIndex'> {
9
9
  colors?: Record<string, string>;
10
10
  rotateXAxisLabels?: boolean;
11
11
  grid?: EChartsOption['grid'];
12
+ captureInterval?: number;
12
13
  }
13
14
  declare const BarChart: import("svelte").Component<BarChartProps, {}, "">;
14
15
  type BarChart = ReturnType<typeof BarChart>;
@@ -40,3 +40,35 @@
40
40
  yAxisName: 'Count'
41
41
  }}
42
42
  />
43
+
44
+ <Story
45
+ name="With Capture Interval"
46
+ args={{
47
+ xAxisCategories: ['0', '1', '2', '3', '4', '5', '6'],
48
+ series: [
49
+ { name: 'Halo', data: [120, 132, 101, 134, 90, 230, 210] },
50
+ { name: 'Spot', data: [220, 182, 191, 234, 290, 330, 310] },
51
+ { name: 'Microbial', data: [150, 232, 201, 154, 190, 330, 410] }
52
+ ],
53
+ height: '400px',
54
+ xAxisName: 'Time',
55
+ yAxisName: 'Count',
56
+ captureInterval: 3600
57
+ }}
58
+ />
59
+
60
+ <Story
61
+ name="With Capture Interval - Short Duration"
62
+ args={{
63
+ xAxisCategories: ['0', '1', '2', '3', '4', '5', '6'],
64
+ series: [
65
+ { name: 'Halo', data: [120, 132, 101, 134, 90, 230, 210] },
66
+ { name: 'Spot', data: [220, 182, 191, 234, 290, 330, 310] },
67
+ { name: 'Microbial', data: [150, 232, 201, 154, 190, 330, 410] }
68
+ ],
69
+ height: '400px',
70
+ xAxisName: 'Time',
71
+ yAxisName: 'Count',
72
+ captureInterval: 600
73
+ }}
74
+ />
@@ -3,25 +3,28 @@
3
3
  import Chart, { type GenericChartProps } from '../chart/Chart.svelte';
4
4
  import type { EChartsOption, BarSeriesOption } from 'echarts';
5
5
  import type { CallbackDataParams } from 'echarts/types/dist/shared';
6
+ import { Duration } from 'luxon';
6
7
 
7
8
  type StackedSeriesItem = {
8
9
  name: string;
9
10
  data: (number | null)[];
10
11
  };
11
12
 
12
- type StackedBarChartProps = {
13
+ interface StackedBarChartProps extends GenericChartProps {
13
14
  xAxisCategories: (string | number)[];
14
15
  series: StackedSeriesItem[];
15
16
  colors?: Record<string, string>; // Optional: Color mapping per series name
16
17
  rotateXAxisLabels?: boolean;
17
18
  grid?: EChartsOption['grid'];
18
- } & Omit<GenericChartProps, 'timeIndex'>;
19
+ captureInterval?: number;
20
+ }
19
21
 
20
22
  let {
21
23
  xAxisCategories,
22
24
  series,
23
25
  colors,
24
26
  rotateXAxisLabels = false,
27
+ captureInterval,
25
28
  ...props
26
29
  }: StackedBarChartProps = $props();
27
30
 
@@ -38,6 +41,16 @@
38
41
 
39
42
  const xAxisData = $derived(xAxisCategories);
40
43
 
44
+ const captureIntervals = captureInterval
45
+ ? Array(xAxisCategories.length)
46
+ .fill(null)
47
+ .map((_, timeIndex) => {
48
+ const seconds = timeIndex * captureInterval;
49
+ const duration = Duration.fromObject({ seconds }).shiftTo('days', 'hours', 'minutes');
50
+ return duration.days >= 1 ? duration.toFormat('dd:hh:mm') : duration.toFormat('hh:mm');
51
+ })
52
+ : undefined;
53
+
41
54
  const createBarDataItem = (value: number | null, color: string) => ({
42
55
  value,
43
56
  itemStyle: {
@@ -135,7 +148,7 @@
135
148
  },
136
149
  xAxis: {
137
150
  type: 'category',
138
- data: xAxisData,
151
+ data: captureIntervals || xAxisData,
139
152
  axisTick: {
140
153
  show: false,
141
154
  alignWithLabel: true
@@ -4,13 +4,14 @@ type StackedSeriesItem = {
4
4
  name: string;
5
5
  data: (number | null)[];
6
6
  };
7
- type StackedBarChartProps = {
7
+ interface StackedBarChartProps extends GenericChartProps {
8
8
  xAxisCategories: (string | number)[];
9
9
  series: StackedSeriesItem[];
10
10
  colors?: Record<string, string>;
11
11
  rotateXAxisLabels?: boolean;
12
12
  grid?: EChartsOption['grid'];
13
- } & Omit<GenericChartProps, 'timeIndex'>;
13
+ captureInterval?: number;
14
+ }
14
15
  declare const StackedBarChart: import("svelte").Component<StackedBarChartProps, {}, "">;
15
16
  type StackedBarChart = ReturnType<typeof StackedBarChart>;
16
17
  export default StackedBarChart;
@@ -109,6 +109,26 @@
109
109
  <LineChart {xAxisName} yAxisName="Value" data={[10, 8, 13, 7, 9, 6, 11]} timeIndex={3} />
110
110
  </div>
111
111
  </Story>
112
+ <Story name="With Capture Interval" asChild>
113
+ <div class="h-[400px] w-full">
114
+ <LineChart
115
+ {xAxisName}
116
+ yAxisName="Value"
117
+ data={[10, 8, 13, 7, 9, 6, 11, 15, 12, 8, 14, 16]}
118
+ captureInterval={3600}
119
+ />
120
+ </div>
121
+ </Story>
122
+ <Story name="With Capture Interval - Short Duration" asChild>
123
+ <div class="h-[400px] w-full">
124
+ <LineChart
125
+ {xAxisName}
126
+ yAxisName="Value"
127
+ data={[10, 8, 13, 7, 9, 6, 11, 15, 12, 8, 14, 16]}
128
+ captureInterval={600}
129
+ />
130
+ </div>
131
+ </Story>
112
132
  <Story
113
133
  name="Interactive Current Location"
114
134
  asChild
@@ -5,12 +5,14 @@
5
5
  import * as echarts from 'echarts/core';
6
6
  import type { GenericChartProps } from '../chart/Chart.svelte';
7
7
  import { createTooltipFormatter } from '../utils/tooltipFormatter';
8
+ import { Duration } from 'luxon';
8
9
 
9
10
  interface LineChartProps extends GenericChartProps {
10
11
  data: (number | null)[];
11
12
  withArea?: boolean;
12
13
  color?: string;
13
14
  grid?: EChartsOption['grid'];
15
+ captureInterval?: number;
14
16
  }
15
17
 
16
18
  let {
@@ -19,6 +21,7 @@
19
21
  yAxisName,
20
22
  withArea = false,
21
23
  color = textColor['icon-blue'],
24
+ captureInterval,
22
25
  ...props
23
26
  }: LineChartProps = $props();
24
27
 
@@ -37,6 +40,16 @@
37
40
  maximumFractionDigits: fractionDigits
38
41
  }) ?? '';
39
42
 
43
+ const captureIntervals = captureInterval
44
+ ? Array(data.length)
45
+ .fill(null)
46
+ .map((_, timeIndex) => {
47
+ const seconds = timeIndex * captureInterval;
48
+ const duration = Duration.fromObject({ seconds }).shiftTo('days', 'hours', 'minutes');
49
+ return duration.days >= 1 ? duration.toFormat('dd:hh:mm') : duration.toFormat('hh:mm');
50
+ })
51
+ : undefined;
52
+
40
53
  let options: EChartsOption = $derived({
41
54
  grid: {
42
55
  top: 10,
@@ -55,26 +68,7 @@
55
68
  axisLine: {
56
69
  onZero: false
57
70
  },
58
- axisLabel: {
59
- interval: (index: number, value: string) => {
60
- const dataLength = data.length;
61
- if (dataLength <= 1) return true; // Show label if only one point
62
-
63
- const targetLabelCount = 10; // Aim for about 10 labels
64
- // Calculate interval, ensuring it's at least 1
65
- const calculatedInterval = Math.max(1, Math.ceil(dataLength / targetLabelCount));
66
-
67
- // Show the first label (index 0)
68
- if (index === 0) return true;
69
- // Show the last label if data exists
70
- if (dataLength > 0 && index === dataLength - 1) return true;
71
- // Show labels at the calculated interval, but not the last one if it was already shown
72
- if (index % calculatedInterval === 0 && index !== dataLength - 1) return true;
73
- // Hide others
74
- return false;
75
- },
76
- hideOverlap: true
77
- },
71
+ data: captureIntervals,
78
72
  ...props.xAxisOptions
79
73
  } as any as EChartsOption['xAxis'],
80
74
  yAxis: {
@@ -5,6 +5,7 @@ interface LineChartProps extends GenericChartProps {
5
5
  withArea?: boolean;
6
6
  color?: string;
7
7
  grid?: EChartsOption['grid'];
8
+ captureInterval?: number;
8
9
  }
9
10
  declare const LineChart: import("svelte").Component<LineChartProps, {}, "">;
10
11
  type LineChart = ReturnType<typeof LineChart>;
@@ -116,26 +116,6 @@
116
116
  onZero: false
117
117
  },
118
118
  data: captureIntervals,
119
- axisLabel: {
120
- interval: (index: number, value: string) => {
121
- const dataLength = longestSeries;
122
- if (dataLength <= 1) return true; // Show label if only one point
123
-
124
- const targetLabelCount = 10; // Aim for about 10 labels
125
- // Calculate interval, ensuring it's at least 1
126
- const calculatedInterval = Math.max(1, Math.ceil(dataLength / targetLabelCount));
127
-
128
- // Show the first label (index 0)
129
- if (index === 0) return true;
130
- // Show the last label if data exists
131
- if (dataLength > 0 && index === dataLength - 1) return true;
132
- // Show labels at the calculated interval, but not the last one if it was already shown
133
- if (index % calculatedInterval === 0 && index !== dataLength - 1) return true;
134
- // Hide others
135
- return false;
136
- },
137
- hideOverlap: true
138
- },
139
119
  ...props.xAxisOptions
140
120
  },
141
121
  yAxis: {
@@ -2,7 +2,7 @@ import type { IconComponentProps } from 'phosphor-svelte';
2
2
  import type { Component } from 'svelte';
3
3
  import type { textColor } from './../../tokens';
4
4
  export type PhosphorIconProps = Component<IconComponentProps, object, ''>;
5
- export type IconName = 'Aperture' | 'ArrowFatLineRight' | 'ArrowCounterClockwise' | 'ArrowRight' | 'ArrowUpRight' | 'ArrowUpLeft' | 'ArrowUUpLeft' | 'Barcode' | 'Bell' | 'BookOpen' | 'BookOpenText' | 'BowlingBall' | 'BugBeetle' | 'Calendar' | 'CalendarBlank' | 'Camera' | 'CameraSlash' | 'CaretDown' | 'CaretLeft' | 'CaretRight' | 'CaretUp' | 'CaretUpDown' | 'ChatTeardropDots' | 'Check' | 'CheckCircle' | 'CheckFat' | 'Circle' | 'CircleDashed' | 'CircleHalf' | 'CirclesFour' | 'CirclesThreePlus' | 'Clock' | 'ClockCountdown' | 'Copy' | 'Crop' | 'Cube' | 'Database' | 'Dna' | 'DotsThree' | 'DotsThreeOutlineVertical' | 'DownloadSimple' | 'Drop' | 'EnvelopeSimple' | 'Eye' | 'Eyedropper' | 'FileCsv' | 'Flag' | 'Flask' | 'Folder' | 'FolderDashed' | 'FolderSimplePlus' | 'FunnelSimple' | 'Gear' | 'GlobeSimple' | 'GlobeSimpleX' | 'GridFour' | 'Hash' | 'House' | 'ImageSquare' | 'ImagesSquare' | 'Info' | 'Lock' | 'LineSegments' | 'List' | 'Link' | 'ListMagnifyingGlass' | 'MagnifyingGlass' | 'MegaphoneSimple' | 'MicrosoftExcelLogo' | 'Moon' | 'Minus' | 'Palette' | 'Pause' | 'Pencil' | 'PencilSimple' | 'Percent' | 'Phone' | 'Plant' | 'Play' | 'Plus' | 'PlusMinus' | 'Question' | 'SealCheck' | 'RadioButton' | 'SealQuestion' | 'SealWarning' | 'SelectionAll' | 'Share' | 'SidebarSimple' | 'SkipBack' | 'SkipForward' | 'SignIn' | 'SignOut' | 'SortAscending' | 'Sparkle' | 'SpinnerGap' | 'SquaresFour' | 'Star' | 'Stop' | 'StopCircle' | 'Sun' | 'Table' | 'Tag' | 'Target' | 'TestTube' | 'Trash' | 'TrashSimple' | 'TreeStructure' | 'UserCircle' | 'UserPlus' | 'Video' | 'Warning' | 'WarningCircle' | 'WifiSlash' | 'X';
5
+ export type IconName = 'Aperture' | 'ArrowFatLineRight' | 'ArrowCounterClockwise' | 'ArrowRight' | 'ArrowUpRight' | 'ArrowUpLeft' | 'ArrowUUpLeft' | 'Barcode' | 'Bell' | 'BookOpen' | 'BookOpenText' | 'BowlingBall' | 'BugBeetle' | 'Calendar' | 'CalendarBlank' | 'Camera' | 'CameraSlash' | 'CaretDown' | 'CaretLeft' | 'CaretRight' | 'CaretUp' | 'CaretUpDown' | 'ChatTeardropDots' | 'Check' | 'CheckCircle' | 'CheckFat' | 'Circle' | 'CircleDashed' | 'CircleHalf' | 'CirclesFour' | 'CirclesThreePlus' | 'Clock' | 'ClockCountdown' | 'Copy' | 'Crop' | 'Cube' | 'Database' | 'Dna' | 'DotsThree' | 'DotsThreeOutlineVertical' | 'DownloadSimple' | 'Drop' | 'EnvelopeSimple' | 'Eye' | 'Eyedropper' | 'FileCsv' | 'Flag' | 'Flask' | 'Folder' | 'FolderDashed' | 'FolderSimplePlus' | 'FunnelSimple' | 'Gear' | 'GlobeSimple' | 'GlobeSimpleX' | 'GridFour' | 'Hash' | 'House' | 'ImageSquare' | 'ImagesSquare' | 'Info' | 'Lock' | 'LineSegments' | 'List' | 'Link' | 'ListMagnifyingGlass' | 'MagnifyingGlass' | 'MegaphoneSimple' | 'MicrosoftExcelLogo' | 'Moon' | 'Minus' | 'Palette' | 'Pause' | 'Pencil' | 'PencilSimple' | 'Percent' | 'Phone' | 'Plant' | 'Play' | 'Plus' | 'PlusMinus' | 'Ruler' | 'Question' | 'SealCheck' | 'RadioButton' | 'SealQuestion' | 'SealWarning' | 'SelectionAll' | 'Share' | 'SidebarSimple' | 'SkipBack' | 'SkipForward' | 'SignIn' | 'SignOut' | 'SortAscending' | 'Sparkle' | 'SpinnerGap' | 'SquaresFour' | 'Star' | 'Stop' | 'StopCircle' | 'Sun' | 'Table' | 'Tag' | 'Target' | 'TestTube' | 'Trash' | 'TrashSimple' | 'TreeStructure' | 'UserCircle' | 'UserPlus' | 'Video' | 'Warning' | 'WarningCircle' | 'WifiSlash' | 'X';
6
6
  export declare const iconMap: Record<IconName, PhosphorIconProps>;
7
7
  export declare function renderIcon(iconName: keyof typeof iconMap): PhosphorIconProps;
8
8
  export type IconColor = keyof typeof textColor;
@@ -79,6 +79,7 @@ import Plant from 'phosphor-svelte/lib/Plant';
79
79
  import Play from 'phosphor-svelte/lib/Play';
80
80
  import Plus from 'phosphor-svelte/lib/Plus';
81
81
  import PlusMinus from 'phosphor-svelte/lib/PlusMinus';
82
+ import Ruler from 'phosphor-svelte/lib/Ruler';
82
83
  import Question from 'phosphor-svelte/lib/Question';
83
84
  import SealCheck from 'phosphor-svelte/lib/SealCheck';
84
85
  import RadioButton from 'phosphor-svelte/lib/RadioButton';
@@ -196,6 +197,7 @@ export const iconMap = {
196
197
  Plus,
197
198
  PlusMinus,
198
199
  RadioButton,
200
+ Ruler,
199
201
  Question,
200
202
  SealCheck,
201
203
  SealQuestion,
@@ -0,0 +1,107 @@
1
+ <script module lang="ts">
2
+ import { notifications } from '../../../notifications';
3
+ import Button from '../button/Button.svelte';
4
+ import Notifications from '../notifications/Notifications.svelte';
5
+ import { defineMeta } from '@storybook/addon-svelte-csf';
6
+
7
+ const { Story } = defineMeta({
8
+ component: Notifications,
9
+ title: 'Design System/Notifications',
10
+ tags: ['autodocs']
11
+ });
12
+ </script>
13
+
14
+ <Story name="Notifications" asChild>
15
+ <Notifications></Notifications>
16
+ <div class="h-24 rounded-md border"></div>
17
+ <div class="pt-4">
18
+ <div class="flex flex-row justify-between gap-2">
19
+ <div class="flex flex-col gap-2">
20
+ <h5>Default</h5>
21
+ <Button
22
+ onClick={() => {
23
+ notifications.default('This is a default notification');
24
+ }}>Show default</Button
25
+ >
26
+ <Button
27
+ onClick={() => {
28
+ notifications.danger('This is a danger notification');
29
+ }}>Show danger</Button
30
+ >
31
+ <Button
32
+ onClick={() => {
33
+ notifications.warning('This is a warning notification');
34
+ }}>Show warning</Button
35
+ >
36
+ <Button
37
+ onClick={() => {
38
+ notifications.info('This is an info notification');
39
+ }}>Show info</Button
40
+ >
41
+ <Button
42
+ onClick={() => {
43
+ notifications.success('This is a success notification');
44
+ }}>Show success</Button
45
+ >
46
+ </div>
47
+ <div class="grow"></div>
48
+ <div class="flex flex-col gap-2">
49
+ <h5>Icons</h5>
50
+ <Button
51
+ onClick={() => {
52
+ notifications.default('This is a default icon notification', 'Bell');
53
+ }}>Show default</Button
54
+ >
55
+ <Button
56
+ onClick={() => {
57
+ notifications.danger('This is a danger icon notification', 'SealWarning');
58
+ }}>Show danger</Button
59
+ >
60
+ <Button
61
+ onClick={() => {
62
+ notifications.warning('This is a warning icon notification', 'Warning');
63
+ }}>Show warning</Button
64
+ >
65
+ <Button
66
+ onClick={() => {
67
+ notifications.info('This is an info icon notification', 'Info');
68
+ }}>Show info</Button
69
+ >
70
+ <Button
71
+ onClick={() => {
72
+ notifications.success('This is a success icon notification', 'Check');
73
+ }}>Show success</Button
74
+ >
75
+ </div>
76
+ <div class="grow"></div>
77
+ <div class="flex flex-col gap-2">
78
+ <h5>Dismissable</h5>
79
+ <Button
80
+ onClick={() => {
81
+ notifications.default('This is a default icon notification', 'Bell', true);
82
+ }}>Show default</Button
83
+ >
84
+ <Button
85
+ onClick={() => {
86
+ notifications.danger('This is a danger icon notification', 'SealWarning', true);
87
+ }}>Show danger</Button
88
+ >
89
+ <Button
90
+ onClick={() => {
91
+ notifications.warning('This is a warning icon notification', 'Warning', true);
92
+ }}>Show warning</Button
93
+ >
94
+ <Button
95
+ onClick={() => {
96
+ notifications.info('This is an info icon notification', 'Info', true);
97
+ }}>Show info</Button
98
+ >
99
+ <Button
100
+ onClick={() => {
101
+ notifications.success('This is a success icon notification', 'Check', true);
102
+ }}>Show success</Button
103
+ >
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </Story>
@@ -0,0 +1,19 @@
1
+ import Notifications from '../notifications/Notifications.svelte';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const Notifications: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type Notifications = InstanceType<typeof Notifications>;
19
+ export default Notifications;
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import { flip } from 'svelte/animate';
3
+ import { fly } from 'svelte/transition';
4
+ import { notifications } from '../../notifications';
5
+ import { type IconColor } from '../icons';
6
+ import { tokens } from '../../tokens';
7
+ import Toast from '../toast/Toast.svelte';
8
+
9
+ const color = {
10
+ danger: tokens.backgroundColor['danger-inverse'],
11
+ success: tokens.backgroundColor['success-inverse-hover'],
12
+ warning: tokens.backgroundColor['warning-inverse-hover'],
13
+ info: tokens.backgroundColor['blue-inverse'],
14
+ default: tokens.backgroundColor.surface
15
+ };
16
+
17
+ const textColor: Record<string, IconColor | 'inherit'> = {
18
+ danger: 'primary-inverse',
19
+ success: 'primary-inverse',
20
+ warning: 'primary-inverse',
21
+ info: 'primary-inverse',
22
+ default: 'inherit'
23
+ };
24
+ </script>
25
+
26
+ <div class="fixed left-0 right-0 top-2 z-50 mx-auto flex w-fit flex-col items-center justify-start">
27
+ {#each $notifications as notification (notification.id)}
28
+ <div animate:flip transition:fly={{ y: -10 }}>
29
+ <Toast {notification} />
30
+ </div>
31
+ {/each}
32
+ </div>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const Notifications: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type Notifications = InstanceType<typeof Notifications>;
18
+ export default Notifications;
@@ -0,0 +1 @@
1
+ export { default as Notifications } from './Notifications.svelte';
@@ -0,0 +1 @@
1
+ export { default as Notifications } from './Notifications.svelte';
@@ -4,7 +4,7 @@ import GroupHeading from './components/SelectGroupHeading.svelte';
4
4
  import Trigger from './components/SelectTrigger.svelte';
5
5
  import MultiSelectTrigger from './components/MultiSelectTrigger.svelte';
6
6
  import Group from './components/Group.svelte';
7
- export declare const Root: import("svelte").Component<import("bits-ui").ComboboxRootPropsWithoutHTML, {}, "open" | "value">;
7
+ export declare const Root: import("svelte").Component<import("bits-ui").SelectRootPropsWithoutHTML, {}, "open" | "value">;
8
8
  export declare const Portal: import("svelte").Component<import("bits-ui").PortalProps, {}, "">;
9
9
  export { Content, Item, Group, GroupHeading, Trigger, MultiSelectTrigger };
10
10
  export * from './types';
@@ -0,0 +1,209 @@
1
+ <script module lang="ts">
2
+ import type { Notification } from '../../../notifications';
3
+ import Toast from './Toast.svelte';
4
+ import { defineMeta } from '@storybook/addon-svelte-csf';
5
+
6
+ const { Story } = defineMeta({
7
+ component: Toast,
8
+ title: 'Design System/Toast',
9
+ tags: ['autodocs']
10
+ });
11
+
12
+ const defaultNotification: Notification = {
13
+ id: '1',
14
+ type: 'default',
15
+ message: 'This is a default notification',
16
+ dismissable: false
17
+ };
18
+
19
+ const dangerNotification: Notification = {
20
+ id: '2',
21
+ type: 'danger',
22
+ message: 'This is a danger notification',
23
+ dismissable: false
24
+ };
25
+
26
+ const warningNotification: Notification = {
27
+ id: '3',
28
+ type: 'warning',
29
+ message: 'This is a warning notification',
30
+ dismissable: false
31
+ };
32
+
33
+ const infoNotification: Notification = {
34
+ id: '4',
35
+ type: 'info',
36
+ message: 'This is an info notification',
37
+ dismissable: false
38
+ };
39
+
40
+ const successNotification: Notification = {
41
+ id: '5',
42
+ type: 'success',
43
+ message: 'This is a success notification',
44
+ dismissable: false
45
+ };
46
+
47
+ const defaultIconNotification: Notification = {
48
+ id: '6',
49
+ type: 'default',
50
+ message: 'This is a default notification with icon',
51
+ icon: 'Bell',
52
+ dismissable: false
53
+ };
54
+
55
+ const dangerIconNotification: Notification = {
56
+ id: '7',
57
+ type: 'danger',
58
+ message: 'This is a danger notification with icon',
59
+ icon: 'SealWarning',
60
+ dismissable: false
61
+ };
62
+
63
+ const warningIconNotification: Notification = {
64
+ id: '8',
65
+ type: 'warning',
66
+ message: 'This is a warning notification with icon',
67
+ icon: 'Warning',
68
+ dismissable: false
69
+ };
70
+
71
+ const infoIconNotification: Notification = {
72
+ id: '9',
73
+ type: 'info',
74
+ message: 'This is an info notification with icon',
75
+ icon: 'Info',
76
+ dismissable: false
77
+ };
78
+
79
+ const successIconNotification: Notification = {
80
+ id: '10',
81
+ type: 'success',
82
+ message: 'This is a success notification with icon',
83
+ icon: 'Check',
84
+ dismissable: false
85
+ };
86
+
87
+ const defaultDismissableNotificationWithIcon: Notification = {
88
+ id: '11',
89
+ type: 'default',
90
+ message: 'This is a dismissable default notification with icon',
91
+ icon: 'Bell',
92
+ dismissable: true
93
+ };
94
+
95
+ const dangerDismissableNotificationWithIcon: Notification = {
96
+ id: '12',
97
+ type: 'danger',
98
+ message: 'This is a dismissable danger notification with icon',
99
+ icon: 'SealWarning',
100
+ dismissable: true
101
+ };
102
+
103
+ const warningDismissableNotificationWithIcon: Notification = {
104
+ id: '13',
105
+ type: 'warning',
106
+ message: 'This is a dismissable warning notification with icon',
107
+ icon: 'Warning',
108
+ dismissable: true
109
+ };
110
+
111
+ const infoDismissableNotificationWithIcon: Notification = {
112
+ id: '14',
113
+ type: 'info',
114
+ message: 'This is a dismissable info notification with icon',
115
+ icon: 'Info',
116
+ dismissable: true
117
+ };
118
+
119
+ const successDismissableNotificationWithIcon: Notification = {
120
+ id: '15',
121
+ type: 'success',
122
+ message: 'This is a dismissable success notification with icon',
123
+ icon: 'Check',
124
+ dismissable: true
125
+ };
126
+
127
+ const defaultDismissableNoIconNotification: Notification = {
128
+ id: '16',
129
+ type: 'default',
130
+ message: 'This is a dismissable default notification without icon',
131
+ dismissable: true
132
+ };
133
+
134
+ const dangerDismissableNoIconNotification: Notification = {
135
+ id: '17',
136
+ type: 'danger',
137
+ message: 'This is a dismissable danger notification without icon',
138
+ dismissable: true
139
+ };
140
+
141
+ const warningDismissableNoIconNotification: Notification = {
142
+ id: '18',
143
+ type: 'warning',
144
+ message: 'This is a dismissable warning notification without icon',
145
+ dismissable: true
146
+ };
147
+
148
+ const infoDismissableNoIconNotification: Notification = {
149
+ id: '19',
150
+ type: 'info',
151
+ message: 'This is a dismissable info notification without icon',
152
+ dismissable: true
153
+ };
154
+
155
+ const successDismissableNoIconNotification: Notification = {
156
+ id: '20',
157
+ type: 'success',
158
+ message: 'This is a dismissable success notification without icon',
159
+ dismissable: true
160
+ };
161
+ </script>
162
+
163
+ <Story name="Notifications" asChild>
164
+ <div class="space-y-8">
165
+ <div class="space-y-4">
166
+ <h5 class="text-lg font-semibold">Basic Notifications</h5>
167
+ <div class="space-y-2">
168
+ <Toast notification={defaultNotification} />
169
+ <Toast notification={dangerNotification} />
170
+ <Toast notification={warningNotification} />
171
+ <Toast notification={infoNotification} />
172
+ <Toast notification={successNotification} />
173
+ </div>
174
+ </div>
175
+
176
+ <div class="space-y-4">
177
+ <h5 class="text-lg font-semibold">Notifications with Icons</h5>
178
+ <div class="space-y-2">
179
+ <Toast notification={defaultIconNotification} />
180
+ <Toast notification={dangerIconNotification} />
181
+ <Toast notification={warningIconNotification} />
182
+ <Toast notification={infoIconNotification} />
183
+ <Toast notification={successIconNotification} />
184
+ </div>
185
+ </div>
186
+
187
+ <div class="space-y-4">
188
+ <h5 class="text-lg font-semibold">Dismissable Notifications</h5>
189
+ <div class="space-y-2">
190
+ <Toast notification={defaultDismissableNoIconNotification} />
191
+ <Toast notification={dangerDismissableNoIconNotification} />
192
+ <Toast notification={warningDismissableNoIconNotification} />
193
+ <Toast notification={infoDismissableNoIconNotification} />
194
+ <Toast notification={successDismissableNoIconNotification} />
195
+ </div>
196
+ </div>
197
+
198
+ <div class="space-y-4">
199
+ <h5 class="text-lg font-semibold">Dismissable Notifications with Icons</h5>
200
+ <div class="space-y-2">
201
+ <Toast notification={defaultDismissableNotificationWithIcon} />
202
+ <Toast notification={dangerDismissableNotificationWithIcon} />
203
+ <Toast notification={warningDismissableNotificationWithIcon} />
204
+ <Toast notification={infoDismissableNotificationWithIcon} />
205
+ <Toast notification={successDismissableNotificationWithIcon} />
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </Story>
@@ -0,0 +1,19 @@
1
+ import Toast from './Toast.svelte';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const Toast: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type Toast = InstanceType<typeof Toast>;
19
+ export default Toast;
@@ -0,0 +1,62 @@
1
+ <script lang="ts">
2
+ import { type Notification, notifications } from '../../notifications';
3
+ import Icon from '../icons/Icon.svelte';
4
+ import { type IconColor } from '../icons';
5
+ import { tokens } from '../../tokens';
6
+ import IconButton from '../icon-button/IconButton.svelte';
7
+
8
+ let { notification }: { notification: Notification } = $props();
9
+
10
+ const color = {
11
+ danger: tokens.backgroundColor['danger-inverse'],
12
+ success: tokens.backgroundColor['success-inverse-hover'],
13
+ warning: tokens.backgroundColor['warning-inverse-hover'],
14
+ info: tokens.backgroundColor['blue-inverse'],
15
+ default: tokens.backgroundColor.surface
16
+ };
17
+
18
+ const textColor: Record<string, IconColor | 'inherit'> = {
19
+ danger: 'primary-inverse',
20
+ success: 'primary-inverse',
21
+ warning: 'primary-inverse',
22
+ info: 'primary-inverse',
23
+ default: 'inherit'
24
+ };
25
+ </script>
26
+
27
+ <div
28
+ class="my-1 flex min-h-10 min-w-80 flex-row items-center rounded-lg px-2 shadow-md"
29
+ style="background: {color[notification.type]}"
30
+ class:border={notification.type === 'default'}
31
+ class:border-base-200={notification.type === 'default'}
32
+ >
33
+ {#if notification.icon}
34
+ <Icon
35
+ color={textColor[notification.type]}
36
+ size={'14'}
37
+ iconName={notification.icon}
38
+ class="flex h-6 w-6 items-center justify-center rounded-md {notification.type !== 'default'
39
+ ? 'bg-neutral-inverse-hover'
40
+ : 'bg-neutral'} p-1 {textColor[notification.type]}"
41
+ />
42
+ {/if}
43
+ <div
44
+ class="p-2 font-medium"
45
+ style="display:block"
46
+ class:text-base-white-default={notification.type !== 'default'}
47
+ >
48
+ {notification.message}
49
+ </div>
50
+ {#if notification.dismissable}
51
+ <div class="grow"></div>
52
+ <div class="py-2">
53
+ <IconButton rounded={false} size="xs" onclick={() => notifications.dismiss(notification.id)}>
54
+ <Icon
55
+ color={textColor[notification.type]}
56
+ iconName={'X'}
57
+ class="rounded-md {textColor[notification.type]}"
58
+ />
59
+ </IconButton>
60
+ </div>
61
+ {/if}
62
+ </div>
@@ -0,0 +1,7 @@
1
+ import { type Notification } from '../../notifications';
2
+ type $$ComponentProps = {
3
+ notification: Notification;
4
+ };
5
+ declare const Toast: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type Toast = ReturnType<typeof Toast>;
7
+ export default Toast;
@@ -0,0 +1 @@
1
+ export { default as Toast } from './Toast.svelte';
@@ -0,0 +1 @@
1
+ export { default as Toast } from './Toast.svelte';
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ export * from './components/markdown/';
22
22
  export * from './components/modal/';
23
23
  export * from './components/manual-cfu-counter/';
24
24
  export * from './components/notification-popup/';
25
+ export * from './components/notifications/';
25
26
  export * from './components/pill/';
26
27
  export * from './components/progress-circle/';
27
28
  export * from './components/required-status-indicator/';
@@ -37,6 +38,7 @@ export * from './components/stepper/';
37
38
  export * from './components/table/';
38
39
  export * from './components/tabs/';
39
40
  export * from './components/tag/';
41
+ export * from './components/toast/';
40
42
  export * from './components/toggle-icon-button/';
41
43
  export * from './components/textarea/index';
42
44
  export * from './components/tooltip/';
@@ -44,4 +46,5 @@ export * from './components/toggle/';
44
46
  export * from './components/checkbox/';
45
47
  export * from './components/sjsf-wrappers/';
46
48
  export { tokens } from './tokens';
49
+ export { notifications } from './notifications';
47
50
  import './app.css';
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ export * from './components/markdown/';
23
23
  export * from './components/modal/';
24
24
  export * from './components/manual-cfu-counter/';
25
25
  export * from './components/notification-popup/';
26
+ export * from './components/notifications/';
26
27
  export * from './components/pill/';
27
28
  export * from './components/progress-circle/';
28
29
  export * from './components/required-status-indicator/';
@@ -38,6 +39,7 @@ export * from './components/stepper/';
38
39
  export * from './components/table/';
39
40
  export * from './components/tabs/';
40
41
  export * from './components/tag/';
42
+ export * from './components/toast/';
41
43
  export * from './components/toggle-icon-button/';
42
44
  export * from './components/textarea/index';
43
45
  export * from './components/tooltip/';
@@ -46,4 +48,5 @@ export * from './components/checkbox/';
46
48
  export * from './components/sjsf-wrappers/';
47
49
  // Styles and Tokens
48
50
  export { tokens } from './tokens';
51
+ export { notifications } from './notifications';
49
52
  import './app.css';
@@ -0,0 +1,22 @@
1
+ import type { IconName } from './';
2
+ export type NotificationType = 'info' | 'success' | 'warning' | 'danger' | 'default';
3
+ export interface Notification {
4
+ id: string;
5
+ type: NotificationType;
6
+ message: string;
7
+ timeout?: number;
8
+ icon?: IconName;
9
+ dismissable: boolean;
10
+ }
11
+ export declare const notifications: {
12
+ subscribe: {
13
+ (this: void, run: import("svelte/store").Subscriber<Notification[]>, invalidate?: (() => void) | undefined): import("svelte/store").Unsubscriber;
14
+ (this: void, run: import("svelte/store").Subscriber<Notification[]>, invalidate?: (() => void) | undefined): import("svelte/store").Unsubscriber;
15
+ };
16
+ dismiss: (id: string) => void;
17
+ default: (message: string, icon?: IconName, dismissable?: boolean, timeout?: number) => void;
18
+ danger: (message: string, icon?: IconName, dismissable?: boolean, timeout?: number) => void;
19
+ warning: (message: string, icon?: IconName, dismissable?: boolean, timeout?: number) => void;
20
+ info: (message: string, icon?: IconName, dismissable?: boolean, timeout?: number) => void;
21
+ success: (message: string, icon?: IconName, dismissable?: boolean, timeout?: number) => void;
22
+ };
@@ -0,0 +1,27 @@
1
+ import { writable } from 'svelte/store';
2
+ const TIMEOUT = 3000;
3
+ const createNotificationStore = () => {
4
+ const { subscribe, update } = writable([]);
5
+ const send = (message, type = 'default', iconName, dismissable = false, timeout = 3000) => {
6
+ const notification = { id: id(), type, message, timeout, icon: iconName, dismissable };
7
+ update((state) => [...state, notification]);
8
+ setTimeout(() => {
9
+ update((state) => state.filter((n) => n.id !== notification.id));
10
+ }, timeout);
11
+ };
12
+ return {
13
+ subscribe,
14
+ dismiss: (id) => {
15
+ update((state) => state.filter((n) => n.id !== id));
16
+ },
17
+ default: (message, icon, dismissable = false, timeout = TIMEOUT) => send(message, 'default', icon, dismissable, timeout),
18
+ danger: (message, icon, dismissable = false, timeout = TIMEOUT) => send(message, 'danger', icon, dismissable, timeout),
19
+ warning: (message, icon, dismissable = false, timeout = TIMEOUT) => send(message, 'warning', icon, dismissable, timeout),
20
+ info: (message, icon, dismissable = false, timeout = TIMEOUT) => send(message, 'info', icon, dismissable, timeout),
21
+ success: (message, icon, dismissable = false, timeout = TIMEOUT) => send(message, 'success', icon, dismissable, timeout)
22
+ };
23
+ };
24
+ const id = () => {
25
+ return crypto.randomUUID();
26
+ };
27
+ export const notifications = createNotificationStore();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reshape-biotech/design-system",
3
- "version": "1.1.2",
3
+ "version": "1.2.1",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build",
@@ -28,6 +28,7 @@
28
28
  "default": "./dist/index.js"
29
29
  },
30
30
  "./tokens": "./dist/tokens.js",
31
+ "./notifications": "./dist/notifications.js",
31
32
  "./styles": "./dist/app.css",
32
33
  "./tailwind": "./dist/tailwind.preset.js",
33
34
  "./tailwind-safelist": "./dist/tailwind-safelist.js"