316 lines
10 KiB
TypeScript
316 lines
10 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { projects } from "../data/projects";
|
|
import UnitTypeImageWithMarkers from "./UnitTypeImageWithMarkers";
|
|
import Button from "./ui/Button";
|
|
import { AnimatePresence, motion } from "framer-motion";
|
|
import ChevronLeftIcon from "./icons/ChevronLeftIcon";
|
|
import ChevronRightIcon from "./icons/ChevronRightIcon";
|
|
import { useSwipeable } from "react-swipeable";
|
|
import clsx from "clsx";
|
|
|
|
interface UnitSliderProps {
|
|
unitTypeVariant: string;
|
|
complexName: string;
|
|
}
|
|
// костыль: в Мараси 2 bedroom b ЕДИНСТВЕННАЯ НЕ ЗЕРКАЛЬНАЯ ХАТА среди всех
|
|
function UnitSlider({ unitTypeVariant, complexName }: UnitSliderProps) {
|
|
const [hasSide, setHasSide] = useState(false);
|
|
|
|
const [selectedSide, setSelectedSide] = useState<"left" | "right">();
|
|
|
|
const [isLoft, setIsLoft] = useState(false);
|
|
|
|
const [currentSlide, setCurrentSlide] = useState(0);
|
|
|
|
useEffect(
|
|
() =>
|
|
setSelectedSide(
|
|
hasSide || unitTypeVariant === "2-bedroom-b" ? undefined : "left"
|
|
),
|
|
[hasSide, unitTypeVariant]
|
|
);
|
|
|
|
useEffect(() => {
|
|
setIsLoft(unitTypeVariant.includes("loft"));
|
|
setHasSide(
|
|
unitTypeVariant.endsWith("-left") || unitTypeVariant.endsWith("-right")
|
|
);
|
|
}, [unitTypeVariant]);
|
|
|
|
const handlers = useSwipeable({
|
|
onSwipedLeft: () =>
|
|
setCurrentSlide(Math.min(currentSlide + 1, isLoft ? 2 : 1)),
|
|
onSwipedRight: () => setCurrentSlide(Math.max(currentSlide - 1, 0)),
|
|
preventScrollOnSwipe: true,
|
|
touchEventOptions: {
|
|
passive: false,
|
|
},
|
|
trackMouse: true,
|
|
});
|
|
|
|
return (
|
|
<div
|
|
className="relative w-full h-full overflow-hidden bg-[#F3F3F2] 2xl:rounded-[1.111vw] rounded-xl group"
|
|
{...handlers}
|
|
>
|
|
<motion.div
|
|
animate={{
|
|
x: `calc(-${currentSlide} * 100%)`,
|
|
}}
|
|
transition={{
|
|
duration: 0.5,
|
|
ease: "easeInOut",
|
|
}}
|
|
className="flex h-full w-full relative top-0"
|
|
>
|
|
{isLoft ? (
|
|
<>
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={`${selectedSide}-lower`}
|
|
initial={{
|
|
opacity: 0,
|
|
}}
|
|
animate={{
|
|
opacity: 1,
|
|
}}
|
|
exit={{
|
|
opacity: 0,
|
|
}}
|
|
className="shrink-0 w-full 2xl:p-[2.222vw] p-6"
|
|
>
|
|
<UnitTypeImageWithMarkers
|
|
complexName={complexName}
|
|
unitTypeVariant={
|
|
hasSide
|
|
? unitTypeVariant
|
|
: `${unitTypeVariant}-lower-${selectedSide}`
|
|
}
|
|
floor={"lower"}
|
|
legend={
|
|
projects
|
|
.find((project) => project.slug === complexName)
|
|
?.types.find(
|
|
(type) =>
|
|
type.slug ===
|
|
(hasSide
|
|
? unitTypeVariant
|
|
.split("-")
|
|
.slice(0, isLoft ? -2 : -1)
|
|
.join("-")
|
|
: unitTypeVariant)
|
|
)?.legend || []
|
|
}
|
|
/>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={`${selectedSide}-upper`}
|
|
initial={{
|
|
opacity: 0,
|
|
}}
|
|
animate={{
|
|
opacity: 1,
|
|
}}
|
|
exit={{
|
|
opacity: 0,
|
|
}}
|
|
className="shrink-0 w-full 2xl:p-[2.222vw] p-6"
|
|
>
|
|
<UnitTypeImageWithMarkers
|
|
complexName={complexName}
|
|
unitTypeVariant={
|
|
hasSide
|
|
? unitTypeVariant.replace("lower", "upper")
|
|
: `${unitTypeVariant}-upper-${selectedSide}`
|
|
}
|
|
floor={"upper"}
|
|
legend={
|
|
projects
|
|
.find((project) => project.slug === complexName)
|
|
?.types.find(
|
|
(type) =>
|
|
type.slug ===
|
|
(hasSide
|
|
? unitTypeVariant
|
|
.split("-")
|
|
.slice(0, isLoft ? -2 : -1)
|
|
.join("-")
|
|
: unitTypeVariant)
|
|
)?.legend || []
|
|
}
|
|
/>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
</>
|
|
) : (
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={`${selectedSide}`}
|
|
initial={{
|
|
opacity: 0,
|
|
}}
|
|
animate={{
|
|
opacity: 1,
|
|
}}
|
|
exit={{
|
|
opacity: 0,
|
|
}}
|
|
className="shrink-0 w-full 2xl:p-[2.222vw] p-6"
|
|
>
|
|
<UnitTypeImageWithMarkers
|
|
complexName={complexName}
|
|
unitTypeVariant={
|
|
hasSide || unitTypeVariant === "2-bedroom-b"
|
|
? unitTypeVariant
|
|
: `${unitTypeVariant}-${selectedSide}`
|
|
}
|
|
legend={
|
|
projects
|
|
.find((project) => project.slug === complexName)
|
|
?.types.find(
|
|
(type) =>
|
|
type.slug ===
|
|
(hasSide
|
|
? unitTypeVariant.split("-").slice(0, -1).join("-")
|
|
: unitTypeVariant)
|
|
)?.legend || []
|
|
}
|
|
/>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
)}
|
|
<div className="w-full shrink-0">
|
|
<img
|
|
src={`/images/interiors/${complexName}/${
|
|
complexName === "marasi-drive"
|
|
? unitTypeVariant.split("-").slice(0, 2).join("-")
|
|
: hasSide && unitTypeVariant !== "2-bedroom-b"
|
|
? unitTypeVariant
|
|
.split("-")
|
|
.slice(0, isLoft ? -2 : -1)
|
|
.join("-")
|
|
: unitTypeVariant
|
|
}.png`}
|
|
alt=""
|
|
className="object-cover h-full pointer-events-none 2xl:rounded-[1.111vw] rounded-2xl"
|
|
/>
|
|
</div>
|
|
</motion.div>
|
|
<AnimatePresence mode="wait">
|
|
{!hasSide &&
|
|
unitTypeVariant !== "2-bedroom-b" &&
|
|
currentSlide !== (isLoft ? 2 : 1) && (
|
|
<motion.div
|
|
initial={{
|
|
opacity: 0,
|
|
}}
|
|
animate={{
|
|
opacity: 1,
|
|
}}
|
|
exit={{
|
|
opacity: 0,
|
|
}}
|
|
className="flex 2xl:gap-[0.556vw] gap-2 items-center absolute 2xl:top-[2.222vw] top-6 left-1/2 -translate-x-1/2 max-md:hidden"
|
|
>
|
|
<p className="text-btn-m">Left</p>
|
|
<div
|
|
className="2xl:w-[2.778vw] w-10 2xl:p-[0.139vw] p-0.5 rounded-full cursor-pointer transition-colors bg-[#00BED7]"
|
|
onClick={() =>
|
|
setSelectedSide(selectedSide === "left" ? "right" : "left")
|
|
}
|
|
>
|
|
<motion.div
|
|
className="2xl:size-[1.389vw] size-5 rounded-full bg-white"
|
|
initial={{
|
|
x: selectedSide === "right" ? "80%" : 0,
|
|
}}
|
|
animate={{
|
|
x: selectedSide === "right" ? "80%" : 0,
|
|
}}
|
|
transition={{ bounce: 0, duration: 0.3 }}
|
|
/>
|
|
</div>
|
|
<p className="text-btn-m">Right</p>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
<div className="absolute flex 2xl:gap-[0.278vw] gap-1 items-center 2xl:bottom-[1.667vw] md:max-2xl:bottom-6 bottom-4 left-1/2 -translate-x-1/2">
|
|
{isLoft ? (
|
|
<>
|
|
<Button
|
|
variant={currentSlide === 0 ? "cta" : "secondary"}
|
|
onClick={() => setCurrentSlide(0)}
|
|
className="max-md:hidden"
|
|
>
|
|
Lower
|
|
</Button>
|
|
<Button
|
|
variant={currentSlide === 1 ? "cta" : "secondary"}
|
|
onClick={() => setCurrentSlide(1)}
|
|
className="max-md:hidden"
|
|
>
|
|
Upper
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<Button
|
|
variant={currentSlide === 0 ? "cta" : "secondary"}
|
|
onClick={() => setCurrentSlide(0)}
|
|
className="max-md:hidden"
|
|
>
|
|
Layout
|
|
</Button>
|
|
)}
|
|
<Button
|
|
variant={currentSlide === (isLoft ? 2 : 1) ? "cta" : "secondary"}
|
|
onClick={() => setCurrentSlide(isLoft ? 2 : 1)}
|
|
className="max-md:hidden"
|
|
>
|
|
Interior
|
|
</Button>
|
|
{Array.from({ length: isLoft ? 3 : 2 }).map((_, index) => (
|
|
<div
|
|
key={index}
|
|
className={clsx(
|
|
"md:hidden size-2 rounded-full transition-colors",
|
|
currentSlide === index ? "bg-[#00BED7]" : "bg-[#E2E2DC]"
|
|
)}
|
|
/>
|
|
))}
|
|
</div>
|
|
<Button
|
|
variant="secondary"
|
|
className="absolute top-1/2 -translate-y-1/2 2xl:left-[2.778vw] group-hover:opacity-100 opacity-0 transition-opacity duration-300 max-2xl:hidden"
|
|
onlyIcon
|
|
onClick={() =>
|
|
setCurrentSlide(currentSlide === 0 ? 0 : currentSlide - 1)
|
|
}
|
|
disabled={currentSlide === 0}
|
|
>
|
|
<div className="2xl:size-[1.389vw] size-5">
|
|
<ChevronLeftIcon />
|
|
</div>
|
|
</Button>
|
|
<Button
|
|
variant="secondary"
|
|
className="absolute top-1/2 -translate-y-1/2 2xl:right-[2.778vw] group-hover:opacity-100 opacity-0 transition-opacity duration-300 max-2xl:hidden"
|
|
onlyIcon
|
|
onClick={() =>
|
|
setCurrentSlide(
|
|
currentSlide === (isLoft ? 2 : 1) ? currentSlide : currentSlide + 1
|
|
)
|
|
}
|
|
disabled={currentSlide === (isLoft ? 2 : 1)}
|
|
>
|
|
<div className="2xl:size-[1.389vw] size-5">
|
|
<ChevronRightIcon />
|
|
</div>
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default UnitSlider;
|