pika-ux 1.0.0-beta.1 → 1.0.0-beta.10

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 (73) hide show
  1. package/dist/cli/index.js +320 -0
  2. package/dist/cli/template-files/.gitignore +24 -0
  3. package/dist/cli/template-files/.npmrc +5 -0
  4. package/dist/cli/template-files/.nvmrc +1 -0
  5. package/dist/cli/template-files/.prettierignore +15 -0
  6. package/dist/cli/template-files/.prettierrc +7 -0
  7. package/dist/cli/template-files/README.md +63 -0
  8. package/dist/cli/template-files/index.html +13 -0
  9. package/dist/cli/template-files/package.json +35 -0
  10. package/dist/cli/template-files/pnpm-workspace.yaml +21 -0
  11. package/dist/cli/template-files/src/App.svelte +10 -0
  12. package/dist/cli/template-files/src/app.css +3 -0
  13. package/dist/cli/template-files/src/lib/counter.svelte +12 -0
  14. package/dist/cli/template-files/src/main.ts +9 -0
  15. package/dist/cli/template-files/svelte.config.js +8 -0
  16. package/dist/cli/template-files/tsconfig.app.json +44 -0
  17. package/dist/cli/template-files/tsconfig.json +25 -0
  18. package/dist/cli/template-files/tsconfig.node.json +28 -0
  19. package/dist/cli/template-files/vite.config.ts +20 -0
  20. package/package.json +33 -12
  21. package/readme.md +74 -3
  22. package/scripts/setup.js +0 -0
  23. package/src/.DS_Store +0 -0
  24. package/src/App.svelte +2 -4
  25. package/src/icons/lucide/index.d.ts +397 -40
  26. package/src/pika/scrollable-tabs/README.md +192 -0
  27. package/src/pika/scrollable-tabs/add-button.svelte +33 -0
  28. package/src/pika/scrollable-tabs/content.svelte +23 -0
  29. package/src/pika/scrollable-tabs/context.svelte.ts +23 -0
  30. package/src/pika/scrollable-tabs/example.svelte +81 -0
  31. package/src/pika/scrollable-tabs/index.ts +31 -0
  32. package/src/pika/scrollable-tabs/list.svelte +15 -0
  33. package/src/pika/scrollable-tabs/overflow-menu.svelte +119 -0
  34. package/src/pika/scrollable-tabs/pinned-section.svelte +138 -0
  35. package/src/pika/scrollable-tabs/pinned-trigger.svelte +81 -0
  36. package/src/pika/scrollable-tabs/root.svelte +34 -0
  37. package/src/pika/scrollable-tabs/scrollable-section.svelte +120 -0
  38. package/src/pika/scrollable-tabs/trigger.svelte +82 -0
  39. package/src/shadcn/carousel/carousel-content.svelte +36 -31
  40. package/src/shadcn/carousel/carousel-item.svelte +22 -18
  41. package/src/shadcn/carousel/carousel-next.svelte +29 -22
  42. package/src/shadcn/carousel/carousel-previous.svelte +29 -22
  43. package/src/shadcn/carousel/carousel.svelte +77 -73
  44. package/src/shadcn/carousel/context.ts +37 -32
  45. package/src/shadcn/dropdown-menu/dropdown-menu-group.svelte +2 -3
  46. package/src/shadcn/dropdown-menu/dropdown-menu-item.svelte +1 -1
  47. package/src/shadcn/dropdown-menu/dropdown-menu-label.svelte +1 -7
  48. package/src/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +3 -13
  49. package/src/shadcn/dropdown-menu/dropdown-menu-radio-item.svelte +0 -1
  50. package/src/shadcn/dropdown-menu/dropdown-menu-separator.svelte +1 -7
  51. package/src/shadcn/dropdown-menu/dropdown-menu-shortcut.svelte +2 -13
  52. package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +0 -1
  53. package/src/shadcn/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -2
  54. package/src/shadcn/dropdown-menu/dropdown-menu-trigger.svelte +2 -3
  55. package/src/shadcn/dropdown-menu/index.ts +44 -45
  56. package/src/shadcn/dropdown-menu copy/dropdown-menu-checkbox-item.svelte +41 -0
  57. package/src/shadcn/dropdown-menu copy/dropdown-menu-content.svelte +27 -0
  58. package/src/shadcn/dropdown-menu copy/dropdown-menu-group-heading.svelte +22 -0
  59. package/src/shadcn/dropdown-menu copy/dropdown-menu-group.svelte +7 -0
  60. package/src/shadcn/dropdown-menu copy/dropdown-menu-item.svelte +27 -0
  61. package/src/shadcn/dropdown-menu copy/dropdown-menu-label.svelte +24 -0
  62. package/src/shadcn/dropdown-menu copy/dropdown-menu-radio-group.svelte +16 -0
  63. package/src/shadcn/dropdown-menu copy/dropdown-menu-radio-item.svelte +26 -0
  64. package/src/shadcn/dropdown-menu copy/dropdown-menu-separator.svelte +13 -0
  65. package/src/shadcn/dropdown-menu copy/dropdown-menu-shortcut.svelte +20 -0
  66. package/src/shadcn/dropdown-menu copy/dropdown-menu-sub-content.svelte +16 -0
  67. package/src/shadcn/dropdown-menu copy/dropdown-menu-sub-trigger.svelte +29 -0
  68. package/src/shadcn/dropdown-menu copy/dropdown-menu-trigger.svelte +7 -0
  69. package/src/shadcn/spinner/index.ts +1 -0
  70. package/src/shadcn/spinner/spinner.svelte +9 -0
  71. package/src/shadcn/toggle-group/toggle-group.svelte +23 -28
  72. package/src/pika/index.ts +0 -29
  73. package/src/shadcn/index.ts +0 -40
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { getScrollableTabsContext } from './context.svelte.js';
4
+ import { cn } from '../../shadcn/utils.js';
5
+ import X from '$icons/lucide/x';
6
+ import PinOff from '$icons/lucide/pin-off';
7
+ import { Button } from '../../shadcn/button/index.js';
8
+
9
+ interface Props {
10
+ value: string;
11
+ class?: string;
12
+ disabled?: boolean;
13
+ children: Snippet;
14
+ }
15
+
16
+ let { value, class: className, disabled = false, children }: Props = $props();
17
+
18
+ const context = getScrollableTabsContext();
19
+
20
+ const isActive = $derived(context.getValue() === value);
21
+ let isHovered = $state(false);
22
+
23
+ function handleClick() {
24
+ if (!disabled) {
25
+ context.onValueChange(value);
26
+ }
27
+ }
28
+
29
+ function handleClose(e: MouseEvent) {
30
+ e.stopPropagation();
31
+ context.onClose?.(value);
32
+ }
33
+
34
+ function handleUnpin(e: MouseEvent) {
35
+ e.stopPropagation();
36
+ context.onUnpin?.(value);
37
+ }
38
+
39
+ function handleKeydown(e: KeyboardEvent) {
40
+ if (e.key === 'Enter' || e.key === ' ') {
41
+ e.preventDefault();
42
+ handleClick();
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <div
48
+ role="tab"
49
+ tabindex={disabled ? -1 : 0}
50
+ aria-selected={isActive}
51
+ onclick={handleClick}
52
+ onkeydown={handleKeydown}
53
+ onmouseenter={() => (isHovered = true)}
54
+ onmouseleave={() => (isHovered = false)}
55
+ class={cn(
56
+ 'group relative px-2 py-1 rounded-md text-xs font-medium rounded-t-md whitespace-nowrap transition-all cursor-pointer',
57
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
58
+ 'flex items-center gap-2',
59
+ isActive ? 'bg-primary/10 text-primary' : 'text-muted-foreground hover:text-foreground hover:bg-muted',
60
+ disabled && 'opacity-50 pointer-events-none',
61
+ className
62
+ )}
63
+ >
64
+ <span class="flex-1">{@render children()}</span>
65
+ <span class="flex items-center">
66
+ {#if context.onUnpin}
67
+ {#if isHovered && !disabled}
68
+ <Button {disabled} variant="ghost" size="icon" onclick={handleUnpin} class="h-4 w-4 p-0">
69
+ <PinOff style="width: 0.75rem; height: 0.75rem;" />
70
+ </Button>
71
+ {:else}
72
+ <div class="h-4 w-4"></div>
73
+ {/if}
74
+ {/if}
75
+ {#if context.onClose}
76
+ <Button {disabled} variant="ghost" size="icon" onclick={handleClose} class="h-4 w-4 p-0">
77
+ <X style="width: 0.75rem; height: 0.75rem;" />
78
+ </Button>
79
+ {/if}
80
+ </span>
81
+ </div>
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import { setScrollableTabsContext } from './context.svelte.js';
3
+ import type { Snippet } from 'svelte';
4
+ import { cn } from '../../shadcn/utils.js';
5
+
6
+ interface Props {
7
+ value: string;
8
+ onValueChange?: (value: string) => void;
9
+ onClose?: (value: string) => void;
10
+ onPin?: (value: string) => void;
11
+ onUnpin?: (value: string) => void;
12
+ class?: string;
13
+ children: Snippet;
14
+ }
15
+
16
+ let { value = $bindable(), onValueChange, onClose, onPin, onUnpin, class: className, children }: Props = $props();
17
+
18
+ function handleValueChange(newValue: string) {
19
+ value = newValue;
20
+ onValueChange?.(newValue);
21
+ }
22
+
23
+ setScrollableTabsContext({
24
+ getValue: () => value,
25
+ onValueChange: handleValueChange,
26
+ onClose,
27
+ onPin,
28
+ onUnpin
29
+ });
30
+ </script>
31
+
32
+ <div class={cn('w-full', className)}>
33
+ {@render children()}
34
+ </div>
@@ -0,0 +1,120 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../../shadcn/utils.js';
4
+ import ChevronLeft from '$icons/lucide/chevron-left';
5
+ import ChevronRight from '$icons/lucide/chevron-right';
6
+ import { Button } from '../../shadcn/button/index.js';
7
+
8
+ interface Props {
9
+ class?: string;
10
+ children: Snippet;
11
+ onInsufficientSpace?: (insufficient: boolean) => void;
12
+ minUsefulWidth?: number;
13
+ }
14
+
15
+ let { class: className, children, onInsufficientSpace, minUsefulWidth = 150 }: Props = $props();
16
+
17
+ let containerWrapper: HTMLDivElement;
18
+ let scrollContainer: HTMLDivElement;
19
+ let hasOverflow = $state(false);
20
+ let canScrollLeft = $state(false);
21
+ let canScrollRight = $state(false);
22
+ let hasInsufficientSpace = $state(false);
23
+ let isUpdating = false;
24
+
25
+ function updateScrollButtons() {
26
+ if (!scrollContainer || !containerWrapper || isUpdating) return;
27
+
28
+ isUpdating = true;
29
+
30
+ try {
31
+ const isOverflowing = scrollContainer.scrollWidth > scrollContainer.clientWidth;
32
+ hasOverflow = isOverflowing;
33
+ canScrollLeft = scrollContainer.scrollLeft > 0;
34
+ canScrollRight = scrollContainer.scrollLeft < scrollContainer.scrollWidth - scrollContainer.clientWidth - 1;
35
+
36
+ // Check if we have enough space to be useful
37
+ const currentWidth = containerWrapper.clientWidth;
38
+ const insufficient = currentWidth < minUsefulWidth;
39
+ if (hasInsufficientSpace !== insufficient) {
40
+ hasInsufficientSpace = insufficient;
41
+ onInsufficientSpace?.(insufficient);
42
+ }
43
+ } finally {
44
+ isUpdating = false;
45
+ }
46
+ }
47
+
48
+ function scrollLeft() {
49
+ if (scrollContainer) {
50
+ scrollContainer.scrollBy({ left: -200, behavior: 'smooth' });
51
+ }
52
+ }
53
+
54
+ function scrollRight() {
55
+ if (scrollContainer) {
56
+ scrollContainer.scrollBy({ left: 200, behavior: 'smooth' });
57
+ }
58
+ }
59
+
60
+ $effect(() => {
61
+ if (scrollContainer && containerWrapper) {
62
+ let timeoutId: ReturnType<typeof setTimeout>;
63
+
64
+ // Initial check with slight delay to ensure content is rendered
65
+ timeoutId = setTimeout(() => updateScrollButtons(), 10);
66
+
67
+ // Watch for resize with debouncing
68
+ const resizeObserver = new ResizeObserver(() => {
69
+ clearTimeout(timeoutId);
70
+ timeoutId = setTimeout(() => updateScrollButtons(), 50);
71
+ });
72
+ resizeObserver.observe(containerWrapper);
73
+ resizeObserver.observe(scrollContainer);
74
+
75
+ // Also observe the inner content
76
+ const innerContent = scrollContainer.querySelector('div');
77
+ if (innerContent) {
78
+ resizeObserver.observe(innerContent);
79
+ }
80
+
81
+ return () => {
82
+ clearTimeout(timeoutId);
83
+ resizeObserver.disconnect();
84
+ };
85
+ }
86
+ });
87
+ </script>
88
+
89
+ <div bind:this={containerWrapper} class={cn('flex-1 flex items-center gap-0 min-w-0 bg-white', className)}>
90
+ <!-- Left scroll button -->
91
+ {#if hasOverflow}
92
+ <div class="flex-shrink-0">
93
+ <Button variant="ghost" size="icon" onclick={scrollLeft} disabled={!canScrollLeft} class="w-7">
94
+ <ChevronLeft style="width: 1rem; height: 1rem;" />
95
+ </Button>
96
+ </div>
97
+ {/if}
98
+
99
+ <!-- Scrollable tabs area -->
100
+ <div bind:this={scrollContainer} onscroll={updateScrollButtons} class="flex-1 min-w-0 overflow-x-auto scroll-smooth" style="scrollbar-width: none; -ms-overflow-style: none;">
101
+ <div class="flex items-center gap-1 px-2">
102
+ {@render children()}
103
+ </div>
104
+ </div>
105
+
106
+ <!-- Right scroll button -->
107
+ {#if hasOverflow}
108
+ <div class="flex-shrink-0">
109
+ <Button variant="ghost" size="icon" onclick={scrollRight} disabled={!canScrollRight} class="w-7">
110
+ <ChevronRight style="width: 1rem; height: 1rem;" />
111
+ </Button>
112
+ </div>
113
+ {/if}
114
+ </div>
115
+
116
+ <style>
117
+ div::-webkit-scrollbar {
118
+ display: none;
119
+ }
120
+ </style>
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { getScrollableTabsContext } from './context.svelte.js';
4
+ import { cn } from '../../shadcn/utils.js';
5
+ import X from '$icons/lucide/x';
6
+ import Pin from '$icons/lucide/pin';
7
+ import { Button } from '../../shadcn/button/index.js';
8
+
9
+ interface Props {
10
+ value: string;
11
+ class?: string;
12
+ disabled?: boolean;
13
+ children: Snippet;
14
+ }
15
+
16
+ let { value, class: className, disabled = false, children }: Props = $props();
17
+
18
+ const context = getScrollableTabsContext();
19
+
20
+ const isActive = $derived(context.getValue() === value);
21
+ let isHovered = $state(false);
22
+
23
+ function handleClick() {
24
+ if (!disabled) {
25
+ context.onValueChange(value);
26
+ }
27
+ }
28
+
29
+ function handleClose(e: MouseEvent) {
30
+ e.stopPropagation();
31
+ context.onClose?.(value);
32
+ }
33
+
34
+ function handlePin(e: MouseEvent) {
35
+ e.stopPropagation();
36
+ context.onPin?.(value);
37
+ }
38
+
39
+ function handleKeydown(e: KeyboardEvent) {
40
+ if (e.key === 'Enter' || e.key === ' ') {
41
+ e.preventDefault();
42
+ handleClick();
43
+ }
44
+ }
45
+ </script>
46
+
47
+ <div
48
+ role="tab"
49
+ tabindex={disabled ? -1 : 0}
50
+ aria-selected={isActive}
51
+ onclick={handleClick}
52
+ onkeydown={handleKeydown}
53
+ onmouseenter={() => (isHovered = true)}
54
+ onmouseleave={() => (isHovered = false)}
55
+ class={cn(
56
+ 'group relative px-2 py-1 rounded-md text-xs font-medium rounded-t-md whitespace-nowrap transition-all cursor-pointer',
57
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
58
+ 'flex items-center gap-2',
59
+ isActive ? 'bg-primary/10 text-primary' : 'text-muted-foreground hover:text-foreground hover:bg-muted',
60
+ disabled && 'opacity-50 pointer-events-none',
61
+ className
62
+ )}
63
+ >
64
+ <span class="flex-1">{@render children()}</span>
65
+ <span class="flex items-center">
66
+ {#if context.onPin}
67
+ {#if isHovered && !disabled}
68
+ <Button {disabled} variant="ghost" size="icon" onclick={handlePin} class="h-4 w-4 p-0">
69
+ <Pin style="width: 0.75rem; height: 0.75rem;" />
70
+ </Button>
71
+ {:else}
72
+ <div class="h-4 w-4"></div>
73
+ {/if}
74
+ {/if}
75
+
76
+ {#if context.onClose}
77
+ <Button {disabled} variant="ghost" size="icon" onclick={handleClose} class="h-4 w-4 p-0">
78
+ <X style="width: 0.75rem; height: 0.75rem;" />
79
+ </Button>
80
+ {/if}
81
+ </span>
82
+ </div>
@@ -1,39 +1,44 @@
1
1
  <script lang="ts">
2
- import emblaCarouselSvelte from 'embla-carousel-svelte';
3
- import type { HTMLAttributes } from 'svelte/elements';
4
- import { getEmblaContext } from './context.js';
5
- import { cn, type WithElementRef } from '../utils.js';
2
+ import emblaCarouselSvelte from "embla-carousel-svelte";
3
+ import type { WithElementRef } from "bits-ui";
4
+ import type { HTMLAttributes } from "svelte/elements";
5
+ import { getEmblaContext } from "./context.js";
6
+ import { cn } from "../utils.js";
6
7
 
7
- let {
8
- ref = $bindable(null),
9
- class: className,
10
- children,
11
- ...restProps
12
- }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
8
+ let {
9
+ ref = $bindable(null),
10
+ class: className,
11
+ children,
12
+ ...restProps
13
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
13
14
 
14
- const emblaCtx = getEmblaContext('<Carousel.Content/>');
15
+ const emblaCtx = getEmblaContext("<Carousel.Content/>");
15
16
  </script>
16
17
 
18
+ <!-- svelte-ignore event_directive_deprecated -->
17
19
  <div
18
- data-slot="carousel-content"
19
- class="overflow-hidden"
20
- use:emblaCarouselSvelte={{
21
- options: {
22
- container: '[data-embla-container]',
23
- slides: '[data-embla-slide]',
24
- ...emblaCtx.options,
25
- axis: emblaCtx.orientation === 'horizontal' ? 'x' : 'y',
26
- },
27
- plugins: emblaCtx.plugins,
28
- }}
29
- onemblaInit={emblaCtx.onInit}
20
+ class="overflow-hidden"
21
+ use:emblaCarouselSvelte={{
22
+ options: {
23
+ container: "[data-embla-container]",
24
+ slides: "[data-embla-slide]",
25
+ ...emblaCtx.options,
26
+ axis: emblaCtx.orientation === "horizontal" ? "x" : "y",
27
+ },
28
+ plugins: emblaCtx.plugins,
29
+ }}
30
+ on:emblaInit={emblaCtx.onInit}
30
31
  >
31
- <div
32
- bind:this={ref}
33
- class={cn('flex', emblaCtx.orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col', className)}
34
- data-embla-container=""
35
- {...restProps}
36
- >
37
- {@render children?.()}
38
- </div>
32
+ <div
33
+ bind:this={ref}
34
+ class={cn(
35
+ "flex",
36
+ emblaCtx.orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
37
+ className
38
+ )}
39
+ data-embla-container=""
40
+ {...restProps}
41
+ >
42
+ {@render children?.()}
43
+ </div>
39
44
  </div>
@@ -1,26 +1,30 @@
1
1
  <script lang="ts">
2
- import type { HTMLAttributes } from 'svelte/elements';
3
- import { getEmblaContext } from './context.js';
4
- import { cn, type WithElementRef } from '../utils.js';
2
+ import type { WithElementRef } from "bits-ui";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import { getEmblaContext } from "./context.js";
5
+ import { cn } from "../utils.js";
5
6
 
6
- let {
7
- ref = $bindable(null),
8
- class: className,
9
- children,
10
- ...restProps
11
- }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
7
+ let {
8
+ ref = $bindable(null),
9
+ class: className,
10
+ children,
11
+ ...restProps
12
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
12
13
 
13
- const emblaCtx = getEmblaContext('<Carousel.Item/>');
14
+ const emblaCtx = getEmblaContext("<Carousel.Item/>");
14
15
  </script>
15
16
 
16
17
  <div
17
- bind:this={ref}
18
- data-slot="carousel-item"
19
- role="group"
20
- aria-roledescription="slide"
21
- class={cn('min-w-0 shrink-0 grow-0 basis-full', emblaCtx.orientation === 'horizontal' ? 'pl-4' : 'pt-4', className)}
22
- data-embla-slide=""
23
- {...restProps}
18
+ bind:this={ref}
19
+ role="group"
20
+ aria-roledescription="slide"
21
+ class={cn(
22
+ "min-w-0 shrink-0 grow-0 basis-full",
23
+ emblaCtx.orientation === "horizontal" ? "pl-4" : "pt-4",
24
+ className
25
+ )}
26
+ data-embla-slide=""
27
+ {...restProps}
24
28
  >
25
- {@render children?.()}
29
+ {@render children?.()}
26
30
  </div>
@@ -1,30 +1,37 @@
1
1
  <script lang="ts">
2
- import ArrowRightIcon from '$icons/lucide/arrow-right';
3
- import type { WithoutChildren } from 'bits-ui';
4
- import { getEmblaContext } from './context.js';
5
- import { cn } from '../utils.js';
6
- import { Button, type Props } from '../button/index.js';
2
+ import ArrowRight from "@lucide/svelte/icons/arrow-right";
3
+ import type { WithoutChildren } from "bits-ui";
4
+ import { getEmblaContext } from "./context.js";
5
+ import { cn } from "../utils.js";
6
+ import { Button, type Props } from "../button/index.js";
7
7
 
8
- let { ref = $bindable(null), class: className, variant = 'outline', size = 'icon', ...restProps }: WithoutChildren<Props> = $props();
8
+ let {
9
+ ref = $bindable(null),
10
+ class: className,
11
+ variant = "outline",
12
+ size = "icon",
13
+ ...restProps
14
+ }: WithoutChildren<Props> = $props();
9
15
 
10
- const emblaCtx = getEmblaContext('<Carousel.Next/>');
16
+ const emblaCtx = getEmblaContext("<Carousel.Next/>");
11
17
  </script>
12
18
 
13
19
  <Button
14
- data-slot="carousel-next"
15
- {variant}
16
- {size}
17
- aria-disabled={!emblaCtx.canScrollNext}
18
- class={cn(
19
- 'absolute size-8 rounded-full',
20
- emblaCtx.orientation === 'horizontal' ? '-right-12 top-1/2 -translate-y-1/2' : '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
21
- className
22
- )}
23
- onclick={emblaCtx.scrollNext}
24
- onkeydown={emblaCtx.handleKeyDown}
25
- bind:ref
26
- {...restProps}
20
+ {variant}
21
+ {size}
22
+ class={cn(
23
+ "absolute size-8 touch-manipulation rounded-full",
24
+ emblaCtx.orientation === "horizontal"
25
+ ? "-right-12 top-1/2 -translate-y-1/2"
26
+ : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
27
+ className
28
+ )}
29
+ disabled={!emblaCtx.canScrollNext}
30
+ onclick={emblaCtx.scrollNext}
31
+ onkeydown={emblaCtx.handleKeyDown}
32
+ bind:ref
33
+ {...restProps}
27
34
  >
28
- <ArrowRightIcon class="size-4" />
29
- <span class="sr-only">Next slide</span>
35
+ <ArrowRight />
36
+ <span class="sr-only">Next slide</span>
30
37
  </Button>
@@ -1,30 +1,37 @@
1
1
  <script lang="ts">
2
- import ArrowLeftIcon from '$icons/lucide/arrow-left';
3
- import type { WithoutChildren } from 'bits-ui';
4
- import { getEmblaContext } from './context.js';
5
- import { cn } from '../utils.js';
6
- import { Button, type Props } from '../button/index.js';
2
+ import ArrowLeft from "@lucide/svelte/icons/arrow-left";
3
+ import type { WithoutChildren } from "bits-ui";
4
+ import { getEmblaContext } from "./context.js";
5
+ import { cn } from "../utils.js";
6
+ import { Button, type Props } from "../button/index.js";
7
7
 
8
- let { ref = $bindable(null), class: className, variant = 'outline', size = 'icon', ...restProps }: WithoutChildren<Props> = $props();
8
+ let {
9
+ ref = $bindable(null),
10
+ class: className,
11
+ variant = "outline",
12
+ size = "icon",
13
+ ...restProps
14
+ }: WithoutChildren<Props> = $props();
9
15
 
10
- const emblaCtx = getEmblaContext('<Carousel.Previous/>');
16
+ const emblaCtx = getEmblaContext("<Carousel.Previous/>");
11
17
  </script>
12
18
 
13
19
  <Button
14
- data-slot="carousel-previous"
15
- {variant}
16
- {size}
17
- aria-disabled={!emblaCtx.canScrollPrev}
18
- class={cn(
19
- 'absolute size-8 rounded-full',
20
- emblaCtx.orientation === 'horizontal' ? '-left-12 top-1/2 -translate-y-1/2' : '-top-12 left-1/2 -translate-x-1/2 rotate-90',
21
- className
22
- )}
23
- onclick={emblaCtx.scrollPrev}
24
- onkeydown={emblaCtx.handleKeyDown}
25
- {...restProps}
26
- bind:ref
20
+ {variant}
21
+ {size}
22
+ class={cn(
23
+ "absolute size-8 touch-manipulation rounded-full",
24
+ emblaCtx.orientation === "horizontal"
25
+ ? "-left-12 top-1/2 -translate-y-1/2"
26
+ : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
27
+ className
28
+ )}
29
+ disabled={!emblaCtx.canScrollPrev}
30
+ onclick={emblaCtx.scrollPrev}
31
+ onkeydown={emblaCtx.handleKeyDown}
32
+ {...restProps}
33
+ bind:ref
27
34
  >
28
- <ArrowLeftIcon class="size-4" />
29
- <span class="sr-only">Previous slide</span>
35
+ <ArrowLeft />
36
+ <span class="sr-only">Previous slide</span>
30
37
  </Button>