대시보드 방식 이전
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import React, { forwardRef, useState, useCallback, useEffect } from "react";
|
||||
import React, { forwardRef, useState, useCallback, useMemo } from "react";
|
||||
import { DashboardElement, ElementType, ElementSubtype, DragData } from "./types";
|
||||
import { CanvasElement } from "./CanvasElement";
|
||||
import { GRID_CONFIG, snapToGrid } from "./gridUtils";
|
||||
import { GRID_CONFIG, snapToGrid, calculateGridConfig } from "./gridUtils";
|
||||
|
||||
interface DashboardCanvasProps {
|
||||
elements: DashboardElement[];
|
||||
@@ -14,6 +14,8 @@ interface DashboardCanvasProps {
|
||||
onSelectElement: (id: string | null) => void;
|
||||
onConfigureElement?: (element: DashboardElement) => void;
|
||||
backgroundColor?: string;
|
||||
canvasWidth?: number;
|
||||
canvasHeight?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,11 +36,17 @@ export const DashboardCanvas = forwardRef<HTMLDivElement, DashboardCanvasProps>(
|
||||
onSelectElement,
|
||||
onConfigureElement,
|
||||
backgroundColor = "#f9fafb",
|
||||
canvasWidth = 1560,
|
||||
canvasHeight = 768,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [isDragOver, setIsDragOver] = useState(false);
|
||||
|
||||
// 현재 캔버스 크기에 맞는 그리드 설정 계산
|
||||
const gridConfig = useMemo(() => calculateGridConfig(canvasWidth), [canvasWidth]);
|
||||
const cellSize = gridConfig.CELL_SIZE;
|
||||
|
||||
// 드래그 오버 처리
|
||||
const handleDragOver = useCallback((e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -71,20 +79,20 @@ export const DashboardCanvas = forwardRef<HTMLDivElement, DashboardCanvasProps>(
|
||||
const rawX = e.clientX - rect.left + (ref.current?.scrollLeft || 0);
|
||||
const rawY = e.clientY - rect.top + (ref.current?.scrollTop || 0);
|
||||
|
||||
// 그리드에 스냅 (고정 셀 크기 사용)
|
||||
let snappedX = snapToGrid(rawX, GRID_CONFIG.CELL_SIZE);
|
||||
const snappedY = snapToGrid(rawY, GRID_CONFIG.CELL_SIZE);
|
||||
// 그리드에 스냅 (동적 셀 크기 사용)
|
||||
let snappedX = snapToGrid(rawX, cellSize);
|
||||
const snappedY = snapToGrid(rawY, cellSize);
|
||||
|
||||
// X 좌표가 캔버스 너비를 벗어나지 않도록 제한
|
||||
const maxX = GRID_CONFIG.CANVAS_WIDTH - GRID_CONFIG.CELL_SIZE * 2; // 최소 2칸 너비 보장
|
||||
const maxX = canvasWidth - cellSize * 2; // 최소 2칸 너비 보장
|
||||
snappedX = Math.max(0, Math.min(snappedX, maxX));
|
||||
|
||||
onCreateElement(dragData.type, dragData.subtype, snappedX, snappedY);
|
||||
} catch (error) {
|
||||
// console.error('드롭 데이터 파싱 오류:', error);
|
||||
} catch {
|
||||
// 드롭 데이터 파싱 오류 무시
|
||||
}
|
||||
},
|
||||
[ref, onCreateElement],
|
||||
[ref, onCreateElement, canvasWidth, cellSize],
|
||||
);
|
||||
|
||||
// 캔버스 클릭 시 선택 해제
|
||||
@@ -97,28 +105,23 @@ export const DashboardCanvas = forwardRef<HTMLDivElement, DashboardCanvasProps>(
|
||||
[onSelectElement],
|
||||
);
|
||||
|
||||
// 고정 그리드 크기
|
||||
const cellWithGap = GRID_CONFIG.CELL_SIZE + GRID_CONFIG.GAP;
|
||||
// 동적 그리드 크기 계산
|
||||
const cellWithGap = cellSize + GRID_CONFIG.GAP;
|
||||
const gridSize = `${cellWithGap}px ${cellWithGap}px`;
|
||||
|
||||
// 캔버스 높이를 요소들의 최대 y + height 기준으로 계산 (최소 화면 높이 보장)
|
||||
const minCanvasHeight = Math.max(
|
||||
typeof window !== "undefined" ? window.innerHeight : 800,
|
||||
...elements.map((el) => el.position.y + el.size.height + 100), // 하단 여백 100px
|
||||
);
|
||||
// 12개 컬럼 구분선 위치 계산
|
||||
const columnLines = Array.from({ length: GRID_CONFIG.COLUMNS + 1 }, (_, i) => i * cellWithGap);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={`relative rounded-lg shadow-inner ${isDragOver ? "bg-blue-50/50" : ""} `}
|
||||
className={`relative h-full w-full rounded-lg shadow-inner ${isDragOver ? "bg-blue-50/50" : ""} `}
|
||||
style={{
|
||||
backgroundColor,
|
||||
width: `${GRID_CONFIG.CANVAS_WIDTH}px`,
|
||||
minHeight: `${minCanvasHeight}px`,
|
||||
// 12 컬럼 그리드 배경
|
||||
// 세밀한 그리드 배경
|
||||
backgroundImage: `
|
||||
linear-gradient(rgba(59, 130, 246, 0.15) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(59, 130, 246, 0.15) 1px, transparent 1px)
|
||||
linear-gradient(rgba(59, 130, 246, 0.08) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(59, 130, 246, 0.08) 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: gridSize,
|
||||
backgroundPosition: "0 0",
|
||||
@@ -129,13 +132,33 @@ export const DashboardCanvas = forwardRef<HTMLDivElement, DashboardCanvasProps>(
|
||||
onDrop={handleDrop}
|
||||
onClick={handleCanvasClick}
|
||||
>
|
||||
{/* 12개 컬럼 메인 구분선 */}
|
||||
{columnLines.map((x, i) => (
|
||||
<div
|
||||
key={`col-${i}`}
|
||||
className="pointer-events-none absolute top-0 h-full"
|
||||
style={{
|
||||
left: `${x}px`,
|
||||
width: "2px",
|
||||
zIndex: 1,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{/* 배치된 요소들 렌더링 */}
|
||||
{elements.length === 0 && (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center text-gray-400">
|
||||
<div className="text-center">
|
||||
<div className="text-sm">상단 메뉴에서 차트나 위젯을 선택하세요</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{elements.map((element) => (
|
||||
<CanvasElement
|
||||
key={element.id}
|
||||
element={element}
|
||||
isSelected={selectedElement === element.id}
|
||||
cellSize={GRID_CONFIG.CELL_SIZE}
|
||||
cellSize={cellSize}
|
||||
canvasWidth={canvasWidth}
|
||||
onUpdate={onUpdateElement}
|
||||
onRemove={onRemoveElement}
|
||||
onSelect={onSelectElement}
|
||||
|
||||
Reference in New Issue
Block a user