sv5ui 1.2.0 → 1.4.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.
Files changed (71) hide show
  1. package/README.md +16 -11
  2. package/dist/CheckboxGroup/CheckboxGroup.svelte +215 -0
  3. package/dist/CheckboxGroup/CheckboxGroup.svelte.d.ts +5 -0
  4. package/dist/CheckboxGroup/checkbox-group.types.d.ts +130 -0
  5. package/dist/CheckboxGroup/checkbox-group.types.js +1 -0
  6. package/dist/CheckboxGroup/checkbox-group.variants.d.ts +553 -0
  7. package/dist/CheckboxGroup/checkbox-group.variants.js +231 -0
  8. package/dist/CheckboxGroup/index.d.ts +2 -0
  9. package/dist/CheckboxGroup/index.js +1 -0
  10. package/dist/Collapsible/Collapsible.svelte +69 -0
  11. package/dist/Collapsible/Collapsible.svelte.d.ts +6 -0
  12. package/dist/Collapsible/CollapsibleTestWrapper.svelte +17 -0
  13. package/dist/Collapsible/CollapsibleTestWrapper.svelte.d.ts +4 -0
  14. package/dist/Collapsible/collapsible.types.d.ts +75 -0
  15. package/dist/Collapsible/collapsible.types.js +1 -0
  16. package/dist/Collapsible/collapsible.variants.d.ts +53 -0
  17. package/dist/Collapsible/collapsible.variants.js +21 -0
  18. package/dist/Collapsible/index.d.ts +2 -0
  19. package/dist/Collapsible/index.js +1 -0
  20. package/dist/Command/Command.svelte +183 -0
  21. package/dist/Command/Command.svelte.d.ts +6 -0
  22. package/dist/Command/CommandTestWrapper.svelte +13 -0
  23. package/dist/Command/CommandTestWrapper.svelte.d.ts +4 -0
  24. package/dist/Command/command.types.d.ts +98 -0
  25. package/dist/Command/command.types.js +1 -0
  26. package/dist/Command/command.variants.d.ts +226 -0
  27. package/dist/Command/command.variants.js +86 -0
  28. package/dist/Command/index.d.ts +2 -0
  29. package/dist/Command/index.js +1 -0
  30. package/dist/FileUpload/FileUpload.svelte +561 -0
  31. package/dist/FileUpload/FileUpload.svelte.d.ts +8 -0
  32. package/dist/FileUpload/file-upload.types.d.ts +164 -0
  33. package/dist/FileUpload/file-upload.types.js +1 -0
  34. package/dist/FileUpload/file-upload.variants.d.ts +397 -0
  35. package/dist/FileUpload/file-upload.variants.js +224 -0
  36. package/dist/FileUpload/index.d.ts +2 -0
  37. package/dist/FileUpload/index.js +1 -0
  38. package/dist/PinInput/PinInput.svelte +150 -0
  39. package/dist/PinInput/PinInput.svelte.d.ts +6 -0
  40. package/dist/PinInput/index.d.ts +2 -0
  41. package/dist/PinInput/index.js +1 -0
  42. package/dist/PinInput/pin-input.types.d.ts +99 -0
  43. package/dist/PinInput/pin-input.types.js +1 -0
  44. package/dist/PinInput/pin-input.variants.d.ts +303 -0
  45. package/dist/PinInput/pin-input.variants.js +196 -0
  46. package/dist/Select/select.variants.js +1 -1
  47. package/dist/SelectMenu/select-menu.variants.js +1 -1
  48. package/dist/Slider/Slider.svelte +135 -0
  49. package/dist/Slider/Slider.svelte.d.ts +6 -0
  50. package/dist/Slider/index.d.ts +2 -0
  51. package/dist/Slider/index.js +1 -0
  52. package/dist/Slider/slider.types.d.ts +55 -0
  53. package/dist/Slider/slider.types.js +1 -0
  54. package/dist/Slider/slider.variants.d.ts +383 -0
  55. package/dist/Slider/slider.variants.js +102 -0
  56. package/dist/Toast/Toaster.svelte +618 -0
  57. package/dist/Toast/Toaster.svelte.d.ts +5 -0
  58. package/dist/Toast/index.d.ts +4 -0
  59. package/dist/Toast/index.js +2 -0
  60. package/dist/Toast/toast.d.ts +38 -0
  61. package/dist/Toast/toast.js +73 -0
  62. package/dist/Toast/toast.types.d.ts +19 -0
  63. package/dist/Toast/toast.types.js +1 -0
  64. package/dist/Toast/toast.variants.d.ts +7 -0
  65. package/dist/Toast/toast.variants.js +5 -0
  66. package/dist/config.d.ts +5 -0
  67. package/dist/config.js +6 -1
  68. package/dist/index.d.ts +7 -0
  69. package/dist/index.js +7 -0
  70. package/dist/theme.css +36 -0
  71. package/package.json +2 -1
@@ -0,0 +1,231 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const checkboxGroupVariants = tv({
3
+ slots: {
4
+ root: '',
5
+ fieldset: 'flex m-0 p-0 border-0 min-w-0',
6
+ legend: 'w-full font-medium text-on-surface mb-1',
7
+ item: 'flex items-start',
8
+ container: 'flex items-center',
9
+ wrapper: '',
10
+ base: [
11
+ 'inline-flex items-center justify-center shrink-0 rounded-md border',
12
+ 'focus-visible:outline-2 focus-visible:outline-offset-2',
13
+ 'transition-colors duration-200',
14
+ 'border-outline-variant',
15
+ 'data-[state=unchecked]:bg-transparent'
16
+ ],
17
+ indicator: 'flex items-center justify-center',
18
+ icon: 'shrink-0 text-surface',
19
+ label: 'block font-medium text-on-surface',
20
+ description: 'text-on-surface-variant'
21
+ },
22
+ variants: {
23
+ color: {
24
+ primary: '',
25
+ secondary: '',
26
+ tertiary: '',
27
+ success: '',
28
+ warning: '',
29
+ error: '',
30
+ info: '',
31
+ surface: ''
32
+ },
33
+ size: {
34
+ xs: {
35
+ fieldset: 'gap-2',
36
+ legend: 'text-xs',
37
+ base: 'size-3.5 rounded',
38
+ container: 'h-4',
39
+ icon: 'size-2.5',
40
+ wrapper: 'text-xs'
41
+ },
42
+ sm: {
43
+ fieldset: 'gap-2.5',
44
+ legend: 'text-xs',
45
+ base: 'size-4 rounded',
46
+ container: 'h-4',
47
+ icon: 'size-3',
48
+ wrapper: 'text-xs'
49
+ },
50
+ md: {
51
+ fieldset: 'gap-3',
52
+ legend: 'text-sm',
53
+ base: 'size-4.5 rounded-md',
54
+ container: 'h-5',
55
+ icon: 'size-3.5',
56
+ wrapper: 'text-sm'
57
+ },
58
+ lg: {
59
+ fieldset: 'gap-3.5',
60
+ legend: 'text-sm',
61
+ base: 'size-5 rounded-md',
62
+ container: 'h-5',
63
+ icon: 'size-4',
64
+ wrapper: 'text-sm'
65
+ },
66
+ xl: {
67
+ fieldset: 'gap-4',
68
+ legend: 'text-base',
69
+ base: 'size-5.5 rounded-md',
70
+ container: 'h-6',
71
+ icon: 'size-4.5',
72
+ wrapper: 'text-base'
73
+ }
74
+ },
75
+ orientation: {
76
+ horizontal: {
77
+ fieldset: 'flex-row flex-wrap items-start'
78
+ },
79
+ vertical: {
80
+ fieldset: 'flex-col'
81
+ }
82
+ },
83
+ variant: {
84
+ list: '',
85
+ card: {
86
+ item: 'border border-outline-variant rounded-lg cursor-pointer select-none'
87
+ }
88
+ },
89
+ indicator: {
90
+ start: {
91
+ wrapper: 'ms-2'
92
+ },
93
+ end: {
94
+ item: 'flex-row-reverse',
95
+ wrapper: 'me-2'
96
+ },
97
+ hidden: {
98
+ container: 'sr-only'
99
+ }
100
+ },
101
+ loading: {
102
+ true: {
103
+ icon: 'animate-spin'
104
+ }
105
+ },
106
+ required: {
107
+ true: {
108
+ legend: "after:content-['*'] after:ms-0.5 after:text-error"
109
+ }
110
+ },
111
+ disabled: {
112
+ true: {
113
+ root: 'opacity-75',
114
+ base: 'cursor-not-allowed',
115
+ label: 'cursor-not-allowed',
116
+ description: 'cursor-not-allowed'
117
+ }
118
+ }
119
+ },
120
+ compoundVariants: [
121
+ // ========== COLOR (checked bg + border + focus ring) ==========
122
+ {
123
+ color: 'primary',
124
+ class: {
125
+ base: 'data-[state=checked]:bg-primary data-[state=checked]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:border-primary focus-visible:outline-primary'
126
+ }
127
+ },
128
+ {
129
+ color: 'secondary',
130
+ class: {
131
+ base: 'data-[state=checked]:bg-secondary data-[state=checked]:border-secondary data-[state=indeterminate]:bg-secondary data-[state=indeterminate]:border-secondary focus-visible:outline-secondary'
132
+ }
133
+ },
134
+ {
135
+ color: 'tertiary',
136
+ class: {
137
+ base: 'data-[state=checked]:bg-tertiary data-[state=checked]:border-tertiary data-[state=indeterminate]:bg-tertiary data-[state=indeterminate]:border-tertiary focus-visible:outline-tertiary'
138
+ }
139
+ },
140
+ {
141
+ color: 'success',
142
+ class: {
143
+ base: 'data-[state=checked]:bg-success data-[state=checked]:border-success data-[state=indeterminate]:bg-success data-[state=indeterminate]:border-success focus-visible:outline-success'
144
+ }
145
+ },
146
+ {
147
+ color: 'warning',
148
+ class: {
149
+ base: 'data-[state=checked]:bg-warning data-[state=checked]:border-warning data-[state=indeterminate]:bg-warning data-[state=indeterminate]:border-warning focus-visible:outline-warning'
150
+ }
151
+ },
152
+ {
153
+ color: 'error',
154
+ class: {
155
+ base: 'data-[state=checked]:bg-error data-[state=checked]:border-error data-[state=indeterminate]:bg-error data-[state=indeterminate]:border-error focus-visible:outline-error'
156
+ }
157
+ },
158
+ {
159
+ color: 'info',
160
+ class: {
161
+ base: 'data-[state=checked]:bg-info data-[state=checked]:border-info data-[state=indeterminate]:bg-info data-[state=indeterminate]:border-info focus-visible:outline-info'
162
+ }
163
+ },
164
+ {
165
+ color: 'surface',
166
+ class: {
167
+ base: 'data-[state=checked]:bg-on-surface data-[state=checked]:border-on-surface data-[state=indeterminate]:bg-on-surface data-[state=indeterminate]:border-on-surface focus-visible:outline-outline'
168
+ }
169
+ },
170
+ // ========== CARD × SIZE (padding) ==========
171
+ { variant: 'card', size: 'xs', class: { item: 'p-2' } },
172
+ { variant: 'card', size: 'sm', class: { item: 'p-2.5' } },
173
+ { variant: 'card', size: 'md', class: { item: 'p-3' } },
174
+ { variant: 'card', size: 'lg', class: { item: 'p-3.5' } },
175
+ { variant: 'card', size: 'xl', class: { item: 'p-4' } },
176
+ // ========== CARD × COLOR (checked border) ==========
177
+ {
178
+ variant: 'card',
179
+ color: 'primary',
180
+ class: { item: 'has-[[data-state=checked]]:border-primary' }
181
+ },
182
+ {
183
+ variant: 'card',
184
+ color: 'secondary',
185
+ class: { item: 'has-[[data-state=checked]]:border-secondary' }
186
+ },
187
+ {
188
+ variant: 'card',
189
+ color: 'tertiary',
190
+ class: { item: 'has-[[data-state=checked]]:border-tertiary' }
191
+ },
192
+ {
193
+ variant: 'card',
194
+ color: 'success',
195
+ class: { item: 'has-[[data-state=checked]]:border-success' }
196
+ },
197
+ {
198
+ variant: 'card',
199
+ color: 'warning',
200
+ class: { item: 'has-[[data-state=checked]]:border-warning' }
201
+ },
202
+ {
203
+ variant: 'card',
204
+ color: 'error',
205
+ class: { item: 'has-[[data-state=checked]]:border-error' }
206
+ },
207
+ {
208
+ variant: 'card',
209
+ color: 'info',
210
+ class: { item: 'has-[[data-state=checked]]:border-info' }
211
+ },
212
+ {
213
+ variant: 'card',
214
+ color: 'surface',
215
+ class: { item: 'has-[[data-state=checked]]:border-on-surface' }
216
+ },
217
+ // ========== CARD × DISABLED ==========
218
+ { variant: 'card', disabled: true, class: { item: 'cursor-not-allowed' } }
219
+ ],
220
+ defaultVariants: {
221
+ color: 'primary',
222
+ size: 'md',
223
+ variant: 'list',
224
+ indicator: 'start',
225
+ orientation: 'vertical'
226
+ }
227
+ });
228
+ export const checkboxGroupDefaults = {
229
+ defaultVariants: checkboxGroupVariants.defaultVariants,
230
+ slots: {}
231
+ };
@@ -0,0 +1,2 @@
1
+ export { default as CheckboxGroup } from './CheckboxGroup.svelte';
2
+ export type { CheckboxGroupProps, CheckboxGroupItem } from './checkbox-group.types.js';
@@ -0,0 +1 @@
1
+ export { default as CheckboxGroup } from './CheckboxGroup.svelte';
@@ -0,0 +1,69 @@
1
+ <script lang="ts" module>
2
+ import type { CollapsibleProps } from './collapsible.types.js'
3
+
4
+ export type Props = CollapsibleProps
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import { Collapsible } from 'bits-ui'
9
+ import { collapsibleVariants, collapsibleDefaults } from './collapsible.variants.js'
10
+ import { getComponentConfig } from '../config.js'
11
+
12
+ const config = getComponentConfig('collapsible', collapsibleDefaults)
13
+
14
+ let {
15
+ ref = $bindable(null),
16
+ open = $bindable(false),
17
+ onOpenChange,
18
+ onOpenChangeComplete,
19
+ disabled = false,
20
+ trigger: triggerSlot,
21
+ content: contentSlot,
22
+ children,
23
+ ui,
24
+ class: className,
25
+ ...restProps
26
+ }: Props = $props()
27
+
28
+ const slots = $derived(collapsibleVariants({ disabled }))
29
+ const classes = $derived.by(() => {
30
+ const u = ui ?? {}
31
+ return {
32
+ root: slots.root({ class: [config.slots.root, className, u.root] }),
33
+ content: slots.content({ class: [config.slots.content, u.content] })
34
+ }
35
+ })
36
+
37
+ function handleOpenChange(value: boolean) {
38
+ open = value
39
+ onOpenChange?.(value)
40
+ }
41
+ </script>
42
+
43
+ <Collapsible.Root
44
+ {...restProps}
45
+ bind:ref
46
+ {open}
47
+ onOpenChange={handleOpenChange}
48
+ {onOpenChangeComplete}
49
+ {disabled}
50
+ class={classes.root}
51
+ >
52
+ {#if triggerSlot}
53
+ <Collapsible.Trigger>
54
+ {#snippet child({ props })}
55
+ {@render triggerSlot({ open, props })}
56
+ {/snippet}
57
+ </Collapsible.Trigger>
58
+ {/if}
59
+
60
+ {#if contentSlot}
61
+ <Collapsible.Content class={classes.content}>
62
+ {@render contentSlot()}
63
+ </Collapsible.Content>
64
+ {/if}
65
+
66
+ {#if children}
67
+ {@render children()}
68
+ {/if}
69
+ </Collapsible.Root>
@@ -0,0 +1,6 @@
1
+ import type { CollapsibleProps } from './collapsible.types.js';
2
+ export type Props = CollapsibleProps;
3
+ import { Collapsible } from 'bits-ui';
4
+ declare const Collapsible: import("svelte").Component<CollapsibleProps, {}, "ref" | "open">;
5
+ type Collapsible = ReturnType<typeof Collapsible>;
6
+ export default Collapsible;
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ import Collapsible from './Collapsible.svelte'
3
+ import type { CollapsibleProps } from './collapsible.types.js'
4
+
5
+ let { ...props }: Omit<CollapsibleProps, 'trigger' | 'content' | 'children'> = $props()
6
+ </script>
7
+
8
+ <Collapsible {...props}>
9
+ {#snippet trigger({ open, props: triggerProps })}
10
+ <button type="button" {...triggerProps}>
11
+ {open ? 'Close' : 'Open'}
12
+ </button>
13
+ {/snippet}
14
+ {#snippet content()}
15
+ <div data-testid="collapsible-body">Collapsible content</div>
16
+ {/snippet}
17
+ </Collapsible>
@@ -0,0 +1,4 @@
1
+ import type { CollapsibleProps } from './collapsible.types.js';
2
+ declare const CollapsibleTestWrapper: import("svelte").Component<Omit<CollapsibleProps, "children" | "content" | "trigger">, {}, "">;
3
+ type CollapsibleTestWrapper = ReturnType<typeof CollapsibleTestWrapper>;
4
+ export default CollapsibleTestWrapper;
@@ -0,0 +1,75 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassNameValue } from 'tailwind-merge';
3
+ import type { CollapsibleRootPropsWithoutHTML } from 'bits-ui';
4
+ import type { CollapsibleSlots } from './collapsible.variants.js';
5
+ /**
6
+ * Props for the Collapsible component.
7
+ *
8
+ * Wraps bits-ui's Collapsible primitives with a themed, slot-based API.
9
+ *
10
+ * @example
11
+ * ```svelte
12
+ * <Collapsible>
13
+ * {#snippet trigger({ open })}
14
+ * <Button>{open ? 'Hide' : 'Show'}</Button>
15
+ * {/snippet}
16
+ * {#snippet content()}
17
+ * <p>Collapsible content here</p>
18
+ * {/snippet}
19
+ * </Collapsible>
20
+ * ```
21
+ *
22
+ * @see https://bits-ui.com/docs/components/collapsible
23
+ */
24
+ export interface CollapsibleProps extends Pick<CollapsibleRootPropsWithoutHTML, 'open' | 'onOpenChange' | 'onOpenChangeComplete' | 'disabled'> {
25
+ /**
26
+ * Bindable reference to the root DOM element.
27
+ */
28
+ ref?: HTMLElement | null;
29
+ /**
30
+ * Override classes for collapsible component slots.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * ui={{ root: 'border rounded-lg', content: 'p-4' }}
35
+ * ```
36
+ */
37
+ ui?: Partial<Record<CollapsibleSlots, ClassNameValue>>;
38
+ /**
39
+ * Additional CSS classes for the root element.
40
+ */
41
+ class?: ClassNameValue;
42
+ /**
43
+ * Snippet for the trigger element that toggles the collapsible.
44
+ * Receives `{ open }` to reflect the current state.
45
+ *
46
+ * @example
47
+ * ```svelte
48
+ * {#snippet trigger({ open })}
49
+ * <Button variant="ghost">
50
+ * {open ? 'Collapse' : 'Expand'}
51
+ * </Button>
52
+ * {/snippet}
53
+ * ```
54
+ */
55
+ trigger?: Snippet<[{
56
+ open: boolean;
57
+ props: Record<string, unknown>;
58
+ }]>;
59
+ /**
60
+ * Snippet for the collapsible content area.
61
+ *
62
+ * @example
63
+ * ```svelte
64
+ * {#snippet content()}
65
+ * <p>Hidden content revealed on expand.</p>
66
+ * {/snippet}
67
+ * ```
68
+ */
69
+ content?: Snippet;
70
+ /**
71
+ * Default slot children. If provided, rendered inside the root
72
+ * for full custom layouts using bits-ui primitives directly.
73
+ */
74
+ children?: Snippet;
75
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ import { type VariantProps } from 'tailwind-variants';
2
+ export declare const collapsibleVariants: import("tailwind-variants").TVReturnType<{
3
+ disabled: {
4
+ true: {
5
+ root: string;
6
+ };
7
+ };
8
+ }, {
9
+ root: string;
10
+ content: string;
11
+ }, undefined, {
12
+ disabled: {
13
+ true: {
14
+ root: string;
15
+ };
16
+ };
17
+ }, {
18
+ root: string;
19
+ content: string;
20
+ }, import("tailwind-variants").TVReturnType<{
21
+ disabled: {
22
+ true: {
23
+ root: string;
24
+ };
25
+ };
26
+ }, {
27
+ root: string;
28
+ content: string;
29
+ }, undefined, unknown, unknown, undefined>>;
30
+ export type CollapsibleVariantProps = VariantProps<typeof collapsibleVariants>;
31
+ export type CollapsibleSlots = keyof ReturnType<typeof collapsibleVariants>;
32
+ export declare const collapsibleDefaults: {
33
+ defaultVariants: import("tailwind-variants").TVDefaultVariants<{
34
+ disabled: {
35
+ true: {
36
+ root: string;
37
+ };
38
+ };
39
+ }, {
40
+ root: string;
41
+ content: string;
42
+ }, {
43
+ disabled: {
44
+ true: {
45
+ root: string;
46
+ };
47
+ };
48
+ }, {
49
+ root: string;
50
+ content: string;
51
+ }>;
52
+ slots: Partial<Record<CollapsibleSlots, string>>;
53
+ };
@@ -0,0 +1,21 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const collapsibleVariants = tv({
3
+ slots: {
4
+ root: '',
5
+ content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] overflow-hidden'
6
+ },
7
+ variants: {
8
+ disabled: {
9
+ true: {
10
+ root: 'opacity-75 cursor-not-allowed'
11
+ }
12
+ }
13
+ },
14
+ defaultVariants: {
15
+ disabled: false
16
+ }
17
+ });
18
+ export const collapsibleDefaults = {
19
+ defaultVariants: collapsibleVariants.defaultVariants,
20
+ slots: {}
21
+ };
@@ -0,0 +1,2 @@
1
+ export { default as Collapsible } from './Collapsible.svelte';
2
+ export type { CollapsibleProps } from './collapsible.types.js';
@@ -0,0 +1 @@
1
+ export { default as Collapsible } from './Collapsible.svelte';
@@ -0,0 +1,183 @@
1
+ <script lang="ts" module>
2
+ import type { CommandProps } from './command.types.js'
3
+
4
+ export type Props = CommandProps
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import { Command } from 'bits-ui'
9
+ import { commandVariants, commandDefaults } from './command.variants.js'
10
+ import { getComponentConfig, iconsDefaults } from '../config.js'
11
+ import Icon from '../Icon/Icon.svelte'
12
+
13
+ const config = getComponentConfig('command', commandDefaults)
14
+ const icons = getComponentConfig('icons', iconsDefaults)
15
+
16
+ let {
17
+ ref = $bindable(null),
18
+ value = $bindable(''),
19
+ search = $bindable(''),
20
+ onValueChange,
21
+ groups = [],
22
+ placeholder = 'Type a command or search...',
23
+ loading = false,
24
+ emptyText = 'No results found.',
25
+ icon,
26
+ label,
27
+ filter,
28
+ shouldFilter = true,
29
+ loop = false,
30
+ vimBindings = true,
31
+ size = config.defaultVariants.size,
32
+ item: itemSlot,
33
+ itemLeading: itemLeadingSlot,
34
+ itemLabel: itemLabelSlot,
35
+ itemTrailing: itemTrailingSlot,
36
+ empty: emptySlot,
37
+ footer: footerSlot,
38
+ ui,
39
+ class: className,
40
+ ...restProps
41
+ }: Props = $props()
42
+
43
+ const slots = $derived(commandVariants({ size }))
44
+ const classes = $derived.by(() => {
45
+ const u = ui ?? {}
46
+ return {
47
+ root: slots.root({ class: [config.slots.root, className, u.root] }),
48
+ inputWrapper: slots.inputWrapper({
49
+ class: [config.slots.inputWrapper, u.inputWrapper]
50
+ }),
51
+ inputIcon: slots.inputIcon({ class: [config.slots.inputIcon, u.inputIcon] }),
52
+ input: slots.input({ class: [config.slots.input, u.input] }),
53
+ list: slots.list({ class: [config.slots.list, u.list] }),
54
+ empty: slots.empty({ class: [config.slots.empty, u.empty] }),
55
+ loading: slots.loading({ class: [config.slots.loading, u.loading] }),
56
+ group: slots.group({ class: [config.slots.group, u.group] }),
57
+ groupHeading: slots.groupHeading({
58
+ class: [config.slots.groupHeading, u.groupHeading]
59
+ }),
60
+ groupItems: slots.groupItems({ class: [config.slots.groupItems, u.groupItems] }),
61
+ separator: slots.separator({ class: [config.slots.separator, u.separator] }),
62
+ item: slots.item({ class: [config.slots.item, u.item] }),
63
+ itemIcon: slots.itemIcon({ class: [config.slots.itemIcon, u.itemIcon] }),
64
+ itemWrapper: slots.itemWrapper({ class: [config.slots.itemWrapper, u.itemWrapper] }),
65
+ itemLabel: slots.itemLabel({ class: [config.slots.itemLabel, u.itemLabel] }),
66
+ itemDescription: slots.itemDescription({
67
+ class: [config.slots.itemDescription, u.itemDescription]
68
+ }),
69
+ itemTrailing: slots.itemTrailing({
70
+ class: [config.slots.itemTrailing, u.itemTrailing]
71
+ }),
72
+ footer: slots.footer({ class: [config.slots.footer, u.footer] })
73
+ }
74
+ })
75
+
76
+ function handleValueChange(v: string) {
77
+ value = v
78
+ onValueChange?.(v)
79
+ }
80
+ </script>
81
+
82
+ <Command.Root
83
+ {...restProps}
84
+ bind:ref
85
+ {value}
86
+ {label}
87
+ onValueChange={handleValueChange}
88
+ {filter}
89
+ {shouldFilter}
90
+ {loop}
91
+ {vimBindings}
92
+ class={classes.root}
93
+ >
94
+ <div class={classes.inputWrapper}>
95
+ <Icon name={icon ?? icons.search ?? 'lucide:search'} class={classes.inputIcon} />
96
+ <Command.Input bind:value={search} {placeholder} class={classes.input} />
97
+ </div>
98
+
99
+ <Command.List class={classes.list}>
100
+ {#if loading}
101
+ <Command.Loading class={classes.loading}>
102
+ <Icon name={icons.loading} class="mx-auto size-5 animate-spin" />
103
+ </Command.Loading>
104
+ {:else}
105
+ <Command.Empty class={classes.empty}>
106
+ {#if emptySlot}
107
+ {@render emptySlot({ search })}
108
+ {:else}
109
+ {emptyText}
110
+ {/if}
111
+ </Command.Empty>
112
+
113
+ {#each groups as group, gi (group.id)}
114
+ {#if gi > 0}
115
+ <Command.Separator class={classes.separator} />
116
+ {/if}
117
+
118
+ <Command.Group value={group.id} class={classes.group}>
119
+ {#if group.label}
120
+ <Command.GroupHeading class={classes.groupHeading}>
121
+ {group.label}
122
+ </Command.GroupHeading>
123
+ {/if}
124
+
125
+ <Command.GroupItems class={classes.groupItems}>
126
+ {#each group.items as cmdItem, i (cmdItem.value)}
127
+ {#if itemSlot}
128
+ {@render itemSlot({ item: cmdItem, index: i })}
129
+ {:else}
130
+ <Command.Item
131
+ value={cmdItem.value}
132
+ keywords={cmdItem.keywords}
133
+ disabled={cmdItem.disabled}
134
+ onSelect={cmdItem.onSelect}
135
+ class={cmdItem.class
136
+ ? slots.item({
137
+ class: [config.slots.item, ui?.item, cmdItem.class]
138
+ })
139
+ : classes.item}
140
+ >
141
+ {#if itemLeadingSlot}
142
+ {@render itemLeadingSlot({ item: cmdItem, index: i })}
143
+ {:else if cmdItem.icon}
144
+ <Icon name={cmdItem.icon} class={classes.itemIcon} />
145
+ {/if}
146
+
147
+ {#if itemLabelSlot}
148
+ {@render itemLabelSlot({ item: cmdItem, index: i })}
149
+ {:else}
150
+ <div class={classes.itemWrapper}>
151
+ {#if cmdItem.label}
152
+ <span class={classes.itemLabel}
153
+ >{cmdItem.label}</span
154
+ >
155
+ {/if}
156
+ {#if cmdItem.description}
157
+ <span class={classes.itemDescription}
158
+ >{cmdItem.description}</span
159
+ >
160
+ {/if}
161
+ </div>
162
+ {/if}
163
+
164
+ {#if itemTrailingSlot}
165
+ <span class={classes.itemTrailing}>
166
+ {@render itemTrailingSlot({ item: cmdItem, index: i })}
167
+ </span>
168
+ {/if}
169
+ </Command.Item>
170
+ {/if}
171
+ {/each}
172
+ </Command.GroupItems>
173
+ </Command.Group>
174
+ {/each}
175
+ {/if}
176
+ </Command.List>
177
+
178
+ {#if footerSlot}
179
+ <div class={classes.footer}>
180
+ {@render footerSlot()}
181
+ </div>
182
+ {/if}
183
+ </Command.Root>