93 lines
3.0 KiB
TypeScript
93 lines
3.0 KiB
TypeScript
"use client"
|
|
import {
|
|
LayoutComponentDefinition,
|
|
LayoutContext,
|
|
} from "@vibentec/component-map"
|
|
import styles from "./index.module.css"
|
|
import useEmblaCarousel from "embla-carousel-react"
|
|
import Autoplay from "embla-carousel-autoplay"
|
|
import { useMemo } from "react"
|
|
import { DotButton, useDotButton } from "./carousel-dot-button"
|
|
import { NextButton, PrevButton, usePrevNextButtons } from "./carousel-arrow-button"
|
|
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
|
|
|
export function VtCarousel({
|
|
nodes,
|
|
context,
|
|
}: {
|
|
nodes: LayoutComponentDefinition
|
|
context: LayoutContext
|
|
}) {
|
|
const props = nodes.config ?? {}
|
|
const { options } = props as any
|
|
const images: string[] = props.images ?? props.slides ?? []
|
|
const links: (string | undefined)[] = props.links ?? []
|
|
const durationSeconds: number = props.duration ?? 4
|
|
const showControls = images.length > 1
|
|
|
|
const plugins = useMemo(() => {
|
|
if (!durationSeconds || durationSeconds <= 0) return []
|
|
return [
|
|
Autoplay({
|
|
delay: durationSeconds * 1000,
|
|
stopOnInteraction: false,
|
|
stopOnMouseEnter: true,
|
|
}),
|
|
]
|
|
}, [durationSeconds])
|
|
|
|
const [emblaRef, emblaApi] = useEmblaCarousel(options, plugins)
|
|
const { selectedIndex, scrollSnaps, onDotButtonClick } =
|
|
useDotButton(emblaApi)
|
|
|
|
const {
|
|
prevBtnDisabled,
|
|
nextBtnDisabled,
|
|
onPrevButtonClick,
|
|
onNextButtonClick,
|
|
} = usePrevNextButtons(emblaApi)
|
|
return (
|
|
<section className={styles["embla"]}>
|
|
<div className={styles["embla__viewport"]} ref={emblaRef}>
|
|
<div className={styles["embla__container"]}>
|
|
{images && images.map((src: string, index: number) => (
|
|
<div className={styles["embla__slide"]} key={index + src}>
|
|
<div className={styles["embla__slide__number"]}>
|
|
{links[index] ? (
|
|
<LocalizedClientLink href={links[index]}>
|
|
<img src={src} alt={`slide-${index + 1}`} className={styles["embla__slide__image"]} />
|
|
</LocalizedClientLink>
|
|
) : (
|
|
<img src={src} alt={`slide-${index + 1}`} className={styles["embla__slide__image"]} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{showControls && (
|
|
<div className={styles["embla__controls"]}>
|
|
<div className={styles["embla__buttons"]}>
|
|
<PrevButton onClick={onPrevButtonClick} disabled={prevBtnDisabled} />
|
|
<NextButton onClick={onNextButtonClick} disabled={nextBtnDisabled} />
|
|
</div>
|
|
|
|
<div className={styles["embla__dots"]}>
|
|
{scrollSnaps.map((_, index) => (
|
|
<DotButton
|
|
key={index}
|
|
onClick={() => onDotButtonClick(index)}
|
|
className={[
|
|
styles["embla__dot"],
|
|
index === selectedIndex ? styles["embla__dot--selected"] : "",
|
|
].filter(Boolean).join(" ")}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</section>
|
|
)
|
|
}
|