84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
|
|
import { useKeenSlider } from "keen-slider/react";
|
|
import { useState, type MouseEvent } from "react";
|
|
|
|
interface SliderProps {
|
|
className?: React.ComponentProps<'div'>['className'];
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
export default function Slider({ className, children }: SliderProps) {
|
|
const [currentSlide, setCurrentSlide] = useState(0);
|
|
const [loaded, setLoaded] = useState(false);
|
|
const [sliderRef, instanceRef] = useKeenSlider<HTMLDivElement>({
|
|
initial: 0,
|
|
slideChanged(slider) {
|
|
setCurrentSlide(slider.track.details.rel);
|
|
},
|
|
created() {
|
|
setLoaded(true);
|
|
},
|
|
});
|
|
|
|
const goPrev = (e: MouseEvent<unknown>) => {
|
|
e.stopPropagation();
|
|
if (currentSlide !== 0) {
|
|
instanceRef.current?.prev();
|
|
}
|
|
};
|
|
|
|
const goNext = (e: MouseEvent<unknown>) => {
|
|
e.stopPropagation();
|
|
if (instanceRef.current && currentSlide !== instanceRef.current.track.details.slides.length - 1) {
|
|
instanceRef.current.next();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className={`${className} relative`}>
|
|
<div>
|
|
<div ref={sliderRef} className="flex overflow-hidden relative w-full">
|
|
{children}
|
|
</div>
|
|
{loaded && instanceRef.current && (
|
|
<>
|
|
<Arrow left onClick={goPrev} disabled={currentSlide === 0} />
|
|
<Arrow onClick={goNext} disabled={instanceRef.current ? currentSlide === instanceRef.current.track.details.slides.length - 1 : true} />
|
|
</>
|
|
)}
|
|
</div>
|
|
{loaded && instanceRef.current && (
|
|
<div className="flex justify-center py-2">
|
|
{Array.from({ length: instanceRef.current.track.details.slides.length }, (_, idx) => (
|
|
<button
|
|
key={idx}
|
|
onClick={() => instanceRef.current?.moveToIdx(idx)}
|
|
className={`w-2.5 h-2.5 bg-white rounded-full m-1 cursor-pointer ${currentSlide === idx ? "opacity-30" : ""}`}
|
|
></button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface ArrowProps {
|
|
disabled: boolean;
|
|
left?: boolean;
|
|
onClick: (e: MouseEvent<unknown>) => void;
|
|
}
|
|
|
|
function Arrow({ disabled, left, onClick }: ArrowProps) {
|
|
const Icon = left ? ChevronLeftIcon : ChevronRightIcon;
|
|
const position = left ? '-left-10' : 'left-auto -right-10';
|
|
|
|
return (
|
|
<Icon
|
|
onClick={onClick}
|
|
height={50}
|
|
width={50}
|
|
className={`absolute ${position} top-1/2 cursor-pointer -translate-y-1/2 ${disabled ? "opacity-30" : ""}`}
|
|
/>
|
|
);
|
|
}
|