From fe306aed26039c6f1df2990d3595b27e909aa33d Mon Sep 17 00:00:00 2001 From: kjs Date: Thu, 6 Nov 2025 11:40:59 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=9C=84=EC=A0=AF=EC=97=90=20=EB=93=9C=EB=9E=98=EA=B7=B8=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=9C=20=EB=A6=AC=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EC=A0=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 좌우 영역을 드래그로 조절 가능 - 리사이저: GripVertical 아이콘으로 시각적 표시 - 좌측 영역: 최소 10%, 최대 40%로 제한 - 호버 시 배경색 변경으로 피드백 제공 - 드래그 중 커서 및 텍스트 선택 방지 --- .../screen/widgets/CategoryWidget.tsx | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/frontend/components/screen/widgets/CategoryWidget.tsx b/frontend/components/screen/widgets/CategoryWidget.tsx index 0191cba3..54c8f98b 100644 --- a/frontend/components/screen/widgets/CategoryWidget.tsx +++ b/frontend/components/screen/widgets/CategoryWidget.tsx @@ -1,8 +1,9 @@ "use client"; -import React, { useState } from "react"; +import React, { useState, useRef, useCallback } from "react"; import { CategoryColumnList } from "@/components/table-category/CategoryColumnList"; import { CategoryValueManager } from "@/components/table-category/CategoryValueManager"; +import { GripVertical } from "lucide-react"; interface CategoryWidgetProps { widgetId: string; @@ -19,11 +20,49 @@ export function CategoryWidget({ widgetId, tableName }: CategoryWidgetProps) { columnName: string; columnLabel: string; } | null>(null); + + const [leftWidth, setLeftWidth] = useState(15); // 초기값 15% + const containerRef = useRef(null); + const isDraggingRef = useRef(false); + + const handleMouseDown = useCallback(() => { + isDraggingRef.current = true; + document.body.style.cursor = "col-resize"; + document.body.style.userSelect = "none"; + }, []); + + const handleMouseMove = useCallback((e: MouseEvent) => { + if (!isDraggingRef.current || !containerRef.current) return; + + const containerRect = containerRef.current.getBoundingClientRect(); + const newLeftWidth = ((e.clientX - containerRect.left) / containerRect.width) * 100; + + // 최소 10%, 최대 40%로 제한 + if (newLeftWidth >= 10 && newLeftWidth <= 40) { + setLeftWidth(newLeftWidth); + } + }, []); + + const handleMouseUp = useCallback(() => { + isDraggingRef.current = false; + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + }, []); + + React.useEffect(() => { + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, [handleMouseMove, handleMouseUp]); return ( -
- {/* 좌측: 카테고리 컬럼 리스트 (15%) */} -
+
+ {/* 좌측: 카테고리 컬럼 리스트 */} +
- {/* 우측: 카테고리 값 관리 (85%) */} -
+ {/* 리사이저 */} +
+ +
+ + {/* 우측: 카테고리 값 관리 */} +
{selectedColumn ? (