159 lines
4.9 KiB
TypeScript
159 lines
4.9 KiB
TypeScript
"use client";
|
||
import { AnimatePresence, motion, PanInfo } from "framer-motion";
|
||
import Close from "../../../../public/icons/close.svg";
|
||
import Coin from "../../../../public/icons/coin.svg";
|
||
import { categories, categoryDescription } from "@/consts/categories";
|
||
import { useEffect, useState } from "react";
|
||
import { useModalStore } from "@/stores/useModalStore";
|
||
import VideoPrimeModal from "./VideoPrimeModal";
|
||
|
||
const variantsAnimations = {
|
||
enter: (direction: number) => {
|
||
return {
|
||
x: direction > 0 ? 300 : -300,
|
||
opacity: 0,
|
||
};
|
||
},
|
||
center: {
|
||
x: 0,
|
||
opacity: 1,
|
||
transition: { duration: 0.2 },
|
||
},
|
||
exit: (direction: number) => {
|
||
return {
|
||
x: direction < 0 ? 300 : -300,
|
||
opacity: 0,
|
||
transition: { duration: 0.2 },
|
||
};
|
||
},
|
||
};
|
||
|
||
function CategoryModal({
|
||
titleCategory,
|
||
}: {
|
||
titleCategory: keyof typeof categories;
|
||
}) {
|
||
const description = categoryDescription[titleCategory];
|
||
const [[page, direction], setPage] = useState([0, 0]);
|
||
const { modal, setModal } = useModalStore();
|
||
|
||
const paginate = (newDirection: number) => {
|
||
if (
|
||
(newDirection > 0 && page < description.length - 1) ||
|
||
(newDirection < 0 && page > 0)
|
||
) {
|
||
setPage([page + newDirection, newDirection]);
|
||
}
|
||
};
|
||
function onDragEnd(
|
||
event: MouseEvent | TouchEvent | PointerEvent,
|
||
{ offset, velocity }: PanInfo
|
||
) {
|
||
const swipe = Math.abs(offset.x) * velocity.x;
|
||
const swipeThreshold = 100;
|
||
|
||
if (swipe < -swipeThreshold) {
|
||
paginate(1);
|
||
} else if (swipe > swipeThreshold) {
|
||
paginate(-1);
|
||
}
|
||
}
|
||
|
||
useEffect(() => {
|
||
document.body.style.overflow = "hidden";
|
||
return () => {
|
||
document.body.style.overflow = "auto";
|
||
};
|
||
}, []);
|
||
|
||
return (
|
||
<div
|
||
className={`fixed inset-0 bg-[#0F1011] z-[1] px-2.5 py-4 flex flex-col gap-5 h-full w-full md:p-10 md:gap-10`}
|
||
>
|
||
<div className="h-12 flex justify-between items-center">
|
||
<h1
|
||
className={`text-[24px] leading-[0.85] tracking-[-0.04em] w-fit md:text-3xl`}
|
||
>
|
||
{titleCategory}
|
||
</h1>
|
||
<motion.button
|
||
className="w-12 h-12 z-[2] bg-[#37393B99] rounded-2xl flex items-center justify-center md:cursor-pointer"
|
||
onTap={() => setModal(null)}
|
||
>
|
||
<Close className="w-5 h-5" />
|
||
</motion.button>
|
||
</div>
|
||
<motion.div
|
||
key={page}
|
||
initial="enter"
|
||
animate="center"
|
||
exit="exit"
|
||
custom={direction}
|
||
drag="x"
|
||
dragConstraints={{ left: 0, right: 0 }}
|
||
dragElastic={0.1}
|
||
onDragEnd={onDragEnd}
|
||
onDragStart={(e) => {
|
||
e.preventDefault();
|
||
}}
|
||
variants={variantsAnimations}
|
||
className="absolute top-20 left-1/2 -translate-x-1/2 w-full flex flex-col items-center md:top-25"
|
||
>
|
||
{description[page].type === "video" ? (
|
||
<video
|
||
src={description[page].content}
|
||
autoPlay
|
||
loop
|
||
muted
|
||
className="md:w-[60%] md:h-[40vh] max-md:w-[94%] max-md:rounded-2xl"
|
||
></video>
|
||
) : description[page].type === "videoScreen" ? (
|
||
<VideoPrimeModal src={description[page].content} />
|
||
) : (
|
||
<div
|
||
className={`bg-no-repeat w-[94.444vw] h-[30vh] ${description[page].content}`}
|
||
></div>
|
||
)}
|
||
|
||
<div className="bg-[#37393B99] rounded-2xl px-5 py-6 flex flex-col gap-3 w-[95%] ">
|
||
<h2 className="font-medium text-[20px] leading-[24px] tracking-[-0.02em]">
|
||
{description[page].title}
|
||
</h2>
|
||
<div className="md:flex md:gap-2.5 max-md:flex max-md:flex-col max-md:gap-3">
|
||
<p className="font-normal text-[14px] leading-[135%] tracking-[0em] md:flex-1">
|
||
{description[page].text1}
|
||
</p>
|
||
<p className="font-normal text-[14px] leading-[135%] tracking-[0em] md:flex-1">
|
||
{description[page].text2}
|
||
</p>
|
||
</div>
|
||
<div className="mt-[32px] flex gap-2">
|
||
<div className="btns w-fit px-3 py-[7px] rounded-[17px] bg-gradient-to-r from-[#6078F2] via-[#7583f3] to-[#C868F5]">
|
||
{description[page].package}
|
||
</div>
|
||
{description[page].isOption && (
|
||
<div className="btns w-fit px-3 py-[7px] rounded-[17px] bg-[#37393B99] flex gap-1 items-center">
|
||
<Coin className="w-4 h-4" /> Опция
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
{description.length > 1 && (
|
||
<div className="absolute w-full flex justify-center gap-0.5 bottom-4">
|
||
{description.map((item, index) => (
|
||
<div
|
||
className={`w-2 h-2 rounded-full ${
|
||
index === page ? "bg-white" : "bg-[#37393B99]"
|
||
}`}
|
||
key={index}
|
||
></div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default CategoryModal;
|