Files
irth-new-client-120/src/components/NewUnitSlider.tsx
T

114 lines
3.0 KiB
TypeScript

import React, { type PropsWithChildren, useState } from "react";
import Button from "./ui/Button";
import { useSwipeable } from "react-swipeable";
import { motion } from "motion/react";
import clsx from "clsx";
interface SliderItemProps {
text: string;
}
function flattenChildren(children: React.ReactNode): React.ReactElement[] {
const result: React.ReactElement[] = [];
React.Children.forEach(children, (child) => {
if (React.isValidElement(child)) {
if (child.type === React.Fragment) {
result.push(
...flattenChildren(
(child.props as { children?: React.ReactNode }).children
)
);
} else {
result.push(child);
}
}
});
return result;
}
function NewUnitSlider({ children }: PropsWithChildren) {
const [currentSlide, setCurrentSlide] = useState(0);
const flattenedChildren = flattenChildren(children);
const slides = flattenedChildren.map((element, index) => {
if (
React.isValidElement<PropsWithChildren<SliderItemProps>>(element) &&
element.props.text
) {
return {
index,
element,
text: element.props.text,
child: element.props.children,
};
}
return {
index,
text: `Slide ${index + 1}`,
element,
};
});
const handlers = useSwipeable({
onSwipedLeft: () => {
const slideText = slides[currentSlide]?.text;
if (slideText !== "Interior") {
setCurrentSlide(Math.min(currentSlide + 1, slides.length - 1));
}
},
onSwipedRight: () => setCurrentSlide(Math.max(currentSlide - 1, 0)),
preventScrollOnSwipe: true,
touchEventOptions: {
passive: false,
},
trackMouse: true,
});
return (
<div
className="relative size-full overflow-hidden 2xl:rounded-[1.111vw] rounded-xl border border-[#E2E2DC]"
{...handlers}
>
<motion.div
animate={{
x: `calc(-${currentSlide} * 100%)`,
}}
transition={{
duration: 0.5,
ease: "easeInOut",
}}
className="flex relative top-0 size-full"
>
{slides.map((slide) => slide.element)}
</motion.div>
<div className="absolute flex 2xl:gap-[0.278vw] gap-1 items-center 2xl:bottom-[1.667vw] md:max-2xl:bottom-6 left-1/2 -translate-x-1/2 max-md:hidden">
{slides.map((slide, index) => (
<Button
key={slide.text}
variant={currentSlide === index ? "cta" : "primary"}
onClick={() => setCurrentSlide(index)}
className="max-md:hidden"
>
{slide.text}
</Button>
))}
</div>
<div className="flex absolute bottom-4 left-1/2 gap-1 -translate-x-1/2 md:hidden">
{slides.map((_, index) => (
<div
key={index}
className={clsx(
"size-2 rounded-full transition-colors duration-300",
currentSlide === index ? "bg-[#00BED7]" : "bg-[#E2E2DC]"
)}
/>
))}
</div>
</div>
);
}
export default NewUnitSlider;