134 lines
4.4 KiB
TypeScript
134 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { BaseLayoutRenderer, LayoutRendererProps } from "./BaseLayoutRenderer";
|
|
|
|
export default class GridLayoutRenderer extends BaseLayoutRenderer {
|
|
render(): React.ReactElement {
|
|
const { layout, isDesignMode, isSelected, onClick, className } = this.props;
|
|
|
|
if (!layout.layoutConfig.grid) {
|
|
return <div className="error-layout">그리드 설정이 없습니다.</div>;
|
|
}
|
|
|
|
const gridConfig = layout.layoutConfig.grid;
|
|
const containerStyle = this.getLayoutContainerStyle();
|
|
|
|
// 그리드 스타일 설정
|
|
const gridStyle: React.CSSProperties = {
|
|
...containerStyle,
|
|
display: "grid",
|
|
gridTemplateRows: `repeat(${gridConfig.rows}, 1fr)`,
|
|
gridTemplateColumns: `repeat(${gridConfig.columns}, 1fr)`,
|
|
gap: `${gridConfig.gap}px`,
|
|
gridRowGap: gridConfig.rowGap ? `${gridConfig.rowGap}px` : undefined,
|
|
gridColumnGap: gridConfig.columnGap ? `${gridConfig.columnGap}px` : undefined,
|
|
gridAutoRows: gridConfig.autoRows,
|
|
gridAutoColumns: gridConfig.autoColumns,
|
|
};
|
|
|
|
// 디자인 모드 스타일
|
|
if (isDesignMode) {
|
|
gridStyle.border = isSelected ? "2px solid #3b82f6" : "1px solid #e2e8f0";
|
|
gridStyle.borderRadius = "8px";
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={`grid-layout ${isDesignMode ? "design-mode" : ""} ${className || ""}`}
|
|
style={gridStyle}
|
|
onClick={onClick}
|
|
draggable={isDesignMode}
|
|
onDragStart={this.props.onDragStart}
|
|
onDragEnd={this.props.onDragEnd}
|
|
>
|
|
{layout.zones.map((zone) => {
|
|
const zoneChildren = this.getZoneChildren(zone.id);
|
|
|
|
// 그리드 위치 설정
|
|
const zoneStyle: React.CSSProperties = {
|
|
gridRow: zone.position.row !== undefined ? zone.position.row + 1 : undefined,
|
|
gridColumn: zone.position.column !== undefined ? zone.position.column + 1 : undefined,
|
|
};
|
|
|
|
return this.renderZone(zone, zoneChildren, {
|
|
style: zoneStyle,
|
|
className: "grid-zone",
|
|
});
|
|
})}
|
|
|
|
{/* 디자인 모드에서 빈 그리드 셀 표시 */}
|
|
{isDesignMode && this.renderEmptyGridCells()}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 빈 그리드 셀들을 렌더링합니다.
|
|
*/
|
|
private renderEmptyGridCells(): React.ReactElement[] {
|
|
const { layout } = this.props;
|
|
const gridConfig = layout.layoutConfig.grid!;
|
|
const totalCells = gridConfig.rows * gridConfig.columns;
|
|
const occupiedCells = new Set(
|
|
layout.zones
|
|
.map((zone) =>
|
|
zone.position.row !== undefined && zone.position.column !== undefined
|
|
? zone.position.row * gridConfig.columns + zone.position.column
|
|
: -1,
|
|
)
|
|
.filter((index) => 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(
|
|
<div
|
|
key={`empty-${i}`}
|
|
className="empty-grid-cell"
|
|
style={{
|
|
gridRow: row + 1,
|
|
gridColumn: column + 1,
|
|
border: isDesignMode ? "1px dashed #cbd5e1" : "1px solid #f1f5f9",
|
|
borderRadius: "4px",
|
|
backgroundColor: isDesignMode ? "rgba(148, 163, 184, 0.05)" : "rgba(248, 250, 252, 0.3)",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
fontSize: "10px",
|
|
color: "#94a3b8",
|
|
minHeight: "40px",
|
|
transition: "all 0.2s ease",
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
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}` : ""}
|
|
</div>,
|
|
);
|
|
}
|
|
}
|
|
|
|
return emptyCells;
|
|
}
|
|
}
|
|
|
|
// React 컴포넌트로 래핑
|
|
export const GridLayout: React.FC<LayoutRendererProps> = (props) => {
|
|
const renderer = new GridLayoutRenderer(props);
|
|
return renderer.render();
|
|
};
|