svelora 3.0.6 → 3.0.8
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/BentoGrid/BentoCard.svelte +45 -0
- package/dist/BentoGrid/BentoCard.svelte.d.ts +4 -0
- package/dist/BentoGrid/BentoGrid.svelte +9 -0
- package/dist/BentoGrid/BentoGrid.svelte.d.ts +4 -0
- package/dist/BentoGrid/bento-grid.types.d.ts +47 -0
- package/dist/BentoGrid/bento-grid.types.js +1 -0
- package/dist/BentoGrid/bento-grid.variants.d.ts +30 -0
- package/dist/BentoGrid/bento-grid.variants.js +16 -0
- package/dist/BentoGrid/index.d.ts +5 -0
- package/dist/BentoGrid/index.js +5 -0
- package/dist/Chart/Chart.svelte +48 -0
- package/dist/Chart/Chart.svelte.d.ts +4 -0
- package/dist/Chart/chart.types.d.ts +16 -0
- package/dist/Chart/chart.types.js +1 -0
- package/dist/Chart/index.d.ts +2 -0
- package/dist/Chart/index.js +2 -0
- package/dist/Chat/ChatBubble.svelte +30 -0
- package/dist/Chat/ChatBubble.svelte.d.ts +4 -0
- package/dist/Chat/ChatInput.svelte +50 -0
- package/dist/Chat/ChatInput.svelte.d.ts +4 -0
- package/dist/Chat/ChatMessage.svelte +15 -0
- package/dist/Chat/ChatMessage.svelte.d.ts +4 -0
- package/dist/Chat/chat.types.d.ts +63 -0
- package/dist/Chat/chat.types.js +1 -0
- package/dist/Chat/chat.variants.d.ts +117 -0
- package/dist/Chat/chat.variants.js +47 -0
- package/dist/Chat/index.d.ts +6 -0
- package/dist/Chat/index.js +6 -0
- package/dist/ColorPicker/ColorPicker.svelte +109 -0
- package/dist/ColorPicker/ColorPicker.svelte.d.ts +4 -0
- package/dist/ColorPicker/color-picker.types.d.ts +26 -0
- package/dist/ColorPicker/color-picker.types.js +1 -0
- package/dist/ColorPicker/color-picker.variants.d.ts +69 -0
- package/dist/ColorPicker/color-picker.variants.js +13 -0
- package/dist/ColorPicker/index.d.ts +4 -0
- package/dist/ColorPicker/index.js +4 -0
- package/dist/DateRangePicker/DateRangePicker.svelte +59 -0
- package/dist/DateRangePicker/DateRangePicker.svelte.d.ts +4 -0
- package/dist/DateRangePicker/date-range-picker.types.d.ts +34 -0
- package/dist/DateRangePicker/date-range-picker.types.js +1 -0
- package/dist/DateRangePicker/date-range-picker.variants.d.ts +39 -0
- package/dist/DateRangePicker/date-range-picker.variants.js +20 -0
- package/dist/DateRangePicker/index.d.ts +4 -0
- package/dist/DateRangePicker/index.js +4 -0
- package/dist/List/List.svelte +14 -0
- package/dist/List/List.svelte.d.ts +4 -0
- package/dist/List/ListItem.svelte +64 -0
- package/dist/List/ListItem.svelte.d.ts +4 -0
- package/dist/List/index.d.ts +5 -0
- package/dist/List/index.js +5 -0
- package/dist/List/list.types.d.ts +62 -0
- package/dist/List/list.types.js +1 -0
- package/dist/List/list.variants.d.ts +99 -0
- package/dist/List/list.variants.js +42 -0
- package/dist/Marquee/Marquee.svelte +50 -0
- package/dist/Marquee/Marquee.svelte.d.ts +4 -0
- package/dist/Marquee/index.d.ts +4 -0
- package/dist/Marquee/index.js +4 -0
- package/dist/Marquee/marquee.types.d.ts +38 -0
- package/dist/Marquee/marquee.types.js +1 -0
- package/dist/Marquee/marquee.variants.d.ts +78 -0
- package/dist/Marquee/marquee.variants.js +28 -0
- package/dist/NavigationMenu/NavigationMenu.svelte +212 -0
- package/dist/NavigationMenu/NavigationMenu.svelte.d.ts +4 -0
- package/dist/NavigationMenu/index.d.ts +3 -0
- package/dist/NavigationMenu/index.js +3 -0
- package/dist/NavigationMenu/navigation-menu.types.d.ts +104 -0
- package/dist/NavigationMenu/navigation-menu.types.js +1 -0
- package/dist/NavigationMenu/navigation-menu.variants.d.ts +121 -0
- package/dist/NavigationMenu/navigation-menu.variants.js +64 -0
- package/dist/NumberTicker/NumberTicker.svelte +59 -0
- package/dist/NumberTicker/NumberTicker.svelte.d.ts +4 -0
- package/dist/NumberTicker/index.d.ts +4 -0
- package/dist/NumberTicker/index.js +4 -0
- package/dist/NumberTicker/number-ticker.types.d.ts +26 -0
- package/dist/NumberTicker/number-ticker.types.js +1 -0
- package/dist/NumberTicker/number-ticker.variants.d.ts +27 -0
- package/dist/NumberTicker/number-ticker.variants.js +6 -0
- package/dist/PasswordInput/PasswordInput.svelte +74 -0
- package/dist/PasswordInput/PasswordInput.svelte.d.ts +4 -0
- package/dist/PasswordInput/index.d.ts +4 -0
- package/dist/PasswordInput/index.js +4 -0
- package/dist/PasswordInput/password-input.types.d.ts +18 -0
- package/dist/PasswordInput/password-input.types.js +1 -0
- package/dist/PasswordInput/password-input.variants.d.ts +57 -0
- package/dist/PasswordInput/password-input.variants.js +11 -0
- package/dist/Prose/Prose.svelte +13 -0
- package/dist/Prose/Prose.svelte.d.ts +4 -0
- package/dist/Prose/index.d.ts +4 -0
- package/dist/Prose/index.js +4 -0
- package/dist/Prose/prose.types.d.ts +22 -0
- package/dist/Prose/prose.types.js +1 -0
- package/dist/Prose/prose.variants.d.ts +45 -0
- package/dist/Prose/prose.variants.js +45 -0
- package/dist/Rating/Rating.svelte +93 -0
- package/dist/Rating/Rating.svelte.d.ts +4 -0
- package/dist/Rating/index.d.ts +4 -0
- package/dist/Rating/index.js +4 -0
- package/dist/Rating/rating.types.d.ts +59 -0
- package/dist/Rating/rating.types.js +1 -0
- package/dist/Rating/rating.variants.d.ts +93 -0
- package/dist/Rating/rating.variants.js +32 -0
- package/dist/Resizable/Resizable.svelte +9 -0
- package/dist/Resizable/Resizable.svelte.d.ts +4 -0
- package/dist/Resizable/index.d.ts +4 -0
- package/dist/Resizable/index.js +4 -0
- package/dist/Resizable/resizable.types.d.ts +18 -0
- package/dist/Resizable/resizable.types.js +1 -0
- package/dist/Resizable/resizable.variants.d.ts +48 -0
- package/dist/Resizable/resizable.variants.js +17 -0
- package/dist/ScrollArea/ScrollArea.svelte +54 -0
- package/dist/ScrollArea/ScrollArea.svelte.d.ts +4 -0
- package/dist/ScrollArea/index.d.ts +4 -0
- package/dist/ScrollArea/index.js +4 -0
- package/dist/ScrollArea/scroll-area.types.d.ts +27 -0
- package/dist/ScrollArea/scroll-area.types.js +1 -0
- package/dist/ScrollArea/scroll-area.variants.d.ts +45 -0
- package/dist/ScrollArea/scroll-area.variants.js +27 -0
- package/dist/Search/Search.svelte +94 -0
- package/dist/Search/Search.svelte.d.ts +4 -0
- package/dist/Search/index.d.ts +2 -0
- package/dist/Search/index.js +1 -0
- package/dist/Search/search.types.d.ts +40 -0
- package/dist/Search/search.types.js +1 -0
- package/dist/Sidebar/Sidebar.svelte +30 -0
- package/dist/Sidebar/Sidebar.svelte.d.ts +4 -0
- package/dist/Sidebar/index.d.ts +4 -0
- package/dist/Sidebar/index.js +4 -0
- package/dist/Sidebar/sidebar.types.d.ts +31 -0
- package/dist/Sidebar/sidebar.types.js +1 -0
- package/dist/Sidebar/sidebar.variants.d.ts +69 -0
- package/dist/Sidebar/sidebar.variants.js +23 -0
- package/dist/Spotlight/Spotlight.svelte +31 -0
- package/dist/Spotlight/Spotlight.svelte.d.ts +4 -0
- package/dist/Spotlight/index.d.ts +4 -0
- package/dist/Spotlight/index.js +4 -0
- package/dist/Spotlight/spotlight.types.d.ts +22 -0
- package/dist/Spotlight/spotlight.types.js +1 -0
- package/dist/Spotlight/spotlight.variants.d.ts +39 -0
- package/dist/Spotlight/spotlight.variants.js +8 -0
- package/dist/TagsInput/TagsInput.svelte +100 -0
- package/dist/TagsInput/TagsInput.svelte.d.ts +4 -0
- package/dist/TagsInput/index.d.ts +4 -0
- package/dist/TagsInput/index.js +4 -0
- package/dist/TagsInput/tags-input.types.d.ts +32 -0
- package/dist/TagsInput/tags-input.types.js +1 -0
- package/dist/TagsInput/tags-input.variants.d.ts +45 -0
- package/dist/TagsInput/tags-input.variants.js +22 -0
- package/dist/TreeView/TreeView.svelte +95 -0
- package/dist/TreeView/TreeView.svelte.d.ts +4 -0
- package/dist/TreeView/index.d.ts +4 -0
- package/dist/TreeView/index.js +4 -0
- package/dist/TreeView/tree-view.types.d.ts +68 -0
- package/dist/TreeView/tree-view.types.js +1 -0
- package/dist/TreeView/tree-view.variants.d.ts +69 -0
- package/dist/TreeView/tree-view.variants.js +30 -0
- package/dist/docs/navigation.js +119 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.js +19 -1
- package/dist/mcp/svelora-docs.data.json +44 -6
- package/package.json +9 -8
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
2
|
+
export declare const listVariants: import("tailwind-variants").TVReturnType<{
|
|
3
|
+
variant: {
|
|
4
|
+
default: {
|
|
5
|
+
base: string;
|
|
6
|
+
};
|
|
7
|
+
bordered: {
|
|
8
|
+
base: string;
|
|
9
|
+
item: string;
|
|
10
|
+
};
|
|
11
|
+
separated: {
|
|
12
|
+
base: string;
|
|
13
|
+
item: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
interactive: {
|
|
17
|
+
true: {
|
|
18
|
+
item: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
disabled: {
|
|
22
|
+
true: {
|
|
23
|
+
item: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}, {
|
|
27
|
+
base: string;
|
|
28
|
+
item: string;
|
|
29
|
+
itemContent: string;
|
|
30
|
+
itemTitle: string;
|
|
31
|
+
itemDescription: string;
|
|
32
|
+
leading: string;
|
|
33
|
+
trailing: string;
|
|
34
|
+
}, undefined, {
|
|
35
|
+
variant: {
|
|
36
|
+
default: {
|
|
37
|
+
base: string;
|
|
38
|
+
};
|
|
39
|
+
bordered: {
|
|
40
|
+
base: string;
|
|
41
|
+
item: string;
|
|
42
|
+
};
|
|
43
|
+
separated: {
|
|
44
|
+
base: string;
|
|
45
|
+
item: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
interactive: {
|
|
49
|
+
true: {
|
|
50
|
+
item: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
disabled: {
|
|
54
|
+
true: {
|
|
55
|
+
item: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
}, {
|
|
59
|
+
base: string;
|
|
60
|
+
item: string;
|
|
61
|
+
itemContent: string;
|
|
62
|
+
itemTitle: string;
|
|
63
|
+
itemDescription: string;
|
|
64
|
+
leading: string;
|
|
65
|
+
trailing: string;
|
|
66
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
67
|
+
variant: {
|
|
68
|
+
default: {
|
|
69
|
+
base: string;
|
|
70
|
+
};
|
|
71
|
+
bordered: {
|
|
72
|
+
base: string;
|
|
73
|
+
item: string;
|
|
74
|
+
};
|
|
75
|
+
separated: {
|
|
76
|
+
base: string;
|
|
77
|
+
item: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
interactive: {
|
|
81
|
+
true: {
|
|
82
|
+
item: string;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
disabled: {
|
|
86
|
+
true: {
|
|
87
|
+
item: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
}, {
|
|
91
|
+
base: string;
|
|
92
|
+
item: string;
|
|
93
|
+
itemContent: string;
|
|
94
|
+
itemTitle: string;
|
|
95
|
+
itemDescription: string;
|
|
96
|
+
leading: string;
|
|
97
|
+
trailing: string;
|
|
98
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
99
|
+
export type ListVariantProps = VariantProps<typeof listVariants>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { tv } from 'tailwind-variants';
|
|
2
|
+
export const listVariants = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
base: 'flex flex-col w-full text-surface-900 dark:text-surface-50',
|
|
5
|
+
item: 'flex items-center justify-between w-full px-4 py-3',
|
|
6
|
+
itemContent: 'flex flex-col flex-1 min-w-0',
|
|
7
|
+
itemTitle: 'text-sm font-medium truncate',
|
|
8
|
+
itemDescription: 'text-xs text-surface-500 dark:text-surface-400 truncate mt-0.5',
|
|
9
|
+
leading: 'mr-3 shrink-0 flex items-center justify-center',
|
|
10
|
+
trailing: 'ml-3 shrink-0 flex items-center justify-center text-surface-500 dark:text-surface-400'
|
|
11
|
+
},
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
default: {
|
|
15
|
+
base: ''
|
|
16
|
+
},
|
|
17
|
+
bordered: {
|
|
18
|
+
base: 'border border-outline-variant rounded-lg overflow-hidden',
|
|
19
|
+
item: 'border-b border-outline-variant last:border-b-0'
|
|
20
|
+
},
|
|
21
|
+
separated: {
|
|
22
|
+
base: 'gap-2',
|
|
23
|
+
item: 'rounded-lg border border-outline-variant'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
interactive: {
|
|
27
|
+
true: {
|
|
28
|
+
item: 'transition-colors hover:bg-surface-100 dark:hover:bg-surface-800 cursor-pointer active:bg-surface-200 dark:active:bg-surface-700'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
disabled: {
|
|
32
|
+
true: {
|
|
33
|
+
item: 'opacity-50 cursor-not-allowed hover:bg-transparent dark:hover:bg-transparent active:bg-transparent dark:active:bg-transparent'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: 'default',
|
|
39
|
+
interactive: false,
|
|
40
|
+
disabled: false
|
|
41
|
+
}
|
|
42
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script lang="ts">import { twMerge } from "tailwind-merge";
|
|
2
|
+
import { marqueeVariants } from "./marquee.variants.js";
|
|
3
|
+
let { direction = "left", pauseOnHover = false, duration = "40s", gap = "1rem", repeat = 2, class: className, children, ...restProps } = $props();
|
|
4
|
+
let styles = $derived(marqueeVariants({
|
|
5
|
+
direction,
|
|
6
|
+
pauseOnHover
|
|
7
|
+
}));
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<div
|
|
11
|
+
class={twMerge(styles.root() as string, className)}
|
|
12
|
+
style:--gap={gap}
|
|
13
|
+
style:--duration={duration}
|
|
14
|
+
{...restProps}
|
|
15
|
+
>
|
|
16
|
+
{#each Array(Math.max(2, repeat)) as _}
|
|
17
|
+
<div class={styles.content() as string}>
|
|
18
|
+
{@render children?.()}
|
|
19
|
+
</div>
|
|
20
|
+
{/each}
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
/* Injected keyframes for marquee animation since it might not be in tailwind config */
|
|
25
|
+
:global(.animate-marquee) {
|
|
26
|
+
animation: marquee var(--duration) linear infinite;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
:global(.animate-marquee-vertical) {
|
|
30
|
+
animation: marquee-vertical var(--duration) linear infinite;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@keyframes marquee {
|
|
34
|
+
0% {
|
|
35
|
+
transform: translateX(0);
|
|
36
|
+
}
|
|
37
|
+
100% {
|
|
38
|
+
transform: translateX(calc(-100% - var(--gap)));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@keyframes marquee-vertical {
|
|
43
|
+
0% {
|
|
44
|
+
transform: translateY(0);
|
|
45
|
+
}
|
|
46
|
+
100% {
|
|
47
|
+
transform: translateY(calc(-100% - var(--gap)));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
</style>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { MarqueeVariantProps } from './marquee.variants.js';
|
|
4
|
+
export interface MarqueeProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* Direction of the marquee animation.
|
|
7
|
+
* @default 'left'
|
|
8
|
+
*/
|
|
9
|
+
direction?: MarqueeVariantProps['direction'];
|
|
10
|
+
/**
|
|
11
|
+
* Whether to pause the animation when hovered.
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
pauseOnHover?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Duration of the animation loop (e.g. '20s', '40s').
|
|
17
|
+
* @default '40s'
|
|
18
|
+
*/
|
|
19
|
+
duration?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Gap between items.
|
|
22
|
+
* @default '1rem'
|
|
23
|
+
*/
|
|
24
|
+
gap?: string;
|
|
25
|
+
/**
|
|
26
|
+
* How many times to repeat the content. To ensure smooth looping, it should be at least 2.
|
|
27
|
+
* @default 2
|
|
28
|
+
*/
|
|
29
|
+
repeat?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Additional CSS classes.
|
|
32
|
+
*/
|
|
33
|
+
class?: string;
|
|
34
|
+
/**
|
|
35
|
+
* The content to scroll.
|
|
36
|
+
*/
|
|
37
|
+
children?: Snippet;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
2
|
+
export declare const marqueeVariants: import("tailwind-variants").TVReturnType<{
|
|
3
|
+
direction: {
|
|
4
|
+
left: {
|
|
5
|
+
content: string;
|
|
6
|
+
};
|
|
7
|
+
right: {
|
|
8
|
+
content: string;
|
|
9
|
+
};
|
|
10
|
+
up: {
|
|
11
|
+
root: string;
|
|
12
|
+
content: string;
|
|
13
|
+
};
|
|
14
|
+
down: {
|
|
15
|
+
root: string;
|
|
16
|
+
content: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
pauseOnHover: {
|
|
20
|
+
true: {
|
|
21
|
+
content: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}, {
|
|
25
|
+
root: string;
|
|
26
|
+
content: string;
|
|
27
|
+
}, undefined, {
|
|
28
|
+
direction: {
|
|
29
|
+
left: {
|
|
30
|
+
content: string;
|
|
31
|
+
};
|
|
32
|
+
right: {
|
|
33
|
+
content: string;
|
|
34
|
+
};
|
|
35
|
+
up: {
|
|
36
|
+
root: string;
|
|
37
|
+
content: string;
|
|
38
|
+
};
|
|
39
|
+
down: {
|
|
40
|
+
root: string;
|
|
41
|
+
content: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
pauseOnHover: {
|
|
45
|
+
true: {
|
|
46
|
+
content: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}, {
|
|
50
|
+
root: string;
|
|
51
|
+
content: string;
|
|
52
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
53
|
+
direction: {
|
|
54
|
+
left: {
|
|
55
|
+
content: string;
|
|
56
|
+
};
|
|
57
|
+
right: {
|
|
58
|
+
content: string;
|
|
59
|
+
};
|
|
60
|
+
up: {
|
|
61
|
+
root: string;
|
|
62
|
+
content: string;
|
|
63
|
+
};
|
|
64
|
+
down: {
|
|
65
|
+
root: string;
|
|
66
|
+
content: string;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
pauseOnHover: {
|
|
70
|
+
true: {
|
|
71
|
+
content: string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}, {
|
|
75
|
+
root: string;
|
|
76
|
+
content: string;
|
|
77
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
78
|
+
export type MarqueeVariantProps = VariantProps<typeof marqueeVariants>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { tv } from 'tailwind-variants';
|
|
2
|
+
export const marqueeVariants = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
root: 'group flex overflow-hidden p-2 [--gap:1rem] [gap:var(--gap)]',
|
|
5
|
+
content: 'flex shrink-0 justify-around [gap:var(--gap)] animate-marquee'
|
|
6
|
+
},
|
|
7
|
+
variants: {
|
|
8
|
+
direction: {
|
|
9
|
+
left: { content: '[animation-direction:normal]' },
|
|
10
|
+
right: { content: '[animation-direction:reverse]' },
|
|
11
|
+
up: {
|
|
12
|
+
root: 'flex-col',
|
|
13
|
+
content: 'flex-col animate-marquee-vertical [animation-direction:normal]'
|
|
14
|
+
},
|
|
15
|
+
down: {
|
|
16
|
+
root: 'flex-col',
|
|
17
|
+
content: 'flex-col animate-marquee-vertical [animation-direction:reverse]'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
pauseOnHover: {
|
|
21
|
+
true: { content: 'group-hover:[animation-play-state:paused]' }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
direction: 'left',
|
|
26
|
+
pauseOnHover: false
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
<script lang="ts">import { untrack } from "svelte";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
import Icon from "../Icon/Icon.svelte";
|
|
4
|
+
import Badge from "../Badge/Badge.svelte";
|
|
5
|
+
import Kbd from "../Kbd/Kbd.svelte";
|
|
6
|
+
import DropdownMenu from "../DropdownMenu/DropdownMenu.svelte";
|
|
7
|
+
import { navigationMenuVariants } from "./navigation-menu.variants.js";
|
|
8
|
+
let { items = [], variant = "default", orientation = "horizontal", accordion = false, class: className, ui, children } = $props();
|
|
9
|
+
let styles = $derived(navigationMenuVariants({
|
|
10
|
+
variant,
|
|
11
|
+
orientation
|
|
12
|
+
}));
|
|
13
|
+
let normalizedItems = $derived.by(() => {
|
|
14
|
+
if (!items || items.length === 0) return [];
|
|
15
|
+
if (Array.isArray(items[0])) {
|
|
16
|
+
return items;
|
|
17
|
+
}
|
|
18
|
+
return [items];
|
|
19
|
+
});
|
|
20
|
+
// --- Accordion State (for vertical) ---
|
|
21
|
+
function getInitialOpenGroups() {
|
|
22
|
+
const opened = [];
|
|
23
|
+
for (const group of normalizedItems) {
|
|
24
|
+
for (const item of group) {
|
|
25
|
+
if (item.defaultOpen && item.label) opened.push(item.label);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return opened;
|
|
29
|
+
}
|
|
30
|
+
let openGroupLabel = $state(untrack(() => accordion ? getInitialOpenGroups()[0] ?? null : null));
|
|
31
|
+
let openGroups = $state(untrack(() => !accordion ? getInitialOpenGroups() : []));
|
|
32
|
+
function handleGroupToggle(item) {
|
|
33
|
+
if (!item.label) return;
|
|
34
|
+
if (accordion) {
|
|
35
|
+
openGroupLabel = openGroupLabel === item.label ? null : item.label;
|
|
36
|
+
} else {
|
|
37
|
+
if (openGroups.includes(item.label)) {
|
|
38
|
+
openGroups = openGroups.filter((l) => l !== item.label);
|
|
39
|
+
} else {
|
|
40
|
+
openGroups = [...openGroups, item.label];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isGroupOpen(item) {
|
|
45
|
+
if (accordion) return openGroupLabel === item.label;
|
|
46
|
+
if (!item.label) return false;
|
|
47
|
+
return openGroups.includes(item.label);
|
|
48
|
+
}
|
|
49
|
+
// --- Auto Active State ---
|
|
50
|
+
let currentPath = $state("");
|
|
51
|
+
$effect(() => {
|
|
52
|
+
if (typeof window !== "undefined") {
|
|
53
|
+
currentPath = window.location.pathname;
|
|
54
|
+
const onPopState = () => {
|
|
55
|
+
currentPath = window.location.pathname;
|
|
56
|
+
};
|
|
57
|
+
window.addEventListener("popstate", onPopState);
|
|
58
|
+
return () => window.removeEventListener("popstate", onPopState);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
function isActive(item) {
|
|
62
|
+
if (item.active) return true;
|
|
63
|
+
if (item.href) {
|
|
64
|
+
return currentPath === item.href || currentPath.startsWith(item.href + "/");
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// Helper for keyboard shortcuts
|
|
69
|
+
function getKbds(item) {
|
|
70
|
+
return item.kbds || item.shortcut || [];
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
{#snippet renderItem(item: NavigationMenuItemType)}
|
|
75
|
+
<li class={orientation === 'vertical' ? 'w-full' : ''}>
|
|
76
|
+
{#if item.type === 'separator'}
|
|
77
|
+
<!-- SEPARATOR -->
|
|
78
|
+
<div class={orientation === 'horizontal' ? 'w-px h-6 bg-outline-variant mx-1' : 'h-px w-full bg-outline-variant my-1'}></div>
|
|
79
|
+
{:else if (item.items && item.items.length > 0) || (item.children && item.children.length > 0)}
|
|
80
|
+
{@const subItems = item.items || item.children || []}
|
|
81
|
+
|
|
82
|
+
{#if orientation === 'horizontal'}
|
|
83
|
+
<!-- HORIZONTAL: Dropdown -->
|
|
84
|
+
<DropdownMenu
|
|
85
|
+
items={subItems as any}
|
|
86
|
+
side="bottom"
|
|
87
|
+
align="start"
|
|
88
|
+
sideOffset={4}
|
|
89
|
+
>
|
|
90
|
+
{#snippet children({ props, open })}
|
|
91
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
{...props}
|
|
95
|
+
class={twMerge(styles.item({ active: isActive(item) }), item.class)}
|
|
96
|
+
disabled={item.disabled}
|
|
97
|
+
>
|
|
98
|
+
{#if item.icon}
|
|
99
|
+
<Icon name={item.icon} class={styles.icon({ active: isActive(item) })} />
|
|
100
|
+
{/if}
|
|
101
|
+
<span>{item.label}</span>
|
|
102
|
+
{#if item.badge !== undefined}
|
|
103
|
+
<Badge label={String(item.badge)} color={item.badgeColor || 'primary'} size="sm" class="ml-1" />
|
|
104
|
+
{/if}
|
|
105
|
+
<span class="ml-auto flex items-center">
|
|
106
|
+
<Icon
|
|
107
|
+
name="lucide:chevron-down"
|
|
108
|
+
class={twMerge(styles.chevron(), open ? 'rotate-180' : '')}
|
|
109
|
+
/>
|
|
110
|
+
</span>
|
|
111
|
+
</button>
|
|
112
|
+
{/snippet}
|
|
113
|
+
</DropdownMenu>
|
|
114
|
+
{:else}
|
|
115
|
+
<!-- VERTICAL: Accordion -->
|
|
116
|
+
<button
|
|
117
|
+
type="button"
|
|
118
|
+
class={twMerge(
|
|
119
|
+
styles.accordionTrigger(),
|
|
120
|
+
isGroupOpen(item) ? styles.item({ active: true }) : '',
|
|
121
|
+
item.class
|
|
122
|
+
)}
|
|
123
|
+
disabled={item.disabled}
|
|
124
|
+
onclick={() => handleGroupToggle(item)}
|
|
125
|
+
>
|
|
126
|
+
<div class="flex flex-1 items-center truncate">
|
|
127
|
+
{#if item.icon}
|
|
128
|
+
<Icon name={item.icon} class={styles.icon({ active: isActive(item) })} />
|
|
129
|
+
{/if}
|
|
130
|
+
<span class="truncate">{item.label}</span>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="flex items-center gap-2">
|
|
133
|
+
{#if item.badge !== undefined || getKbds(item).length > 0}
|
|
134
|
+
<span class="flex items-center gap-2">
|
|
135
|
+
{#if item.badge !== undefined}
|
|
136
|
+
<Badge label={String(item.badge)} color={item.badgeColor || 'primary'} size="sm" />
|
|
137
|
+
{/if}
|
|
138
|
+
{#if getKbds(item).length > 0}
|
|
139
|
+
<span class="flex items-center gap-0.5">
|
|
140
|
+
{#each getKbds(item) as key}
|
|
141
|
+
<Kbd value={typeof key === 'string' ? key : key.value} size="sm" />
|
|
142
|
+
{/each}
|
|
143
|
+
</span>
|
|
144
|
+
{/if}
|
|
145
|
+
</span>
|
|
146
|
+
{/if}
|
|
147
|
+
<Icon
|
|
148
|
+
name="lucide:chevron-down"
|
|
149
|
+
class={twMerge(styles.chevron(), isGroupOpen(item) ? 'rotate-180' : '')}
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
</button>
|
|
153
|
+
{#if isGroupOpen(item)}
|
|
154
|
+
<ul class={styles.accordionGroupContent()}>
|
|
155
|
+
{#each subItems as subItem}
|
|
156
|
+
{@render renderItem(subItem)}
|
|
157
|
+
{/each}
|
|
158
|
+
</ul>
|
|
159
|
+
{/if}
|
|
160
|
+
{/if}
|
|
161
|
+
{:else}
|
|
162
|
+
<!-- STANDARD LINK -->
|
|
163
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
164
|
+
<svelte:element
|
|
165
|
+
this={item.href ? 'a' : 'button'}
|
|
166
|
+
href={item.href}
|
|
167
|
+
target={item.target}
|
|
168
|
+
type={!item.href ? 'button' : undefined}
|
|
169
|
+
class={twMerge(styles.item({ active: isActive(item) }), item.class)}
|
|
170
|
+
disabled={item.disabled}
|
|
171
|
+
onclick={item.onClick}
|
|
172
|
+
>
|
|
173
|
+
<div class="flex flex-1 items-center truncate gap-1.5">
|
|
174
|
+
{#if item.icon}
|
|
175
|
+
<Icon name={item.icon} class={styles.icon({ active: isActive(item) })} />
|
|
176
|
+
{/if}
|
|
177
|
+
{#if item.label}
|
|
178
|
+
<span class="truncate">{item.label}</span>
|
|
179
|
+
{/if}
|
|
180
|
+
</div>
|
|
181
|
+
{#if item.badge !== undefined || getKbds(item).length > 0}
|
|
182
|
+
<span class="ml-auto flex items-center gap-2">
|
|
183
|
+
{#if item.badge !== undefined}
|
|
184
|
+
<Badge label={String(item.badge)} color={item.badgeColor || 'primary'} size="sm" />
|
|
185
|
+
{/if}
|
|
186
|
+
{#if getKbds(item).length > 0}
|
|
187
|
+
<span class="flex items-center gap-0.5">
|
|
188
|
+
{#each getKbds(item) as key}
|
|
189
|
+
<Kbd value={typeof key === 'string' ? key : key.value} size="sm" />
|
|
190
|
+
{/each}
|
|
191
|
+
</span>
|
|
192
|
+
{/if}
|
|
193
|
+
</span>
|
|
194
|
+
{/if}
|
|
195
|
+
</svelte:element>
|
|
196
|
+
{/if}
|
|
197
|
+
</li>
|
|
198
|
+
{/snippet}
|
|
199
|
+
|
|
200
|
+
<nav class={twMerge(styles.base(), className)}>
|
|
201
|
+
{#if children}
|
|
202
|
+
{@render children()}
|
|
203
|
+
{:else}
|
|
204
|
+
{#each normalizedItems as group}
|
|
205
|
+
<ul class={styles.group()}>
|
|
206
|
+
{#each group as item}
|
|
207
|
+
{@render renderItem(item)}
|
|
208
|
+
{/each}
|
|
209
|
+
</ul>
|
|
210
|
+
{/each}
|
|
211
|
+
{/if}
|
|
212
|
+
</nav>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { ClassNameValue } from 'tailwind-merge';
|
|
3
|
+
import type { BadgeProps } from '../Badge/badge.types.js';
|
|
4
|
+
import type { KbdProps } from '../Kbd/kbd.types.js';
|
|
5
|
+
import type { NavigationMenuVariantProps, NavigationMenuSlots } from './navigation-menu.variants.js';
|
|
6
|
+
export type NavigationMenuItemType = {
|
|
7
|
+
/**
|
|
8
|
+
* Item type for compatibility with DropdownMenu.
|
|
9
|
+
*/
|
|
10
|
+
type?: 'item' | 'label' | 'separator' | 'sub';
|
|
11
|
+
/**
|
|
12
|
+
* The visible text displayed in the menu item.
|
|
13
|
+
*/
|
|
14
|
+
label?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Optional icon to display before the label.
|
|
17
|
+
*/
|
|
18
|
+
icon?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Optional badge text/number to display after the label.
|
|
21
|
+
*/
|
|
22
|
+
badge?: string | number;
|
|
23
|
+
/**
|
|
24
|
+
* Badge color (e.g. 'primary', 'error', 'success').
|
|
25
|
+
*/
|
|
26
|
+
badgeColor?: BadgeProps['color'];
|
|
27
|
+
/**
|
|
28
|
+
* URL to navigate to when clicked.
|
|
29
|
+
*/
|
|
30
|
+
href?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Target attribute for the link (e.g., '_blank').
|
|
33
|
+
*/
|
|
34
|
+
target?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Force the active state styling.
|
|
37
|
+
*/
|
|
38
|
+
active?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Disable interaction for this item.
|
|
41
|
+
*/
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether this group should be open by default (when orientation="vertical").
|
|
45
|
+
*/
|
|
46
|
+
defaultOpen?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Keyboard shortcuts to display.
|
|
49
|
+
*/
|
|
50
|
+
shortcut?: string[];
|
|
51
|
+
kbds?: (string | Pick<KbdProps, 'value' | 'size' | 'variant' | 'color'>)[];
|
|
52
|
+
/**
|
|
53
|
+
* Sub-items for this menu item (Svelora standard).
|
|
54
|
+
* If provided, the item renders as a dropdown trigger or accordion.
|
|
55
|
+
*/
|
|
56
|
+
items?: NavigationMenuItemType[];
|
|
57
|
+
/**
|
|
58
|
+
* Alias for sub-items (Nuxt UI style compatibility).
|
|
59
|
+
*/
|
|
60
|
+
children?: NavigationMenuItemType[];
|
|
61
|
+
/**
|
|
62
|
+
* Custom click handler for standard items.
|
|
63
|
+
*/
|
|
64
|
+
onClick?: () => void;
|
|
65
|
+
/**
|
|
66
|
+
* Custom class for this specific item.
|
|
67
|
+
*/
|
|
68
|
+
class?: ClassNameValue;
|
|
69
|
+
};
|
|
70
|
+
export type NavigationMenuProps = {
|
|
71
|
+
/**
|
|
72
|
+
* Array of navigation items. Supports 1D or 2D arrays for grouping.
|
|
73
|
+
*/
|
|
74
|
+
items?: NavigationMenuItemType[] | NavigationMenuItemType[][];
|
|
75
|
+
/**
|
|
76
|
+
* Visual variant of the navigation menu.
|
|
77
|
+
* @default 'default'
|
|
78
|
+
*/
|
|
79
|
+
variant?: NonNullable<NavigationMenuVariantProps['variant']>;
|
|
80
|
+
/**
|
|
81
|
+
* Orientation of the navigation menu.
|
|
82
|
+
* @default 'horizontal'
|
|
83
|
+
*/
|
|
84
|
+
orientation?: NonNullable<NavigationMenuVariantProps['orientation']>;
|
|
85
|
+
/**
|
|
86
|
+
* Enable accordion mode (only affects orientation="vertical").
|
|
87
|
+
* If true, only one group can be open at a time.
|
|
88
|
+
* @default false
|
|
89
|
+
*/
|
|
90
|
+
accordion?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Additional CSS classes.
|
|
93
|
+
*/
|
|
94
|
+
class?: ClassNameValue;
|
|
95
|
+
/**
|
|
96
|
+
* Override specific slot classes.
|
|
97
|
+
*/
|
|
98
|
+
ui?: Partial<Record<NavigationMenuSlots, ClassNameValue>>;
|
|
99
|
+
/**
|
|
100
|
+
* Custom snippet for rendering the entire list.
|
|
101
|
+
* Overrides `items` if provided.
|
|
102
|
+
*/
|
|
103
|
+
children?: Snippet;
|
|
104
|
+
};
|