"use client"; /** * TableCanvasEditor — 테이블 레이아웃 탭의 시각적 편집기 * * - 컨트롤 바(열 추가, 행 조절)는 고정 * - 테이블 미리보기만 가로/세로 스크롤 */ import React, { useState, useRef, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Plus, Trash2, Minus, Rows3, Columns3 } from "lucide-react"; import type { ComponentConfig } from "@/types/report"; type TableColumn = NonNullable[number]; const MIN_COL_WIDTH = 60; const DEFAULT_COL_WIDTH = 120; const MIN_ROWS = 1; const MAX_ROWS = 50; const MAX_COLUMNS = 14; const DEFAULT_ROWS = 3; interface TableCanvasEditorProps { columns: TableColumn[]; onColumnsChange: (columns: TableColumn[]) => void; rowCount?: number; onRowCountChange?: (count: number) => void; } export function TableCanvasEditor({ columns, onColumnsChange, rowCount, onRowCountChange }: TableCanvasEditorProps) { const displayRows = rowCount ?? DEFAULT_ROWS; const [resizingIdx, setResizingIdx] = useState(null); const startXRef = useRef(0); const startWidthRef = useRef(0); const canAddColumn = columns.length < MAX_COLUMNS; const handleAddColumn = useCallback(() => { if (!canAddColumn) return; onColumnsChange([ ...columns, { field: "", header: `열 ${columns.length + 1}`, width: DEFAULT_COL_WIDTH, align: "left", mappingType: "field", summaryType: "NONE", visible: true, numberFormat: "none", }, ]); }, [columns, onColumnsChange, canAddColumn]); const handleRemoveColumn = useCallback( (idx: number) => { if (columns.length <= 1) return; const remaining = columns.filter((_, i) => i !== idx); const renumbered = remaining.map((col, i) => { const isDefaultHeader = /^열 \d+$/.test(col.header); return isDefaultHeader ? { ...col, header: `열 ${i + 1}` } : col; }); onColumnsChange(renumbered); }, [columns, onColumnsChange], ); const handleRemoveLastColumn = useCallback(() => { if (columns.length <= 1) return; handleRemoveColumn(columns.length - 1); }, [columns, handleRemoveColumn]); const handleResizeStart = useCallback( (idx: number, e: React.MouseEvent) => { e.preventDefault(); setResizingIdx(idx); startXRef.current = e.clientX; startWidthRef.current = columns[idx]?.width || DEFAULT_COL_WIDTH; const handleMouseMove = (moveEvent: MouseEvent) => { const delta = moveEvent.clientX - startXRef.current; const newWidth = Math.max(MIN_COL_WIDTH, startWidthRef.current + delta); onColumnsChange( columns.map((col, i) => (i === idx ? { ...col, width: newWidth } : col)), ); }; const handleMouseUp = () => { setResizingIdx(null); document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); }, [columns, onColumnsChange], ); const totalWidth = columns.reduce((sum, col) => sum + (col.width || DEFAULT_COL_WIDTH), 0); return (
{/* 컨트롤 바 */}
테이블 레이아웃
{displayRows}행
{columns.length}열
{/* 테이블 미리보기 */} {columns.length > 0 ? (
{columns.map((col, idx) => ( ))} {/* 열 번호 헤더 */} ))} {/* 열 이름 헤더 */} ))} {Array.from({ length: displayRows }).map((_, rowIdx) => ( {columns.map((col, colIdx) => ( ))} ))}
{columns.map((col, idx) => ( {idx + 1} {columns.length > 1 && ( )} {idx < columns.length - 1 && (
handleResizeStart(idx, e)} /> )}
{columns.map((col, idx) => ( {col.header || `열 ${idx + 1}`}
{rowIdx + 1}
) : (
셀을 클릭하여 선택하세요
)}
); }