@lanrenbang/basecoat-ultra-svelte 0.1.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 (88) hide show
  1. package/dist/components/Accordion.svelte +64 -0
  2. package/dist/components/Accordion.svelte.d.ts +14 -0
  3. package/dist/components/Alert.svelte +51 -0
  4. package/dist/components/Alert.svelte.d.ts +12 -0
  5. package/dist/components/Avatar.svelte +57 -0
  6. package/dist/components/Avatar.svelte.d.ts +13 -0
  7. package/dist/components/Badge.svelte +30 -0
  8. package/dist/components/Badge.svelte.d.ts +10 -0
  9. package/dist/components/Breadcrumb.svelte +54 -0
  10. package/dist/components/Breadcrumb.svelte.d.ts +14 -0
  11. package/dist/components/Button.svelte +38 -0
  12. package/dist/components/Button.svelte.d.ts +12 -0
  13. package/dist/components/ButtonGroup.svelte +26 -0
  14. package/dist/components/ButtonGroup.svelte.d.ts +10 -0
  15. package/dist/components/Card.svelte +67 -0
  16. package/dist/components/Card.svelte.d.ts +13 -0
  17. package/dist/components/Carousel.svelte +142 -0
  18. package/dist/components/Carousel.svelte.d.ts +11 -0
  19. package/dist/components/CatppuccinThemeSwitcher.svelte +132 -0
  20. package/dist/components/CatppuccinThemeSwitcher.svelte.d.ts +3 -0
  21. package/dist/components/Checkbox.svelte +20 -0
  22. package/dist/components/Checkbox.svelte.d.ts +8 -0
  23. package/dist/components/Collapsible.svelte +39 -0
  24. package/dist/components/Collapsible.svelte.d.ts +13 -0
  25. package/dist/components/Command.svelte +78 -0
  26. package/dist/components/Command.svelte.d.ts +12 -0
  27. package/dist/components/DatePicker.svelte +172 -0
  28. package/dist/components/DatePicker.svelte.d.ts +13 -0
  29. package/dist/components/Dialog.svelte +91 -0
  30. package/dist/components/Dialog.svelte.d.ts +14 -0
  31. package/dist/components/Drawer.svelte +127 -0
  32. package/dist/components/Drawer.svelte.d.ts +12 -0
  33. package/dist/components/DropdownMenu.svelte +62 -0
  34. package/dist/components/DropdownMenu.svelte.d.ts +14 -0
  35. package/dist/components/Empty.svelte +58 -0
  36. package/dist/components/Empty.svelte.d.ts +12 -0
  37. package/dist/components/Input.svelte +22 -0
  38. package/dist/components/Input.svelte.d.ts +9 -0
  39. package/dist/components/InputOTP.svelte +189 -0
  40. package/dist/components/InputOTP.svelte.d.ts +12 -0
  41. package/dist/components/Item.svelte +64 -0
  42. package/dist/components/Item.svelte.d.ts +14 -0
  43. package/dist/components/Kbd.svelte +28 -0
  44. package/dist/components/Kbd.svelte.d.ts +9 -0
  45. package/dist/components/Label.svelte +30 -0
  46. package/dist/components/Label.svelte.d.ts +10 -0
  47. package/dist/components/Pagination.svelte +120 -0
  48. package/dist/components/Pagination.svelte.d.ts +35 -0
  49. package/dist/components/Popover.svelte +68 -0
  50. package/dist/components/Popover.svelte.d.ts +16 -0
  51. package/dist/components/Progress.svelte +26 -0
  52. package/dist/components/Progress.svelte.d.ts +9 -0
  53. package/dist/components/Radio.svelte +22 -0
  54. package/dist/components/Radio.svelte.d.ts +9 -0
  55. package/dist/components/Resizable.svelte +66 -0
  56. package/dist/components/Resizable.svelte.d.ts +13 -0
  57. package/dist/components/Select.svelte +183 -0
  58. package/dist/components/Select.svelte.d.ts +16 -0
  59. package/dist/components/Separator.svelte +19 -0
  60. package/dist/components/Separator.svelte.d.ts +8 -0
  61. package/dist/components/Sheet.svelte +182 -0
  62. package/dist/components/Sheet.svelte.d.ts +13 -0
  63. package/dist/components/Skeleton.svelte +27 -0
  64. package/dist/components/Skeleton.svelte.d.ts +8 -0
  65. package/dist/components/Slider.svelte +38 -0
  66. package/dist/components/Slider.svelte.d.ts +11 -0
  67. package/dist/components/Spinner.svelte +28 -0
  68. package/dist/components/Spinner.svelte.d.ts +8 -0
  69. package/dist/components/Switch.svelte +20 -0
  70. package/dist/components/Switch.svelte.d.ts +8 -0
  71. package/dist/components/Table.svelte +61 -0
  72. package/dist/components/Table.svelte.d.ts +13 -0
  73. package/dist/components/Tabs.svelte +97 -0
  74. package/dist/components/Tabs.svelte.d.ts +15 -0
  75. package/dist/components/Textarea.svelte +22 -0
  76. package/dist/components/Textarea.svelte.d.ts +9 -0
  77. package/dist/components/Toast.svelte +73 -0
  78. package/dist/components/Toast.svelte.d.ts +3 -0
  79. package/dist/components/Toggle.svelte +69 -0
  80. package/dist/components/Toggle.svelte.d.ts +13 -0
  81. package/dist/components/ToggleGroup.svelte +69 -0
  82. package/dist/components/ToggleGroup.svelte.d.ts +12 -0
  83. package/dist/components/Tooltip.svelte +32 -0
  84. package/dist/components/Tooltip.svelte.d.ts +11 -0
  85. package/dist/index.d.ts +42 -0
  86. package/dist/index.js +47 -0
  87. package/dist/reference.css +2 -0
  88. package/package.json +70 -0
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ items = [],
6
+ multiple = false,
7
+ class: className = '',
8
+ ...rest
9
+ }: {
10
+ items: Array<{ title: string | Snippet; content: string | Snippet; open?: boolean }>;
11
+ multiple?: boolean;
12
+ class?: string;
13
+ [key: string]: any;
14
+ } = $props();
15
+
16
+ // Track open state for each item - use $derived for reactive initialization
17
+ let openStates = $state<boolean[]>([]);
18
+
19
+ // Initialize open states when items change
20
+ $effect(() => {
21
+ if (openStates.length !== items.length) {
22
+ openStates = items.map(item => item.open ?? false);
23
+ }
24
+ });
25
+
26
+ function handleToggle(index: number) {
27
+ if (multiple) {
28
+ // Multiple mode: just toggle the clicked item
29
+ openStates[index] = !openStates[index];
30
+ } else {
31
+ // Single mode: close all others, toggle clicked
32
+ openStates = openStates.map((_, i) => i === index ? !openStates[index] : false);
33
+ }
34
+ }
35
+ </script>
36
+
37
+ <section class={['accordion', className].filter(Boolean).join(' ')} {...rest}>
38
+ {#each items as item, index}
39
+ <details class="group border-b last:border-b-0" open={openStates[index]}>
40
+ <summary
41
+ class="w-full flex items-center justify-between py-4 text-sm font-medium hover:underline cursor-pointer outline-none"
42
+ onclick={(e) => { e.preventDefault(); handleToggle(index); }}
43
+ >
44
+ {#if typeof item.title === 'string'}
45
+ {item.title}
46
+ {:else}
47
+ {@render item.title()}
48
+ {/if}
49
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="transition-transform duration-200 group-open:rotate-180 text-muted-foreground"><path d="m6 9 6 6 6-6"/></svg>
50
+ </summary>
51
+ <div class="pb-4 text-sm text-muted-foreground">
52
+ {#if typeof item.content === 'string'}
53
+ {item.content}
54
+ {:else}
55
+ {@render item.content()}
56
+ {/if}
57
+ </div>
58
+ </details>
59
+ {/each}
60
+ </section>
61
+
62
+ <style>
63
+ @reference "../reference.css";
64
+ </style>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ items: Array<{
4
+ title: string | Snippet;
5
+ content: string | Snippet;
6
+ open?: boolean;
7
+ }>;
8
+ multiple?: boolean;
9
+ class?: string;
10
+ [key: string]: any;
11
+ };
12
+ declare const Accordion: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type Accordion = ReturnType<typeof Accordion>;
14
+ export default Accordion;
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ children,
6
+ icon,
7
+ title,
8
+ variant = 'default',
9
+ class: className = '',
10
+ ...rest
11
+ }: {
12
+ children?: Snippet;
13
+ icon?: Snippet;
14
+ title?: Snippet | string;
15
+ variant?: 'default' | 'destructive';
16
+ class?: string;
17
+ [key: string]: any;
18
+ } = $props();
19
+
20
+ const baseClass = $derived(variant === 'destructive' ? 'alert-destructive' : 'alert');
21
+ const finalClass = $derived([baseClass, className]
22
+ .filter(Boolean)
23
+ .join(' '));
24
+ </script>
25
+
26
+ <div role="alert" class={finalClass} {...rest}>
27
+ {#if icon}
28
+ {@render icon()}
29
+ {/if}
30
+
31
+ {#if title}
32
+ {#if typeof title === 'string'}
33
+ <strong data-title>{title}</strong>
34
+ {:else}
35
+ <div data-title>
36
+ {@render title()}
37
+ </div>
38
+ {/if}
39
+ {/if}
40
+
41
+ {#if children}
42
+ <section>
43
+ {@render children()}
44
+ </section>
45
+ {/if}
46
+ </div>
47
+
48
+ <style>
49
+ @import "../../../../ultra/src/css/parts/components/alert.css";
50
+ @reference "../reference.css";
51
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ icon?: Snippet;
5
+ title?: Snippet | string;
6
+ variant?: 'default' | 'destructive';
7
+ class?: string;
8
+ [key: string]: any;
9
+ };
10
+ declare const Alert: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type Alert = ReturnType<typeof Alert>;
12
+ export default Alert;
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ src,
6
+ alt = 'Avatar',
7
+ fallback,
8
+ class: className = '',
9
+ size = 'md',
10
+ rounded = 'full',
11
+ ...rest
12
+ }: {
13
+ src?: string;
14
+ alt?: string;
15
+ fallback?: Snippet | string;
16
+ class?: string;
17
+ size?: 'sm' | 'md' | 'lg' | string;
18
+ rounded?: 'full' | 'lg' | 'md' | 'sm' | 'none';
19
+ [key: string]: any;
20
+ } = $props();
21
+
22
+ const sizeMap: Record<string, string> = {
23
+ sm: 'size-8',
24
+ md: 'size-10',
25
+ lg: 'size-12'
26
+ };
27
+
28
+ const roundedMap: Record<string, string> = {
29
+ full: 'rounded-full',
30
+ lg: 'rounded-lg',
31
+ md: 'rounded-md',
32
+ sm: 'rounded-sm',
33
+ none: 'rounded-none'
34
+ };
35
+
36
+ const sizeClass = $derived(sizeMap[size] || size);
37
+ const roundedClass = $derived(roundedMap[rounded] || rounded);
38
+
39
+ const finalClass = $derived([
40
+ 'avatar overflow-hidden bg-muted flex items-center justify-center shrink-0 object-cover',
41
+ sizeClass,
42
+ roundedClass,
43
+ className
44
+ ].filter(Boolean).join(' '));
45
+ </script>
46
+
47
+ <div class={finalClass} {...rest}>
48
+ {#if src}
49
+ <img {src} {alt} class="h-full w-full object-cover" />
50
+ {:else if fallback}
51
+ {#if typeof fallback === 'string'}
52
+ <span class="text-xs font-medium uppercase">{fallback}</span>
53
+ {:else}
54
+ {@render fallback()}
55
+ {/if}
56
+ {/if}
57
+ </div>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ src?: string;
4
+ alt?: string;
5
+ fallback?: Snippet | string;
6
+ class?: string;
7
+ size?: 'sm' | 'md' | 'lg' | string;
8
+ rounded?: 'full' | 'lg' | 'md' | 'sm' | 'none';
9
+ [key: string]: any;
10
+ };
11
+ declare const Avatar: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Avatar = ReturnType<typeof Avatar>;
13
+ export default Avatar;
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ children,
6
+ variant = 'primary',
7
+ class: className = '',
8
+ ...rest
9
+ }: {
10
+ children?: Snippet;
11
+ variant?: 'primary' | 'secondary' | 'destructive' | 'outline';
12
+ class?: string;
13
+ [key: string]: any;
14
+ } = $props();
15
+
16
+ const baseClass = 'badge';
17
+ const variantClass = $derived(variant !== 'primary' ? `badge-${variant}` : '');
18
+ const finalClass = $derived([baseClass, variantClass, className]
19
+ .filter(Boolean)
20
+ .join(' '));
21
+ </script>
22
+
23
+ <span class={finalClass} {...rest}>
24
+ {@render children?.()}
25
+ </span>
26
+
27
+ <style>
28
+ @import "../../../../ultra/src/css/parts/components/badge.css";
29
+ @reference "../reference.css";
30
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ variant?: 'primary' | 'secondary' | 'destructive' | 'outline';
5
+ class?: string;
6
+ [key: string]: any;
7
+ };
8
+ declare const Badge: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type Badge = ReturnType<typeof Badge>;
10
+ export default Badge;
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ items = [],
6
+ separator = '/',
7
+ class: className = '',
8
+ ...rest
9
+ }: {
10
+ items: Array<{ label: string | Snippet; href?: string; active?: boolean }>;
11
+ separator?: string | Snippet;
12
+ class?: string;
13
+ [key: string]: any;
14
+ } = $props();
15
+
16
+ const finalClass = $derived(['flex text-sm text-muted-foreground', className].filter(Boolean).join(' '));
17
+ </script>
18
+
19
+ <nav aria-label="Breadcrumb" class={finalClass} {...rest}>
20
+ <ol class="flex items-center gap-2">
21
+ {#each items as item, i}
22
+ {#if i > 0}
23
+ <li>
24
+ <span class="opacity-50">
25
+ {#if typeof separator === 'string'}
26
+ {separator}
27
+ {:else}
28
+ {@render separator()}
29
+ {/if}
30
+ </span>
31
+ </li>
32
+ {/if}
33
+ <li>
34
+ {#if item.href && !item.active}
35
+ <a href={item.href} class="hover:text-foreground transition-colors">
36
+ {#if typeof item.label === 'string'}
37
+ {item.label}
38
+ {:else}
39
+ {@render item.label()}
40
+ {/if}
41
+ </a>
42
+ {:else}
43
+ <span class={item.active ? 'text-foreground font-medium' : ''}>
44
+ {#if typeof item.label === 'string'}
45
+ {item.label}
46
+ {:else}
47
+ {@render item.label()}
48
+ {/if}
49
+ </span>
50
+ {/if}
51
+ </li>
52
+ {/each}
53
+ </ol>
54
+ </nav>
@@ -0,0 +1,14 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ items: Array<{
4
+ label: string | Snippet;
5
+ href?: string;
6
+ active?: boolean;
7
+ }>;
8
+ separator?: string | Snippet;
9
+ class?: string;
10
+ [key: string]: any;
11
+ };
12
+ declare const Breadcrumb: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type Breadcrumb = ReturnType<typeof Breadcrumb>;
14
+ export default Breadcrumb;
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ // Svelte 5 Runes: 使用 $props() 接收属性
5
+ let {
6
+ children,
7
+ type = 'button',
8
+ variant = 'primary',
9
+ size = 'md',
10
+ class: className = '',
11
+ ...rest
12
+ }: {
13
+ children?: Snippet;
14
+ type?: 'button' | 'submit' | 'reset';
15
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';
16
+ size?: 'sm' | 'md' | 'lg' | 'icon';
17
+ class?: string;
18
+ [key: string]: any;
19
+ } = $props();
20
+
21
+ // 使用 $derived 确保属性更新时类名同步更新
22
+ const baseClass = 'btn';
23
+ const variantClass = $derived(variant !== 'primary' ? `btn-${variant}` : '');
24
+ const sizeClass = $derived(size !== 'md' ? `btn-${size}` : '');
25
+
26
+ const finalClass = $derived([baseClass, variantClass, sizeClass, className]
27
+ .filter(Boolean)
28
+ .join(' '));
29
+ </script>
30
+
31
+ <button {type} class={finalClass} {...rest}>
32
+ {@render children?.()}
33
+ </button>
34
+
35
+ <style>
36
+ @import "../../../../ultra/src/css/parts/components/button.css";
37
+ @reference "../reference.css";
38
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ type?: 'button' | 'submit' | 'reset';
5
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';
6
+ size?: 'sm' | 'md' | 'lg' | 'icon';
7
+ class?: string;
8
+ [key: string]: any;
9
+ };
10
+ declare const Button: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type Button = ReturnType<typeof Button>;
12
+ export default Button;
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ children,
6
+ orientation = 'horizontal',
7
+ class: className = '',
8
+ ...rest
9
+ }: {
10
+ children?: Snippet;
11
+ orientation?: 'horizontal' | 'vertical';
12
+ class?: string;
13
+ [key: string]: any;
14
+ } = $props();
15
+
16
+ const finalClass = $derived(['button-group', className].filter(Boolean).join(' '));
17
+ </script>
18
+
19
+ <div class={finalClass} data-orientation={orientation} {...rest}>
20
+ {@render children?.()}
21
+ </div>
22
+
23
+ <style>
24
+ @import "../../../../ultra/src/css/parts/components/button-group.css";
25
+ @reference "../reference.css";
26
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ orientation?: 'horizontal' | 'vertical';
5
+ class?: string;
6
+ [key: string]: any;
7
+ };
8
+ declare const ButtonGroup: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type ButtonGroup = ReturnType<typeof ButtonGroup>;
10
+ export default ButtonGroup;
@@ -0,0 +1,67 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ children,
6
+ header,
7
+ footer,
8
+ title,
9
+ description,
10
+ class: className = '',
11
+ ...rest
12
+ }: {
13
+ children?: Snippet;
14
+ header?: Snippet;
15
+ footer?: Snippet;
16
+ title?: Snippet | string;
17
+ description?: Snippet | string;
18
+ class?: string;
19
+ [key: string]: any;
20
+ } = $props();
21
+
22
+ const finalClass = $derived(['card', className]
23
+ .filter(Boolean)
24
+ .join(' '));
25
+ </script>
26
+
27
+ <article class={finalClass} {...rest}>
28
+ {#if header || title || description}
29
+ <header>
30
+ {#if header}
31
+ {@render header()}
32
+ {:else}
33
+ {#if title}
34
+ {#if typeof title === 'string'}
35
+ <h2>{title}</h2>
36
+ {:else}
37
+ {@render title()}
38
+ {/if}
39
+ {/if}
40
+ {#if description}
41
+ {#if typeof description === 'string'}
42
+ <p>{description}</p>
43
+ {:else}
44
+ {@render description()}
45
+ {/if}
46
+ {/if}
47
+ {/if}
48
+ </header>
49
+ {/if}
50
+
51
+ {#if children}
52
+ <section>
53
+ {@render children()}
54
+ </section>
55
+ {/if}
56
+
57
+ {#if footer}
58
+ <footer>
59
+ {@render footer()}
60
+ </footer>
61
+ {/if}
62
+ </article>
63
+
64
+ <style>
65
+ @import "../../../../ultra/src/css/parts/components/card.css";
66
+ @reference "../reference.css";
67
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ header?: Snippet;
5
+ footer?: Snippet;
6
+ title?: Snippet | string;
7
+ description?: Snippet | string;
8
+ class?: string;
9
+ [key: string]: any;
10
+ };
11
+ declare const Card: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Card = ReturnType<typeof Card>;
13
+ export default Card;
@@ -0,0 +1,142 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { onMount } from 'svelte';
4
+
5
+ let {
6
+ orientation = 'horizontal',
7
+ class: className = '',
8
+ children,
9
+ showButtons = true,
10
+ ...rest
11
+ }: {
12
+ orientation?: 'horizontal' | 'vertical';
13
+ class?: string;
14
+ children?: Snippet;
15
+ showButtons?: boolean;
16
+ [key: string]: any;
17
+ } = $props();
18
+
19
+ let scrollContainer: HTMLDivElement;
20
+ let canScrollPrev = $state(false);
21
+ let canScrollNext = $state(true);
22
+
23
+ function getScrollAmount() {
24
+ if (!scrollContainer) return 0;
25
+ const firstItem = scrollContainer.querySelector('.carousel-item') as HTMLElement;
26
+ if (!firstItem) {
27
+ return orientation === 'vertical' ? scrollContainer.offsetHeight : scrollContainer.offsetWidth;
28
+ }
29
+
30
+ const style = window.getComputedStyle(firstItem);
31
+ if (orientation === 'vertical') {
32
+ const itemHeight = firstItem.offsetHeight;
33
+ const marginTop = parseFloat(style.marginTop);
34
+ const marginBottom = parseFloat(style.marginBottom);
35
+ return itemHeight + marginTop + marginBottom;
36
+ } else {
37
+ const itemWidth = firstItem.offsetWidth;
38
+ const marginLeft = parseFloat(style.marginLeft);
39
+ const marginRight = parseFloat(style.marginRight);
40
+ return itemWidth + marginLeft + marginRight;
41
+ }
42
+ }
43
+
44
+ function scroll(direction: 'prev' | 'next') {
45
+ if (!scrollContainer) return;
46
+ const amount = getScrollAmount();
47
+ const value = direction === 'next' ? amount : -amount;
48
+
49
+ if (orientation === 'vertical') {
50
+ scrollContainer.scrollBy({ top: value, behavior: 'smooth' });
51
+ } else {
52
+ scrollContainer.scrollBy({ left: value, behavior: 'smooth' });
53
+ }
54
+ }
55
+
56
+ function updateButtons() {
57
+ if (!scrollContainer) return;
58
+
59
+ const { scrollLeft, scrollTop, scrollWidth, scrollHeight, clientWidth, clientHeight } = scrollContainer;
60
+
61
+ if (orientation === 'vertical') {
62
+ canScrollPrev = scrollTop > 1;
63
+ canScrollNext = scrollTop < scrollHeight - clientHeight - 1;
64
+ } else {
65
+ canScrollPrev = scrollLeft > 1;
66
+ canScrollNext = scrollLeft < scrollWidth - clientWidth - 1;
67
+ }
68
+ }
69
+
70
+ onMount(() => {
71
+ updateButtons();
72
+
73
+ // Use ResizeObserver to update buttons when content changes
74
+ const resizeObserver = new ResizeObserver(updateButtons);
75
+ resizeObserver.observe(scrollContainer);
76
+
77
+ return () => resizeObserver.disconnect();
78
+ });
79
+ </script>
80
+
81
+ <div
82
+ class={['carousel relative', className].filter(Boolean).join(' ')}
83
+ data-orientation={orientation}
84
+ {...rest}
85
+ >
86
+ {#if showButtons && orientation === 'horizontal'}
87
+ <button
88
+ class="carousel-previous absolute left-0 top-1/2 -translate-y-1/2 -translate-x-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
89
+ onclick={() => scroll('prev')}
90
+ disabled={!canScrollPrev}
91
+ aria-label="Previous slide"
92
+ >
93
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
94
+ </button>
95
+ {/if}
96
+
97
+ <div
98
+ bind:this={scrollContainer}
99
+ class={[
100
+ 'carousel-content flex overflow-hidden scroll-smooth',
101
+ orientation === 'vertical' ? 'flex-col -mt-4 pb-4' : 'flex-row -ml-4'
102
+ ].join(' ')}
103
+ onscroll={updateButtons}
104
+ >
105
+ {@render children?.()}
106
+ </div>
107
+
108
+ {#if showButtons && orientation === 'horizontal'}
109
+ <button
110
+ class="carousel-next absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
111
+ onclick={() => scroll('next')}
112
+ disabled={!canScrollNext}
113
+ aria-label="Next slide"
114
+ >
115
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
116
+ </button>
117
+ {/if}
118
+
119
+ {#if showButtons && orientation === 'vertical'}
120
+ <button
121
+ class="carousel-previous absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
122
+ onclick={() => scroll('prev')}
123
+ disabled={!canScrollPrev}
124
+ aria-label="Previous slide"
125
+ >
126
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>
127
+ </button>
128
+ <button
129
+ class="carousel-next absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 bg-background border rounded-full p-2 shadow-sm z-10 disabled:opacity-50"
130
+ onclick={() => scroll('next')}
131
+ disabled={!canScrollNext}
132
+ aria-label="Next slide"
133
+ >
134
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
135
+ </button>
136
+ {/if}
137
+ </div>
138
+
139
+ <style>
140
+ @import "../../../../ultra/src/css/parts/custom/carousel.css";
141
+ @reference "../reference.css";
142
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ orientation?: 'horizontal' | 'vertical';
4
+ class?: string;
5
+ children?: Snippet;
6
+ showButtons?: boolean;
7
+ [key: string]: any;
8
+ };
9
+ declare const Carousel: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type Carousel = ReturnType<typeof Carousel>;
11
+ export default Carousel;