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.

This commit is contained in:
2025-10-21 19:08:39 +05:00
parent d2b818ea90
commit db484bdb15
2 changed files with 69 additions and 19 deletions
+66 -17
View File
@@ -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 {
* </DraggableContainer>
*
* @example
* // С указанием начального угла и отступами в процентах
* <DraggableContainer initialCorner="bottom-right" padding="2%">
* <YourContent />
* </DraggableContainer>
*
* @example
* // С отступами в vw
* <DraggableContainer initialCorner="top-left" padding="5vw">
* <YourContent />
* </DraggableContainer>
*
* @example
* // С ограничением перетаскивания границами окна
* <DraggableContainer constrainToBounds={true}>
* <DraggableContainer constrainToBounds={true} initialCorner="top-left">
* <YourContent />
* </DraggableContainer>
*
@@ -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<Position>(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<Position>(getInitialPosition());
const [isDragging, setIsDragging] = useState(false);
const [dragStartAlignment, setDragStartAlignment] = useState<string>("");
@@ -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;
};
+3 -2
View File
@@ -35,8 +35,9 @@ function SessionUsersPanel2() {
<DraggableContainer
enableSnapping={true}
autoAlign={true}
initialPosition={{ top: 20, left: 20 }}
padding={20}
// initialPosition={{ top: 20, left: 20 }}
initialCorner="bottom-right"
padding="1.111vw"
className="flex gap-4"
>
{users.map((user) => (