99 lines
2.3 KiB
Svelte
99 lines
2.3 KiB
Svelte
<script lang="ts">
|
|
import type { ImageCarouselItem } from '.'
|
|
import * as Carousel from '../carousel'
|
|
import ImgCarouselItem from './ImageCarouselItem.svelte'
|
|
import type { CarouselAPI } from '$lib/components/ui/carousel/context.js'
|
|
import { Button } from '../button'
|
|
import { ChevronLeftIcon, ChevronRightIcon } from '@lucide/svelte'
|
|
|
|
const {
|
|
items,
|
|
preview = false,
|
|
buttonPosition = 'default',
|
|
}: {
|
|
items: ImageCarouselItem[]
|
|
preview?: boolean
|
|
buttonPosition?: 'contained' | 'default' | 'hidden'
|
|
} = $props()
|
|
|
|
let api = $state<CarouselAPI>()
|
|
let currentImageIndex = $state(0)
|
|
|
|
$effect(() => {
|
|
if (!api) return
|
|
|
|
api.on('select', () => {
|
|
if (!api) return
|
|
currentImageIndex = api.selectedScrollSnap()
|
|
})
|
|
})
|
|
|
|
function onPreviewImageClick(toIndex: number) {
|
|
if (!api) return
|
|
|
|
api.scrollTo(toIndex, false)
|
|
}
|
|
|
|
function prevImage() {
|
|
if (!api) return
|
|
|
|
api.scrollPrev()
|
|
}
|
|
function nextImage() {
|
|
if (!api) return
|
|
|
|
api.scrollNext()
|
|
}
|
|
</script>
|
|
|
|
<div>
|
|
<Carousel.Root setApi={(emblaApi) => (api = emblaApi)}>
|
|
<Carousel.Content>
|
|
{#each items as item}
|
|
<ImgCarouselItem {item} />
|
|
{/each}
|
|
</Carousel.Content>
|
|
{#if buttonPosition === 'default'}
|
|
<Carousel.Next />
|
|
{:else if buttonPosition === 'contained'}
|
|
<Button
|
|
onclick={prevImage}
|
|
variant="outline"
|
|
size="icon"
|
|
class="absolute top-1/2 -translate-y-1/2 left-4 rounded-full"
|
|
><ChevronLeftIcon /></Button
|
|
>
|
|
{/if}
|
|
{#if buttonPosition === 'default'}
|
|
<Carousel.Previous />
|
|
{:else if buttonPosition === 'contained'}
|
|
<Button
|
|
onclick={nextImage}
|
|
variant="outline"
|
|
size="icon"
|
|
class="absolute top-1/2 -translate-y-1/2 right-4 rounded-full"
|
|
><ChevronRightIcon /></Button
|
|
>
|
|
{/if}
|
|
</Carousel.Root>
|
|
{#if preview}
|
|
<Carousel.Root
|
|
opts={{
|
|
align: 'start',
|
|
}}
|
|
class="hidden md:block"
|
|
>
|
|
<Carousel.Content>
|
|
{#each items as item, i}
|
|
<ImgCarouselItem
|
|
onclick={() => onPreviewImageClick(i)}
|
|
{item}
|
|
class="md:basis-1/3 lg:basis-1/4 mt-4"
|
|
isSelected={i === currentImageIndex}
|
|
/>
|
|
{/each}
|
|
</Carousel.Content>
|
|
</Carousel.Root>
|
|
{/if}
|
|
</div>
|