@reshape-biotech/design-system 2.6.3 → 2.6.5
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 +13 -6
- package/dist/components/card/Card.stories.svelte +96 -21
- package/dist/components/card/Card.svelte +5 -5
- package/dist/components/card/Card.svelte.d.ts +1 -1
- package/dist/components/card/CardWrapper.svelte +17 -24
- package/dist/components/card/index.d.ts +1 -0
- package/dist/components/card/index.js +1 -0
- package/dist/components/graphs/scatterplot/Scatterplot.svelte +44 -17
- package/dist/components/graphs/scatterplot/Scatterplot.svelte.d.ts +3 -0
- package/dist/components/icon-button/IconButton.svelte +9 -0
- package/dist/components/icon-button/IconButton.svelte.d.ts +1 -1
- package/dist/components/icons/index.d.ts +1 -1
- package/dist/components/icons/index.js +2 -0
- package/dist/components/input/Input.svelte +58 -45
- package/dist/components/input/Input.svelte.d.ts +1 -0
- package/dist/components/legend/components/legend-item.svelte +1 -1
- package/dist/components/legend/components/legend-root.svelte +2 -2
- package/dist/components/stat-card/StatCard.stories.svelte +23 -0
- package/dist/components/stat-card/StatCard.svelte +27 -10
- package/dist/components/stat-card/StatCard.svelte.d.ts +2 -1
- package/dist/components/textarea/Textarea.svelte +39 -24
- package/dist/components/textarea/Textarea.svelte.d.ts +1 -0
- package/dist/tokens/colors.js +1 -1
- package/package.json +1 -1
|
@@ -175,17 +175,24 @@
|
|
|
175
175
|
|
|
176
176
|
height: 3rem;
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
border-radius: 0.75rem;
|
|
179
179
|
|
|
180
|
-
padding-
|
|
180
|
+
padding-left: 1.25rem;
|
|
181
|
+
|
|
182
|
+
padding-right: 1.25rem;
|
|
181
183
|
|
|
182
184
|
padding-top: 1rem;
|
|
183
185
|
|
|
184
|
-
padding-bottom: 1rem
|
|
186
|
+
padding-bottom: 1rem
|
|
187
|
+
}
|
|
188
|
+
@media (min-width: 1280px) {
|
|
189
|
+
|
|
190
|
+
.btn-size-lg {
|
|
185
191
|
|
|
186
|
-
|
|
192
|
+
font-size: 1rem;
|
|
187
193
|
|
|
188
|
-
|
|
194
|
+
line-height: 1.5rem
|
|
195
|
+
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
/* Button variants */
|
|
@@ -272,7 +279,7 @@
|
|
|
272
279
|
}
|
|
273
280
|
.btn-outline:hover {
|
|
274
281
|
|
|
275
|
-
border-color: #
|
|
282
|
+
border-color: #12192A1A;
|
|
276
283
|
|
|
277
284
|
background-color: #12192a0A;
|
|
278
285
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
2
|
import { defineMeta } from '@storybook/addon-svelte-csf';
|
|
3
3
|
import Card from './Card.svelte';
|
|
4
|
+
import CardWrapper from './CardWrapper.svelte';
|
|
4
5
|
import RequiredStatusIndicator from '../required-status-indicator/RequiredStatusIndicator.svelte';
|
|
5
6
|
|
|
6
7
|
const { Story } = defineMeta({
|
|
@@ -13,6 +14,9 @@
|
|
|
13
14
|
headerBorder: { control: 'boolean', description: 'Show a border below the header' },
|
|
14
15
|
class: { control: 'text', description: 'Additional CSS classes for the card' },
|
|
15
16
|
},
|
|
17
|
+
parameters: {
|
|
18
|
+
layout: 'centered',
|
|
19
|
+
},
|
|
16
20
|
});
|
|
17
21
|
</script>
|
|
18
22
|
|
|
@@ -30,21 +34,11 @@
|
|
|
30
34
|
</Card>
|
|
31
35
|
</Story>
|
|
32
36
|
|
|
33
|
-
<Story name="Header Only" args={{ class: 'w-96' }} asChild>
|
|
34
|
-
<Card class="w-96">
|
|
35
|
-
{#snippet Header()}
|
|
36
|
-
<p class="text-primary">Card with Header Only</p>
|
|
37
|
-
<p class="text-sm text-tertiary">No main content here, just a header.</p>
|
|
38
|
-
{/snippet}
|
|
39
|
-
{#snippet children()}{/snippet}
|
|
40
|
-
</Card>
|
|
41
|
-
</Story>
|
|
42
|
-
|
|
43
37
|
<Story name="Header and Content" args={{ class: 'w-96' }} asChild>
|
|
44
38
|
<Card class="w-96">
|
|
45
39
|
{#snippet Header()}
|
|
46
|
-
<div class="flex items-center justify-between">
|
|
47
|
-
<
|
|
40
|
+
<div class="flex w-full items-center justify-between">
|
|
41
|
+
<h5>Insect Health</h5>
|
|
48
42
|
<span class="text-sm text-success">Target</span>
|
|
49
43
|
</div>
|
|
50
44
|
{/snippet}
|
|
@@ -78,8 +72,8 @@
|
|
|
78
72
|
<Story name="Header, Content, and Border" args={{ headerBorder: true, class: 'w-96' }} asChild>
|
|
79
73
|
<Card headerBorder={true} class="w-96">
|
|
80
74
|
{#snippet Header()}
|
|
81
|
-
<div class="flex items-center justify-between">
|
|
82
|
-
<
|
|
75
|
+
<div class="flex w-full items-center justify-between">
|
|
76
|
+
<h6>Insect Count</h6>
|
|
83
77
|
<RequiredStatusIndicator requiredCount={2} currentCount={2} />
|
|
84
78
|
</div>
|
|
85
79
|
{/snippet}
|
|
@@ -111,18 +105,99 @@
|
|
|
111
105
|
</Card>
|
|
112
106
|
</Story>
|
|
113
107
|
|
|
108
|
+
<Story name="Card nested in CardWrapper" args={{ class: 'w-96' }} asChild>
|
|
109
|
+
<CardWrapper class="w-96" variant="compact">
|
|
110
|
+
{#snippet Header()}
|
|
111
|
+
<h5>Overview</h5>
|
|
112
|
+
<span class="text-sm text-success">Active</span>
|
|
113
|
+
{/snippet}
|
|
114
|
+
{#snippet children()}
|
|
115
|
+
<Card>
|
|
116
|
+
{#snippet children()}
|
|
117
|
+
<h3 class="mb-2 text-lg font-semibold text-primary">Nested Card Content</h3>
|
|
118
|
+
<p class="text-secondary">
|
|
119
|
+
This demonstrates a Card nested in a compact CardWrapper. The wrapper provides the outer
|
|
120
|
+
container with header and footer, while the Card provides the inner styling with
|
|
121
|
+
content.
|
|
122
|
+
</p>
|
|
123
|
+
{/snippet}
|
|
124
|
+
</Card>
|
|
125
|
+
{/snippet}
|
|
126
|
+
{#snippet Footer()}
|
|
127
|
+
<p class="py-2 text-xs text-secondary">Last updated 2 hours ago</p>
|
|
128
|
+
{/snippet}
|
|
129
|
+
</CardWrapper>
|
|
130
|
+
</Story>
|
|
131
|
+
|
|
132
|
+
<Story name="CardWrapper with Multiple Cards" args={{ class: 'w-[600px]' }} asChild>
|
|
133
|
+
<CardWrapper class="w-[600px]">
|
|
134
|
+
{#snippet Header()}
|
|
135
|
+
<h6>Project Dashboard</h6>
|
|
136
|
+
<span class="text-sm text-success">3 Active</span>
|
|
137
|
+
{/snippet}
|
|
138
|
+
{#snippet children()}
|
|
139
|
+
<Card headerBorder>
|
|
140
|
+
{#snippet Header()}
|
|
141
|
+
<div class="flex items-center gap-2">
|
|
142
|
+
<div class="h-3 w-3 rounded-full bg-success-inverse"></div>
|
|
143
|
+
<h6>Analysis Model</h6>
|
|
144
|
+
</div>
|
|
145
|
+
{/snippet}
|
|
146
|
+
{#snippet children()}
|
|
147
|
+
<p class="mb-2 text-sm text-secondary">Cell Count Analysis</p>
|
|
148
|
+
<div class="flex justify-between text-xs text-tertiary">
|
|
149
|
+
<span>Status: Active</span>
|
|
150
|
+
<span>Last run: 2h ago</span>
|
|
151
|
+
</div>
|
|
152
|
+
{/snippet}
|
|
153
|
+
</Card>
|
|
154
|
+
|
|
155
|
+
<Card headerBorder>
|
|
156
|
+
{#snippet Header()}
|
|
157
|
+
<div class="flex items-center gap-2">
|
|
158
|
+
<div class="h-3 w-3 rounded-full bg-warning-inverse"></div>
|
|
159
|
+
<h6>Validation</h6>
|
|
160
|
+
</div>
|
|
161
|
+
{/snippet}
|
|
162
|
+
{#snippet children()}
|
|
163
|
+
<p class="mb-2 text-sm text-secondary">Quality Check</p>
|
|
164
|
+
<div class="flex justify-between text-xs text-tertiary">
|
|
165
|
+
<span>Status: Pending</span>
|
|
166
|
+
<span>Next run: 1d</span>
|
|
167
|
+
</div>
|
|
168
|
+
{/snippet}
|
|
169
|
+
</Card>
|
|
170
|
+
|
|
171
|
+
<Card headerBorder>
|
|
172
|
+
{#snippet Header()}
|
|
173
|
+
<div class="flex items-center gap-2">
|
|
174
|
+
<div class="h-3 w-3 rounded-full bg-danger-inverse"></div>
|
|
175
|
+
<h6>Export</h6>
|
|
176
|
+
</div>
|
|
177
|
+
{/snippet}
|
|
178
|
+
{#snippet children()}
|
|
179
|
+
<p class="mb-2 text-sm text-secondary">Data Export</p>
|
|
180
|
+
<div class="flex justify-between text-xs text-tertiary">
|
|
181
|
+
<span>Status: Failed</span>
|
|
182
|
+
<span>Last run: 5h ago</span>
|
|
183
|
+
</div>
|
|
184
|
+
{/snippet}
|
|
185
|
+
</Card>
|
|
186
|
+
{/snippet}
|
|
187
|
+
</CardWrapper>
|
|
188
|
+
</Story>
|
|
189
|
+
|
|
114
190
|
<Story name="With Custom Styling" args={{}} asChild>
|
|
115
|
-
<Card class="w-
|
|
191
|
+
<Card class="w-72 text-accent shadow-lg" headerBorder>
|
|
116
192
|
{#snippet Header()}
|
|
117
|
-
<
|
|
193
|
+
<h6>Custom Styled Card</h6>
|
|
118
194
|
{/snippet}
|
|
119
195
|
{#snippet children()}
|
|
120
196
|
<p>
|
|
121
|
-
This card uses the <code class="rounded bg-
|
|
122
|
-
different background (<code class="rounded bg-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class="rounded bg-black/20 px-1">shadow-lg</code
|
|
197
|
+
This card uses the <code class="rounded bg-accent px-1">class</code> prop to apply a
|
|
198
|
+
different background text color (<code class="rounded bg-accent px-1">text-accent</code>),
|
|
199
|
+
width (<code class="rounded bg-accent px-1">w-72</code>), and a larger shadow (<code
|
|
200
|
+
class="rounded bg-accent px-1">shadow-lg</code
|
|
126
201
|
>).
|
|
127
202
|
</p>
|
|
128
203
|
{/snippet}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
children: Snippet;
|
|
7
7
|
headerBorder?: boolean;
|
|
8
8
|
class?: string;
|
|
9
|
-
|
|
9
|
+
padding?: number;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
const {
|
|
@@ -14,16 +14,16 @@
|
|
|
14
14
|
children,
|
|
15
15
|
headerBorder = false,
|
|
16
16
|
class: additionalClasses = '',
|
|
17
|
-
|
|
17
|
+
padding = 4,
|
|
18
18
|
}: Props = $props();
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
<div
|
|
22
|
-
class="overflow-hidden rounded-
|
|
22
|
+
class="overflow-hidden rounded-lg border border-static bg-surface shadow-container {additionalClasses}"
|
|
23
23
|
>
|
|
24
24
|
{#if Header}
|
|
25
25
|
<header
|
|
26
|
-
class="flex h-12 items-center px-4"
|
|
26
|
+
class="flex min-h-12 items-center px-4"
|
|
27
27
|
class:border-b={headerBorder}
|
|
28
28
|
class:border-static={headerBorder}
|
|
29
29
|
>
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
</header>
|
|
32
32
|
{/if}
|
|
33
33
|
{#if children}
|
|
34
|
-
<div class={
|
|
34
|
+
<div class={`p-${padding}`}>
|
|
35
35
|
{@render children()}
|
|
36
36
|
</div>
|
|
37
37
|
{/if}
|
|
@@ -20,15 +20,23 @@
|
|
|
20
20
|
|
|
21
21
|
<div class="wrapper {variant} {additionalClasses}">
|
|
22
22
|
{#if Header}
|
|
23
|
-
<header
|
|
23
|
+
<header
|
|
24
|
+
class="flex w-full items-center justify-between {variant === 'default'
|
|
25
|
+
? 'min-h-10 px-4'
|
|
26
|
+
: 'min-h-8 px-3'}"
|
|
27
|
+
>
|
|
24
28
|
{@render Header()}
|
|
25
29
|
</header>
|
|
26
30
|
{/if}
|
|
27
|
-
<div
|
|
31
|
+
<div
|
|
32
|
+
class="flex w-full !overflow-hidden [&>*]:w-full {variant === 'default'
|
|
33
|
+
? '[&>*]:rounded-xl'
|
|
34
|
+
: '[&>*]:rounded-[10px]'}"
|
|
35
|
+
>
|
|
28
36
|
{@render children()}
|
|
29
37
|
</div>
|
|
30
38
|
{#if Footer}
|
|
31
|
-
<footer class="
|
|
39
|
+
<footer class="flex min-h-8 w-full items-center {variant === 'default' ? 'px-4' : 'px-3'}">
|
|
32
40
|
{@render Footer()}
|
|
33
41
|
</footer>
|
|
34
42
|
{/if}
|
|
@@ -41,17 +49,17 @@
|
|
|
41
49
|
|
|
42
50
|
flex-direction: column;
|
|
43
51
|
|
|
44
|
-
border-radius: 1rem;
|
|
45
|
-
|
|
46
52
|
--tw-bg-opacity: 1;
|
|
47
53
|
|
|
48
|
-
background-color: rgb(
|
|
54
|
+
background-color: rgb(250 250 250 / var(--tw-bg-opacity, 1))
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
.wrapper.default {
|
|
52
58
|
|
|
53
59
|
gap: 0.25rem;
|
|
54
60
|
|
|
61
|
+
border-radius: 1rem;
|
|
62
|
+
|
|
55
63
|
padding: 0.25rem
|
|
56
64
|
}
|
|
57
65
|
|
|
@@ -59,31 +67,16 @@
|
|
|
59
67
|
|
|
60
68
|
gap: 0.125rem;
|
|
61
69
|
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.wrapper-header {
|
|
66
|
-
|
|
67
|
-
display: flex;
|
|
68
|
-
|
|
69
|
-
min-height: 2rem;
|
|
70
|
+
border-radius: 0.75rem;
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
align-items: center;
|
|
74
|
-
|
|
75
|
-
justify-content: space-between;
|
|
76
|
-
|
|
77
|
-
padding-left: 1rem;
|
|
78
|
-
|
|
79
|
-
padding-right: 1rem
|
|
72
|
+
padding: 0.125rem
|
|
80
73
|
}
|
|
81
74
|
|
|
82
75
|
.wrapper-footer {
|
|
83
76
|
|
|
84
77
|
display: flex;
|
|
85
78
|
|
|
86
|
-
min-height:
|
|
79
|
+
min-height: 2rem;
|
|
87
80
|
|
|
88
81
|
width: 100%;
|
|
89
82
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { backgroundColor } from '../../../tokens';
|
|
2
|
+
import { backgroundColor, textColor } from '../../../tokens';
|
|
3
3
|
import Icon from '../../icons/Icon.svelte';
|
|
4
4
|
import Chart, { type GenericChartProps } from '../chart/Chart.svelte';
|
|
5
5
|
import type {
|
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
import Tooltip from '../../tooltip/Tooltip.svelte';
|
|
12
12
|
import type { Snippet } from 'svelte';
|
|
13
13
|
|
|
14
|
-
export type DataPoint = {
|
|
14
|
+
export type DataPoint = {
|
|
15
|
+
value: [number, number];
|
|
16
|
+
metadata: any;
|
|
17
|
+
error_value?: number;
|
|
18
|
+
disagreement?: boolean | null;
|
|
19
|
+
highlighted?: boolean | null;
|
|
20
|
+
};
|
|
15
21
|
|
|
16
22
|
export type ScatterPlotEchartsEvent = echarts.ECElementEvent & {
|
|
17
23
|
data: DataPoint;
|
|
@@ -29,6 +35,7 @@
|
|
|
29
35
|
showConfidenceBand?: boolean;
|
|
30
36
|
showLegend?: boolean;
|
|
31
37
|
legendItems?: LegendItem[];
|
|
38
|
+
highlightIndex?: number | null;
|
|
32
39
|
children?: Snippet;
|
|
33
40
|
} & Omit<GenericChartProps, 'timeIndex'>;
|
|
34
41
|
|
|
@@ -46,6 +53,7 @@
|
|
|
46
53
|
{ label: 'Count', color: backgroundColor['blue-inverse'], type: 'point' },
|
|
47
54
|
{ label: 'Perfect agreement', color: backgroundColor['lilac-inverse'], type: 'line' },
|
|
48
55
|
],
|
|
56
|
+
highlightIndex = null,
|
|
49
57
|
children,
|
|
50
58
|
...props
|
|
51
59
|
}: ScatterPlotProps = $props();
|
|
@@ -85,6 +93,7 @@
|
|
|
85
93
|
const xValue = api.value(0) as number;
|
|
86
94
|
const yValue = api.value(1) as number;
|
|
87
95
|
const error = api.value(2) as number;
|
|
96
|
+
const disagreement = api.value(3) as number;
|
|
88
97
|
|
|
89
98
|
const highPoint = api.coord([xValue, yValue + error]);
|
|
90
99
|
const lowPoint = api.coord([xValue, yValue - error]);
|
|
@@ -98,8 +107,7 @@
|
|
|
98
107
|
const baseWidth = Array.isArray(sizeValue) ? sizeValue[0] : 10;
|
|
99
108
|
const halfWidth = Math.min((baseWidth ?? 10) * 1.5, 3);
|
|
100
109
|
const style = {
|
|
101
|
-
stroke:
|
|
102
|
-
fill: undefined,
|
|
110
|
+
stroke: disagreement ? textColor['icon-tertiary'] : backgroundColor['blue-inverse'],
|
|
103
111
|
};
|
|
104
112
|
|
|
105
113
|
return {
|
|
@@ -148,7 +156,7 @@
|
|
|
148
156
|
const series: SeriesOption[] = [];
|
|
149
157
|
const errorBarData = data
|
|
150
158
|
.filter((d) => d.error_value != 0)
|
|
151
|
-
.map((d) => [...d.value, d.error_value]);
|
|
159
|
+
.map((d) => [...d.value, d.error_value, d.disagreement ? 1 : 0]);
|
|
152
160
|
|
|
153
161
|
if (props.lineData && props.lineData.length > 0) {
|
|
154
162
|
const extendedLineData = [
|
|
@@ -170,7 +178,6 @@
|
|
|
170
178
|
disabled: true,
|
|
171
179
|
},
|
|
172
180
|
silent: true,
|
|
173
|
-
zlevel: 0,
|
|
174
181
|
});
|
|
175
182
|
|
|
176
183
|
if (showConfidenceBand && props.lineData && props.lineData.length > 0) {
|
|
@@ -205,7 +212,6 @@
|
|
|
205
212
|
disabled: true,
|
|
206
213
|
},
|
|
207
214
|
silent: true,
|
|
208
|
-
zlevel: 0,
|
|
209
215
|
});
|
|
210
216
|
}
|
|
211
217
|
}
|
|
@@ -215,17 +221,43 @@
|
|
|
215
221
|
data,
|
|
216
222
|
type: 'scatter',
|
|
217
223
|
itemStyle: {
|
|
218
|
-
color:
|
|
219
|
-
|
|
224
|
+
color: (params: any) =>
|
|
225
|
+
params?.data?.disagreement ? textColor['icon-tertiary'] : backgroundColor['blue-inverse'],
|
|
226
|
+
opacity: 1,
|
|
220
227
|
},
|
|
221
228
|
emphasis: {
|
|
222
229
|
itemStyle: {
|
|
223
|
-
color:
|
|
230
|
+
color: (params: any) => {
|
|
231
|
+
const point = params?.data as DataPoint;
|
|
232
|
+
return point?.disagreement
|
|
233
|
+
? textColor['icon-tertiary']
|
|
234
|
+
: backgroundColor['blue-inverse'];
|
|
235
|
+
},
|
|
224
236
|
opacity: 1,
|
|
225
237
|
},
|
|
226
238
|
},
|
|
227
239
|
animation: false,
|
|
228
|
-
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const p =
|
|
243
|
+
highlightIndex !== null && highlightIndex >= 0 && highlightIndex < data.length
|
|
244
|
+
? data[highlightIndex]
|
|
245
|
+
: null;
|
|
246
|
+
series.push({
|
|
247
|
+
id: 'highlight-overlay',
|
|
248
|
+
type: 'scatter',
|
|
249
|
+
data: [p],
|
|
250
|
+
symbolSize: 16,
|
|
251
|
+
itemStyle: {
|
|
252
|
+
color: (params: any) => {
|
|
253
|
+
const point = params?.data as DataPoint;
|
|
254
|
+
return point?.disagreement
|
|
255
|
+
? backgroundColor['neutral-hover']
|
|
256
|
+
: backgroundColor['blue-hover'];
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
z: -1,
|
|
260
|
+
silent: true,
|
|
229
261
|
});
|
|
230
262
|
|
|
231
263
|
if (errorBarData.length > 0) {
|
|
@@ -233,16 +265,11 @@
|
|
|
233
265
|
type: 'custom',
|
|
234
266
|
name: 'error',
|
|
235
267
|
silent: true,
|
|
236
|
-
itemStyle: {
|
|
237
|
-
borderWidth: 1.5,
|
|
238
|
-
color: backgroundColor['blue-inverse'],
|
|
239
|
-
},
|
|
268
|
+
itemStyle: { borderWidth: 1.5 },
|
|
240
269
|
renderItem: renderErrorBarItem,
|
|
241
270
|
data: errorBarData,
|
|
242
|
-
zlevel: 0,
|
|
243
271
|
});
|
|
244
272
|
}
|
|
245
|
-
|
|
246
273
|
return {
|
|
247
274
|
xAxis: {
|
|
248
275
|
type: 'value',
|
|
@@ -4,6 +4,8 @@ export type DataPoint = {
|
|
|
4
4
|
value: [number, number];
|
|
5
5
|
metadata: any;
|
|
6
6
|
error_value?: number;
|
|
7
|
+
disagreement?: boolean | null;
|
|
8
|
+
highlighted?: boolean | null;
|
|
7
9
|
};
|
|
8
10
|
export type ScatterPlotEchartsEvent = echarts.ECElementEvent & {
|
|
9
11
|
data: DataPoint;
|
|
@@ -19,6 +21,7 @@ type ScatterPlotProps = {
|
|
|
19
21
|
showConfidenceBand?: boolean;
|
|
20
22
|
showLegend?: boolean;
|
|
21
23
|
legendItems?: LegendItem[];
|
|
24
|
+
highlightIndex?: number | null;
|
|
22
25
|
children?: Snippet;
|
|
23
26
|
} & Omit<GenericChartProps, 'timeIndex'>;
|
|
24
27
|
declare const Scatterplot: import("svelte").Component<ScatterPlotProps, {}, "">;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
| 'secondary'
|
|
9
9
|
| 'transparent'
|
|
10
10
|
| 'outline'
|
|
11
|
+
| 'accent'
|
|
11
12
|
| 'danger'
|
|
12
13
|
| 'secondary-inverse'
|
|
13
14
|
| 'transparent-inverse';
|
|
@@ -164,6 +165,14 @@
|
|
|
164
165
|
}
|
|
165
166
|
.color-secondary:hover {
|
|
166
167
|
background-color: #12192A1A
|
|
168
|
+
}
|
|
169
|
+
.color-accent {
|
|
170
|
+
background-color: #5750ee1A;
|
|
171
|
+
--tw-text-opacity: 1;
|
|
172
|
+
color: rgb(255 255 255 / var(--tw-text-opacity, 1))
|
|
173
|
+
}
|
|
174
|
+
.color-accent:hover {
|
|
175
|
+
background-color: #5750ee40
|
|
167
176
|
}
|
|
168
177
|
.color-transparent {
|
|
169
178
|
background-color: transparent;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
|
-
type Variant = 'primary' | 'secondary' | 'transparent' | 'outline' | 'danger' | 'secondary-inverse' | 'transparent-inverse';
|
|
3
|
+
type Variant = 'primary' | 'secondary' | 'transparent' | 'outline' | 'accent' | 'danger' | 'secondary-inverse' | 'transparent-inverse';
|
|
4
4
|
interface Props extends HTMLButtonAttributes {
|
|
5
5
|
variant?: Variant;
|
|
6
6
|
type?: 'button' | 'submit' | 'reset' | null | undefined;
|
|
@@ -4,7 +4,7 @@ import type { textColor, backgroundColor } from './../../tokens';
|
|
|
4
4
|
export * from './custom';
|
|
5
5
|
import type { CustomIconName } from './custom';
|
|
6
6
|
export type PhosphorIconProps = Component<IconComponentProps, object, ''>;
|
|
7
|
-
export type IconName = 'Aperture' | 'Archive' | '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' | 'CursorClick' | 'CraneTower' | 'Database' | 'Dna' | 'DotsThree' | 'DotsThreeOutlineVertical' | 'DownloadSimple' | 'Drop' | 'EnvelopeSimple' | 'Eye' | 'Eyedropper' | 'EyeSlash' | 'FileCsv' | 'Flag' | 'Flask' | 'Folder' | 'FolderDashed' | 'FolderSimplePlus' | 'FunnelSimple' | 'Gear' | 'GlobeSimple' | 'GlobeSimpleX' | 'GridFour' | 'Hash' | 'House' | 'ImageSquare' | 'ImagesSquare' | 'Info' | 'IntersectThree' | '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' | 'XCircle';
|
|
7
|
+
export type IconName = 'Aperture' | 'Archive' | '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' | 'CursorClick' | 'CraneTower' | 'Database' | 'Dna' | 'DotsThree' | 'DotsThreeOutlineVertical' | 'DownloadSimple' | 'Drop' | 'EnvelopeSimple' | 'Eye' | 'Eyedropper' | 'EyeSlash' | 'FileCsv' | 'Flag' | 'Flask' | 'Folder' | 'FolderDashed' | 'FolderSimplePlus' | 'FunnelSimple' | 'Gear' | 'GlobeSimple' | 'GlobeSimpleX' | 'GridFour' | 'Hash' | 'House' | 'ImageSquare' | 'ImagesSquare' | 'Info' | 'IntersectThree' | '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' | 'Users' | 'Video' | 'Warning' | 'WarningCircle' | 'WifiSlash' | 'X' | 'XCircle';
|
|
8
8
|
export type AllIconName = IconName | CustomIconName;
|
|
9
9
|
export declare const iconMap: Record<IconName, PhosphorIconProps>;
|
|
10
10
|
export declare function renderIcon(iconName: keyof typeof iconMap): PhosphorIconProps;
|
|
@@ -115,6 +115,7 @@ import TrashSimple from 'phosphor-svelte/lib/TrashSimple';
|
|
|
115
115
|
import TreeStructure from 'phosphor-svelte/lib/TreeStructure';
|
|
116
116
|
import UserCircle from 'phosphor-svelte/lib/UserCircle';
|
|
117
117
|
import UserPlus from 'phosphor-svelte/lib/UserPlus';
|
|
118
|
+
import Users from 'phosphor-svelte/lib/Users';
|
|
118
119
|
import Video from 'phosphor-svelte/lib/Video';
|
|
119
120
|
import Warning from 'phosphor-svelte/lib/Warning';
|
|
120
121
|
import WarningCircle from 'phosphor-svelte/lib/WarningCircle';
|
|
@@ -240,6 +241,7 @@ export const iconMap = {
|
|
|
240
241
|
TreeStructure,
|
|
241
242
|
UserCircle,
|
|
242
243
|
UserPlus,
|
|
244
|
+
Users,
|
|
243
245
|
Video,
|
|
244
246
|
Warning,
|
|
245
247
|
WarningCircle,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
interface InputProps extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'suffix'> {
|
|
8
8
|
label?: string | null;
|
|
9
|
+
labelPlacement?: 'top' | 'left';
|
|
9
10
|
id?: string | undefined;
|
|
10
11
|
variant?: 'primary' | 'secondary' | 'transparent' | 'borderless';
|
|
11
12
|
validator?: (a: string | number) => boolean;
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
|
|
24
25
|
let {
|
|
25
26
|
label = null,
|
|
27
|
+
labelPlacement = 'top',
|
|
26
28
|
variant = 'primary',
|
|
27
29
|
value = $bindable(),
|
|
28
30
|
type = 'text',
|
|
@@ -67,9 +69,18 @@
|
|
|
67
69
|
export type InputSize = 'xs' | 'sm' | 'md' | 'lg' | 'dynamic';
|
|
68
70
|
</script>
|
|
69
71
|
|
|
70
|
-
<div
|
|
72
|
+
<div
|
|
73
|
+
class="flex w-full gap-2"
|
|
74
|
+
class:flex-col={labelPlacement === 'top'}
|
|
75
|
+
class:items-center={labelPlacement === 'left'}
|
|
76
|
+
>
|
|
71
77
|
{#if label}
|
|
72
|
-
<label
|
|
78
|
+
<label
|
|
79
|
+
for={id ?? inputId}
|
|
80
|
+
class="flex text-sm capitalize text-secondary {labelPlacement === 'left'
|
|
81
|
+
? 'w-1/4'
|
|
82
|
+
: 'w-full'}"
|
|
83
|
+
>
|
|
73
84
|
{label}
|
|
74
85
|
{#if rest.required}
|
|
75
86
|
<span class="ml-0.5 text-danger">*</span>
|
|
@@ -77,51 +88,53 @@
|
|
|
77
88
|
</label>
|
|
78
89
|
{/if}
|
|
79
90
|
|
|
80
|
-
<div
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
<div class="flex-1">
|
|
92
|
+
<div
|
|
93
|
+
class="flex w-full items-center gap-1 size-{size} transition-colors"
|
|
94
|
+
class:!border-error={!valid}
|
|
95
|
+
class:primary={variant === 'primary'}
|
|
96
|
+
class:secondary={variant === 'secondary'}
|
|
97
|
+
class:transparent={variant === 'transparent'}
|
|
98
|
+
class:borderless={variant === 'borderless'}
|
|
99
|
+
>
|
|
100
|
+
<div class="whitespace-nowrap text-secondary">
|
|
101
|
+
{@render prefix?.({ valid })}
|
|
102
|
+
</div>
|
|
103
|
+
<input
|
|
104
|
+
{id}
|
|
105
|
+
class="inline leading-none"
|
|
106
|
+
class:has-text={value}
|
|
107
|
+
class:has-placeholder={rest.placeholder}
|
|
108
|
+
aria-label={label}
|
|
109
|
+
{type}
|
|
110
|
+
oninput={handleInput}
|
|
111
|
+
onblur={handleBlur}
|
|
112
|
+
bind:this={input}
|
|
113
|
+
bind:value
|
|
114
|
+
{...rest}
|
|
115
|
+
/>
|
|
116
|
+
<div class="whitespace-nowrap text-secondary">
|
|
117
|
+
{@render suffix?.()}
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
{#if clearable && value}
|
|
121
|
+
<IconButton
|
|
122
|
+
variant="transparent"
|
|
123
|
+
rounded={false}
|
|
124
|
+
size="xs"
|
|
125
|
+
onclick={() => {
|
|
126
|
+
value = '';
|
|
127
|
+
input?.focus();
|
|
128
|
+
onclear();
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<Icon iconName="X" />
|
|
132
|
+
</IconButton>
|
|
133
|
+
{/if}
|
|
106
134
|
</div>
|
|
107
135
|
|
|
108
|
-
{
|
|
109
|
-
<IconButton
|
|
110
|
-
variant="transparent"
|
|
111
|
-
rounded={false}
|
|
112
|
-
size="xs"
|
|
113
|
-
onclick={() => {
|
|
114
|
-
value = '';
|
|
115
|
-
input?.focus();
|
|
116
|
-
onclear();
|
|
117
|
-
}}
|
|
118
|
-
>
|
|
119
|
-
<Icon iconName="X" />
|
|
120
|
-
</IconButton>
|
|
121
|
-
{/if}
|
|
136
|
+
{@render error?.()}
|
|
122
137
|
</div>
|
|
123
|
-
|
|
124
|
-
{@render error?.()}
|
|
125
138
|
</div>
|
|
126
139
|
|
|
127
140
|
<style>
|
|
@@ -191,7 +204,7 @@ input::placeholder {
|
|
|
191
204
|
|
|
192
205
|
--tw-bg-opacity: 1;
|
|
193
206
|
|
|
194
|
-
background-color: rgb(
|
|
207
|
+
background-color: rgb(250 250 250 / var(--tw-bg-opacity, 1))
|
|
195
208
|
}
|
|
196
209
|
|
|
197
210
|
.transparent {
|
|
@@ -2,6 +2,7 @@ import type { Snippet } from 'svelte';
|
|
|
2
2
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
3
|
interface InputProps extends Omit<HTMLInputAttributes, 'size' | 'prefix' | 'suffix'> {
|
|
4
4
|
label?: string | null;
|
|
5
|
+
labelPlacement?: 'top' | 'left';
|
|
5
6
|
id?: string | undefined;
|
|
6
7
|
variant?: 'primary' | 'secondary' | 'transparent' | 'borderless';
|
|
7
8
|
validator?: (a: string | number) => boolean;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
>
|
|
20
20
|
<div class="flex h-2 w-2 flex-shrink-0 rounded-sm" style="background-color: {color}"></div>
|
|
21
21
|
<div
|
|
22
|
-
class="flex max-w-
|
|
22
|
+
class="flex max-w-40 select-none truncate text-xs"
|
|
23
23
|
class:text-secondary={!selected}
|
|
24
24
|
class:text-primary={selected}
|
|
25
25
|
>
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
let { children, items }: Props = $props();
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
|
-
<div class="inline-flex w-full flex-col items-center rounded-[10px] bg-
|
|
12
|
+
<div class="inline-flex w-full flex-col items-center rounded-[10px] bg-base">
|
|
13
13
|
<div
|
|
14
14
|
class="flex w-full px-0.5 pt-0.5 [&>*]:rounded-lg [&>*]:border [&>*]:border-static [&>*]:p-1"
|
|
15
15
|
>
|
|
16
16
|
{@render children()}
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
<div class="flex flex-wrap items-center justify-center gap-
|
|
19
|
+
<div class="flex flex-wrap items-center justify-center gap-1 overflow-clip p-1">
|
|
20
20
|
{#if items}
|
|
21
21
|
{@render items()}
|
|
22
22
|
{/if}
|
|
@@ -38,3 +38,26 @@
|
|
|
38
38
|
editable: true,
|
|
39
39
|
}}
|
|
40
40
|
/>
|
|
41
|
+
|
|
42
|
+
<Story
|
|
43
|
+
name="Primary Variant"
|
|
44
|
+
args={{
|
|
45
|
+
title: 'Primary Stat',
|
|
46
|
+
value: '42',
|
|
47
|
+
unit: 'units',
|
|
48
|
+
variant: 'primary',
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<Story name="Size Variants" asChild>
|
|
53
|
+
<div class="flex flex-col gap-4">
|
|
54
|
+
<div class="flex flex-col items-center gap-2">
|
|
55
|
+
<p class="text-sm text-secondary">Small</p>
|
|
56
|
+
<StatCard title="Small Size" value="123" unit="units" size="sm" />
|
|
57
|
+
</div>
|
|
58
|
+
<div class="flex flex-col items-center gap-2">
|
|
59
|
+
<p class="text-sm text-secondary">Medium</p>
|
|
60
|
+
<StatCard title="Medium Size" value="123" unit="units" size="md" />
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</Story>
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
editable?: boolean;
|
|
16
16
|
onsubmit?: (value: string | number) => void;
|
|
17
17
|
inputType?: 'text' | 'number';
|
|
18
|
-
variant?: '
|
|
18
|
+
variant?: 'primary' | 'secondary';
|
|
19
|
+
size?: 'sm' | 'md';
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
let {
|
|
@@ -26,8 +27,9 @@
|
|
|
26
27
|
showTitleTooltip = false,
|
|
27
28
|
editable = false,
|
|
28
29
|
inputType = 'text',
|
|
29
|
-
variant = '
|
|
30
|
+
variant = 'secondary',
|
|
30
31
|
onsubmit,
|
|
32
|
+
size = 'md',
|
|
31
33
|
}: Props = $props();
|
|
32
34
|
const formattedValue = $derived(typeof value === 'number' ? value.toLocaleString() : value);
|
|
33
35
|
let isEditing = $state(false);
|
|
@@ -94,16 +96,23 @@
|
|
|
94
96
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
95
97
|
<div
|
|
96
98
|
data-testid="stat-card-body"
|
|
97
|
-
class="flex w-full flex-shrink-0 flex-grow basis-0 flex-col items-start gap-2 overflow-clip rounded-lg
|
|
98
|
-
class:bg-neutral={variant === '
|
|
99
|
-
class:bg-surface={variant === '
|
|
99
|
+
class="flex w-full flex-shrink-0 flex-grow basis-0 flex-col items-start gap-2 overflow-clip rounded-lg text-left transition-colors"
|
|
100
|
+
class:bg-neutral={variant === 'secondary'}
|
|
101
|
+
class:bg-surface={variant === 'primary'}
|
|
102
|
+
class:shadow-container={variant === 'primary'}
|
|
103
|
+
class:p-4={size === 'md'}
|
|
104
|
+
class:p-3={size === 'sm'}
|
|
100
105
|
class:hover:bg-neutral-hover={editable && !isEditing && value !== null}
|
|
101
106
|
class:cursor-pointer={editable && !isEditing && value !== null}
|
|
102
107
|
onclick={handleCardClick}
|
|
103
108
|
onkeydown={editable && !isEditing && value !== null ? handleInputKeydown : undefined}
|
|
104
109
|
aria-label={editable && !isEditing && value !== null ? `Edit ${title}` : undefined}
|
|
105
110
|
>
|
|
106
|
-
<p
|
|
111
|
+
<p
|
|
112
|
+
class="flex items-center justify-start gap-2 truncate font-medium text-secondary"
|
|
113
|
+
class:text-label={size === 'sm'}
|
|
114
|
+
class:text-sm={size === 'md'}
|
|
115
|
+
>
|
|
107
116
|
{title}
|
|
108
117
|
{#if titleTooltip && showTitleTooltip}
|
|
109
118
|
<Tooltip>
|
|
@@ -118,7 +127,7 @@
|
|
|
118
127
|
</Tooltip>
|
|
119
128
|
{/if}
|
|
120
129
|
</p>
|
|
121
|
-
<div class="flex
|
|
130
|
+
<div class="flex w-full items-center gap-1" class:h-8={size === 'md'} class:h-7={size === 'sm'}>
|
|
122
131
|
{#if value !== null}
|
|
123
132
|
{#if isEditing}
|
|
124
133
|
<div class="flex-1">
|
|
@@ -132,13 +141,21 @@
|
|
|
132
141
|
</div>
|
|
133
142
|
{:else}
|
|
134
143
|
<div class="flex flex-1 flex-nowrap items-baseline gap-1">
|
|
135
|
-
<p class="text-2xl
|
|
144
|
+
<p class="font-medium" class:text-2xl={size === 'md'} class:text-xl={size === 'sm'}>
|
|
145
|
+
{formattedValue}
|
|
146
|
+
</p>
|
|
136
147
|
{#if unit}
|
|
137
|
-
<p
|
|
148
|
+
<p
|
|
149
|
+
class="flex-1 truncate font-medium text-tertiary"
|
|
150
|
+
class:text-2xl={size === 'md'}
|
|
151
|
+
class:text-xl={size === 'sm'}
|
|
152
|
+
>
|
|
153
|
+
{unit}
|
|
154
|
+
</p>
|
|
138
155
|
{/if}
|
|
139
156
|
</div>
|
|
140
157
|
{#if editable}
|
|
141
|
-
<IconButton onclick={(e) => startEditing(e)}>
|
|
158
|
+
<IconButton onclick={(e) => startEditing(e)} rounded={false}>
|
|
142
159
|
<Icon iconName="PencilSimple" />
|
|
143
160
|
</IconButton>
|
|
144
161
|
{/if}
|
|
@@ -7,7 +7,8 @@ interface Props {
|
|
|
7
7
|
editable?: boolean;
|
|
8
8
|
onsubmit?: (value: string | number) => void;
|
|
9
9
|
inputType?: 'text' | 'number';
|
|
10
|
-
variant?: '
|
|
10
|
+
variant?: 'primary' | 'secondary';
|
|
11
|
+
size?: 'sm' | 'md';
|
|
11
12
|
}
|
|
12
13
|
declare const StatCard: import("svelte").Component<Props, {}, "">;
|
|
13
14
|
type StatCard = ReturnType<typeof StatCard>;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
input?: HTMLTextAreaElement;
|
|
10
10
|
size?: 'xs' | 'sm' | 'md' | 'lg' | 'dynamic';
|
|
11
11
|
label?: string;
|
|
12
|
+
labelPlacement?: 'top' | 'left';
|
|
12
13
|
error?: Snippet;
|
|
13
14
|
placeholder?: string;
|
|
14
15
|
}
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
onkeydown = () => {},
|
|
21
22
|
input = $bindable(),
|
|
22
23
|
label,
|
|
24
|
+
labelPlacement = 'top',
|
|
23
25
|
placeholder,
|
|
24
26
|
error,
|
|
25
27
|
id,
|
|
@@ -66,29 +68,42 @@
|
|
|
66
68
|
});
|
|
67
69
|
</script>
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{
|
|
71
|
+
<div
|
|
72
|
+
class="flex w-full gap-2"
|
|
73
|
+
class:flex-col={labelPlacement === 'top'}
|
|
74
|
+
class:items-start={labelPlacement === 'left'}
|
|
75
|
+
>
|
|
76
|
+
{#if label}
|
|
77
|
+
<label
|
|
78
|
+
for={inputId}
|
|
79
|
+
class="flex text-sm capitalize text-secondary {labelPlacement === 'left'
|
|
80
|
+
? 'w-1/4'
|
|
81
|
+
: 'w-full'}"
|
|
82
|
+
>
|
|
83
|
+
{label}
|
|
84
|
+
{#if rest.required}
|
|
85
|
+
<span class="ml-0.5 text-danger">*</span>
|
|
86
|
+
{/if}
|
|
87
|
+
</label>
|
|
88
|
+
{/if}
|
|
77
89
|
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
<div class="flex-1">
|
|
91
|
+
<textarea
|
|
92
|
+
id={inputId}
|
|
93
|
+
class="flex min-h-10 w-full items-center gap-1 rounded-lg border border-input bg-surface px-3 py-2 shadow-input transition-colors hover:border-hover focus:border-focus focus:outline-none {className ??
|
|
94
|
+
''}"
|
|
95
|
+
class:!border-error={error}
|
|
96
|
+
class:has-text={value}
|
|
97
|
+
class:has-placeholder={placeholder}
|
|
98
|
+
{placeholder}
|
|
99
|
+
oninput={handleInputInternal}
|
|
100
|
+
onblur={handleBlurInternal}
|
|
101
|
+
bind:this={input}
|
|
102
|
+
{value}
|
|
103
|
+
{...rest}
|
|
104
|
+
{onkeydown}
|
|
105
|
+
></textarea>
|
|
93
106
|
|
|
94
|
-
{@render error?.()}
|
|
107
|
+
{@render error?.()}
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
package/dist/tokens/colors.js
CHANGED