From fdc476a9e02d933f1794eeda84a5418e4647b7a3 Mon Sep 17 00:00:00 2001 From: dohyeons Date: Thu, 2 Oct 2025 11:54:15 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B0=80=EB=A1=9C=20=EC=84=B8=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=80=EC=9A=B4=EB=8D=B0=20=EC=95=88=EB=82=B4=EC=84=A0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/contexts/ReportDesignerContext.tsx | 123 +++++++++++--------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/frontend/contexts/ReportDesignerContext.tsx b/frontend/contexts/ReportDesignerContext.tsx index 62725ec3..2f341b2f 100644 --- a/frontend/contexts/ReportDesignerContext.tsx +++ b/frontend/contexts/ReportDesignerContext.tsx @@ -168,60 +168,6 @@ export function ReportDesignerProvider({ reportId, children }: { reportId: strin [snapToGrid, gridSize], ); - // 정렬 가이드라인 계산 (드래그 중인 컴포넌트 제외) - const calculateAlignmentGuides = useCallback( - (draggingId: string, x: number, y: number, width: number, height: number) => { - const verticalLines: number[] = []; - const horizontalLines: number[] = []; - - // 드래그 중인 컴포넌트의 주요 위치 - const left = x; - const right = x + width; - const centerX = x + width / 2; - const top = y; - const bottom = y + height; - const centerY = y + height / 2; - - // 다른 컴포넌트들과 비교 - components.forEach((comp) => { - if (comp.id === draggingId) return; - - const compLeft = comp.x; - const compRight = comp.x + comp.width; - const compCenterX = comp.x + comp.width / 2; - const compTop = comp.y; - const compBottom = comp.y + comp.height; - const compCenterY = comp.y + comp.height / 2; - - // 세로 정렬 체크 (left, center, right) - 정확히 일치할 때만 - if (left === compLeft) verticalLines.push(compLeft); - if (left === compRight) verticalLines.push(compRight); - if (right === compLeft) verticalLines.push(compLeft); - if (right === compRight) verticalLines.push(compRight); - if (centerX === compCenterX) verticalLines.push(compCenterX); - - // 가로 정렬 체크 (top, center, bottom) - 정확히 일치할 때만 - if (top === compTop) horizontalLines.push(compTop); - if (top === compBottom) horizontalLines.push(compBottom); - if (bottom === compTop) horizontalLines.push(compTop); - if (bottom === compBottom) horizontalLines.push(compBottom); - if (centerY === compCenterY) horizontalLines.push(compCenterY); - }); - - // 중복 제거 - setAlignmentGuides({ - vertical: Array.from(new Set(verticalLines)), - horizontal: Array.from(new Set(horizontalLines)), - }); - }, - [components], - ); - - // 정렬 가이드라인 초기화 - const clearAlignmentGuides = useCallback(() => { - setAlignmentGuides({ vertical: [], horizontal: [] }); - }, []); - // 복사 (Ctrl+C) const copyComponents = useCallback(() => { if (selectedComponentIds.length > 0) { @@ -778,6 +724,75 @@ export function ReportDesignerProvider({ reportId, children }: { reportId: strin right: 20, }); + // 정렬 가이드라인 계산 (캔버스 중앙선 포함) + const calculateAlignmentGuides = useCallback( + (draggingId: string, x: number, y: number, width: number, height: number) => { + const verticalLines: number[] = []; + const horizontalLines: number[] = []; + const threshold = 5; // 5px 오차 허용 + + // 캔버스를 픽셀로 변환 (1mm = 3.7795px) + const canvasWidthPx = canvasWidth * 3.7795; + const canvasHeightPx = canvasHeight * 3.7795; + const canvasCenterX = canvasWidthPx / 2; + const canvasCenterY = canvasHeightPx / 2; + + // 드래그 중인 컴포넌트의 주요 위치 + const left = x; + const right = x + width; + const centerX = x + width / 2; + const top = y; + const bottom = y + height; + const centerY = y + height / 2; + + // 캔버스 중앙선 체크 + if (Math.abs(centerX - canvasCenterX) < threshold) { + verticalLines.push(canvasCenterX); + } + if (Math.abs(centerY - canvasCenterY) < threshold) { + horizontalLines.push(canvasCenterY); + } + + // 다른 컴포넌트들과 비교 + components.forEach((comp) => { + if (comp.id === draggingId) return; + + const compLeft = comp.x; + const compRight = comp.x + comp.width; + const compCenterX = comp.x + comp.width / 2; + const compTop = comp.y; + const compBottom = comp.y + comp.height; + const compCenterY = comp.y + comp.height / 2; + + // 세로 정렬 체크 (left, center, right) - 오차 허용 + if (Math.abs(left - compLeft) < threshold) verticalLines.push(compLeft); + if (Math.abs(left - compRight) < threshold) verticalLines.push(compRight); + if (Math.abs(right - compLeft) < threshold) verticalLines.push(compLeft); + if (Math.abs(right - compRight) < threshold) verticalLines.push(compRight); + if (Math.abs(centerX - compCenterX) < threshold) verticalLines.push(compCenterX); + + // 가로 정렬 체크 (top, center, bottom) - 오차 허용 + if (Math.abs(top - compTop) < threshold) horizontalLines.push(compTop); + if (Math.abs(top - compBottom) < threshold) horizontalLines.push(compBottom); + if (Math.abs(bottom - compTop) < threshold) horizontalLines.push(compTop); + if (Math.abs(bottom - compBottom) < threshold) horizontalLines.push(compBottom); + if (Math.abs(centerY - compCenterY) < threshold) horizontalLines.push(compCenterY); + }); + + // 중복 제거 + setAlignmentGuides({ + vertical: Array.from(new Set(verticalLines)), + horizontal: Array.from(new Set(horizontalLines)), + }); + }, + [components, canvasWidth, canvasHeight], + ); + + // 정렬 가이드라인 초기화 + const clearAlignmentGuides = useCallback(() => { + setAlignmentGuides({ vertical: [], horizontal: [] }); + }, []); + // 리포트 및 레이아웃 로드 const loadLayout = useCallback(async () => { setIsLoading(true);