pika-ux 1.0.0-beta.0 → 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.
- package/dist/cli/index.js +320 -0
- package/dist/cli/template-files/.gitignore +24 -0
- package/dist/cli/template-files/.npmrc +5 -0
- package/dist/cli/template-files/.nvmrc +1 -0
- package/dist/cli/template-files/.prettierignore +15 -0
- package/dist/cli/template-files/.prettierrc +7 -0
- package/dist/cli/template-files/README.md +63 -0
- package/dist/cli/template-files/index.html +13 -0
- package/dist/cli/template-files/package.json +35 -0
- package/dist/cli/template-files/pnpm-workspace.yaml +21 -0
- package/dist/cli/template-files/src/App.svelte +10 -0
- package/dist/cli/template-files/src/app.css +3 -0
- package/dist/cli/template-files/src/lib/counter.svelte +12 -0
- package/dist/cli/template-files/src/main.ts +9 -0
- package/dist/cli/template-files/svelte.config.js +8 -0
- package/dist/cli/template-files/tsconfig.app.json +44 -0
- package/dist/cli/template-files/tsconfig.json +25 -0
- package/dist/cli/template-files/tsconfig.node.json +28 -0
- package/dist/cli/template-files/vite.config.ts +20 -0
- package/package.json +33 -12
- package/readme.md +74 -3
- package/scripts/setup.js +0 -0
- package/src/.DS_Store +0 -0
- package/src/App.svelte +2 -4
- package/src/icons/lucide/index.d.ts +397 -40
- package/src/pika/scrollable-tabs/README.md +192 -0
- package/src/pika/scrollable-tabs/add-button.svelte +33 -0
- package/src/pika/scrollable-tabs/content.svelte +23 -0
- package/src/pika/scrollable-tabs/context.svelte.ts +23 -0
- package/src/pika/scrollable-tabs/example.svelte +81 -0
- package/src/pika/scrollable-tabs/index.ts +31 -0
- package/src/pika/scrollable-tabs/list.svelte +15 -0
- package/src/pika/scrollable-tabs/overflow-menu.svelte +119 -0
- package/src/pika/scrollable-tabs/pinned-section.svelte +138 -0
- package/src/pika/scrollable-tabs/pinned-trigger.svelte +81 -0
- package/src/pika/scrollable-tabs/root.svelte +34 -0
- package/src/pika/scrollable-tabs/scrollable-section.svelte +120 -0
- package/src/pika/scrollable-tabs/trigger.svelte +82 -0
- package/src/shadcn/carousel/carousel-content.svelte +36 -31
- package/src/shadcn/carousel/carousel-item.svelte +22 -18
- package/src/shadcn/carousel/carousel-next.svelte +29 -22
- package/src/shadcn/carousel/carousel-previous.svelte +29 -22
- package/src/shadcn/carousel/carousel.svelte +77 -73
- package/src/shadcn/carousel/context.ts +37 -32
- package/src/shadcn/dropdown-menu/dropdown-menu-group.svelte +2 -3
- package/src/shadcn/dropdown-menu/dropdown-menu-item.svelte +1 -1
- package/src/shadcn/dropdown-menu/dropdown-menu-label.svelte +1 -7
- package/src/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +3 -13
- package/src/shadcn/dropdown-menu/dropdown-menu-radio-item.svelte +0 -1
- package/src/shadcn/dropdown-menu/dropdown-menu-separator.svelte +1 -7
- package/src/shadcn/dropdown-menu/dropdown-menu-shortcut.svelte +2 -13
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-content.svelte +0 -1
- package/src/shadcn/dropdown-menu/dropdown-menu-sub-trigger.svelte +1 -2
- package/src/shadcn/dropdown-menu/dropdown-menu-trigger.svelte +2 -3
- package/src/shadcn/dropdown-menu/index.ts +44 -45
- package/src/shadcn/dropdown-menu copy/dropdown-menu-checkbox-item.svelte +41 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-content.svelte +27 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-group-heading.svelte +22 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-group.svelte +7 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-item.svelte +27 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-label.svelte +24 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-radio-group.svelte +16 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-radio-item.svelte +26 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-separator.svelte +13 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-shortcut.svelte +20 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-sub-content.svelte +16 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-sub-trigger.svelte +29 -0
- package/src/shadcn/dropdown-menu copy/dropdown-menu-trigger.svelte +7 -0
- package/src/shadcn/spinner/index.ts +1 -0
- package/src/shadcn/spinner/spinner.svelte +9 -0
- package/src/shadcn/toggle-group/toggle-group.svelte +23 -28
- package/src/pika/index.ts +0 -29
- 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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
let {
|
|
9
|
+
ref = $bindable(null),
|
|
10
|
+
class: className,
|
|
11
|
+
children,
|
|
12
|
+
...restProps
|
|
13
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
const emblaCtx = getEmblaContext("<Carousel.Content/>");
|
|
15
16
|
</script>
|
|
16
17
|
|
|
18
|
+
<!-- svelte-ignore event_directive_deprecated -->
|
|
17
19
|
<div
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
let {
|
|
8
|
+
ref = $bindable(null),
|
|
9
|
+
class: className,
|
|
10
|
+
children,
|
|
11
|
+
...restProps
|
|
12
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
const emblaCtx = getEmblaContext("<Carousel.Item/>");
|
|
14
15
|
</script>
|
|
15
16
|
|
|
16
17
|
<div
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
29
|
+
{@render children?.()}
|
|
26
30
|
</div>
|
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
8
|
+
let {
|
|
9
|
+
ref = $bindable(null),
|
|
10
|
+
class: className,
|
|
11
|
+
variant = "outline",
|
|
12
|
+
size = "icon",
|
|
13
|
+
...restProps
|
|
14
|
+
}: WithoutChildren<Props> = $props();
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
const emblaCtx = getEmblaContext("<Carousel.Next/>");
|
|
11
17
|
</script>
|
|
12
18
|
|
|
13
19
|
<Button
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
35
|
+
<ArrowRight />
|
|
36
|
+
<span class="sr-only">Next slide</span>
|
|
30
37
|
</Button>
|
|
@@ -1,30 +1,37 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
8
|
+
let {
|
|
9
|
+
ref = $bindable(null),
|
|
10
|
+
class: className,
|
|
11
|
+
variant = "outline",
|
|
12
|
+
size = "icon",
|
|
13
|
+
...restProps
|
|
14
|
+
}: WithoutChildren<Props> = $props();
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
const emblaCtx = getEmblaContext("<Carousel.Previous/>");
|
|
11
17
|
</script>
|
|
12
18
|
|
|
13
19
|
<Button
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
35
|
+
<ArrowLeft />
|
|
36
|
+
<span class="sr-only">Previous slide</span>
|
|
30
37
|
</Button>
|