From d01ade4e4fd229ec49ec64ddd6daa2c6f995a63b Mon Sep 17 00:00:00 2001 From: dohyeons Date: Wed, 1 Oct 2025 16:27:05 +0900 Subject: [PATCH] =?UTF-8?q?=EB=88=88=EA=B8=88=EC=9E=90(Ruler)=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/designer/ReportDesignerCanvas.tsx | 130 +++++++++------- .../report/designer/ReportDesignerToolbar.tsx | 13 ++ frontend/components/report/designer/Ruler.tsx | 145 ++++++++++++++++++ frontend/contexts/ReportDesignerContext.tsx | 10 ++ 4 files changed, 244 insertions(+), 54 deletions(-) create mode 100644 frontend/components/report/designer/Ruler.tsx diff --git a/frontend/components/report/designer/ReportDesignerCanvas.tsx b/frontend/components/report/designer/ReportDesignerCanvas.tsx index 21b1335d..84629f9e 100644 --- a/frontend/components/report/designer/ReportDesignerCanvas.tsx +++ b/frontend/components/report/designer/ReportDesignerCanvas.tsx @@ -5,6 +5,7 @@ import { useDrop } from "react-dnd"; import { useReportDesigner } from "@/contexts/ReportDesignerContext"; import { ComponentConfig } from "@/types/report"; import { CanvasComponent } from "./CanvasComponent"; +import { Ruler } from "./Ruler"; import { v4 as uuidv4 } from "uuid"; export function ReportDesignerCanvas() { @@ -27,6 +28,7 @@ export function ReportDesignerCanvas() { pasteComponents, undo, redo, + showRuler, } = useReportDesigner(); const [{ isOver }, drop] = useDrop(() => ({ @@ -201,62 +203,82 @@ export function ReportDesignerCanvas() { {/* 캔버스 스크롤 영역 */}
-
{ - canvasRef.current = node; - drop(node); - }} - className={`relative mx-auto bg-white shadow-lg ${isOver ? "ring-2 ring-blue-500" : ""}`} - style={{ - width: `${canvasWidth}mm`, - minHeight: `${canvasHeight}mm`, - backgroundImage: showGrid - ? ` - linear-gradient(to right, #e5e7eb 1px, transparent 1px), - linear-gradient(to bottom, #e5e7eb 1px, transparent 1px) - ` - : undefined, - backgroundSize: showGrid ? `${gridSize}px ${gridSize}px` : undefined, - }} - onClick={handleCanvasClick} - > - {/* 정렬 가이드라인 렌더링 */} - {alignmentGuides.vertical.map((x, index) => ( -
- ))} - {alignmentGuides.horizontal.map((y, index) => ( -
- ))} - - {/* 컴포넌트 렌더링 */} - {components.map((component) => ( - - ))} - - {/* 빈 캔버스 안내 */} - {components.length === 0 && ( -
-

왼쪽에서 컴포넌트를 드래그하여 추가하세요

+ {/* 눈금자와 캔버스를 감싸는 컨테이너 */} +
+ {/* 좌상단 코너 + 가로 눈금자 */} + {showRuler && ( +
+ {/* 좌상단 코너 (20x20) */} +
+ {/* 가로 눈금자 */} +
)} + + {/* 세로 눈금자 + 캔버스 */} +
+ {/* 세로 눈금자 */} + {showRuler && } + + {/* 캔버스 */} +
{ + canvasRef.current = node; + drop(node); + }} + className={`relative bg-white shadow-lg ${isOver ? "ring-2 ring-blue-500" : ""}`} + style={{ + width: `${canvasWidth}mm`, + minHeight: `${canvasHeight}mm`, + backgroundImage: showGrid + ? ` + linear-gradient(to right, #e5e7eb 1px, transparent 1px), + linear-gradient(to bottom, #e5e7eb 1px, transparent 1px) + ` + : undefined, + backgroundSize: showGrid ? `${gridSize}px ${gridSize}px` : undefined, + }} + onClick={handleCanvasClick} + > + {/* 정렬 가이드라인 렌더링 */} + {alignmentGuides.vertical.map((x, index) => ( +
+ ))} + {alignmentGuides.horizontal.map((y, index) => ( +
+ ))} + + {/* 컴포넌트 렌더링 */} + {components.map((component) => ( + + ))} + + {/* 빈 캔버스 안내 */} + {components.length === 0 && ( +
+

왼쪽에서 컴포넌트를 드래그하여 추가하세요

+
+ )} +
+
diff --git a/frontend/components/report/designer/ReportDesignerToolbar.tsx b/frontend/components/report/designer/ReportDesignerToolbar.tsx index 3b637011..b554d191 100644 --- a/frontend/components/report/designer/ReportDesignerToolbar.tsx +++ b/frontend/components/report/designer/ReportDesignerToolbar.tsx @@ -28,6 +28,7 @@ import { ChevronUp, Lock, Unlock, + Ruler as RulerIcon, } from "lucide-react"; import { useRouter } from "next/navigation"; import { useReportDesigner } from "@/contexts/ReportDesignerContext"; @@ -83,6 +84,8 @@ export function ReportDesignerToolbar() { toggleLock, lockComponents, unlockComponents, + showRuler, + setShowRuler, } = useReportDesigner(); const [showPreview, setShowPreview] = useState(false); const [showSaveAsTemplate, setShowSaveAsTemplate] = useState(false); @@ -211,6 +214,16 @@ export function ReportDesignerToolbar() { {snapToGrid && showGrid ? "Grid ON" : "Grid OFF"} +