"use client"; import React from "react"; import { LayoutRendererProps } from "../BaseLayoutRenderer"; import { filterDOMProps } from "@/lib/utils/domPropsFilter"; /** * 그리드 레이아웃 컴포넌트 */ export interface GridLayoutProps extends LayoutRendererProps { renderer: any; // GridLayoutRenderer 타입 } export const GridLayout: React.FC = ({ layout, isDesignMode = false, isSelected = false, onClick, className = "", renderer, onZoneComponentDrop, onZoneClick, ...props }) => { if (!layout.layoutConfig.grid) { return (
그리드 레이아웃 설정이 없습니다.
layoutConfig.grid가 필요합니다.
); } const gridConfig = layout.layoutConfig.grid; const containerStyle = renderer.getLayoutContainerStyle(); // 그리드 컨테이너 스타일 const gridStyle: React.CSSProperties = { display: "grid", gridTemplateRows: `repeat(${gridConfig.rows}, 1fr)`, gridTemplateColumns: `repeat(${gridConfig.columns}, 1fr)`, gap: `${gridConfig.gap || 16}px`, // 레이아웃 컴포넌트의 높이 적용 - 부모 높이를 100% 따라가도록 height: layout.size?.height ? `${layout.size.height}px` : "100%", width: layout.size?.width ? `${layout.size.width}px` : "100%", minHeight: layout.size?.height ? `${layout.size.height}px` : "200px", // 최소 높이 보장 // containerStyle을 나중에 적용하되, grid 관련 속성은 덮어쓰지 않도록 ...containerStyle, // grid 속성들을 다시 강제 적용 display: "grid", gridTemplateRows: `repeat(${gridConfig.rows}, 1fr)`, gridTemplateColumns: `repeat(${gridConfig.columns}, 1fr)`, height: layout.size?.height ? `${layout.size.height}px` : "100%", minHeight: layout.size?.height ? `${layout.size.height}px` : "200px", }; // 디자인 모드 스타일 if (isDesignMode) { gridStyle.border = isSelected ? "2px solid #3b82f6" : "1px solid #e2e8f0"; gridStyle.borderRadius = "8px"; } // DOM 안전한 props만 필터링 const domProps = filterDOMProps(props); return ( <>
{layout.zones.map((zone: any) => { const zoneChildren = renderer.getZoneChildren(zone.id); // 레이아웃 전체 높이를 행 수로 나누어 각 존의 기본 높이 계산 // layout은 LayoutComponent이고, 실제 높이는 layout.size.height에 있음 const layoutHeight = layout.size?.height || 400; // 기본값 400px const rowHeight = Math.floor(layoutHeight / gridConfig.rows); console.log("🔍 GridLayout 높이 정보:", { layoutId: layout.id, layoutSize: layout.size, layoutHeight, rowHeight, gridRows: gridConfig.rows, zoneId: zone.id, zoneSize: zone.size, }); // 그리드 위치 설정 const zoneStyle: React.CSSProperties = { gridRow: zone.position.row !== undefined ? zone.position.row + 1 : undefined, gridColumn: zone.position.column !== undefined ? zone.position.column + 1 : undefined, // 카드 레이아웃처럼 항상 명확한 경계 표시 backgroundColor: "white", border: "1px solid #e5e7eb", borderRadius: "8px", padding: "16px", boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.1)", // 존의 높이: 개별 설정이 있으면 우선, 없으면 부모 높이를 100% 따라가도록 height: zone.size?.height ? typeof zone.size.height === "number" ? `${zone.size.height}px` : zone.size.height : "100%", // 그리드 셀의 높이를 100% 따라감 minHeight: zone.size?.minHeight ? typeof zone.size.minHeight === "number" ? `${zone.size.minHeight}px` : zone.size.minHeight : "100px", maxHeight: zone.size?.maxHeight ? typeof zone.size.maxHeight === "number" ? `${zone.size.maxHeight}px` : zone.size.maxHeight : "none", position: "relative", transition: "all 0.2s ease", overflow: "hidden", display: "flex", flexDirection: "column", }; return (
{ e.currentTarget.style.borderColor = "#3b82f6"; e.currentTarget.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(59, 130, 246, 0.1)"; }} onMouseLeave={(e) => { e.currentTarget.style.borderColor = "#e5e7eb"; e.currentTarget.style.boxShadow = "0 1px 3px 0 rgba(0, 0, 0, 0.1)"; }} // 드롭 이벤트 제거 - 일반 캔버스 드롭만 사용 onClick={(e) => { e.stopPropagation(); if (onZoneClick) { onZoneClick(zone.id); } }} > {/* 존 라벨 */} {isDesignMode && (
{zone.name || zone.id}
)} {/* 존 내용 */}
{/* 존 안의 컴포넌트들을 절대 위치로 렌더링 */} {zoneChildren.map((child: any) => (
{renderer.renderChild(child)}
))}
); })} {/* 디자인 모드에서 빈 그리드 셀 표시 */} {isDesignMode && }
); }; /** * 빈 그리드 셀들을 렌더링하는 컴포넌트 */ const GridEmptyCells: React.FC<{ gridConfig: any; layout: any; isDesignMode: boolean; }> = ({ gridConfig, layout, isDesignMode }) => { const totalCells = gridConfig.rows * gridConfig.columns; const occupiedCells = new Set( layout.zones .map((zone: any) => zone.position.row !== undefined && zone.position.column !== undefined ? zone.position.row * gridConfig.columns + zone.position.column : -1, ) .filter((index: number) => index >= 0), ); const emptyCells: React.ReactElement[] = []; for (let i = 0; i < totalCells; i++) { if (!occupiedCells.has(i)) { const row = Math.floor(i / gridConfig.columns); const column = i % gridConfig.columns; emptyCells.push(
{ e.currentTarget.style.backgroundColor = "rgba(59, 130, 246, 0.05)"; e.currentTarget.style.borderColor = "#3b82f6"; }} onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = isDesignMode ? "rgba(148, 163, 184, 0.05)" : "rgba(248, 250, 252, 0.3)"; e.currentTarget.style.borderColor = isDesignMode ? "#cbd5e1" : "#f1f5f9"; }} > {isDesignMode ? `${row + 1},${column + 1}` : ""}
, ); } } return <>{emptyCells}; };