diff --git a/client/src/components/DraggableContainer.tsx b/client/src/components/DraggableContainer.tsx index 23b2b34..f50da2d 100644 --- a/client/src/components/DraggableContainer.tsx +++ b/client/src/components/DraggableContainer.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from "react"; +import { flushSync } from "react-dom"; import type { ReactNode } from "react"; import clsx from "clsx"; @@ -282,11 +283,19 @@ export default function DraggableContainer({ setDragStartAlignment(getAlignmentClassesFromPosition(position)); } - // Если контейнер был по центру с transform, убираем transform и используем абсолютную позицию - if (position.transform) { - setPosition({ - top: rect.top, - left: rect.left, + // Конвертируем позицию в left/top перед началом драга + // Это нужно чтобы избежать скачка при переходе с right/bottom на left/top + // Используем flushSync для синхронного обновления DOM + if ( + position.transform || + position.right !== undefined || + position.bottom !== undefined + ) { + flushSync(() => { + setPosition({ + top: rect.top, + left: rect.left, + }); }); } @@ -496,7 +505,7 @@ export default function DraggableContainer({ className={clsx( "pointer-events-auto", // draggable && "touch-none", - !isDragging && "transition-all duration-500 ease-out", + !isDragging && enableSnapping && "transition-all duration-500 ease-out", draggable && !dragHandleRef && (isDragging ? "cursor-grabbing" : "cursor-grab"), diff --git a/client/src/components/ui/PopoverWrapper.tsx b/client/src/components/ui/PopoverWrapper.tsx index 02503e6..b0da57a 100644 --- a/client/src/components/ui/PopoverWrapper.tsx +++ b/client/src/components/ui/PopoverWrapper.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { createPortal } from "react-dom"; import { AnimatePresence, motion } from "motion/react"; import clsx from "clsx"; @@ -19,6 +20,7 @@ function PopoverWrapper({ }: PopoverProps) { const [openUpwards, setOpenUpwards] = useState(false); const [menuHeight, setMenuHeight] = useState(0); + const [, forceUpdate] = useState(0); useEffect(() => { if ( @@ -33,6 +35,26 @@ function PopoverWrapper({ } }, [parentElRef, isOpened, menuHeight, position]); + // Обновляем позицию при изменении положения родительского элемента + useEffect(() => { + if (!isOpened) return; + + let animationFrameId: number; + + const updatePosition = () => { + forceUpdate((prev) => prev + 1); + animationFrameId = requestAnimationFrame(updatePosition); + }; + + animationFrameId = requestAnimationFrame(updatePosition); + + return () => { + if (animationFrameId) { + cancelAnimationFrame(animationFrameId); + } + }; + }, [isOpened]); + const getPositionStyles = () => { if (!parentElRef.current) return {}; @@ -66,7 +88,7 @@ function PopoverWrapper({ }; }; - return ( + return createPortal( {isOpened && ( {children} )} - + , + document.body ); } diff --git a/client/src/pages/HomePage.tsx b/client/src/pages/HomePage.tsx index d962c29..50667c4 100644 --- a/client/src/pages/HomePage.tsx +++ b/client/src/pages/HomePage.tsx @@ -11,6 +11,7 @@ import ChatPopup from "../components/popups/ChatPopup"; import ChatFilledIcon from "../components/icons/ChatFilledIcon"; import ParticipantsPopup from "../components/popups/ParticipantsPopup"; import ControlsPopover from "../components/ui/ControlsPopover"; +import SessionUsersPanel2 from "../components/SessionUsersPanel2"; function HomePage() { const { data: user } = useMe(); @@ -59,6 +60,7 @@ function HomePage() { +