svelora 3.0.5 → 3.0.7
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 +47 -0
- package/dist/Chart/Chart.svelte.d.ts +4 -0
- package/dist/Chart/chart.types.d.ts +20 -0
- package/dist/Chart/chart.types.js +1 -0
- package/dist/Chart/chart.variants.d.ts +3 -0
- package/dist/Chart/chart.variants.js +4 -0
- package/dist/Chart/index.d.ts +4 -0
- package/dist/Chart/index.js +4 -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/Fonts/fonts.js +3 -1
- package/dist/Link/Link.context-harness.svelte +8 -0
- package/dist/Link/Link.context-harness.svelte.d.ts +7 -0
- package/dist/Link/Link.svelte +57 -30
- package/dist/Link/index.d.ts +2 -0
- package/dist/Link/index.js +1 -0
- package/dist/Link/location-context.d.ts +4 -0
- package/dist/Link/location-context.js +1 -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/Menu/Menu.svelte +134 -0
- package/dist/Menu/Menu.svelte.d.ts +4 -0
- package/dist/Menu/index.d.ts +4 -0
- package/dist/Menu/index.js +4 -0
- package/dist/Menu/menu.types.d.ts +82 -0
- package/dist/Menu/menu.types.js +1 -0
- package/dist/Menu/menu.variants.d.ts +46 -0
- package/dist/Menu/menu.variants.js +32 -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/SelectMenu/SelectMenu.svelte +46 -14
- 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/Stepper/Stepper.svelte +12 -9
- 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 +162 -0
- package/dist/hooks/index.d.ts +14 -0
- package/dist/hooks/index.js +9 -0
- package/dist/hooks/useDebouncedState.svelte.d.ts +30 -0
- package/dist/hooks/useDebouncedState.svelte.js +45 -0
- package/dist/hooks/useEventListener.svelte.d.ts +30 -0
- package/dist/hooks/useEventListener.svelte.js +16 -0
- package/dist/hooks/useFocusTrap.svelte.d.ts +42 -0
- package/dist/hooks/useFocusTrap.svelte.js +87 -0
- package/dist/hooks/useIntersectionObserver.svelte.d.ts +30 -0
- package/dist/hooks/useIntersectionObserver.svelte.js +46 -0
- package/dist/hooks/useLocalStorage.svelte.d.ts +39 -0
- package/dist/hooks/useLocalStorage.svelte.js +73 -0
- package/dist/hooks/useResizeObserver.svelte.d.ts +50 -0
- package/dist/hooks/useResizeObserver.svelte.js +71 -0
- package/dist/hooks/useScrollLock.svelte.d.ts +28 -0
- package/dist/hooks/useScrollLock.svelte.js +79 -0
- package/dist/hooks/useThrottle.svelte.d.ts +37 -0
- package/dist/hooks/useThrottle.svelte.js +72 -0
- package/dist/hooks/useTimers.svelte.d.ts +62 -0
- package/dist/hooks/useTimers.svelte.js +90 -0
- package/dist/hooks/utils.d.ts +1 -0
- package/dist/hooks/utils.js +3 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +18 -0
- package/dist/mcp/svelora-docs.data.json +59 -5
- package/package.json +8 -6
|
@@ -42,13 +42,33 @@ const selectedItems = $derived(selectedValues.map((v) => itemsMap.get(v)).filter
|
|
|
42
42
|
const hasSelection = $derived(selectedValues.length > 0);
|
|
43
43
|
const singleSelectedItem = $derived(multiple ? undefined : selectedItems[0]);
|
|
44
44
|
const displayLabel = $derived(multiple ? selectedItems.map((i) => i.label ?? i.value).join(separator) : singleSelectedItem?.label ?? singleSelectedItem?.value ?? "");
|
|
45
|
+
let isAlive = true;
|
|
46
|
+
$effect(() => {
|
|
47
|
+
return () => {
|
|
48
|
+
isAlive = false;
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
function getSelectedValues() {
|
|
52
|
+
if (!multiple) {
|
|
53
|
+
return typeof value === "string" && value !== "" ? [value] : [];
|
|
54
|
+
}
|
|
55
|
+
return Array.isArray(value) ? value : [];
|
|
56
|
+
}
|
|
57
|
+
function getCombinedItems() {
|
|
58
|
+
const itemList = items;
|
|
59
|
+
const propValues = new Set(itemList.filter((i) => !("type" in i)).map((i) => i.value));
|
|
60
|
+
const extras = createdItems.filter((c) => !propValues.has(c.value));
|
|
61
|
+
return [...itemList, ...extras];
|
|
62
|
+
}
|
|
45
63
|
function removeValue(val) {
|
|
46
64
|
if (!multiple) return;
|
|
47
|
-
|
|
65
|
+
if (!isAlive) return;
|
|
66
|
+
value = getSelectedValues().filter((v) => v !== val);
|
|
48
67
|
emit.onChange();
|
|
49
68
|
}
|
|
50
69
|
function clearSelection() {
|
|
51
70
|
if (!multiple) return;
|
|
71
|
+
if (!isAlive) return;
|
|
52
72
|
value = [];
|
|
53
73
|
emit.onChange();
|
|
54
74
|
}
|
|
@@ -97,29 +117,41 @@ const showCreateItem = $derived.by(() => {
|
|
|
97
117
|
return !exactMatchExists;
|
|
98
118
|
});
|
|
99
119
|
const resolvedCreateLabel = $derived(typeof createItemLabel === "function" ? createItemLabel(trimmedSearch) : createItemLabel);
|
|
100
|
-
function
|
|
120
|
+
function hasExactMatch(query, combined) {
|
|
101
121
|
const q = query.toLowerCase();
|
|
102
|
-
for (const it of
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
for (const it of combined) {
|
|
123
|
+
if ("type" in it) continue;
|
|
124
|
+
if (it.value.toLowerCase() === q || (it.label ?? it.value).toLowerCase() === q) return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
function findItemByCaseInsensitive(query, combined) {
|
|
129
|
+
const q = query.toLowerCase();
|
|
130
|
+
for (const it of combined) {
|
|
131
|
+
if ("type" in it) continue;
|
|
132
|
+
if (it.value.toLowerCase() === q || (it.label ?? it.value).toLowerCase() === q) return it;
|
|
106
133
|
}
|
|
107
134
|
return undefined;
|
|
108
135
|
}
|
|
109
136
|
function selectValue(val) {
|
|
137
|
+
if (!isAlive) return;
|
|
138
|
+
const current = getSelectedValues();
|
|
110
139
|
if (multiple) {
|
|
111
|
-
if (!
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
value = val;
|
|
140
|
+
if (!current.includes(val)) value = [...current, val];
|
|
141
|
+
return;
|
|
116
142
|
}
|
|
143
|
+
value = val;
|
|
117
144
|
}
|
|
118
145
|
function handleCreate() {
|
|
119
|
-
if (!
|
|
120
|
-
|
|
146
|
+
if (!isAlive) return;
|
|
147
|
+
if (!createItem) return;
|
|
148
|
+
const newValue = searchTerm.trim();
|
|
121
149
|
if (!newValue) return;
|
|
122
|
-
const
|
|
150
|
+
const combined = getCombinedItems();
|
|
151
|
+
const mode = createItem === true ? "lazy" : createItem;
|
|
152
|
+
const shouldShow = mode === "always" ? true : !hasExactMatch(newValue, combined);
|
|
153
|
+
if (!shouldShow) return;
|
|
154
|
+
const existing = findItemByCaseInsensitive(newValue, combined);
|
|
123
155
|
if (existing) {
|
|
124
156
|
selectValue(existing.value);
|
|
125
157
|
} else {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">import { twMerge } from "tailwind-merge";
|
|
2
|
+
import { setContext } from "svelte";
|
|
3
|
+
import { sidebarVariants } from "./sidebar.variants.js";
|
|
4
|
+
let { collapsed = false, position = "left", class: className, header, footer, children, ...restProps } = $props();
|
|
5
|
+
let styles = $derived(sidebarVariants({
|
|
6
|
+
collapsed,
|
|
7
|
+
position
|
|
8
|
+
}));
|
|
9
|
+
// Provide context to children (like Menu) so they know if sidebar is collapsed
|
|
10
|
+
// Useful if we want the Menu to hide labels when collapsed
|
|
11
|
+
setContext("sidebar-collapsed", () => collapsed);
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<aside class={twMerge(styles.base(), className)} {...restProps}>
|
|
15
|
+
{#if header}
|
|
16
|
+
<div class={styles.header()}>
|
|
17
|
+
{@render header()}
|
|
18
|
+
</div>
|
|
19
|
+
{/if}
|
|
20
|
+
|
|
21
|
+
<div class={styles.content()}>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
{#if footer}
|
|
26
|
+
<div class={styles.footer()}>
|
|
27
|
+
{@render footer()}
|
|
28
|
+
</div>
|
|
29
|
+
{/if}
|
|
30
|
+
</aside>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { SidebarVariantProps } from './sidebar.variants.js';
|
|
4
|
+
export interface SidebarProps extends HTMLAttributes<HTMLElement> {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the sidebar is collapsed (icons only).
|
|
7
|
+
* @default false
|
|
8
|
+
*/
|
|
9
|
+
collapsed?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Position of the sidebar.
|
|
12
|
+
* @default 'left'
|
|
13
|
+
*/
|
|
14
|
+
position?: SidebarVariantProps['position'];
|
|
15
|
+
/**
|
|
16
|
+
* Additional CSS classes.
|
|
17
|
+
*/
|
|
18
|
+
class?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Snippet for the top header section (e.g. Logo).
|
|
21
|
+
*/
|
|
22
|
+
header?: Snippet;
|
|
23
|
+
/**
|
|
24
|
+
* Snippet for the main content (e.g. Navigation Menu).
|
|
25
|
+
*/
|
|
26
|
+
children?: Snippet;
|
|
27
|
+
/**
|
|
28
|
+
* Snippet for the bottom footer section (e.g. User Profile).
|
|
29
|
+
*/
|
|
30
|
+
footer?: Snippet;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
2
|
+
export declare const sidebarVariants: import("tailwind-variants").TVReturnType<{
|
|
3
|
+
position: {
|
|
4
|
+
left: {
|
|
5
|
+
base: string;
|
|
6
|
+
};
|
|
7
|
+
right: {
|
|
8
|
+
base: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
collapsed: {
|
|
12
|
+
true: {
|
|
13
|
+
base: string;
|
|
14
|
+
};
|
|
15
|
+
false: {
|
|
16
|
+
base: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}, {
|
|
20
|
+
base: string;
|
|
21
|
+
header: string;
|
|
22
|
+
content: string;
|
|
23
|
+
footer: string;
|
|
24
|
+
}, undefined, {
|
|
25
|
+
position: {
|
|
26
|
+
left: {
|
|
27
|
+
base: string;
|
|
28
|
+
};
|
|
29
|
+
right: {
|
|
30
|
+
base: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
collapsed: {
|
|
34
|
+
true: {
|
|
35
|
+
base: string;
|
|
36
|
+
};
|
|
37
|
+
false: {
|
|
38
|
+
base: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
}, {
|
|
42
|
+
base: string;
|
|
43
|
+
header: string;
|
|
44
|
+
content: string;
|
|
45
|
+
footer: string;
|
|
46
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
47
|
+
position: {
|
|
48
|
+
left: {
|
|
49
|
+
base: string;
|
|
50
|
+
};
|
|
51
|
+
right: {
|
|
52
|
+
base: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
collapsed: {
|
|
56
|
+
true: {
|
|
57
|
+
base: string;
|
|
58
|
+
};
|
|
59
|
+
false: {
|
|
60
|
+
base: string;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
}, {
|
|
64
|
+
base: string;
|
|
65
|
+
header: string;
|
|
66
|
+
content: string;
|
|
67
|
+
footer: string;
|
|
68
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
69
|
+
export type SidebarVariantProps = VariantProps<typeof sidebarVariants>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { tv } from 'tailwind-variants';
|
|
2
|
+
export const sidebarVariants = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
base: 'flex flex-col h-full bg-surface-50 dark:bg-surface-900 border-outline-variant text-surface-900 dark:text-surface-50 transition-all duration-300',
|
|
5
|
+
header: 'flex items-center px-4 h-16 shrink-0 border-b border-outline-variant',
|
|
6
|
+
content: 'flex-1 overflow-y-auto px-3 py-4',
|
|
7
|
+
footer: 'p-4 mt-auto border-t border-outline-variant shrink-0'
|
|
8
|
+
},
|
|
9
|
+
variants: {
|
|
10
|
+
position: {
|
|
11
|
+
left: { base: 'border-r' },
|
|
12
|
+
right: { base: 'border-l' }
|
|
13
|
+
},
|
|
14
|
+
collapsed: {
|
|
15
|
+
true: { base: 'w-16' },
|
|
16
|
+
false: { base: 'w-64' }
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
position: 'left',
|
|
21
|
+
collapsed: false
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script lang="ts">import { twMerge } from "tailwind-merge";
|
|
2
|
+
import { spotlightVariants } from "./spotlight.variants.js";
|
|
3
|
+
let { size = 400, color = "rgba(255, 255, 255, 0.1)", class: className, children, ...restProps } = $props();
|
|
4
|
+
let styles = $derived(spotlightVariants());
|
|
5
|
+
let mouseX = $state(0);
|
|
6
|
+
let mouseY = $state(0);
|
|
7
|
+
function handleMouseMove(e) {
|
|
8
|
+
const target = e.currentTarget;
|
|
9
|
+
const rect = target.getBoundingClientRect();
|
|
10
|
+
mouseX = e.clientX - rect.left;
|
|
11
|
+
mouseY = e.clientY - rect.top;
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
16
|
+
<div
|
|
17
|
+
class={twMerge(styles.root() as string, className)}
|
|
18
|
+
onmousemove={handleMouseMove}
|
|
19
|
+
{...restProps}
|
|
20
|
+
>
|
|
21
|
+
<!-- Spotlight Effect Background -->
|
|
22
|
+
<div
|
|
23
|
+
class={styles.spotlight() as string}
|
|
24
|
+
style:background={`radial-gradient(${size}px circle at ${mouseX}px ${mouseY}px, ${color}, transparent 80%)`}
|
|
25
|
+
></div>
|
|
26
|
+
|
|
27
|
+
<!-- Content -->
|
|
28
|
+
<div class={styles.content() as string}>
|
|
29
|
+
{@render children?.()}
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
export interface SpotlightProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
/**
|
|
5
|
+
* Size of the spotlight in pixels.
|
|
6
|
+
* @default 400
|
|
7
|
+
*/
|
|
8
|
+
size?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Color of the spotlight in rgba, hex, or CSS variable.
|
|
11
|
+
* @default 'rgba(255, 255, 255, 0.1)'
|
|
12
|
+
*/
|
|
13
|
+
color?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Additional CSS classes.
|
|
16
|
+
*/
|
|
17
|
+
class?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Content to render inside the spotlight container.
|
|
20
|
+
*/
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
2
|
+
export declare const spotlightVariants: import("tailwind-variants").TVReturnType<{
|
|
3
|
+
[key: string]: {
|
|
4
|
+
[key: string]: import("tailwind-merge").ClassNameValue | {
|
|
5
|
+
content?: import("tailwind-merge").ClassNameValue;
|
|
6
|
+
root?: import("tailwind-merge").ClassNameValue;
|
|
7
|
+
spotlight?: import("tailwind-merge").ClassNameValue;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
} | {
|
|
11
|
+
[x: string]: {
|
|
12
|
+
[x: string]: import("tailwind-merge").ClassNameValue | {
|
|
13
|
+
content?: import("tailwind-merge").ClassNameValue;
|
|
14
|
+
root?: import("tailwind-merge").ClassNameValue;
|
|
15
|
+
spotlight?: import("tailwind-merge").ClassNameValue;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
} | {}, {
|
|
19
|
+
root: string;
|
|
20
|
+
spotlight: string;
|
|
21
|
+
content: string;
|
|
22
|
+
}, undefined, {
|
|
23
|
+
[key: string]: {
|
|
24
|
+
[key: string]: import("tailwind-merge").ClassNameValue | {
|
|
25
|
+
content?: import("tailwind-merge").ClassNameValue;
|
|
26
|
+
root?: import("tailwind-merge").ClassNameValue;
|
|
27
|
+
spotlight?: import("tailwind-merge").ClassNameValue;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
} | {}, {
|
|
31
|
+
root: string;
|
|
32
|
+
spotlight: string;
|
|
33
|
+
content: string;
|
|
34
|
+
}, import("tailwind-variants").TVReturnType<unknown, {
|
|
35
|
+
root: string;
|
|
36
|
+
spotlight: string;
|
|
37
|
+
content: string;
|
|
38
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
39
|
+
export type SpotlightVariantProps = VariantProps<typeof spotlightVariants>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { tv } from 'tailwind-variants';
|
|
2
|
+
export const spotlightVariants = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
root: 'relative overflow-hidden group',
|
|
5
|
+
spotlight: 'pointer-events-none absolute -inset-px rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100',
|
|
6
|
+
content: 'relative z-10 h-full w-full'
|
|
7
|
+
}
|
|
8
|
+
});
|
|
@@ -55,28 +55,31 @@ function handleKeydown(e, index) {
|
|
|
55
55
|
}
|
|
56
56
|
const apiInstance = {
|
|
57
57
|
next() {
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const current = untrack(() => activeIndex);
|
|
59
|
+
if (current >= items.length - 1) return;
|
|
60
|
+
const target = items[current + 1];
|
|
60
61
|
if (!target) return;
|
|
61
|
-
setValue(getItemValue(target,
|
|
62
|
+
setValue(getItemValue(target, current + 1));
|
|
62
63
|
},
|
|
63
64
|
prev() {
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const current = untrack(() => activeIndex);
|
|
66
|
+
if (current <= 0) return;
|
|
67
|
+
const target = items[current - 1];
|
|
66
68
|
if (!target) return;
|
|
67
|
-
setValue(getItemValue(target,
|
|
69
|
+
setValue(getItemValue(target, current - 1));
|
|
68
70
|
},
|
|
69
71
|
goTo(next) {
|
|
70
72
|
setValue(next);
|
|
71
73
|
},
|
|
72
74
|
get hasNext() {
|
|
73
|
-
|
|
75
|
+
const current = untrack(() => activeIndex);
|
|
76
|
+
return current >= 0 && current < items.length - 1;
|
|
74
77
|
},
|
|
75
78
|
get hasPrev() {
|
|
76
|
-
return activeIndex > 0;
|
|
79
|
+
return untrack(() => activeIndex) > 0;
|
|
77
80
|
},
|
|
78
81
|
get activeIndex() {
|
|
79
|
-
return activeIndex;
|
|
82
|
+
return untrack(() => activeIndex);
|
|
80
83
|
}
|
|
81
84
|
};
|
|
82
85
|
api = apiInstance;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script lang="ts">import { twMerge } from "tailwind-merge";
|
|
2
|
+
import Icon from "../Icon/Icon.svelte";
|
|
3
|
+
import { tagsInputVariants } from "./tags-input.variants.js";
|
|
4
|
+
let { value = $bindable([]), placeholder = "Add a tag...", disabled = false, maxTags, separators = [",", "Enter"], class: className, onchange, ...restProps } = $props();
|
|
5
|
+
let styles = $derived(tagsInputVariants({ disabled }));
|
|
6
|
+
let inputValue = $state("");
|
|
7
|
+
let inputRef = $state(null);
|
|
8
|
+
function addTag(tag) {
|
|
9
|
+
const t = tag.trim();
|
|
10
|
+
if (!t) return;
|
|
11
|
+
if (maxTags && value.length >= maxTags) return;
|
|
12
|
+
if (value.includes(t)) return;
|
|
13
|
+
value = [...value, t];
|
|
14
|
+
inputValue = "";
|
|
15
|
+
onchange?.(value);
|
|
16
|
+
}
|
|
17
|
+
function removeTag(index) {
|
|
18
|
+
if (disabled) return;
|
|
19
|
+
value = value.filter((_, i) => i !== index);
|
|
20
|
+
onchange?.(value);
|
|
21
|
+
}
|
|
22
|
+
function handleKeyDown(e) {
|
|
23
|
+
if (disabled) return;
|
|
24
|
+
// Backspace to remove last tag if input is empty
|
|
25
|
+
if (e.key === "Backspace" && inputValue === "" && value.length > 0) {
|
|
26
|
+
removeTag(value.length - 1);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Add tag on separator
|
|
30
|
+
if (separators.includes(e.key)) {
|
|
31
|
+
e.preventDefault();
|
|
32
|
+
addTag(inputValue);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Also handle pasting comma-separated values
|
|
36
|
+
function handlePaste(e) {
|
|
37
|
+
if (disabled) return;
|
|
38
|
+
const pasteData = e.clipboardData?.getData("text");
|
|
39
|
+
if (pasteData) {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
const tags = pasteData.split(separators.includes(",") ? "," : "\n").map((t) => t.trim()).filter(Boolean);
|
|
42
|
+
let newTags = [...value];
|
|
43
|
+
for (const t of tags) {
|
|
44
|
+
if (maxTags && newTags.length >= maxTags) break;
|
|
45
|
+
if (!newTags.includes(t)) {
|
|
46
|
+
newTags.push(t);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (newTags.length !== value.length) {
|
|
50
|
+
value = newTags;
|
|
51
|
+
onchange?.(value);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function handleContainerClick() {
|
|
56
|
+
if (!disabled && inputRef) {
|
|
57
|
+
inputRef.focus();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
63
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
64
|
+
<div
|
|
65
|
+
class={twMerge(styles.base() as any, className)}
|
|
66
|
+
onclick={handleContainerClick}
|
|
67
|
+
{...restProps}
|
|
68
|
+
>
|
|
69
|
+
{#each value as tag, i}
|
|
70
|
+
<span class={styles.tag() as any}>
|
|
71
|
+
<span class={styles.tagText() as any}>{tag}</span>
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
class={styles.removeButton() as any}
|
|
75
|
+
onclick={(e) => {
|
|
76
|
+
e.stopPropagation()
|
|
77
|
+
removeTag(i)
|
|
78
|
+
}}
|
|
79
|
+
{disabled}
|
|
80
|
+
aria-label={`Remove tag ${tag}`}
|
|
81
|
+
>
|
|
82
|
+
<Icon name="lucide:x" class="w-3 h-3" />
|
|
83
|
+
</button>
|
|
84
|
+
</span>
|
|
85
|
+
{/each}
|
|
86
|
+
|
|
87
|
+
{#if !maxTags || value.length < maxTags}
|
|
88
|
+
<input
|
|
89
|
+
bind:this={inputRef}
|
|
90
|
+
bind:value={inputValue}
|
|
91
|
+
type="text"
|
|
92
|
+
{placeholder}
|
|
93
|
+
{disabled}
|
|
94
|
+
class={styles.input() as any}
|
|
95
|
+
onkeydown={handleKeyDown}
|
|
96
|
+
onpaste={handlePaste}
|
|
97
|
+
onblur={() => addTag(inputValue)}
|
|
98
|
+
/>
|
|
99
|
+
{/if}
|
|
100
|
+
</div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
export interface TagsInputProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
|
|
3
|
+
/**
|
|
4
|
+
* Array of tag strings.
|
|
5
|
+
*/
|
|
6
|
+
value?: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Placeholder text for the input field.
|
|
9
|
+
*/
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the input is disabled.
|
|
13
|
+
*/
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Maximum number of tags allowed.
|
|
17
|
+
*/
|
|
18
|
+
maxTags?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Array of characters that trigger tag creation.
|
|
21
|
+
* @default [',', 'Enter']
|
|
22
|
+
*/
|
|
23
|
+
separators?: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Additional CSS classes.
|
|
26
|
+
*/
|
|
27
|
+
class?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Event fired when tags change.
|
|
30
|
+
*/
|
|
31
|
+
onchange?: (tags: string[]) => void;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
2
|
+
export declare const tagsInputVariants: import("tailwind-variants").TVReturnType<{
|
|
3
|
+
disabled: {
|
|
4
|
+
true: {
|
|
5
|
+
base: string;
|
|
6
|
+
input: string;
|
|
7
|
+
removeButton: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
}, {
|
|
11
|
+
base: string;
|
|
12
|
+
tag: string;
|
|
13
|
+
tagText: string;
|
|
14
|
+
removeButton: string;
|
|
15
|
+
input: string;
|
|
16
|
+
}, undefined, {
|
|
17
|
+
disabled: {
|
|
18
|
+
true: {
|
|
19
|
+
base: string;
|
|
20
|
+
input: string;
|
|
21
|
+
removeButton: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}, {
|
|
25
|
+
base: string;
|
|
26
|
+
tag: string;
|
|
27
|
+
tagText: string;
|
|
28
|
+
removeButton: string;
|
|
29
|
+
input: string;
|
|
30
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
31
|
+
disabled: {
|
|
32
|
+
true: {
|
|
33
|
+
base: string;
|
|
34
|
+
input: string;
|
|
35
|
+
removeButton: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}, {
|
|
39
|
+
base: string;
|
|
40
|
+
tag: string;
|
|
41
|
+
tagText: string;
|
|
42
|
+
removeButton: string;
|
|
43
|
+
input: string;
|
|
44
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
45
|
+
export type TagsInputVariantProps = VariantProps<typeof tagsInputVariants>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { tv } from 'tailwind-variants';
|
|
2
|
+
export const tagsInputVariants = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
base: 'flex flex-wrap items-center gap-2 p-2 w-full rounded-md border border-outline-variant bg-surface-50 dark:bg-surface-900 focus-within:ring-2 focus-within:ring-primary focus-within:border-primary transition-colors cursor-text',
|
|
5
|
+
tag: 'flex items-center gap-1 px-2.5 py-1 text-sm rounded-full bg-surface-200 dark:bg-surface-800 text-surface-900 dark:text-surface-50',
|
|
6
|
+
tagText: 'truncate max-w-[150px]',
|
|
7
|
+
removeButton: 'flex items-center justify-center w-4 h-4 rounded-full hover:bg-surface-300 dark:hover:bg-surface-700 transition-colors focus:outline-none focus:ring-2 focus:ring-primary',
|
|
8
|
+
input: 'flex-1 min-w-[120px] bg-transparent border-0 outline-none focus:ring-0 text-sm text-surface-900 dark:text-surface-50 placeholder:text-surface-500 dark:placeholder:text-surface-400 p-1'
|
|
9
|
+
},
|
|
10
|
+
variants: {
|
|
11
|
+
disabled: {
|
|
12
|
+
true: {
|
|
13
|
+
base: 'opacity-50 cursor-not-allowed',
|
|
14
|
+
input: 'cursor-not-allowed',
|
|
15
|
+
removeButton: 'pointer-events-none'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
disabled: false
|
|
21
|
+
}
|
|
22
|
+
});
|