feat: added product page
This commit is contained in:
98
src/lib/components/ui/image-carousel/ImageCarousel.svelte
Normal file
98
src/lib/components/ui/image-carousel/ImageCarousel.svelte
Normal file
@@ -0,0 +1,98 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user