정렬 가이드라인 구현

This commit is contained in:
dohyeons
2025-10-01 15:35:16 +09:00
parent ae23a4408e
commit c5c6d9239c
3 changed files with 120 additions and 4 deletions

View File

@@ -316,6 +316,11 @@ interface ReportDesignerContextType {
snapToGrid: boolean;
setSnapToGrid: (snap: boolean) => void;
snapValueToGrid: (value: number) => number;
// 정렬 가이드라인
alignmentGuides: { vertical: number[]; horizontal: number[] };
calculateAlignmentGuides: (draggingId: string, x: number, y: number, width: number, height: number) => void;
clearAlignmentGuides: () => void;
}
const ReportDesignerContext = createContext<ReportDesignerContextType | undefined>(undefined);
@@ -336,6 +341,12 @@ export function ReportDesignerProvider({ reportId, children }: { reportId: strin
const [showGrid, setShowGrid] = useState(true); // Grid 표시 여부
const [snapToGrid, setSnapToGrid] = useState(true); // Grid Snap 활성화
// 정렬 가이드라인
const [alignmentGuides, setAlignmentGuides] = useState<{
vertical: number[];
horizontal: number[];
}>({ vertical: [], horizontal: [] });
// Grid Snap 함수
const snapValueToGrid = useCallback(
(value: number): number => {
@@ -345,6 +356,61 @@ export function ReportDesignerProvider({ reportId, children }: { reportId: strin
[snapToGrid, gridSize],
);
// 정렬 가이드라인 계산 (드래그 중인 컴포넌트 제외)
const calculateAlignmentGuides = useCallback(
(draggingId: string, x: number, y: number, width: number, height: number) => {
const threshold = 5; // 정렬 감지 임계값 (px)
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 (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],
);
// 정렬 가이드라인 초기화
const clearAlignmentGuides = useCallback(() => {
setAlignmentGuides({ vertical: [], horizontal: [] });
}, []);
// 캔버스 설정 (기본값)
const [canvasWidth, setCanvasWidth] = useState(210);
const [canvasHeight, setCanvasHeight] = useState(297);
@@ -676,6 +742,10 @@ export function ReportDesignerProvider({ reportId, children }: { reportId: strin
snapToGrid,
setSnapToGrid,
snapValueToGrid,
// 정렬 가이드라인
alignmentGuides,
calculateAlignmentGuides,
clearAlignmentGuides,
};
return <ReportDesignerContext.Provider value={value}>{children}</ReportDesignerContext.Provider>;