Enhance DraggableContainer with new props for enabling/disabling functionality and dragging. Update Popup components to improve structure and responsiveness, removing unnecessary draggable props. Integrate console logging for debugging in PopupContainer and ControlsPopover.
This commit is contained in:
@@ -15,6 +15,10 @@ export type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
||||
interface DraggableContainerProps {
|
||||
/** Содержимое контейнера */
|
||||
children: ReactNode;
|
||||
/** Включить весь функционал компонента (по умолчанию true). Если false, компонент просто рендерит children без стилей и позиционирования */
|
||||
enabled?: boolean;
|
||||
/** Включить возможность перетаскивания (по умолчанию true) */
|
||||
draggable?: boolean;
|
||||
/** Включить снэпинг к ближайшей четверти экрана при отпускании (по умолчанию true) */
|
||||
enableSnapping?: boolean;
|
||||
/** Автоматическое flex-выравнивание в зависимости от прижатого угла (по умолчанию false) */
|
||||
@@ -70,6 +74,24 @@ interface DraggableContainerProps {
|
||||
* </DraggableContainer>
|
||||
*
|
||||
* @example
|
||||
* // Центрирование по вертикали с указанием позиции справа
|
||||
* <DraggableContainer centerVertical={true} initialPosition={{ right: "4.444vw" }}>
|
||||
* <YourContent />
|
||||
* </DraggableContainer>
|
||||
*
|
||||
* @example
|
||||
* // Отключение перетаскивания (статичный контейнер)
|
||||
* <DraggableContainer draggable={false} initialPosition={{ top: "20px", right: "20px" }}>
|
||||
* <YourContent />
|
||||
* </DraggableContainer>
|
||||
*
|
||||
* @example
|
||||
* // Полное отключение функционала (компонент не применяет никаких стилей)
|
||||
* <DraggableContainer enabled={false}>
|
||||
* <YourContent />
|
||||
* </DraggableContainer>
|
||||
*
|
||||
* @example
|
||||
* // С указанием начального угла и отступами в процентах
|
||||
* <DraggableContainer initialCorner="bottom-right" padding="2%">
|
||||
* <YourContent />
|
||||
@@ -95,6 +117,8 @@ interface DraggableContainerProps {
|
||||
*/
|
||||
export default function DraggableContainer({
|
||||
children,
|
||||
enabled = true,
|
||||
draggable = true,
|
||||
enableSnapping = false,
|
||||
autoAlign = false,
|
||||
constrainToBounds = false,
|
||||
@@ -102,7 +126,7 @@ export default function DraggableContainer({
|
||||
centerHorizontal = false,
|
||||
initialCorner,
|
||||
initialPosition,
|
||||
padding = "16px",
|
||||
padding = "1.111vw",
|
||||
className = "",
|
||||
onPositionChange,
|
||||
}: DraggableContainerProps) {
|
||||
@@ -135,16 +159,26 @@ export default function DraggableContainer({
|
||||
const position: Position = {};
|
||||
const transforms: string[] = [];
|
||||
|
||||
// Вертикальное позиционирование
|
||||
if (centerVertical) {
|
||||
position.top = "50%";
|
||||
transforms.push("translateY(-50%)");
|
||||
} else if (initialPosition?.top !== undefined) {
|
||||
position.top = initialPosition.top;
|
||||
} else if (initialPosition?.bottom !== undefined) {
|
||||
position.bottom = initialPosition.bottom;
|
||||
} else {
|
||||
position.top = padding;
|
||||
}
|
||||
|
||||
// Горизонтальное позиционирование
|
||||
if (centerHorizontal) {
|
||||
position.left = "50%";
|
||||
transforms.push("translateX(-50%)");
|
||||
} else if (initialPosition?.left !== undefined) {
|
||||
position.left = initialPosition.left;
|
||||
} else if (initialPosition?.right !== undefined) {
|
||||
position.right = initialPosition.right;
|
||||
} else {
|
||||
position.left = padding;
|
||||
}
|
||||
@@ -246,10 +280,12 @@ export default function DraggableContainer({
|
||||
};
|
||||
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
if (!draggable) return;
|
||||
startDrag(e.clientX, e.clientY);
|
||||
};
|
||||
|
||||
const handleTouchStart = (e: React.TouchEvent) => {
|
||||
if (!draggable) return;
|
||||
if (e.touches.length > 0) {
|
||||
startDrag(e.touches[0].clientX, e.touches[0].clientY);
|
||||
}
|
||||
@@ -407,19 +443,25 @@ export default function DraggableContainer({
|
||||
return getAlignmentClassesFromPosition(position);
|
||||
};
|
||||
|
||||
// Если компонент отключен, просто рендерим children без стилей и логики
|
||||
if (!enabled) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={clsx(
|
||||
"pointer-events-auto select-none touch-none",
|
||||
"pointer-events-auto select-none",
|
||||
draggable && "touch-none",
|
||||
!isDragging && "transition-all duration-500 ease-out",
|
||||
isDragging ? "cursor-grabbing" : "cursor-grab",
|
||||
draggable && (isDragging ? "cursor-grabbing" : "cursor-grab"),
|
||||
getAlignmentClasses(),
|
||||
className
|
||||
)}
|
||||
style={getContainerStyle()}
|
||||
onMouseDown={handleMouseDown}
|
||||
onTouchStart={handleTouchStart}
|
||||
onMouseDown={draggable ? handleMouseDown : undefined}
|
||||
onTouchStart={draggable ? handleTouchStart : undefined}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import usePopupStore from "../store/popupStore";
|
||||
import { useEffect } from "react";
|
||||
|
||||
function PopupContainer() {
|
||||
const { popup } = usePopupStore();
|
||||
|
||||
const isMobile = innerWidth < 640;
|
||||
|
||||
useEffect(() => {
|
||||
console.log(popup);
|
||||
}, [popup]);
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{popup && (
|
||||
<motion.div
|
||||
className="absolute max-sm:fixed"
|
||||
className="fixed bottom-0"
|
||||
initial={{ opacity: 0, y: isMobile ? "100%" : undefined }}
|
||||
animate={{ opacity: 1, y: isMobile ? "0%" : undefined }}
|
||||
exit={{ opacity: 0, y: isMobile ? "100%" : undefined }}
|
||||
transition={{ bounce: 0, ease: "easeInOut" }}
|
||||
>
|
||||
{popup}
|
||||
</motion.div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import clsx from "clsx";
|
||||
import usePopupStore from "../store/popupStore";
|
||||
import XMarkIcon from "./icons/XMarkIcon";
|
||||
import Button from "./ui/Button";
|
||||
@@ -6,22 +5,16 @@ import Button from "./ui/Button";
|
||||
interface PopupHeaderProps {
|
||||
title?: string;
|
||||
leftButton?: React.ReactNode;
|
||||
draggable?: boolean;
|
||||
}
|
||||
|
||||
function PopupHeader({ title, leftButton, draggable }: PopupHeaderProps) {
|
||||
function PopupHeader({ title, leftButton }: PopupHeaderProps) {
|
||||
const { setPopup } = usePopupStore();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"2xl:p-[1.111vw] p-4 flex justify-between items-center select-none relative",
|
||||
draggable && "cursor-grab active:cursor-grabbing"
|
||||
)}
|
||||
>
|
||||
<div className="2xl:p-[1.111vw] p-4 flex justify-between items-center select-none relative">
|
||||
<div className="2xl:size-[2.222vw] size-8">{leftButton}</div>
|
||||
{title && (
|
||||
<p className="title-s flex-1 font-medium text-center">{title}</p>
|
||||
<p className="flex-1 font-medium text-center title-s">{title}</p>
|
||||
)}
|
||||
<Button variant="secondary" size="small" onClick={() => setPopup(null)}>
|
||||
<div className="2xl:size-[1.111vw] size-4">
|
||||
|
||||
@@ -7,7 +7,6 @@ interface PopupWrapperProps {
|
||||
className?: string;
|
||||
title?: string;
|
||||
leftButton?: React.ReactNode;
|
||||
draggable?: boolean;
|
||||
}
|
||||
|
||||
function PopupWrapper({
|
||||
@@ -15,7 +14,6 @@ function PopupWrapper({
|
||||
className,
|
||||
title,
|
||||
leftButton,
|
||||
draggable,
|
||||
}: PopupWrapperProps) {
|
||||
return (
|
||||
<div
|
||||
@@ -25,15 +23,11 @@ function PopupWrapper({
|
||||
)}
|
||||
>
|
||||
{/* Полоска-ручка для свайпа на мобильных */}
|
||||
<div className="hidden max-sm:flex justify-center pt-1 pb-1 absolute -top-3 left-1/2 -translate-x-1/2">
|
||||
<div className="hidden absolute -top-3 left-1/2 justify-center pt-1 pb-1 -translate-x-1/2 max-sm:flex">
|
||||
<div className="w-8 h-1 bg-[#141414] rounded-full opacity-50" />
|
||||
</div>
|
||||
|
||||
<PopupHeader
|
||||
title={title}
|
||||
leftButton={leftButton}
|
||||
draggable={draggable}
|
||||
/>
|
||||
<PopupHeader title={title} leftButton={leftButton} />
|
||||
<div className="2xl:p-[1.389vw] p-5">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -33,8 +33,13 @@ export default function ChatPopup() {
|
||||
}
|
||||
|
||||
return (
|
||||
<DraggableContainer centerVertical>
|
||||
<PopupWrapper title="Чат" draggable className="sm:overflow-hidden">
|
||||
<DraggableContainer
|
||||
enabled={window.innerWidth >= 640}
|
||||
centerVertical
|
||||
constrainToBounds
|
||||
initialPosition={{ right: "5vw" }}
|
||||
>
|
||||
<PopupWrapper title="Чат" className="sm:overflow-hidden">
|
||||
<div className="flex flex-col 2xl:h-[27.778vw] relative 2xl:-m-[1.389vw] -m-5">
|
||||
<MessageFeed messages={messages} />
|
||||
<MessageInput onMessageSend={onMessageSend} />
|
||||
|
||||
@@ -38,17 +38,26 @@ function ControlsPopover() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { setPopup } = usePopupStore();
|
||||
const { popup, setPopup } = usePopupStore();
|
||||
const { setModal } = useModalStore();
|
||||
|
||||
function handleClickOpenChatPopup() {
|
||||
console.log("handleClickOpenChatPopup");
|
||||
setPopup(<ChatPopup />);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log(popup);
|
||||
}, [popup]);
|
||||
|
||||
return (
|
||||
<div className="2xl:hidden order-3 relative">
|
||||
<div className="relative order-3 2xl:hidden">
|
||||
<FloatingActionButton
|
||||
ref={buttonRef}
|
||||
className={clsx(isOpened && "!bg-[#7B60F3]")}
|
||||
onClick={() => setIsOpened(!isOpened)}
|
||||
>
|
||||
<div className="size-4 text-white">
|
||||
<div className="text-white size-4">
|
||||
<MoreIcon />
|
||||
</div>
|
||||
</FloatingActionButton>
|
||||
@@ -60,7 +69,7 @@ function ControlsPopover() {
|
||||
<Button
|
||||
variant="tertiary"
|
||||
className="w-full !justify-start"
|
||||
onClick={() => setPopup(<ChatPopup />)}
|
||||
onClick={handleClickOpenChatPopup}
|
||||
>
|
||||
<div className="size-4">
|
||||
<ChatFilledIcon />
|
||||
|
||||
@@ -76,6 +76,7 @@ function NewSessionPage() {
|
||||
const session = sessionData?.session;
|
||||
|
||||
function handleChatOpen() {
|
||||
console.log("handleChatOpen");
|
||||
setPopup(<ChatPopup />);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user