"use client";
/**
* TextProperties.tsx — 텍스트/레이블 컴포넌트 설정
*
* - section="data": TextLayoutTabs (데이터 바인딩 / 텍스트 서식 / 표시 조건)
* - 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 {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { ChevronRight, Bold, Italic, Underline, Strikethrough, AlignLeft, AlignCenter, AlignRight } from "lucide-react";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
import { TextLayoutTabs } from "../modals/TextLayoutTabs";
import type { ComponentConfig } from "@/types/report";
const FONT_FAMILIES = [
"Malgun Gothic",
"NanumGothic",
"NanumMyeongjo",
"굴림",
"돋움",
"바탕",
"Times New Roman",
"Arial",
];
const TEXT_STYLE_PRESETS = {
title: {
label: "제목",
fontSize: 24,
fontWeight: "bold",
fontColor: "#111827",
lineHeight: 1.3,
},
subtitle: {
label: "부제목",
fontSize: 16,
fontWeight: "600",
fontColor: "#374151",
lineHeight: 1.4,
},
body: {
label: "본문",
fontSize: 12,
fontWeight: "normal",
fontColor: "#374151",
lineHeight: 1.6,
},
caption: {
label: "캡션",
fontSize: 10,
fontWeight: "normal",
fontColor: "#6b7280",
fontStyle: "italic" as const,
lineHeight: 1.4,
},
header: {
label: "헤더",
fontSize: 14,
fontWeight: "bold",
fontColor: "#1e40af",
lineHeight: 1.5,
backgroundColor: "#eff6ff",
borderWidth: 1,
borderColor: "#bfdbfe",
padding: 8,
},
footer: {
label: "푸터",
fontSize: 9,
fontWeight: "normal",
fontColor: "#9ca3af",
lineHeight: 1.4,
},
} as const;
interface Props {
component: ComponentConfig;
/** 우측 패널: "style" | 모달: "data" | 미전달: 전체 표시 (하위 호환) */
section?: "style" | "data";
}
function StyleAccordion({
label,
isOpen,
onToggle,
children,
}: {
label: string;
isOpen: boolean;
onToggle: () => void;
children: React.ReactNode;
}) {
return (
{isOpen && (
{children}
)}
);
}
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"
/>
);
}
function ToggleBtn({
active,
onClick,
children,
title,
}: {
active: boolean;
onClick: () => void;
children: React.ReactNode;
title?: string;
}) {
return (
);
}
export function TextProperties({ 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 TEXT_STYLE_PRESETS) => {
const { label, ...values } = TEXT_STYLE_PRESETS[key];
update({ ...values, textPreset: key });
},
[update],
);
return (
<>
{/* 모달(section="data")에서 표시: TextLayoutTabs 3탭 구조 */}
{showData && }
{/* 우측 패널(section="style")에서 표시: StyleAccordion 패턴 */}
{showStyle && (
{/* 프리셋 */}
toggleSection("preset")}>
{(Object.entries(TEXT_STYLE_PRESETS) as [keyof typeof TEXT_STYLE_PRESETS, typeof TEXT_STYLE_PRESETS[keyof typeof TEXT_STYLE_PRESETS]][]).map(
([key, preset]) => (
),
)}
{/* 폰트 */}
toggleSection("font")}>
update({ fontWeight: component.fontWeight === "bold" || component.fontWeight === "700" ? "normal" : "bold" })}
title="굵게"
>
update({ fontStyle: component.fontStyle === "italic" ? "normal" : "italic" })}
title="기울임"
>
update({ textDecoration: component.textDecoration === "underline" ? "none" : "underline" })}
title="밑줄"
>
update({ textDecoration: component.textDecoration === "line-through" ? "none" : "line-through" })}
title="취소선"
>
{/* 색상 */}
toggleSection("color")}>
update({ fontColor: v })}
/>
update({ backgroundColor: v })}
/>
{/* 정렬 & 간격 */}
toggleSection("align")}>
update({ textAlign: "left" })} title="왼쪽">
update({ textAlign: "center" })} title="가운데">
update({ textAlign: "right" })} title="오른쪽">
{/* 테두리 */}
toggleSection("border")}>
{(component.borderWidth ?? 0) > 0 && (
update({ borderColor: v })}
/>
)}
)}
>
);
}