123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
/* eslint-disable react-hooks/exhaustive-deps */
|
|
import clsx from "clsx";
|
|
import PopupHeader from "./PopupHeader";
|
|
import { useEffect, useState } from "react";
|
|
import { useRef } from "react";
|
|
import usePopupStore from "../store/popupStore";
|
|
|
|
interface PopupWrapperProps {
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
title?: string;
|
|
leftButton?: React.ReactNode;
|
|
draggable?: boolean;
|
|
}
|
|
|
|
function PopupWrapper({
|
|
children,
|
|
className,
|
|
title,
|
|
leftButton,
|
|
draggable,
|
|
}: PopupWrapperProps) {
|
|
const { position, setPosition } = usePopupStore();
|
|
const [mouseDown, setMouseDown] = useState(false);
|
|
const [mouseDownPosition, setMouseDownPosition] = useState(position);
|
|
|
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
const headerRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
addEventListener("mouseup", () => setMouseDown(false));
|
|
addEventListener("touchend", () => setMouseDown(false));
|
|
return () => {
|
|
removeEventListener("mouseup", () => setMouseDown(false));
|
|
removeEventListener("touchend", () => setMouseDown(false));
|
|
};
|
|
}, []);
|
|
|
|
function handleMove(e: MouseEvent | TouchEvent) {
|
|
if (draggable && mouseDown && wrapperRef.current) {
|
|
e.preventDefault();
|
|
const x = "clientX" in e ? e.clientX : e.touches[0].clientX;
|
|
const y = "clientY" in e ? e.clientY : e.touches[0].clientY;
|
|
setPosition({
|
|
x: Math.min(
|
|
Math.max(0, position.x + x - mouseDownPosition.x),
|
|
innerWidth - wrapperRef.current.clientWidth
|
|
),
|
|
y: Math.min(
|
|
Math.max(0, position.y + y - mouseDownPosition.y),
|
|
innerHeight - wrapperRef.current.clientHeight
|
|
),
|
|
});
|
|
setMouseDownPosition({ x, y });
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
addEventListener("mousemove", handleMove);
|
|
addEventListener("touchmove", handleMove);
|
|
return () => {
|
|
removeEventListener("mousemove", handleMove);
|
|
removeEventListener("touchmove", handleMove);
|
|
};
|
|
}, [handleMove]);
|
|
|
|
useEffect(() => {
|
|
if (headerRef.current) {
|
|
headerRef.current.addEventListener("mousedown", (e) => {
|
|
setMouseDown(true);
|
|
setMouseDownPosition({ x: e.clientX, y: e.clientY });
|
|
});
|
|
headerRef.current.addEventListener("touchstart", (e) => {
|
|
setMouseDown(true);
|
|
setMouseDownPosition({
|
|
x: e.touches[0].clientX,
|
|
y: e.touches[0].clientY,
|
|
});
|
|
});
|
|
}
|
|
return () => {
|
|
if (headerRef.current) {
|
|
headerRef.current.removeEventListener("mousedown", (e) => {
|
|
setMouseDown(true);
|
|
setMouseDownPosition({ x: e.clientX, y: e.clientY });
|
|
});
|
|
headerRef.current.removeEventListener("touchstart", (e) => {
|
|
setMouseDown(true);
|
|
setMouseDownPosition({
|
|
x: e.touches[0].clientX,
|
|
y: e.touches[0].clientY,
|
|
});
|
|
});
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
ref={wrapperRef}
|
|
className={clsx(
|
|
"2xl:rounded-[2.222vw] relative bg-white shadow-[0_4px_40px_0_rgba(15,16,17,0.1)] 2xl:w-[21.667vw] sm:rounded-[32px] max-sm:w-screen max-sm:rounded-t-[32px]",
|
|
className
|
|
)}
|
|
>
|
|
{/* Полоска-ручка для свайпа на мобильных */}
|
|
<div className="hidden max-sm:flex justify-center pt-1 pb-1 absolute -top-3 left-1/2 -translate-x-1/2">
|
|
<div className="w-8 h-1 bg-[#141414] rounded-full opacity-50" />
|
|
</div>
|
|
|
|
<PopupHeader
|
|
headerRef={headerRef}
|
|
title={title}
|
|
leftButton={leftButton}
|
|
draggable={draggable}
|
|
/>
|
|
<div className="2xl:p-[1.389vw] p-5">{children}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default PopupWrapper;
|