"use client";
/**
* TableProperties.tsx — 테이블 컴포넌트 설정
*
* - section="data": TableLayoutTabs (컬럼 구성 / 요약 설정 탭)
* - section="style": StyleAccordion 패턴 (프리셋 + 헤더 / 셀 / 테두리)
*/
import { useState, useCallback } from "react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { ChevronRight } from "lucide-react";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
import { TableLayoutTabs } from "../modals/TableLayoutTabs";
import type { ComponentConfig, GridCell } from "@/types/report";
// ─── 타입 ──────────────────────────────────────────────────────────────────────
interface Props {
component: ComponentConfig;
section?: "style" | "data";
}
// ─── 프리셋 ────────────────────────────────────────────────────────────────────
const TABLE_STYLE_PRESETS = {
default: {
headerBackgroundColor: "#f3f4f6",
headerTextColor: "#111827",
showBorder: true,
rowHeight: 32,
},
dark: {
headerBackgroundColor: "#1e293b",
headerTextColor: "#ffffff",
showBorder: true,
rowHeight: 32,
},
blue: {
headerBackgroundColor: "#1d4ed8",
headerTextColor: "#ffffff",
showBorder: true,
rowHeight: 32,
},
minimal: {
headerBackgroundColor: "#f8fafc",
headerTextColor: "#374151",
showBorder: false,
rowHeight: 28,
},
} as const;
// ─── StyleAccordion ────────────────────────────────────────────────────────────
function StyleAccordion({
label,
isOpen,
onToggle,
children,
}: {
label: string;
isOpen: boolean;
onToggle: () => void;
children: React.ReactNode;
}) {
return (
{isOpen && (
{children}
)}
);
}
// ─── ColorInput ────────────────────────────────────────────────────────────────
function ColorInput({ value, onChange }: { value: string; onChange: (v: string) => void }) {
return (
onChange(e.target.value)}
className="h-7 w-7 shrink-0 cursor-pointer rounded border-0 p-0"
/>
onChange(e.target.value)}
className="h-7 border-0 bg-transparent px-1 font-mono text-xs shadow-none focus-visible:ring-0"
/>
);
}
// ─── 컴포넌트 ──────────────────────────────────────────────────────────────────
export function TableProperties({ component, section }: Props) {
const { updateComponent } = useReportDesigner();
const showStyle = !section || section === "style";
const showData = !section || section === "data";
const [openSections, setOpenSections] = useState>(new Set(["preset"]));
const toggleSection = (id: string) => {
setOpenSections((prev) => {
const next = new Set(prev);
if (next.has(id)) next.delete(id);
else next.add(id);
return next;
});
};
const update = useCallback(
(updates: Partial) => updateComponent(component.id, updates),
[component.id, updateComponent],
);
const applyPreset = useCallback(
(key: keyof typeof TABLE_STYLE_PRESETS) => {
const preset = TABLE_STYLE_PRESETS[key];
const updates: Partial = { ...preset };
if (component.gridMode && component.gridCells) {
const headerRows = component.gridHeaderRows ?? 1;
const headerCols = component.gridHeaderCols ?? 1;
const newCells = component.gridCells.map((cell: GridCell) => {
if (cell.merged) return cell;
const isHeader = cell.row < headerRows || cell.col < headerCols;
if (!isHeader) return cell;
return {
...cell,
backgroundColor: preset.headerBackgroundColor,
textColor: preset.headerTextColor,
};
});
updates.gridCells = newCells;
}
update(updates);
},
[update, component.gridMode, component.gridCells, component.gridHeaderRows, component.gridHeaderCols],
);
return (
<>
{showData && }
{showStyle && (
{/* 프리셋 */}
toggleSection("preset")}>
{(["default", "dark", "blue", "minimal"] as const).map((key) => (
))}
{/* 헤더 스타일 */}
toggleSection("header")}>
update({ headerBackgroundColor: v })}
/>
update({ headerTextColor: v })}
/>
{/* 셀 스타일 */}
toggleSection("cell")}>
{/* 테두리 */}
toggleSection("border")}>
update({ showBorder: checked })}
/>
)}
>
);
}