그리드 박스 기반 스냅 시스템 구현
This commit is contained in:
parent
7c3a2dff4c
commit
41c763c019
|
|
@ -4,7 +4,7 @@ import React, { useState, useCallback, useRef, useEffect } from "react";
|
|||
import dynamic from "next/dynamic";
|
||||
import { DashboardElement, QueryResult, Position } from "./types";
|
||||
import { ChartRenderer } from "./charts/ChartRenderer";
|
||||
import { GRID_CONFIG, magneticSnap } from "./gridUtils";
|
||||
import { GRID_CONFIG, magneticSnap, snapSizeToGrid } from "./gridUtils";
|
||||
|
||||
// 위젯 동적 임포트
|
||||
const WeatherWidget = dynamic(() => import("@/components/dashboard/widgets/WeatherWidget"), {
|
||||
|
|
@ -378,9 +378,9 @@ export function CanvasElement({
|
|||
const snappedX = magneticSnap(newX, verticalGuidelines);
|
||||
const snappedY = magneticSnap(newY, horizontalGuidelines);
|
||||
|
||||
// 크기는 12px 단위로 스냅
|
||||
const snappedWidth = Math.round(newWidth / 12) * 12;
|
||||
const snappedHeight = Math.round(newHeight / 12) * 12;
|
||||
// 크기는 그리드 박스 단위로 스냅
|
||||
const snappedWidth = snapSizeToGrid(newWidth, canvasWidth || 1560);
|
||||
const snappedHeight = snapSizeToGrid(newHeight, canvasWidth || 1560);
|
||||
|
||||
// 스냅 후 경계 체크
|
||||
const finalSnappedX = Math.max(0, Math.min(snappedX, canvasWidth - snappedWidth));
|
||||
|
|
|
|||
|
|
@ -7,7 +7,14 @@ import { DashboardTopMenu } from "./DashboardTopMenu";
|
|||
import { ElementConfigSidebar } from "./ElementConfigSidebar";
|
||||
import { DashboardSaveModal } from "./DashboardSaveModal";
|
||||
import { DashboardElement, ElementType, ElementSubtype } from "./types";
|
||||
import { GRID_CONFIG, snapToGrid, snapSizeToGrid, calculateCellSize, calculateGridConfig } from "./gridUtils";
|
||||
import {
|
||||
GRID_CONFIG,
|
||||
snapToGrid,
|
||||
snapSizeToGrid,
|
||||
calculateCellSize,
|
||||
calculateGridConfig,
|
||||
calculateBoxSize,
|
||||
} from "./gridUtils";
|
||||
import { Resolution, RESOLUTIONS, detectScreenResolution } from "./ResolutionSelector";
|
||||
import { DashboardProvider } from "@/contexts/DashboardContext";
|
||||
import { useMenu } from "@/contexts/MenuContext";
|
||||
|
|
@ -89,8 +96,8 @@ export default function DashboardDesigner({ dashboardId: initialDashboardId }: D
|
|||
// 그리드에 스냅 (X, Y, 너비, 높이 모두)
|
||||
const snappedX = snapToGrid(scaledX, newCellSize);
|
||||
const snappedY = snapToGrid(el.position.y, newCellSize);
|
||||
const snappedWidth = snapSizeToGrid(scaledWidth, 2, newCellSize);
|
||||
const snappedHeight = snapSizeToGrid(el.size.height, 2, newCellSize);
|
||||
const snappedWidth = snapSizeToGrid(scaledWidth, newConfig.width);
|
||||
const snappedHeight = snapSizeToGrid(el.size.height, newConfig.width);
|
||||
|
||||
return {
|
||||
...el,
|
||||
|
|
@ -215,22 +222,25 @@ export default function DashboardDesigner({ dashboardId: initialDashboardId }: D
|
|||
return;
|
||||
}
|
||||
|
||||
// 기본 크기 설정 (서브그리드 기준)
|
||||
const gridConfig = calculateGridConfig(canvasConfig.width);
|
||||
const subGridSize = gridConfig.SUB_GRID_SIZE;
|
||||
// 기본 크기 설정 (그리드 박스 단위)
|
||||
const boxSize = calculateBoxSize(canvasConfig.width);
|
||||
|
||||
// 서브그리드 기준 기본 크기 (픽셀)
|
||||
let defaultWidth = subGridSize * 10; // 기본 위젯: 서브그리드 10칸
|
||||
let defaultHeight = subGridSize * 10; // 기본 위젯: 서브그리드 10칸
|
||||
// 그리드 박스 단위 기본 크기
|
||||
let boxesWidth = 3; // 기본 위젯: 박스 3개
|
||||
let boxesHeight = 3; // 기본 위젯: 박스 3개
|
||||
|
||||
if (type === "chart") {
|
||||
defaultWidth = subGridSize * 20; // 차트: 서브그리드 20칸
|
||||
defaultHeight = subGridSize * 15; // 차트: 서브그리드 15칸
|
||||
boxesWidth = 4; // 차트: 박스 4개
|
||||
boxesHeight = 3; // 차트: 박스 3개
|
||||
} else if (type === "widget" && subtype === "calendar") {
|
||||
defaultWidth = subGridSize * 10; // 달력: 서브그리드 10칸
|
||||
defaultHeight = subGridSize * 15; // 달력: 서브그리드 15칸
|
||||
boxesWidth = 3; // 달력: 박스 3개
|
||||
boxesHeight = 4; // 달력: 박스 4개
|
||||
}
|
||||
|
||||
// 박스 개수를 픽셀로 변환 (마지막 간격 제거)
|
||||
const defaultWidth = boxesWidth * boxSize + (boxesWidth - 1) * GRID_CONFIG.GRID_BOX_GAP;
|
||||
const defaultHeight = boxesHeight * boxSize + (boxesHeight - 1) * GRID_CONFIG.GRID_BOX_GAP;
|
||||
|
||||
// 크기 유효성 검사
|
||||
if (isNaN(defaultWidth) || isNaN(defaultHeight) || defaultWidth <= 0 || defaultHeight <= 0) {
|
||||
// console.error("Invalid size calculated:", {
|
||||
|
|
|
|||
|
|
@ -54,9 +54,11 @@ export function calculateGridConfig(canvasWidth: number) {
|
|||
|
||||
/**
|
||||
* 실제 그리드 셀 크기 계산 (gap 포함)
|
||||
* @param canvasWidth - 캔버스 너비
|
||||
*/
|
||||
export const getCellWithGap = () => {
|
||||
return GRID_CONFIG.CELL_SIZE + GRID_CONFIG.GAP;
|
||||
export const getCellWithGap = (canvasWidth: number = 1560) => {
|
||||
const boxSize = calculateBoxSize(canvasWidth);
|
||||
return boxSize + GRID_CONFIG.GRID_BOX_GAP;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -70,14 +72,14 @@ export const getCanvasWidth = () => {
|
|||
/**
|
||||
* 좌표를 서브 그리드에 스냅 (세밀한 조정 가능)
|
||||
* @param value - 스냅할 좌표값
|
||||
* @param subGridSize - 서브 그리드 크기 (선택사항, 기본값: cellSize/3 ≈ 43px)
|
||||
* @param subGridSize - 서브 그리드 크기 (선택사항)
|
||||
* @returns 스냅된 좌표값
|
||||
*/
|
||||
export const snapToGrid = (value: number, subGridSize?: number): number => {
|
||||
// 서브 그리드 크기가 지정되지 않으면 기본 그리드 크기의 1/3 사용 (3x3 서브그리드)
|
||||
const snapSize = subGridSize ?? Math.floor(GRID_CONFIG.CELL_SIZE / 3);
|
||||
// 서브 그리드 크기가 지정되지 않으면 기본 박스 크기 사용
|
||||
const snapSize = subGridSize ?? calculateBoxSize(1560);
|
||||
|
||||
// 서브 그리드 단위로 스냅
|
||||
// 그리드 단위로 스냅
|
||||
const gridIndex = Math.round(value / snapSize);
|
||||
return gridIndex * snapSize;
|
||||
};
|
||||
|
|
@ -88,8 +90,9 @@ export const snapToGrid = (value: number, subGridSize?: number): number => {
|
|||
* @param cellSize - 셀 크기 (선택사항)
|
||||
* @returns 스냅된 좌표값 (임계값 내에 있으면 스냅, 아니면 원래 값)
|
||||
*/
|
||||
export const snapToGridWithThreshold = (value: number, cellSize: number = GRID_CONFIG.CELL_SIZE): number => {
|
||||
const snapped = snapToGrid(value, cellSize);
|
||||
export const snapToGridWithThreshold = (value: number, cellSize?: number): number => {
|
||||
const snapSize = cellSize ?? calculateBoxSize(1560);
|
||||
const snapped = snapToGrid(value, snapSize);
|
||||
const distance = Math.abs(value - snapped);
|
||||
|
||||
return distance <= GRID_CONFIG.SNAP_THRESHOLD ? snapped : value;
|
||||
|
|
@ -102,15 +105,7 @@ export const snapToGridWithThreshold = (value: number, cellSize: number = GRID_C
|
|||
* @param cellSize - 셀 크기 (선택사항)
|
||||
* @returns 스냅된 크기
|
||||
*/
|
||||
export const snapSizeToGrid = (
|
||||
size: number,
|
||||
minCells: number = 2,
|
||||
cellSize: number = GRID_CONFIG.CELL_SIZE,
|
||||
): number => {
|
||||
const cellWithGap = cellSize + GRID_CONFIG.GAP;
|
||||
const cells = Math.max(minCells, Math.round(size / cellWithGap));
|
||||
return cells * cellWithGap - GRID_CONFIG.GAP;
|
||||
};
|
||||
// 기존 snapSizeToGrid 제거 - 새 버전은 269번 줄에 있음
|
||||
|
||||
/**
|
||||
* 위치와 크기를 모두 그리드에 스냅
|
||||
|
|
@ -142,9 +137,10 @@ export const snapBoundsToGrid = (bounds: GridBounds, canvasWidth?: number, canva
|
|||
let snappedX = snapToGrid(bounds.position.x);
|
||||
let snappedY = snapToGrid(bounds.position.y);
|
||||
|
||||
// 크기 스냅
|
||||
const snappedWidth = snapSizeToGrid(bounds.size.width);
|
||||
const snappedHeight = snapSizeToGrid(bounds.size.height);
|
||||
// 크기 스냅 (canvasWidth 기본값 1560)
|
||||
const width = canvasWidth || 1560;
|
||||
const snappedWidth = snapSizeToGrid(bounds.size.width, width);
|
||||
const snappedHeight = snapSizeToGrid(bounds.size.height, width);
|
||||
|
||||
// 캔버스 경계 체크
|
||||
if (canvasWidth) {
|
||||
|
|
@ -264,3 +260,16 @@ export function magneticSnap(value: number, guidelines: number[]): number {
|
|||
const { nearest } = findNearestGuideline(value, guidelines);
|
||||
return nearest; // 거리 체크 없이 무조건 스냅
|
||||
}
|
||||
|
||||
// 크기를 그리드 박스 단위로 스냅 (박스 크기의 배수로만 가능)
|
||||
export function snapSizeToGrid(size: number, canvasWidth: number): number {
|
||||
const boxSize = calculateBoxSize(canvasWidth);
|
||||
const cellSize = boxSize + GRID_CONFIG.GRID_BOX_GAP; // 박스 + 간격
|
||||
|
||||
// 최소 1개 박스 크기
|
||||
const minBoxes = 1;
|
||||
const boxes = Math.max(minBoxes, Math.round(size / cellSize));
|
||||
|
||||
// 박스 개수에서 마지막 간격 제거
|
||||
return boxes * boxSize + (boxes - 1) * GRID_CONFIG.GRID_BOX_GAP;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue