Merge branch 'main' of http://192.168.1.163:3000/inmake/stream.graff.tech-new
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -35,8 +35,9 @@ function SessionUsersPanel2() {
|
||||
<DraggableContainer
|
||||
enableSnapping={true}
|
||||
autoAlign={true}
|
||||
initialPosition={{ bottom: 16, right: 16 }}
|
||||
padding={20}
|
||||
// initialPosition={{ top: 20, left: 20 }}
|
||||
initialCorner="bottom-right"
|
||||
padding="1.111vw"
|
||||
className="flex gap-4"
|
||||
>
|
||||
{users.map((user) => (
|
||||
|
||||
Reference in New Issue
Block a user