Enhance DraggableContainer with centerVertical and centerHorizontal props for improved positioning. Update ChatPopup to utilize DraggableContainer for better layout control and responsiveness.

This commit is contained in:
2025-10-21 19:41:18 +05:00
parent 3b241ae45f
commit b20b7bb8eb
2 changed files with 74 additions and 10 deletions
+63 -2
View File
@@ -7,6 +7,7 @@ export interface Position {
left?: number | string;
right?: number | string;
bottom?: number | string;
transform?: string;
}
export type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
@@ -20,6 +21,10 @@ interface DraggableContainerProps {
autoAlign?: boolean;
/** Ограничить перетаскивание границами окна (по умолчанию false) */
constrainToBounds?: boolean;
/** Центрировать контейнер по вертикали (имеет приоритет над initialCorner и initialPosition) */
centerVertical?: boolean;
/** Центрировать контейнер по горизонтали (имеет приоритет над initialCorner и initialPosition) */
centerHorizontal?: boolean;
/** Начальный угол экрана (имеет приоритет над initialPosition) */
initialCorner?: Corner;
/** Начальная позиция контейнера (используется если не указан initialCorner) */
@@ -47,6 +52,24 @@ interface DraggableContainerProps {
* </DraggableContainer>
*
* @example
* // Размещение по центру экрана (вертикально и горизонтально)
* <DraggableContainer centerVertical={true} centerHorizontal={true}>
* <YourContent />
* </DraggableContainer>
*
* @example
* // Центрирование только по вертикали
* <DraggableContainer centerVertical={true}>
* <YourContent />
* </DraggableContainer>
*
* @example
* // Центрирование только по горизонтали
* <DraggableContainer centerHorizontal={true}>
* <YourContent />
* </DraggableContainer>
*
* @example
* // С указанием начального угла и отступами в процентах
* <DraggableContainer initialCorner="bottom-right" padding="2%">
* <YourContent />
@@ -75,6 +98,8 @@ export default function DraggableContainer({
enableSnapping = false,
autoAlign = false,
constrainToBounds = false,
centerVertical = false,
centerHorizontal = false,
initialCorner,
initialPosition,
padding = "20px",
@@ -105,6 +130,32 @@ export default function DraggableContainer({
// Определяем начальную позицию
const getInitialPosition = (): Position => {
// Если используется центрирование
if (centerVertical || centerHorizontal) {
const position: Position = {};
const transforms: string[] = [];
if (centerVertical) {
position.top = "50%";
transforms.push("translateY(-50%)");
} else {
position.top = padding;
}
if (centerHorizontal) {
position.left = "50%";
transforms.push("translateX(-50%)");
} else {
position.left = padding;
}
if (transforms.length > 0) {
position.transform = transforms.join(" ");
}
return position;
}
if (initialCorner) {
return getPositionFromCorner(initialCorner);
}
@@ -112,7 +163,7 @@ export default function DraggableContainer({
return initialPosition;
}
// По умолчанию - верхний правый угол
return { top: padding, right: padding };
return { top: padding, left: padding };
};
const [position, setPosition] = useState<Position>(getInitialPosition());
@@ -173,6 +224,14 @@ export default function DraggableContainer({
setDragStartAlignment(getAlignmentClassesFromPosition(position));
}
// Если контейнер был по центру с transform, убираем transform и используем абсолютную позицию
if (position.transform) {
setPosition({
top: rect.top,
left: rect.left,
});
}
dragRef.current = {
isDragging: true,
startX: clientX,
@@ -329,7 +388,9 @@ export default function DraggableContainer({
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);
if (position.bottom !== undefined)
style.bottom = formatValue(position.bottom);
if (position.transform !== undefined) style.transform = position.transform;
return style;
};
+11 -8
View File
@@ -4,6 +4,7 @@ import Button from "../ui/Button";
import { useMe } from "../../hooks/useAuth";
import clsx from "clsx";
import PopupWrapper from "../PopupWrapper";
import DraggableContainer from "../DraggableContainer";
export default function ChatPopup() {
const [messages, setMessages] = useState<MessageItemProps[]>([
@@ -32,12 +33,14 @@ export default function ChatPopup() {
}
return (
<PopupWrapper title="Чат" draggable 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} />
</div>
</PopupWrapper>
<DraggableContainer centerVertical>
<PopupWrapper title="Чат" draggable 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} />
</div>
</PopupWrapper>
</DraggableContainer>
);
}
@@ -108,13 +111,13 @@ function MessageItem({ senderId, timestamp, content }: MessageItemProps) {
)}
<div>{content}</div>
</div>
<span className="caption-xs opacity-30">{timestamp}</span>
<span className="opacity-30 caption-xs">{timestamp}</span>
</div>
<div className="2xl:size-[2.222vw] size-8 rounded-full">
<img
src="/img/popups/MessageUserPfp.png"
className="size-full object-cover"
className="object-cover size-full"
alt="user"
/>
</div>