sv5ui 1.6.0 → 1.7.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/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/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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +6 -1
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
} from './select-menu.types.js'
|
|
7
7
|
|
|
8
8
|
export type Props = SelectMenuProps
|
|
9
|
+
|
|
10
|
+
const CREATE_ITEM_VALUE = '@@sv5ui/select-menu/create-item'
|
|
9
11
|
</script>
|
|
10
12
|
|
|
11
13
|
<script lang="ts">
|
|
@@ -37,6 +39,8 @@
|
|
|
37
39
|
name,
|
|
38
40
|
required = false,
|
|
39
41
|
disabled = false,
|
|
42
|
+
multiple = false,
|
|
43
|
+
separator = ', ',
|
|
40
44
|
ui,
|
|
41
45
|
id,
|
|
42
46
|
color = config.defaultVariants.color,
|
|
@@ -53,6 +57,10 @@
|
|
|
53
57
|
filterFields = ['label', 'value'] as string[],
|
|
54
58
|
ignoreFilter = false,
|
|
55
59
|
emptyText = 'No results found.',
|
|
60
|
+
createItem = false,
|
|
61
|
+
createItemLabel = (value: string) => `Create "${value}"`,
|
|
62
|
+
createItemIcon,
|
|
63
|
+
onCreate,
|
|
56
64
|
transition = config.defaultVariants.transition ?? true,
|
|
57
65
|
portal = true,
|
|
58
66
|
side = config.defaultVariants.side ?? 'bottom',
|
|
@@ -72,6 +80,7 @@
|
|
|
72
80
|
itemLeading,
|
|
73
81
|
itemLabel: itemLabelSlot,
|
|
74
82
|
itemTrailing,
|
|
83
|
+
selected: selectedSlot,
|
|
75
84
|
empty: emptySlot,
|
|
76
85
|
content: contentSlot
|
|
77
86
|
}: Props = $props()
|
|
@@ -113,25 +122,70 @@
|
|
|
113
122
|
: `${formFieldContext.ariaId}-description ${formFieldContext.ariaId}-help`
|
|
114
123
|
)
|
|
115
124
|
|
|
125
|
+
// ---- Created items (internal state for createItem) ----
|
|
126
|
+
let createdItems = $state<SelectMenuItem[]>([])
|
|
127
|
+
|
|
128
|
+
const combinedItems = $derived.by(() => {
|
|
129
|
+
const propValues = new Set(
|
|
130
|
+
(items as SelectMenuItemType[])
|
|
131
|
+
.filter((i): i is SelectMenuItem => !('type' in i))
|
|
132
|
+
.map((i) => i.value)
|
|
133
|
+
)
|
|
134
|
+
const extras = createdItems.filter((c) => !propValues.has(c.value))
|
|
135
|
+
return [...(items as SelectMenuItemType[]), ...extras]
|
|
136
|
+
})
|
|
137
|
+
|
|
116
138
|
// ---- Items lookup (O(1) via Map) ----
|
|
117
139
|
const itemsMap = $derived(
|
|
118
140
|
new Map(
|
|
119
|
-
|
|
141
|
+
combinedItems
|
|
120
142
|
.filter((i): i is SelectMenuItem => !('type' in i))
|
|
121
143
|
.map((i) => [i.value, i])
|
|
122
144
|
)
|
|
123
145
|
)
|
|
124
146
|
|
|
125
|
-
|
|
126
|
-
const
|
|
147
|
+
// ---- Selection (single + multiple) ----
|
|
148
|
+
const selectedValues = $derived(
|
|
149
|
+
multiple
|
|
150
|
+
? Array.isArray(value)
|
|
151
|
+
? (value as string[])
|
|
152
|
+
: []
|
|
153
|
+
: typeof value === 'string' && value !== ''
|
|
154
|
+
? [value]
|
|
155
|
+
: []
|
|
156
|
+
)
|
|
157
|
+
const selectedItems = $derived(
|
|
158
|
+
selectedValues
|
|
159
|
+
.map((v) => itemsMap.get(v))
|
|
160
|
+
.filter((i): i is SelectMenuItem => i !== undefined)
|
|
161
|
+
)
|
|
162
|
+
const hasSelection = $derived(selectedValues.length > 0)
|
|
163
|
+
const singleSelectedItem = $derived(multiple ? undefined : selectedItems[0])
|
|
164
|
+
const displayLabel = $derived(
|
|
165
|
+
multiple
|
|
166
|
+
? selectedItems.map((i) => i.label ?? i.value).join(separator)
|
|
167
|
+
: (singleSelectedItem?.label ?? singleSelectedItem?.value ?? '')
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
function removeValue(val: string) {
|
|
171
|
+
if (!multiple) return
|
|
172
|
+
value = selectedValues.filter((v) => v !== val)
|
|
173
|
+
emit.onChange()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function clearSelection() {
|
|
177
|
+
if (!multiple) return
|
|
178
|
+
value = []
|
|
179
|
+
emit.onChange()
|
|
180
|
+
}
|
|
127
181
|
|
|
128
182
|
// ---- Search & filtering ----
|
|
129
183
|
let searchTerm = $state('')
|
|
130
184
|
|
|
131
185
|
const filteredItems = $derived(
|
|
132
186
|
ignoreFilter || !searchTerm.trim()
|
|
133
|
-
?
|
|
134
|
-
:
|
|
187
|
+
? combinedItems
|
|
188
|
+
: combinedItems.filter((item) => {
|
|
135
189
|
if ('type' in item) return true
|
|
136
190
|
const query = searchTerm.toLowerCase()
|
|
137
191
|
return filterFields.some((field) => {
|
|
@@ -143,9 +197,73 @@
|
|
|
143
197
|
|
|
144
198
|
const hasFilteredSelectItems = $derived(filteredItems.some((item) => !('type' in item)))
|
|
145
199
|
|
|
200
|
+
// ---- Create item ----
|
|
201
|
+
const trimmedSearch = $derived(searchTerm.trim())
|
|
202
|
+
const exactMatchExists = $derived.by(() => {
|
|
203
|
+
if (!trimmedSearch) return false
|
|
204
|
+
const query = trimmedSearch.toLowerCase()
|
|
205
|
+
for (const i of combinedItems) {
|
|
206
|
+
if ('type' in i) continue
|
|
207
|
+
if (i.value.toLowerCase() === query || (i.label ?? i.value).toLowerCase() === query) {
|
|
208
|
+
return true
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return false
|
|
212
|
+
})
|
|
213
|
+
const showCreateItem = $derived.by(() => {
|
|
214
|
+
if (!createItem) return false
|
|
215
|
+
if (!trimmedSearch) return false
|
|
216
|
+
const mode = createItem === true ? 'lazy' : createItem
|
|
217
|
+
if (mode === 'always') return true
|
|
218
|
+
return !exactMatchExists
|
|
219
|
+
})
|
|
220
|
+
const resolvedCreateLabel = $derived(
|
|
221
|
+
typeof createItemLabel === 'function' ? createItemLabel(trimmedSearch) : createItemLabel
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
function findItemByCaseInsensitive(query: string): SelectMenuItem | undefined {
|
|
225
|
+
const q = query.toLowerCase()
|
|
226
|
+
for (const it of itemsMap.values()) {
|
|
227
|
+
if (it.value.toLowerCase() === q || (it.label ?? it.value).toLowerCase() === q) {
|
|
228
|
+
return it
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return undefined
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function selectValue(val: string) {
|
|
235
|
+
if (multiple) {
|
|
236
|
+
if (!selectedValues.includes(val)) {
|
|
237
|
+
value = [...selectedValues, val]
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
value = val
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function handleCreate() {
|
|
245
|
+
if (!showCreateItem) return
|
|
246
|
+
const newValue = trimmedSearch
|
|
247
|
+
if (!newValue) return
|
|
248
|
+
|
|
249
|
+
const existing = findItemByCaseInsensitive(newValue)
|
|
250
|
+
if (existing) {
|
|
251
|
+
selectValue(existing.value)
|
|
252
|
+
} else {
|
|
253
|
+
createdItems = [...createdItems, { value: newValue, label: newValue }]
|
|
254
|
+
selectValue(newValue)
|
|
255
|
+
onCreate?.(newValue)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
emit.onChange()
|
|
259
|
+
searchTerm = ''
|
|
260
|
+
}
|
|
261
|
+
|
|
146
262
|
// ---- Leading / trailing ----
|
|
147
|
-
const displayAvatar = $derived(
|
|
148
|
-
const displayIcon = $derived(
|
|
263
|
+
const displayAvatar = $derived(multiple ? avatar : (singleSelectedItem?.avatar ?? avatar))
|
|
264
|
+
const displayIcon = $derived(
|
|
265
|
+
multiple ? (leadingIcon ?? icon) : (singleSelectedItem?.icon ?? leadingIcon ?? icon)
|
|
266
|
+
)
|
|
149
267
|
const isLeading = $derived(!!leadingSlot || !!displayAvatar || !!displayIcon)
|
|
150
268
|
const leadingIconName = $derived(
|
|
151
269
|
loading && isLeading ? loadingIcon : !displayAvatar ? displayIcon : undefined
|
|
@@ -219,6 +337,17 @@
|
|
|
219
337
|
variantSlots.separator({ class: [config.slots.separator, ui?.separator] })
|
|
220
338
|
)
|
|
221
339
|
const emptyClass = $derived(variantSlots.empty({ class: [config.slots.empty, ui?.empty] }))
|
|
340
|
+
const createItemClass = $derived(
|
|
341
|
+
variantSlots.createItem({ class: [config.slots.createItem, ui?.createItem] })
|
|
342
|
+
)
|
|
343
|
+
const createItemIconClass = $derived(
|
|
344
|
+
variantSlots.createItemIcon({ class: [config.slots.createItemIcon, ui?.createItemIcon] })
|
|
345
|
+
)
|
|
346
|
+
const createItemLabelClass = $derived(
|
|
347
|
+
variantSlots.createItemLabel({
|
|
348
|
+
class: [config.slots.createItemLabel, ui?.createItemLabel]
|
|
349
|
+
})
|
|
350
|
+
)
|
|
222
351
|
|
|
223
352
|
// ---- Item classes ----
|
|
224
353
|
const itemClass = $derived(variantSlots.item({ class: [config.slots.item, ui?.item] }))
|
|
@@ -267,7 +396,7 @@
|
|
|
267
396
|
</script>
|
|
268
397
|
|
|
269
398
|
{#snippet renderItem(item: SelectMenuItem, index: number)}
|
|
270
|
-
{@const isSelected =
|
|
399
|
+
{@const isSelected = selectedValues.includes(item.value)}
|
|
271
400
|
<Combobox.Item
|
|
272
401
|
value={item.value}
|
|
273
402
|
label={item.label ?? item.value}
|
|
@@ -320,6 +449,12 @@
|
|
|
320
449
|
placeholder={searchPlaceholder}
|
|
321
450
|
value={searchTerm}
|
|
322
451
|
oninput={(e) => (searchTerm = (e.currentTarget as HTMLInputElement).value)}
|
|
452
|
+
onkeydown={(e: KeyboardEvent) => {
|
|
453
|
+
if (e.key !== 'Enter') return
|
|
454
|
+
if (!showCreateItem) return
|
|
455
|
+
e.preventDefault()
|
|
456
|
+
handleCreate()
|
|
457
|
+
}}
|
|
323
458
|
variant="none"
|
|
324
459
|
size={resolvedSize}
|
|
325
460
|
class={inputClass}
|
|
@@ -343,7 +478,7 @@
|
|
|
343
478
|
{@render itemSlot({
|
|
344
479
|
item: selectItem,
|
|
345
480
|
index,
|
|
346
|
-
selected:
|
|
481
|
+
selected: selectedValues.includes(selectItem.value)
|
|
347
482
|
})}
|
|
348
483
|
{:else}
|
|
349
484
|
{@render renderItem(selectItem, index)}
|
|
@@ -351,31 +486,33 @@
|
|
|
351
486
|
{/if}
|
|
352
487
|
{/each}
|
|
353
488
|
|
|
354
|
-
{#if !hasFilteredSelectItems}
|
|
489
|
+
{#if !hasFilteredSelectItems && !showCreateItem}
|
|
355
490
|
{#if emptySlot}
|
|
356
491
|
{@render emptySlot({ searchTerm })}
|
|
357
492
|
{:else}
|
|
358
493
|
<div class={emptyClass}>{emptyText}</div>
|
|
359
494
|
{/if}
|
|
360
495
|
{/if}
|
|
496
|
+
|
|
497
|
+
{#if showCreateItem}
|
|
498
|
+
<Combobox.Item
|
|
499
|
+
value={CREATE_ITEM_VALUE}
|
|
500
|
+
label={resolvedCreateLabel}
|
|
501
|
+
{disabled}
|
|
502
|
+
class={createItemClass}
|
|
503
|
+
>
|
|
504
|
+
{#if createItemIcon}
|
|
505
|
+
<Icon name={createItemIcon} class={createItemIconClass} />
|
|
506
|
+
{/if}
|
|
507
|
+
<span class={createItemLabelClass}>{resolvedCreateLabel}</span>
|
|
508
|
+
</Combobox.Item>
|
|
509
|
+
{/if}
|
|
361
510
|
</div>
|
|
362
511
|
{/if}
|
|
363
512
|
</Combobox.Content>
|
|
364
513
|
{/snippet}
|
|
365
514
|
|
|
366
|
-
|
|
367
|
-
type="single"
|
|
368
|
-
bind:open
|
|
369
|
-
onOpenChange={onUpdateOpen}
|
|
370
|
-
{disabled}
|
|
371
|
-
{required}
|
|
372
|
-
{value}
|
|
373
|
-
onValueChange={(val) => {
|
|
374
|
-
value = val
|
|
375
|
-
emit.onChange()
|
|
376
|
-
}}
|
|
377
|
-
name={resolvedName}
|
|
378
|
-
>
|
|
515
|
+
{#snippet rootChildren()}
|
|
379
516
|
<div bind:this={ref} class={rootClass}>
|
|
380
517
|
{#if leadingSlot}
|
|
381
518
|
<span class={leadingClass}>
|
|
@@ -407,7 +544,13 @@
|
|
|
407
544
|
aria-invalid={resolvedHighlight ? true : undefined}
|
|
408
545
|
class={baseClass}
|
|
409
546
|
>
|
|
410
|
-
{#if
|
|
547
|
+
{#if selectedSlot && hasSelection}
|
|
548
|
+
{@render selectedSlot({
|
|
549
|
+
items: selectedItems,
|
|
550
|
+
remove: removeValue,
|
|
551
|
+
clear: clearSelection
|
|
552
|
+
})}
|
|
553
|
+
{:else if hasSelection && displayLabel}
|
|
411
554
|
<span class={valueClass}>{displayLabel}</span>
|
|
412
555
|
{:else if placeholder}
|
|
413
556
|
<span class={placeholderClass}>{placeholder}</span>
|
|
@@ -432,4 +575,46 @@
|
|
|
432
575
|
{:else}
|
|
433
576
|
{@render contentEl()}
|
|
434
577
|
{/if}
|
|
435
|
-
|
|
578
|
+
{/snippet}
|
|
579
|
+
|
|
580
|
+
{#if multiple}
|
|
581
|
+
<Combobox.Root
|
|
582
|
+
type="multiple"
|
|
583
|
+
bind:open
|
|
584
|
+
onOpenChange={onUpdateOpen}
|
|
585
|
+
{disabled}
|
|
586
|
+
{required}
|
|
587
|
+
value={selectedValues}
|
|
588
|
+
onValueChange={(val) => {
|
|
589
|
+
if (Array.isArray(val) && val.includes(CREATE_ITEM_VALUE)) {
|
|
590
|
+
handleCreate()
|
|
591
|
+
return
|
|
592
|
+
}
|
|
593
|
+
value = val
|
|
594
|
+
emit.onChange()
|
|
595
|
+
}}
|
|
596
|
+
name={resolvedName}
|
|
597
|
+
>
|
|
598
|
+
{@render rootChildren()}
|
|
599
|
+
</Combobox.Root>
|
|
600
|
+
{:else}
|
|
601
|
+
<Combobox.Root
|
|
602
|
+
type="single"
|
|
603
|
+
bind:open
|
|
604
|
+
onOpenChange={onUpdateOpen}
|
|
605
|
+
{disabled}
|
|
606
|
+
{required}
|
|
607
|
+
value={selectedValues[0] ?? ''}
|
|
608
|
+
onValueChange={(val) => {
|
|
609
|
+
if (val === CREATE_ITEM_VALUE) {
|
|
610
|
+
handleCreate()
|
|
611
|
+
return
|
|
612
|
+
}
|
|
613
|
+
value = val
|
|
614
|
+
emit.onChange()
|
|
615
|
+
}}
|
|
616
|
+
name={resolvedName}
|
|
617
|
+
>
|
|
618
|
+
{@render rootChildren()}
|
|
619
|
+
</Combobox.Root>
|
|
620
|
+
{/if}
|
|
@@ -67,6 +67,17 @@ export interface SelectMenuItemSlotProps {
|
|
|
67
67
|
/** Whether the item is currently selected */
|
|
68
68
|
selected?: boolean;
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Props passed to the `selected` snippet when `multiple` is true.
|
|
72
|
+
*/
|
|
73
|
+
export interface SelectMenuSelectedSlotProps {
|
|
74
|
+
/** The full list of currently selected items resolved from values. */
|
|
75
|
+
items: SelectMenuItem[];
|
|
76
|
+
/** Remove a value from the current selection. */
|
|
77
|
+
remove: (value: string) => void;
|
|
78
|
+
/** Clear all selected values. */
|
|
79
|
+
clear: () => void;
|
|
80
|
+
}
|
|
70
81
|
type ContentProps = Pick<ComboboxContentPropsWithoutHTML, 'side' | 'sideOffset' | 'align' | 'alignOffset' | 'avoidCollisions' | 'collisionBoundary' | 'collisionPadding' | 'onEscapeKeydown' | 'onInteractOutside' | 'forceMount'>;
|
|
71
82
|
/**
|
|
72
83
|
* Props for the SelectMenu component.
|
|
@@ -83,8 +94,25 @@ export interface SelectMenuProps extends ContentProps {
|
|
|
83
94
|
ref?: HTMLElement | null;
|
|
84
95
|
/**
|
|
85
96
|
* The currently selected value. Supports two-way binding with `bind:value`.
|
|
97
|
+
*
|
|
98
|
+
* - When `multiple` is `false`/omitted, this is a `string`.
|
|
99
|
+
* - When `multiple` is `true`, this is a `string[]`.
|
|
100
|
+
*/
|
|
101
|
+
value?: string | string[];
|
|
102
|
+
/**
|
|
103
|
+
* Whether multiple items can be selected at once.
|
|
104
|
+
* When `true`, `value` becomes a `string[]` and the dropdown stays open
|
|
105
|
+
* after each selection. The trigger displays a comma-separated list of
|
|
106
|
+
* selected labels by default; use the `selected` snippet for chips/tags.
|
|
107
|
+
* @default false
|
|
108
|
+
*/
|
|
109
|
+
multiple?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Separator used to join selected labels in the trigger when `multiple` is `true`.
|
|
112
|
+
* Ignored when the `selected` snippet is provided.
|
|
113
|
+
* @default ', '
|
|
86
114
|
*/
|
|
87
|
-
|
|
115
|
+
separator?: string;
|
|
88
116
|
/**
|
|
89
117
|
* Whether the dropdown is open. Supports two-way binding with `bind:open`.
|
|
90
118
|
* @default false
|
|
@@ -192,6 +220,33 @@ export interface SelectMenuProps extends ContentProps {
|
|
|
192
220
|
* @default 'No results found.'
|
|
193
221
|
*/
|
|
194
222
|
emptyText?: string;
|
|
223
|
+
/**
|
|
224
|
+
* Allow the user to create a new item by typing in the search input.
|
|
225
|
+
* - `false` (default): the feature is disabled.
|
|
226
|
+
* - `true` / `'lazy'`: the create option only appears when no existing item
|
|
227
|
+
* matches the current search term (case-insensitive on `value` or `label`).
|
|
228
|
+
* - `'always'`: the create option is shown whenever the search term is
|
|
229
|
+
* non-empty, regardless of existing matches.
|
|
230
|
+
* @default false
|
|
231
|
+
*/
|
|
232
|
+
createItem?: boolean | 'always' | 'lazy';
|
|
233
|
+
/**
|
|
234
|
+
* Label rendered on the "create" option. Receives the trimmed search term.
|
|
235
|
+
* @default (value) => `Create "${value}"`
|
|
236
|
+
*/
|
|
237
|
+
createItemLabel?: string | ((value: string) => string);
|
|
238
|
+
/**
|
|
239
|
+
* Icon shown before the create option label. Pass `false` (or omit) to
|
|
240
|
+
* render no icon.
|
|
241
|
+
* @default undefined
|
|
242
|
+
*/
|
|
243
|
+
createItemIcon?: string | false;
|
|
244
|
+
/**
|
|
245
|
+
* Called when the user picks the "create" option. The new value is also
|
|
246
|
+
* tracked internally so the trigger can render its label even if the
|
|
247
|
+
* caller does not push it into `items`.
|
|
248
|
+
*/
|
|
249
|
+
onCreate?: (value: string) => void;
|
|
195
250
|
/**
|
|
196
251
|
* Animate the dropdown on open and close.
|
|
197
252
|
* @default true
|
|
@@ -224,6 +279,12 @@ export interface SelectMenuProps extends ContentProps {
|
|
|
224
279
|
* Takes precedence over `trailingIcon`.
|
|
225
280
|
*/
|
|
226
281
|
trailingSlot?: Snippet;
|
|
282
|
+
/**
|
|
283
|
+
* Custom rendering for the selected value(s) displayed in the trigger.
|
|
284
|
+
* Useful in `multiple` mode to render chips/tags instead of the default
|
|
285
|
+
* comma-separated label list.
|
|
286
|
+
*/
|
|
287
|
+
selected?: Snippet<[SelectMenuSelectedSlotProps]>;
|
|
227
288
|
/**
|
|
228
289
|
* Custom snippet for rendering individual items in the dropdown.
|
|
229
290
|
* When provided, replaces the default item rendering.
|
|
@@ -3,18 +3,28 @@ export declare const selectMenuVariants: import("tailwind-variants").TVReturnTyp
|
|
|
3
3
|
size: {
|
|
4
4
|
xs: {
|
|
5
5
|
empty: string;
|
|
6
|
+
createItem: string;
|
|
7
|
+
createItemIcon: string;
|
|
6
8
|
};
|
|
7
9
|
sm: {
|
|
8
10
|
empty: string;
|
|
11
|
+
createItem: string;
|
|
12
|
+
createItemIcon: string;
|
|
9
13
|
};
|
|
10
14
|
md: {
|
|
11
15
|
empty: string;
|
|
16
|
+
createItem: string;
|
|
17
|
+
createItemIcon: string;
|
|
12
18
|
};
|
|
13
19
|
lg: {
|
|
14
20
|
empty: string;
|
|
21
|
+
createItem: string;
|
|
22
|
+
createItemIcon: string;
|
|
15
23
|
};
|
|
16
24
|
xl: {
|
|
17
25
|
empty: string;
|
|
26
|
+
createItem: string;
|
|
27
|
+
createItemIcon: string;
|
|
18
28
|
};
|
|
19
29
|
};
|
|
20
30
|
}, {
|
|
@@ -22,6 +32,9 @@ export declare const selectMenuVariants: import("tailwind-variants").TVReturnTyp
|
|
|
22
32
|
input: string;
|
|
23
33
|
viewport: string;
|
|
24
34
|
empty: string;
|
|
35
|
+
createItem: string[];
|
|
36
|
+
createItemIcon: string;
|
|
37
|
+
createItemLabel: string;
|
|
25
38
|
}, undefined, {
|
|
26
39
|
variant: {
|
|
27
40
|
outline: string;
|
|
@@ -594,18 +607,28 @@ export declare const selectMenuDefaults: {
|
|
|
594
607
|
size: {
|
|
595
608
|
xs: {
|
|
596
609
|
empty: string;
|
|
610
|
+
createItem: string;
|
|
611
|
+
createItemIcon: string;
|
|
597
612
|
};
|
|
598
613
|
sm: {
|
|
599
614
|
empty: string;
|
|
615
|
+
createItem: string;
|
|
616
|
+
createItemIcon: string;
|
|
600
617
|
};
|
|
601
618
|
md: {
|
|
602
619
|
empty: string;
|
|
620
|
+
createItem: string;
|
|
621
|
+
createItemIcon: string;
|
|
603
622
|
};
|
|
604
623
|
lg: {
|
|
605
624
|
empty: string;
|
|
625
|
+
createItem: string;
|
|
626
|
+
createItemIcon: string;
|
|
606
627
|
};
|
|
607
628
|
xl: {
|
|
608
629
|
empty: string;
|
|
630
|
+
createItem: string;
|
|
631
|
+
createItemIcon: string;
|
|
609
632
|
};
|
|
610
633
|
};
|
|
611
634
|
}, {
|
|
@@ -613,6 +636,9 @@ export declare const selectMenuDefaults: {
|
|
|
613
636
|
input: string;
|
|
614
637
|
viewport: string;
|
|
615
638
|
empty: string;
|
|
639
|
+
createItem: string[];
|
|
640
|
+
createItemIcon: string;
|
|
641
|
+
createItemLabel: string;
|
|
616
642
|
}, {
|
|
617
643
|
variant: {
|
|
618
644
|
outline: string;
|
|
@@ -15,15 +15,43 @@ export const selectMenuVariants = tv({
|
|
|
15
15
|
],
|
|
16
16
|
input: 'border-b border-outline-variant',
|
|
17
17
|
viewport: 'p-1 flex-1 overflow-y-auto scrollbar-thin',
|
|
18
|
-
empty: 'text-center text-on-surface-variant'
|
|
18
|
+
empty: 'text-center text-on-surface-variant',
|
|
19
|
+
createItem: [
|
|
20
|
+
'group relative flex items-center gap-2 w-full rounded-sm px-2 cursor-pointer select-none',
|
|
21
|
+
'focus:outline-none',
|
|
22
|
+
'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
23
|
+
'data-[highlighted]:bg-surface-container-highest'
|
|
24
|
+
],
|
|
25
|
+
createItemIcon: 'shrink-0 text-primary',
|
|
26
|
+
createItemLabel: 'flex-1 truncate text-on-surface'
|
|
19
27
|
},
|
|
20
28
|
variants: {
|
|
21
29
|
size: {
|
|
22
|
-
xs: {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
xs: {
|
|
31
|
+
empty: 'p-2 text-xs',
|
|
32
|
+
createItem: 'py-1 text-xs',
|
|
33
|
+
createItemIcon: 'size-3'
|
|
34
|
+
},
|
|
35
|
+
sm: {
|
|
36
|
+
empty: 'p-2.5 text-xs',
|
|
37
|
+
createItem: 'py-1.5 text-xs',
|
|
38
|
+
createItemIcon: 'size-3.5'
|
|
39
|
+
},
|
|
40
|
+
md: {
|
|
41
|
+
empty: 'p-2.5 text-sm',
|
|
42
|
+
createItem: 'py-1.5 text-sm',
|
|
43
|
+
createItemIcon: 'size-4'
|
|
44
|
+
},
|
|
45
|
+
lg: {
|
|
46
|
+
empty: 'p-3 text-sm',
|
|
47
|
+
createItem: 'py-2 text-sm',
|
|
48
|
+
createItemIcon: 'size-5'
|
|
49
|
+
},
|
|
50
|
+
xl: {
|
|
51
|
+
empty: 'p-3 text-base',
|
|
52
|
+
createItem: 'py-2.5 text-base',
|
|
53
|
+
createItemIcon: 'size-5'
|
|
54
|
+
}
|
|
27
55
|
}
|
|
28
56
|
}
|
|
29
57
|
});
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
description,
|
|
29
29
|
side = config.defaultVariants.side ?? 'right',
|
|
30
30
|
overlay: showOverlay = config.defaultVariants.overlay ?? true,
|
|
31
|
-
transition = config.defaultVariants.transition ??
|
|
31
|
+
transition = config.defaultVariants.transition ?? 'slide',
|
|
32
|
+
size = config.defaultVariants.size ?? 'md',
|
|
32
33
|
inset = config.defaultVariants.inset ?? false,
|
|
33
34
|
portal = true,
|
|
34
35
|
close: closeProp = true,
|
|
@@ -56,8 +57,18 @@
|
|
|
56
57
|
!!headerSlot || hasHeading || !!actionsSlot || showClose || !!closeSlot
|
|
57
58
|
)
|
|
58
59
|
|
|
60
|
+
const resolvedTransition = $derived(
|
|
61
|
+
transition === false ? 'none' : transition === true ? 'slide' : transition
|
|
62
|
+
)
|
|
63
|
+
|
|
59
64
|
const variantSlots = $derived(
|
|
60
|
-
slideoverVariants({
|
|
65
|
+
slideoverVariants({
|
|
66
|
+
transition: resolvedTransition,
|
|
67
|
+
side,
|
|
68
|
+
size,
|
|
69
|
+
inset,
|
|
70
|
+
overlay: showOverlay
|
|
71
|
+
})
|
|
61
72
|
)
|
|
62
73
|
|
|
63
74
|
const classes = $derived({
|
|
@@ -42,10 +42,21 @@ export interface SlideoverProps extends RootProps, ContentProps {
|
|
|
42
42
|
*/
|
|
43
43
|
overlay?: SlideoverVariantProps['overlay'];
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
45
|
+
* Controls the entrance/exit animation.
|
|
46
|
+
* - `'none'` / `false`: no animation
|
|
47
|
+
* - `'fade'`: overlay + content fade
|
|
48
|
+
* - `'slide'` / `true`: overlay fade + content slide-in from the chosen side (default)
|
|
49
|
+
* - `'scale'`: overlay fade + content scale-in
|
|
50
|
+
* @default 'slide'
|
|
51
|
+
*/
|
|
52
|
+
transition?: SlideoverVariantProps['transition'] | boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Controls the panel dimension along its axis. For `side="left"` /
|
|
55
|
+
* `side="right"` this sets `max-width`; for `side="top"` / `side="bottom"`
|
|
56
|
+
* this sets `max-height`.
|
|
57
|
+
* @default 'md'
|
|
47
58
|
*/
|
|
48
|
-
|
|
59
|
+
size?: SlideoverVariantProps['size'];
|
|
49
60
|
/**
|
|
50
61
|
* Display the slideover with inset margins and rounded corners.
|
|
51
62
|
* @default false
|