sv5ui 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Banner/Banner.svelte +162 -0
- package/dist/Banner/Banner.svelte.d.ts +5 -0
- package/dist/Banner/banner.types.d.ts +148 -0
- package/dist/Banner/banner.types.js +1 -0
- package/dist/Banner/banner.variants.d.ts +293 -0
- package/dist/Banner/banner.variants.js +86 -0
- package/dist/Banner/index.d.ts +2 -0
- package/dist/Banner/index.js +1 -0
- package/dist/Calendar/Calendar.svelte +48 -6
- package/dist/Calendar/calendar.types.d.ts +19 -0
- package/dist/Calendar/calendar.variants.js +2 -1
- package/dist/Carousel/Carousel.svelte +279 -0
- package/dist/Carousel/Carousel.svelte.d.ts +26 -0
- package/dist/Carousel/carousel.types.d.ts +242 -0
- package/dist/Carousel/carousel.types.js +1 -0
- package/dist/Carousel/carousel.variants.d.ts +408 -0
- package/dist/Carousel/carousel.variants.js +88 -0
- package/dist/Carousel/index.d.ts +2 -0
- package/dist/Carousel/index.js +1 -0
- package/dist/Editor/Editor.svelte +738 -0
- package/dist/Editor/Editor.svelte.d.ts +6 -0
- package/dist/Editor/EditorUrlPrompt.svelte +111 -0
- package/dist/Editor/EditorUrlPrompt.svelte.d.ts +15 -0
- package/dist/Editor/SlashPopup.svelte +67 -0
- package/dist/Editor/SlashPopup.svelte.d.ts +9 -0
- package/dist/Editor/editor.extensions.d.ts +23 -0
- package/dist/Editor/editor.extensions.js +123 -0
- package/dist/Editor/editor.schemas.d.ts +4 -0
- package/dist/Editor/editor.schemas.js +3 -0
- package/dist/Editor/editor.slash.svelte.d.ts +34 -0
- package/dist/Editor/editor.slash.svelte.js +273 -0
- package/dist/Editor/editor.suggestion.d.ts +7 -0
- package/dist/Editor/editor.suggestion.js +142 -0
- package/dist/Editor/editor.toolbar.d.ts +11 -0
- package/dist/Editor/editor.toolbar.js +212 -0
- package/dist/Editor/editor.types.d.ts +347 -0
- package/dist/Editor/editor.types.js +1 -0
- package/dist/Editor/editor.variants.d.ts +308 -0
- package/dist/Editor/editor.variants.js +150 -0
- package/dist/Editor/index.d.ts +53 -0
- package/dist/Editor/index.js +52 -0
- package/dist/FileUpload/FileUpload.svelte +81 -10
- package/dist/FileUpload/file-upload.types.d.ts +39 -0
- package/dist/FileUpload/index.d.ts +1 -1
- package/dist/Modal/Modal.svelte +14 -3
- package/dist/Modal/modal.types.d.ts +15 -4
- package/dist/Modal/modal.variants.d.ts +110 -20
- package/dist/Modal/modal.variants.js +27 -9
- package/dist/PinInput/PinInput.svelte +18 -4
- package/dist/PinInput/pin-input.types.d.ts +11 -0
- package/dist/Select/Select.svelte +98 -28
- package/dist/Select/select.types.d.ts +44 -2
- package/dist/SelectMenu/SelectMenu.svelte +210 -25
- package/dist/SelectMenu/select-menu.types.d.ts +62 -1
- package/dist/SelectMenu/select-menu.variants.d.ts +26 -0
- package/dist/SelectMenu/select-menu.variants.js +34 -6
- package/dist/Slideover/Slideover.svelte +13 -2
- package/dist/Slideover/slideover.types.d.ts +14 -3
- package/dist/Slideover/slideover.variants.d.ts +85 -5
- package/dist/Slideover/slideover.variants.js +42 -12
- package/dist/Stepper/Stepper.svelte +292 -0
- package/dist/Stepper/Stepper.svelte.d.ts +5 -0
- package/dist/Stepper/index.d.ts +2 -0
- package/dist/Stepper/index.js +1 -0
- package/dist/Stepper/stepper.types.d.ts +223 -0
- package/dist/Stepper/stepper.types.js +1 -0
- package/dist/Stepper/stepper.variants.d.ts +428 -0
- package/dist/Stepper/stepper.variants.js +204 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +102 -1
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
import { type VariantProps } from 'tailwind-variants';
|
|
2
2
|
export declare const modalVariants: import("tailwind-variants").TVReturnType<{
|
|
3
3
|
transition: {
|
|
4
|
-
|
|
4
|
+
none: {};
|
|
5
|
+
fade: {
|
|
6
|
+
overlay: string;
|
|
7
|
+
content: string;
|
|
8
|
+
};
|
|
9
|
+
slide: {
|
|
10
|
+
overlay: string;
|
|
11
|
+
content: string;
|
|
12
|
+
};
|
|
13
|
+
scale: {
|
|
5
14
|
overlay: string;
|
|
6
15
|
content: string;
|
|
7
16
|
};
|
|
8
17
|
};
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
size: {
|
|
19
|
+
sm: {
|
|
11
20
|
content: string;
|
|
12
21
|
};
|
|
13
|
-
|
|
22
|
+
md: {
|
|
23
|
+
content: string;
|
|
24
|
+
};
|
|
25
|
+
lg: {
|
|
26
|
+
content: string;
|
|
27
|
+
};
|
|
28
|
+
xl: {
|
|
29
|
+
content: string;
|
|
30
|
+
};
|
|
31
|
+
full: {
|
|
14
32
|
content: string;
|
|
15
33
|
};
|
|
16
34
|
};
|
|
@@ -42,16 +60,34 @@ export declare const modalVariants: import("tailwind-variants").TVReturnType<{
|
|
|
42
60
|
close: string;
|
|
43
61
|
}, undefined, {
|
|
44
62
|
transition: {
|
|
45
|
-
|
|
63
|
+
none: {};
|
|
64
|
+
fade: {
|
|
65
|
+
overlay: string;
|
|
66
|
+
content: string;
|
|
67
|
+
};
|
|
68
|
+
slide: {
|
|
69
|
+
overlay: string;
|
|
70
|
+
content: string;
|
|
71
|
+
};
|
|
72
|
+
scale: {
|
|
46
73
|
overlay: string;
|
|
47
74
|
content: string;
|
|
48
75
|
};
|
|
49
76
|
};
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
size: {
|
|
78
|
+
sm: {
|
|
52
79
|
content: string;
|
|
53
80
|
};
|
|
54
|
-
|
|
81
|
+
md: {
|
|
82
|
+
content: string;
|
|
83
|
+
};
|
|
84
|
+
lg: {
|
|
85
|
+
content: string;
|
|
86
|
+
};
|
|
87
|
+
xl: {
|
|
88
|
+
content: string;
|
|
89
|
+
};
|
|
90
|
+
full: {
|
|
55
91
|
content: string;
|
|
56
92
|
};
|
|
57
93
|
};
|
|
@@ -83,16 +119,34 @@ export declare const modalVariants: import("tailwind-variants").TVReturnType<{
|
|
|
83
119
|
close: string;
|
|
84
120
|
}, import("tailwind-variants").TVReturnType<{
|
|
85
121
|
transition: {
|
|
86
|
-
|
|
122
|
+
none: {};
|
|
123
|
+
fade: {
|
|
124
|
+
overlay: string;
|
|
125
|
+
content: string;
|
|
126
|
+
};
|
|
127
|
+
slide: {
|
|
128
|
+
overlay: string;
|
|
129
|
+
content: string;
|
|
130
|
+
};
|
|
131
|
+
scale: {
|
|
87
132
|
overlay: string;
|
|
88
133
|
content: string;
|
|
89
134
|
};
|
|
90
135
|
};
|
|
91
|
-
|
|
92
|
-
|
|
136
|
+
size: {
|
|
137
|
+
sm: {
|
|
93
138
|
content: string;
|
|
94
139
|
};
|
|
95
|
-
|
|
140
|
+
md: {
|
|
141
|
+
content: string;
|
|
142
|
+
};
|
|
143
|
+
lg: {
|
|
144
|
+
content: string;
|
|
145
|
+
};
|
|
146
|
+
xl: {
|
|
147
|
+
content: string;
|
|
148
|
+
};
|
|
149
|
+
full: {
|
|
96
150
|
content: string;
|
|
97
151
|
};
|
|
98
152
|
};
|
|
@@ -128,16 +182,34 @@ export type ModalSlots = keyof ReturnType<typeof modalVariants>;
|
|
|
128
182
|
export declare const modalDefaults: {
|
|
129
183
|
defaultVariants: import("tailwind-variants").TVDefaultVariants<{
|
|
130
184
|
transition: {
|
|
131
|
-
|
|
185
|
+
none: {};
|
|
186
|
+
fade: {
|
|
187
|
+
overlay: string;
|
|
188
|
+
content: string;
|
|
189
|
+
};
|
|
190
|
+
slide: {
|
|
191
|
+
overlay: string;
|
|
192
|
+
content: string;
|
|
193
|
+
};
|
|
194
|
+
scale: {
|
|
132
195
|
overlay: string;
|
|
133
196
|
content: string;
|
|
134
197
|
};
|
|
135
198
|
};
|
|
136
|
-
|
|
137
|
-
|
|
199
|
+
size: {
|
|
200
|
+
sm: {
|
|
138
201
|
content: string;
|
|
139
202
|
};
|
|
140
|
-
|
|
203
|
+
md: {
|
|
204
|
+
content: string;
|
|
205
|
+
};
|
|
206
|
+
lg: {
|
|
207
|
+
content: string;
|
|
208
|
+
};
|
|
209
|
+
xl: {
|
|
210
|
+
content: string;
|
|
211
|
+
};
|
|
212
|
+
full: {
|
|
141
213
|
content: string;
|
|
142
214
|
};
|
|
143
215
|
};
|
|
@@ -169,16 +241,34 @@ export declare const modalDefaults: {
|
|
|
169
241
|
close: string;
|
|
170
242
|
}, {
|
|
171
243
|
transition: {
|
|
172
|
-
|
|
244
|
+
none: {};
|
|
245
|
+
fade: {
|
|
246
|
+
overlay: string;
|
|
247
|
+
content: string;
|
|
248
|
+
};
|
|
249
|
+
slide: {
|
|
250
|
+
overlay: string;
|
|
251
|
+
content: string;
|
|
252
|
+
};
|
|
253
|
+
scale: {
|
|
173
254
|
overlay: string;
|
|
174
255
|
content: string;
|
|
175
256
|
};
|
|
176
257
|
};
|
|
177
|
-
|
|
178
|
-
|
|
258
|
+
size: {
|
|
259
|
+
sm: {
|
|
179
260
|
content: string;
|
|
180
261
|
};
|
|
181
|
-
|
|
262
|
+
md: {
|
|
263
|
+
content: string;
|
|
264
|
+
};
|
|
265
|
+
lg: {
|
|
266
|
+
content: string;
|
|
267
|
+
};
|
|
268
|
+
xl: {
|
|
269
|
+
content: string;
|
|
270
|
+
};
|
|
271
|
+
full: {
|
|
182
272
|
content: string;
|
|
183
273
|
};
|
|
184
274
|
};
|
|
@@ -14,17 +14,35 @@ export const modalVariants = tv({
|
|
|
14
14
|
},
|
|
15
15
|
variants: {
|
|
16
16
|
transition: {
|
|
17
|
-
|
|
17
|
+
none: {},
|
|
18
|
+
fade: {
|
|
19
|
+
overlay: 'data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_150ms_ease-in]',
|
|
20
|
+
content: 'data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_150ms_ease-in]'
|
|
21
|
+
},
|
|
22
|
+
slide: {
|
|
23
|
+
overlay: 'data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_150ms_ease-in]',
|
|
24
|
+
content: 'data-[state=open]:animate-[slide-in-from-top_200ms_ease-out] data-[state=closed]:animate-[slide-out-to-top_150ms_ease-in]'
|
|
25
|
+
},
|
|
26
|
+
scale: {
|
|
18
27
|
overlay: 'data-[state=open]:animate-[fade-in_200ms_ease-out] data-[state=closed]:animate-[fade-out_150ms_ease-in]',
|
|
19
28
|
content: 'data-[state=open]:animate-[scale-in_200ms_cubic-bezier(0.32,0.72,0,1)] data-[state=closed]:animate-[scale-out_150ms_cubic-bezier(0.32,0.72,0,1)]'
|
|
20
29
|
}
|
|
21
30
|
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
content: '
|
|
31
|
+
size: {
|
|
32
|
+
sm: {
|
|
33
|
+
content: 'w-[calc(100vw-2rem)] max-w-md rounded-lg shadow-lg ring ring-outline-variant'
|
|
25
34
|
},
|
|
26
|
-
|
|
35
|
+
md: {
|
|
27
36
|
content: 'w-[calc(100vw-2rem)] max-w-lg rounded-lg shadow-lg ring ring-outline-variant'
|
|
37
|
+
},
|
|
38
|
+
lg: {
|
|
39
|
+
content: 'w-[calc(100vw-2rem)] max-w-2xl rounded-lg shadow-lg ring ring-outline-variant'
|
|
40
|
+
},
|
|
41
|
+
xl: {
|
|
42
|
+
content: 'w-[calc(100vw-2rem)] max-w-4xl rounded-lg shadow-lg ring ring-outline-variant'
|
|
43
|
+
},
|
|
44
|
+
full: {
|
|
45
|
+
content: 'inset-0'
|
|
28
46
|
}
|
|
29
47
|
},
|
|
30
48
|
overlay: {
|
|
@@ -46,22 +64,22 @@ export const modalVariants = tv({
|
|
|
46
64
|
compoundVariants: [
|
|
47
65
|
{
|
|
48
66
|
scrollable: true,
|
|
49
|
-
|
|
67
|
+
size: ['sm', 'md', 'lg', 'xl'],
|
|
50
68
|
class: {
|
|
51
69
|
overlay: 'grid place-items-center p-4 sm:py-8'
|
|
52
70
|
}
|
|
53
71
|
},
|
|
54
72
|
{
|
|
55
73
|
scrollable: false,
|
|
56
|
-
|
|
74
|
+
size: ['sm', 'md', 'lg', 'xl'],
|
|
57
75
|
class: {
|
|
58
76
|
content: 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-h-[calc(100dvh-2rem)] sm:max-h-[calc(100dvh-4rem)] overflow-hidden'
|
|
59
77
|
}
|
|
60
78
|
}
|
|
61
79
|
],
|
|
62
80
|
defaultVariants: {
|
|
63
|
-
transition:
|
|
64
|
-
|
|
81
|
+
transition: 'scale',
|
|
82
|
+
size: 'md',
|
|
65
83
|
overlay: true,
|
|
66
84
|
scrollable: false
|
|
67
85
|
}
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
<script lang="ts">
|
|
8
8
|
import { PinInput, useId } from 'bits-ui'
|
|
9
9
|
import { pinInputVariants, pinInputDefaults } from './pin-input.variants.js'
|
|
10
|
-
import { getComponentConfig } from '../config.js'
|
|
10
|
+
import { getComponentConfig, iconsDefaults } from '../config.js'
|
|
11
11
|
import { useFormField, useFormFieldEmit } from '../hooks/useFormField.svelte.js'
|
|
12
|
+
import Icon from '../Icon/Icon.svelte'
|
|
12
13
|
|
|
13
14
|
const config = getComponentConfig('pinInput', pinInputDefaults)
|
|
15
|
+
const icons = getComponentConfig('icons', iconsDefaults)
|
|
14
16
|
|
|
15
17
|
let {
|
|
16
18
|
ref = $bindable(null),
|
|
@@ -33,6 +35,8 @@
|
|
|
33
35
|
autofocus = false,
|
|
34
36
|
autofocusDelay = 0,
|
|
35
37
|
highlight = false,
|
|
38
|
+
loading = false,
|
|
39
|
+
loadingIcon = icons.loading,
|
|
36
40
|
fixed = false,
|
|
37
41
|
color = config.defaultVariants.color,
|
|
38
42
|
size,
|
|
@@ -45,6 +49,8 @@
|
|
|
45
49
|
const formFieldContext = useFormField()
|
|
46
50
|
const emit = useFormFieldEmit()
|
|
47
51
|
|
|
52
|
+
const isDisabled = $derived(disabled || loading)
|
|
53
|
+
|
|
48
54
|
const autoInputId = useId()
|
|
49
55
|
const hasError = $derived(
|
|
50
56
|
formFieldContext?.error !== undefined && formFieldContext?.error !== false
|
|
@@ -86,7 +92,7 @@
|
|
|
86
92
|
size: resolvedSize,
|
|
87
93
|
highlight: resolvedHighlight,
|
|
88
94
|
fixed,
|
|
89
|
-
disabled
|
|
95
|
+
disabled: isDisabled
|
|
90
96
|
})
|
|
91
97
|
)
|
|
92
98
|
|
|
@@ -107,15 +113,23 @@
|
|
|
107
113
|
})
|
|
108
114
|
</script>
|
|
109
115
|
|
|
110
|
-
<div class="
|
|
116
|
+
<div class="relative inline-flex" {...restProps}>
|
|
111
117
|
{#if resolvedName}
|
|
112
118
|
<input type="hidden" name={resolvedName} {value} />
|
|
113
119
|
{/if}
|
|
120
|
+
{#if loading}
|
|
121
|
+
<span
|
|
122
|
+
class="pointer-events-none absolute inset-0 z-10 flex items-center justify-center bg-surface/60"
|
|
123
|
+
aria-hidden="true"
|
|
124
|
+
>
|
|
125
|
+
<Icon name={loadingIcon} class="size-5 animate-spin text-on-surface-variant" />
|
|
126
|
+
</span>
|
|
127
|
+
{/if}
|
|
114
128
|
<PinInput.Root
|
|
115
129
|
bind:ref
|
|
116
130
|
{value}
|
|
117
131
|
maxlength={length}
|
|
118
|
-
{
|
|
132
|
+
disabled={isDisabled}
|
|
119
133
|
{textalign}
|
|
120
134
|
onComplete={handleComplete}
|
|
121
135
|
pasteTransformer={resolvedPasteTransformer}
|
|
@@ -68,6 +68,17 @@ export type PinInputProps = Pick<PinInputPrimitive.RootProps, 'disabled' | 'text
|
|
|
68
68
|
* @default false
|
|
69
69
|
*/
|
|
70
70
|
highlight?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Show a loading spinner over the cells and disable interaction.
|
|
73
|
+
* Useful when verifying an OTP code against a backend.
|
|
74
|
+
* @default false
|
|
75
|
+
*/
|
|
76
|
+
loading?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Icon displayed as the loading indicator. Defaults to `icons.loading`
|
|
79
|
+
* from the global app config (`lucide:loader-circle`).
|
|
80
|
+
*/
|
|
81
|
+
loadingIcon?: string;
|
|
71
82
|
/**
|
|
72
83
|
* Prevent responsive text size changes on mobile.
|
|
73
84
|
* @default false
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
name,
|
|
32
32
|
required = false,
|
|
33
33
|
disabled = false,
|
|
34
|
+
multiple = false,
|
|
35
|
+
separator = ', ',
|
|
34
36
|
ui,
|
|
35
37
|
id,
|
|
36
38
|
color = config.defaultVariants.color,
|
|
@@ -64,6 +66,7 @@
|
|
|
64
66
|
itemLeading,
|
|
65
67
|
itemLabel: itemLabelSlot,
|
|
66
68
|
itemTrailing,
|
|
69
|
+
selected: selectedSlot,
|
|
67
70
|
content: contentSlot
|
|
68
71
|
}: Props = $props()
|
|
69
72
|
|
|
@@ -113,12 +116,44 @@
|
|
|
113
116
|
)
|
|
114
117
|
)
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
const
|
|
119
|
+
// ---- Selection (single + multiple) ----
|
|
120
|
+
const selectedValues = $derived(
|
|
121
|
+
multiple
|
|
122
|
+
? Array.isArray(value)
|
|
123
|
+
? (value as string[])
|
|
124
|
+
: []
|
|
125
|
+
: typeof value === 'string' && value !== ''
|
|
126
|
+
? [value]
|
|
127
|
+
: []
|
|
128
|
+
)
|
|
129
|
+
const selectedItems = $derived(
|
|
130
|
+
selectedValues.map((v) => itemsMap.get(v)).filter((i): i is SelectItem => i !== undefined)
|
|
131
|
+
)
|
|
132
|
+
const hasSelection = $derived(selectedValues.length > 0)
|
|
133
|
+
const singleSelectedItem = $derived(multiple ? undefined : selectedItems[0])
|
|
134
|
+
const displayLabel = $derived(
|
|
135
|
+
multiple
|
|
136
|
+
? selectedItems.map((i) => i.label ?? i.value).join(separator)
|
|
137
|
+
: (singleSelectedItem?.label ?? singleSelectedItem?.value ?? '')
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
function removeValue(val: string) {
|
|
141
|
+
if (!multiple) return
|
|
142
|
+
value = selectedValues.filter((v) => v !== val)
|
|
143
|
+
emit.onChange()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function clearSelection() {
|
|
147
|
+
if (!multiple) return
|
|
148
|
+
value = []
|
|
149
|
+
emit.onChange()
|
|
150
|
+
}
|
|
118
151
|
|
|
119
152
|
// ---- Leading / trailing ----
|
|
120
|
-
const displayAvatar = $derived(
|
|
121
|
-
const displayIcon = $derived(
|
|
153
|
+
const displayAvatar = $derived(multiple ? avatar : (singleSelectedItem?.avatar ?? avatar))
|
|
154
|
+
const displayIcon = $derived(
|
|
155
|
+
multiple ? (leadingIcon ?? icon) : (singleSelectedItem?.icon ?? leadingIcon ?? icon)
|
|
156
|
+
)
|
|
122
157
|
const isLeading = $derived(!!leadingSlot || !!displayAvatar || !!displayIcon)
|
|
123
158
|
const leadingIconName = $derived(
|
|
124
159
|
loading && isLeading ? loadingIcon : !displayAvatar ? displayIcon : undefined
|
|
@@ -236,7 +271,7 @@
|
|
|
236
271
|
</script>
|
|
237
272
|
|
|
238
273
|
{#snippet renderItem(item: SelectItem, index: number)}
|
|
239
|
-
{@const isSelected =
|
|
274
|
+
{@const isSelected = selectedValues.includes(item.value)}
|
|
240
275
|
<Select.Item
|
|
241
276
|
value={item.value}
|
|
242
277
|
label={item.label ?? item.value}
|
|
@@ -303,7 +338,7 @@
|
|
|
303
338
|
{@render itemSlot({
|
|
304
339
|
item: selectItem,
|
|
305
340
|
index,
|
|
306
|
-
selected:
|
|
341
|
+
selected: selectedValues.includes(selectItem.value)
|
|
307
342
|
})}
|
|
308
343
|
{:else}
|
|
309
344
|
{@render renderItem(selectItem, index)}
|
|
@@ -315,26 +350,7 @@
|
|
|
315
350
|
</Select.Content>
|
|
316
351
|
{/snippet}
|
|
317
352
|
|
|
318
|
-
|
|
319
|
-
type="single"
|
|
320
|
-
bind:open
|
|
321
|
-
onOpenChange={(val) => {
|
|
322
|
-
if (val) {
|
|
323
|
-
emit.onFocus()
|
|
324
|
-
} else {
|
|
325
|
-
emit.onBlur()
|
|
326
|
-
}
|
|
327
|
-
onOpenChange?.(val)
|
|
328
|
-
}}
|
|
329
|
-
{disabled}
|
|
330
|
-
{required}
|
|
331
|
-
items={bitsItems}
|
|
332
|
-
{value}
|
|
333
|
-
onValueChange={(val) => {
|
|
334
|
-
value = val
|
|
335
|
-
emit.onChange()
|
|
336
|
-
}}
|
|
337
|
-
>
|
|
353
|
+
{#snippet rootChildren()}
|
|
338
354
|
<div bind:this={ref} class={rootClass}>
|
|
339
355
|
{#if leadingSlot}
|
|
340
356
|
<span class={leadingClass}>
|
|
@@ -361,7 +377,13 @@
|
|
|
361
377
|
aria-invalid={resolvedHighlight ? true : undefined}
|
|
362
378
|
class={baseClass}
|
|
363
379
|
>
|
|
364
|
-
{#if
|
|
380
|
+
{#if selectedSlot && hasSelection}
|
|
381
|
+
{@render selectedSlot({
|
|
382
|
+
items: selectedItems,
|
|
383
|
+
remove: removeValue,
|
|
384
|
+
clear: clearSelection
|
|
385
|
+
})}
|
|
386
|
+
{:else if hasSelection && displayLabel}
|
|
365
387
|
<span class={valueClass}>{displayLabel}</span>
|
|
366
388
|
{:else if placeholder}
|
|
367
389
|
<span class={placeholderClass}>{placeholder}</span>
|
|
@@ -386,4 +408,52 @@
|
|
|
386
408
|
{:else}
|
|
387
409
|
{@render selectContentEl()}
|
|
388
410
|
{/if}
|
|
389
|
-
|
|
411
|
+
{/snippet}
|
|
412
|
+
|
|
413
|
+
{#if multiple}
|
|
414
|
+
<Select.Root
|
|
415
|
+
type="multiple"
|
|
416
|
+
bind:open
|
|
417
|
+
onOpenChange={(val) => {
|
|
418
|
+
if (val) {
|
|
419
|
+
emit.onFocus()
|
|
420
|
+
} else {
|
|
421
|
+
emit.onBlur()
|
|
422
|
+
}
|
|
423
|
+
onOpenChange?.(val)
|
|
424
|
+
}}
|
|
425
|
+
{disabled}
|
|
426
|
+
{required}
|
|
427
|
+
items={bitsItems}
|
|
428
|
+
value={selectedValues}
|
|
429
|
+
onValueChange={(val) => {
|
|
430
|
+
value = val
|
|
431
|
+
emit.onChange()
|
|
432
|
+
}}
|
|
433
|
+
>
|
|
434
|
+
{@render rootChildren()}
|
|
435
|
+
</Select.Root>
|
|
436
|
+
{:else}
|
|
437
|
+
<Select.Root
|
|
438
|
+
type="single"
|
|
439
|
+
bind:open
|
|
440
|
+
onOpenChange={(val) => {
|
|
441
|
+
if (val) {
|
|
442
|
+
emit.onFocus()
|
|
443
|
+
} else {
|
|
444
|
+
emit.onBlur()
|
|
445
|
+
}
|
|
446
|
+
onOpenChange?.(val)
|
|
447
|
+
}}
|
|
448
|
+
{disabled}
|
|
449
|
+
{required}
|
|
450
|
+
items={bitsItems}
|
|
451
|
+
value={selectedValues[0] ?? ''}
|
|
452
|
+
onValueChange={(val) => {
|
|
453
|
+
value = val
|
|
454
|
+
emit.onChange()
|
|
455
|
+
}}
|
|
456
|
+
>
|
|
457
|
+
{@render rootChildren()}
|
|
458
|
+
</Select.Root>
|
|
459
|
+
{/if}
|
|
@@ -3,6 +3,12 @@ import type { ClassNameValue } from 'tailwind-merge';
|
|
|
3
3
|
import type { SelectVariantProps, SelectSlots } from './select.variants.js';
|
|
4
4
|
import type { AvatarProps } from '../Avatar/avatar.types.js';
|
|
5
5
|
import type { SelectRootPropsWithoutHTML, SelectContentPropsWithoutHTML } from 'bits-ui';
|
|
6
|
+
/**
|
|
7
|
+
* The value shape for the Select.
|
|
8
|
+
* - When `multiple` is `false` (or omitted), the value is a single string (or undefined).
|
|
9
|
+
* - When `multiple` is `true`, the value is an array of strings.
|
|
10
|
+
*/
|
|
11
|
+
export type SelectValue<Multiple extends boolean = false> = Multiple extends true ? string[] : string | undefined;
|
|
6
12
|
/**
|
|
7
13
|
* A single selectable option within the Select.
|
|
8
14
|
*/
|
|
@@ -67,6 +73,17 @@ export interface SelectItemSlotProps {
|
|
|
67
73
|
/** Whether the item is currently selected */
|
|
68
74
|
selected?: boolean;
|
|
69
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Props passed to the `selected` snippet when `multiple` is true.
|
|
78
|
+
*/
|
|
79
|
+
export interface SelectSelectedSlotProps {
|
|
80
|
+
/** The full list of currently selected items resolved from values. */
|
|
81
|
+
items: SelectItem[];
|
|
82
|
+
/** Remove a value from the current selection. */
|
|
83
|
+
remove: (value: string) => void;
|
|
84
|
+
/** Clear all selected values. */
|
|
85
|
+
clear: () => void;
|
|
86
|
+
}
|
|
70
87
|
type RootProps = Pick<SelectRootPropsWithoutHTML, 'open' | 'onOpenChange' | 'name' | 'required' | 'disabled'>;
|
|
71
88
|
type ContentProps = Pick<SelectContentPropsWithoutHTML, 'side' | 'sideOffset' | 'align' | 'alignOffset' | 'avoidCollisions' | 'collisionBoundary' | 'collisionPadding' | 'onEscapeKeydown' | 'onInteractOutside' | 'forceMount' | 'loop'>;
|
|
72
89
|
/**
|
|
@@ -92,12 +109,31 @@ export interface SelectProps extends RootProps, ContentProps {
|
|
|
92
109
|
ref?: HTMLElement | null;
|
|
93
110
|
/**
|
|
94
111
|
* The currently selected value. Supports two-way binding with `bind:value`.
|
|
112
|
+
*
|
|
113
|
+
* - When `multiple` is `false`/omitted, this is a `string`.
|
|
114
|
+
* - When `multiple` is `true`, this is a `string[]`.
|
|
95
115
|
*/
|
|
96
|
-
value?: string;
|
|
116
|
+
value?: string | string[];
|
|
97
117
|
/**
|
|
98
118
|
* The default selected value when uncontrolled.
|
|
119
|
+
*
|
|
120
|
+
* - When `multiple` is `false`/omitted, this is a `string`.
|
|
121
|
+
* - When `multiple` is `true`, this is a `string[]`.
|
|
122
|
+
*/
|
|
123
|
+
defaultValue?: string | string[];
|
|
124
|
+
/**
|
|
125
|
+
* Whether multiple items can be selected at once.
|
|
126
|
+
* When `true`, `value` becomes a `string[]` and the trigger displays
|
|
127
|
+
* a comma-separated list of selected labels by default.
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
130
|
+
multiple?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Separator used to join selected labels in the trigger when `multiple` is `true`.
|
|
133
|
+
* Ignored when the `selected` snippet is provided.
|
|
134
|
+
* @default ', '
|
|
99
135
|
*/
|
|
100
|
-
|
|
136
|
+
separator?: string;
|
|
101
137
|
/**
|
|
102
138
|
* Array of items to display in the select dropdown.
|
|
103
139
|
*/
|
|
@@ -194,6 +230,12 @@ export interface SelectProps extends RootProps, ContentProps {
|
|
|
194
230
|
* Takes precedence over `trailingIcon`.
|
|
195
231
|
*/
|
|
196
232
|
trailingSlot?: Snippet;
|
|
233
|
+
/**
|
|
234
|
+
* Custom rendering for the selected value(s) displayed in the trigger.
|
|
235
|
+
* Useful in `multiple` mode to render chips/tags instead of the default
|
|
236
|
+
* comma-separated label list.
|
|
237
|
+
*/
|
|
238
|
+
selected?: Snippet<[SelectSelectedSlotProps]>;
|
|
197
239
|
/**
|
|
198
240
|
* Custom snippet for rendering individual items in the dropdown.
|
|
199
241
|
* When provided, replaces the default item rendering.
|