sv5ui 1.3.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.
- package/README.md +16 -11
- package/dist/Collapsible/Collapsible.svelte +69 -0
- package/dist/Collapsible/Collapsible.svelte.d.ts +6 -0
- package/dist/Collapsible/CollapsibleTestWrapper.svelte +17 -0
- package/dist/Collapsible/CollapsibleTestWrapper.svelte.d.ts +4 -0
- package/dist/Collapsible/collapsible.types.d.ts +75 -0
- package/dist/Collapsible/collapsible.types.js +1 -0
- package/dist/Collapsible/collapsible.variants.d.ts +53 -0
- package/dist/Collapsible/collapsible.variants.js +21 -0
- package/dist/Collapsible/index.d.ts +2 -0
- package/dist/Collapsible/index.js +1 -0
- package/dist/Command/Command.svelte +183 -0
- package/dist/Command/Command.svelte.d.ts +6 -0
- package/dist/Command/CommandTestWrapper.svelte +13 -0
- package/dist/Command/CommandTestWrapper.svelte.d.ts +4 -0
- package/dist/Command/command.types.d.ts +98 -0
- package/dist/Command/command.types.js +1 -0
- package/dist/Command/command.variants.d.ts +226 -0
- package/dist/Command/command.variants.js +86 -0
- package/dist/Command/index.d.ts +2 -0
- package/dist/Command/index.js +1 -0
- package/dist/Select/select.variants.js +1 -1
- package/dist/SelectMenu/select-menu.variants.js +1 -1
- package/dist/Toast/Toaster.svelte +618 -0
- package/dist/Toast/Toaster.svelte.d.ts +5 -0
- package/dist/Toast/index.d.ts +4 -0
- package/dist/Toast/index.js +2 -0
- package/dist/Toast/toast.d.ts +38 -0
- package/dist/Toast/toast.js +73 -0
- package/dist/Toast/toast.types.d.ts +19 -0
- package/dist/Toast/toast.types.js +1 -0
- package/dist/Toast/toast.variants.d.ts +7 -0
- package/dist/Toast/toast.variants.js +5 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/theme.css +36 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -129,17 +129,22 @@ pnpm add sv5ui
|
|
|
129
129
|
|
|
130
130
|
### Form
|
|
131
131
|
|
|
132
|
-
| Component
|
|
133
|
-
|
|
|
134
|
-
| [**Input**](src/lib/Input)
|
|
135
|
-
| [**Textarea**](src/lib/Textarea)
|
|
136
|
-
| [**Select**](src/lib/Select)
|
|
137
|
-
| [**
|
|
138
|
-
| [**
|
|
139
|
-
| [**
|
|
140
|
-
| [**
|
|
141
|
-
| [**
|
|
142
|
-
| [**
|
|
132
|
+
| Component | Description |
|
|
133
|
+
| :----------------------------------------- | :------------------------------------------------------------------------------------------------------ |
|
|
134
|
+
| [**Input**](src/lib/Input) | Text input with 5 variants, icons, avatar, loading state, and FormField integration |
|
|
135
|
+
| [**Textarea**](src/lib/Textarea) | Multi-line text input with 5 variants, icons, autoresize with maxrows, and FormField integration |
|
|
136
|
+
| [**Select**](src/lib/Select) | Dropdown select with 5 variants, icons, avatars, groups, descriptions, and FormField support |
|
|
137
|
+
| [**SelectMenu**](src/lib/SelectMenu) | Searchable multi-select menu with chips, groups, and FormField integration |
|
|
138
|
+
| [**Switch**](src/lib/Switch) | Toggle switch with 8 colors, 5 sizes, checked/unchecked icons, loading state, and FormField integration |
|
|
139
|
+
| [**Checkbox**](src/lib/Checkbox) | Checkbox with 8 colors, 5 sizes, indeterminate state, custom icons, and FormField integration |
|
|
140
|
+
| [**CheckboxGroup**](src/lib/CheckboxGroup) | Grouped checkboxes with single/multiple selection, per-item disabled, and FormField integration |
|
|
141
|
+
| [**RadioGroup**](src/lib/RadioGroup) | Radio group for single-selection with items API, legend, orientation, and FormField integration |
|
|
142
|
+
| [**Slider**](src/lib/Slider) | Range slider with single/range values, step, orientation, tooltip labels, and FormField integration |
|
|
143
|
+
| [**PinInput**](src/lib/PinInput) | PIN/OTP input with masking, numeric filtering, OTP autocomplete, and FormField integration |
|
|
144
|
+
| [**FileUpload**](src/lib/FileUpload) | Drag-and-drop file upload with preview list, image thumbnails, accept filter, and multiple files |
|
|
145
|
+
| [**FormField**](src/lib/FormField) | Form control wrapper providing label, description, hint, help, and error handling |
|
|
146
|
+
| [**FieldGroup**](src/lib/FieldGroup) | Groups buttons and inputs with seamless borders and shared size/orientation context |
|
|
147
|
+
| [**Calendar**](src/lib/Calendar) | Date picker calendar with single, multiple, and range selection modes |
|
|
143
148
|
|
|
144
149
|
## Theming
|
|
145
150
|
|
|
@@ -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 @@
|
|
|
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>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CommandProps } from './command.types.js';
|
|
2
|
+
export type Props = CommandProps;
|
|
3
|
+
import { Command } from 'bits-ui';
|
|
4
|
+
declare const Command: import("svelte").Component<CommandProps, {}, "search" | "ref" | "value">;
|
|
5
|
+
type Command = ReturnType<typeof Command>;
|
|
6
|
+
export default Command;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Command from './Command.svelte'
|
|
3
|
+
import type { CommandProps } from './command.types.js'
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
...props
|
|
7
|
+
}: Omit<
|
|
8
|
+
CommandProps,
|
|
9
|
+
'item' | 'itemLeading' | 'itemLabel' | 'itemTrailing' | 'empty' | 'footer'
|
|
10
|
+
> = $props()
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<Command {...props} />
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CommandProps } from './command.types.js';
|
|
2
|
+
declare const CommandTestWrapper: import("svelte").Component<Omit<CommandProps, "footer" | "item" | "empty" | "itemLabel" | "itemTrailing" | "itemLeading">, {}, "">;
|
|
3
|
+
type CommandTestWrapper = ReturnType<typeof CommandTestWrapper>;
|
|
4
|
+
export default CommandTestWrapper;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ClassNameValue } from 'tailwind-merge';
|
|
3
|
+
import type { CommandRootPropsWithoutHTML } from 'bits-ui';
|
|
4
|
+
import type { CommandSlots, CommandVariantProps } from './command.variants.js';
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for an individual command item.
|
|
7
|
+
*/
|
|
8
|
+
export interface CommandItem {
|
|
9
|
+
/** Unique identifier and search value for this item. */
|
|
10
|
+
value: string;
|
|
11
|
+
/** Display label. */
|
|
12
|
+
label?: string;
|
|
13
|
+
/** Optional description shown below the label. */
|
|
14
|
+
description?: string;
|
|
15
|
+
/** Leading icon name (Iconify). */
|
|
16
|
+
icon?: string;
|
|
17
|
+
/** Additional search aliases for filtering. */
|
|
18
|
+
keywords?: string[];
|
|
19
|
+
/** Whether this item is disabled. */
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Callback when item is selected. */
|
|
22
|
+
onSelect?: () => void;
|
|
23
|
+
/** Additional CSS classes for this item. */
|
|
24
|
+
class?: ClassNameValue;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Configuration for a command group.
|
|
28
|
+
*/
|
|
29
|
+
export interface CommandGroup {
|
|
30
|
+
/** Unique group identifier. */
|
|
31
|
+
id: string;
|
|
32
|
+
/** Group heading label. */
|
|
33
|
+
label?: string;
|
|
34
|
+
/** Items in this group. */
|
|
35
|
+
items: CommandItem[];
|
|
36
|
+
}
|
|
37
|
+
export interface CommandItemSlotProps {
|
|
38
|
+
item: CommandItem;
|
|
39
|
+
index: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Props for the Command component.
|
|
43
|
+
*
|
|
44
|
+
* Wraps bits-ui Command primitives with a themed, items-based API.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```svelte
|
|
48
|
+
* <Command
|
|
49
|
+
* placeholder="Search..."
|
|
50
|
+
* groups={[
|
|
51
|
+
* { id: 'actions', label: 'Actions', items: [
|
|
52
|
+
* { value: 'new-file', label: 'New File', icon: 'lucide:file-plus' }
|
|
53
|
+
* ]}
|
|
54
|
+
* ]}
|
|
55
|
+
* />
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @see https://bits-ui.com/docs/components/command
|
|
59
|
+
*/
|
|
60
|
+
export interface CommandProps extends Pick<CommandRootPropsWithoutHTML, 'value' | 'onValueChange' | 'filter' | 'shouldFilter' | 'loop' | 'vimBindings' | 'label'> {
|
|
61
|
+
/** Bindable reference to the root DOM element. */
|
|
62
|
+
ref?: HTMLElement | null;
|
|
63
|
+
/** Array of grouped command items. */
|
|
64
|
+
groups?: CommandGroup[];
|
|
65
|
+
/** Placeholder text for the search input. */
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
/**
|
|
68
|
+
* The current search term. Use `bind:search` for two-way binding.
|
|
69
|
+
* @default ''
|
|
70
|
+
*/
|
|
71
|
+
search?: string;
|
|
72
|
+
/** Loading state — shows a loading indicator instead of items. */
|
|
73
|
+
loading?: boolean;
|
|
74
|
+
/** Text shown when no results match the search query. */
|
|
75
|
+
emptyText?: string;
|
|
76
|
+
/** Leading icon for the search input. */
|
|
77
|
+
icon?: string;
|
|
78
|
+
/** Size variant. */
|
|
79
|
+
size?: NonNullable<CommandVariantProps['size']>;
|
|
80
|
+
/** Override classes for component slots. */
|
|
81
|
+
ui?: Partial<Record<CommandSlots, ClassNameValue>>;
|
|
82
|
+
/** Additional CSS classes for the root element. */
|
|
83
|
+
class?: ClassNameValue;
|
|
84
|
+
/** Custom snippet for rendering each item. Replaces default item rendering. */
|
|
85
|
+
item?: Snippet<[CommandItemSlotProps]>;
|
|
86
|
+
/** Custom snippet for the item leading section (icon area). */
|
|
87
|
+
itemLeading?: Snippet<[CommandItemSlotProps]>;
|
|
88
|
+
/** Custom snippet for the item label section. */
|
|
89
|
+
itemLabel?: Snippet<[CommandItemSlotProps]>;
|
|
90
|
+
/** Custom snippet for the item trailing section. */
|
|
91
|
+
itemTrailing?: Snippet<[CommandItemSlotProps]>;
|
|
92
|
+
/** Custom snippet for the empty state. */
|
|
93
|
+
empty?: Snippet<[{
|
|
94
|
+
search: string;
|
|
95
|
+
}]>;
|
|
96
|
+
/** Custom snippet for the footer area below the list. */
|
|
97
|
+
footer?: Snippet;
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|