From db484bdb1509993d510df00c0a4787ddb59a0066 Mon Sep 17 00:00:00 2001 From: inmake Date: Tue, 21 Oct 2025 19:08:39 +0500 Subject: [PATCH] Enhance DraggableContainer to support initial positioning via corner specification and allow padding in various units. Update SessionUsersPanel2 to utilize new initialCorner prop and adjust padding accordingly. --- client/src/components/DraggableContainer.tsx | 83 ++++++++++++++++---- client/src/components/SessionUsersPanel2.tsx | 5 +- 2 files changed, 69 insertions(+), 19 deletions(-) diff --git a/client/src/components/DraggableContainer.tsx b/client/src/components/DraggableContainer.tsx index 7028911..9333b30 100644 --- a/client/src/components/DraggableContainer.tsx +++ b/client/src/components/DraggableContainer.tsx @@ -2,13 +2,15 @@ import { useEffect, useRef, useState } from "react"; import type { ReactNode } from "react"; import clsx from "clsx"; -interface Position { - top?: number; - left?: number; - right?: number; - bottom?: number; +export interface Position { + top?: number | string; + left?: number | string; + right?: number | string; + bottom?: number | string; } +export type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right"; + interface DraggableContainerProps { /** Содержимое контейнера */ children: ReactNode; @@ -18,10 +20,12 @@ interface DraggableContainerProps { autoAlign?: boolean; /** Ограничить перетаскивание границами окна (по умолчанию false) */ constrainToBounds?: boolean; - /** Начальная позиция контейнера */ + /** Начальный угол экрана (имеет приоритет над initialPosition) */ + initialCorner?: Corner; + /** Начальная позиция контейнера (используется если не указан initialCorner) */ initialPosition?: Position; - /** Отступ от краев экрана при снэпинге (по умолчанию 20px) */ - padding?: number; + /** Отступ от краев экрана при снэпинге и начальной позиции (по умолчанию "20px"). Можно указать в px, %, vw, vh */ + padding?: number | string; /** Дополнительные CSS классы */ className?: string; /** Колбэк при изменении позиции */ @@ -43,8 +47,20 @@ interface DraggableContainerProps { * * * @example + * // С указанием начального угла и отступами в процентах + * + * + * + * + * @example + * // С отступами в vw + * + * + * + * + * @example * // С ограничением перетаскивания границами окна - * + * * * * @@ -59,8 +75,9 @@ export default function DraggableContainer({ enableSnapping = false, autoAlign = false, constrainToBounds = false, - initialPosition = { top: 20, right: 20 }, - padding = 20, + initialCorner, + initialPosition, + padding = "20px", className = "", onPositionChange, }: DraggableContainerProps) { @@ -72,7 +89,33 @@ export default function DraggableContainer({ initialPosition: { top: 0, left: 0 }, }); - const [position, setPosition] = useState(initialPosition); + // Функция для преобразования угла в позицию + const getPositionFromCorner = (corner: Corner): Position => { + switch (corner) { + case "top-left": + return { top: padding, left: padding }; + case "top-right": + return { top: padding, right: padding }; + case "bottom-left": + return { bottom: padding, left: padding }; + case "bottom-right": + return { bottom: padding, right: padding }; + } + }; + + // Определяем начальную позицию + const getInitialPosition = (): Position => { + if (initialCorner) { + return getPositionFromCorner(initialCorner); + } + if (initialPosition) { + return initialPosition; + } + // По умолчанию - верхний правый угол + return { top: padding, right: padding }; + }; + + const [position, setPosition] = useState(getInitialPosition()); const [isDragging, setIsDragging] = useState(false); const [dragStartAlignment, setDragStartAlignment] = useState(""); @@ -256,7 +299,9 @@ export default function DraggableContainer({ if (isDragging) { document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); - document.addEventListener("touchmove", handleTouchMove, { passive: false }); + document.addEventListener("touchmove", handleTouchMove, { + passive: false, + }); document.addEventListener("touchend", handleTouchEnd); document.addEventListener("touchcancel", handleTouchEnd); @@ -277,10 +322,14 @@ export default function DraggableContainer({ zIndex: 1000, }; - if (position.top !== undefined) style.top = `${position.top}px`; - if (position.left !== undefined) style.left = `${position.left}px`; - if (position.right !== undefined) style.right = `${position.right}px`; - if (position.bottom !== undefined) style.bottom = `${position.bottom}px`; + const formatValue = (value: number | string): string => { + return typeof value === "number" ? `${value}px` : value; + }; + + if (position.top !== undefined) style.top = formatValue(position.top); + if (position.left !== undefined) style.left = formatValue(position.left); + if (position.right !== undefined) style.right = formatValue(position.right); + if (position.bottom !== undefined) style.bottom = formatValue(position.bottom); return style; }; diff --git a/client/src/components/SessionUsersPanel2.tsx b/client/src/components/SessionUsersPanel2.tsx index 0bfc8af..509271a 100644 --- a/client/src/components/SessionUsersPanel2.tsx +++ b/client/src/components/SessionUsersPanel2.tsx @@ -35,8 +35,9 @@ function SessionUsersPanel2() { {users.map((user) => (