109 lines
3.8 KiB
TypeScript
109 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
import { Button } from "@/components/ui/button";
|
|
import { ChevronRight, Puzzle, FileText, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
import { ComponentPalette } from "./ComponentPalette";
|
|
import { TemplatePalette } from "./TemplatePalette";
|
|
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
|
|
|
|
interface AccordionSection {
|
|
id: string;
|
|
label: string;
|
|
icon: React.ReactNode;
|
|
}
|
|
|
|
const SECTIONS: AccordionSection[] = [
|
|
{ id: "templates", label: "기본 템플릿", icon: <FileText className="h-5 w-5" /> },
|
|
{ id: "components", label: "컴포넌트", icon: <Puzzle className="h-5 w-5" /> },
|
|
];
|
|
|
|
export function ReportDesignerLeftPanel() {
|
|
const [expandedSection, setExpandedSection] = useState<string>("components");
|
|
const { isLeftPanelCollapsed, setIsLeftPanelCollapsed } = useReportDesigner();
|
|
|
|
const toggleSection = (id: string) => {
|
|
setExpandedSection(expandedSection === id ? "" : id);
|
|
};
|
|
|
|
if (isLeftPanelCollapsed) {
|
|
return (
|
|
<div className="flex h-full w-10 shrink-0 flex-col items-center border-r border-gray-200 bg-white pt-2 gap-2">
|
|
<Button
|
|
size="icon"
|
|
variant="ghost"
|
|
className="h-8 w-8"
|
|
onClick={() => setIsLeftPanelCollapsed(false)}
|
|
title="도구상자 열기"
|
|
>
|
|
<PanelLeftOpen className="h-4 w-4" />
|
|
</Button>
|
|
{SECTIONS.map((section) => (
|
|
<Button
|
|
key={section.id}
|
|
size="icon"
|
|
variant="ghost"
|
|
className="h-8 w-8"
|
|
title={section.label}
|
|
onClick={() => {
|
|
setIsLeftPanelCollapsed(false);
|
|
setExpandedSection(section.id);
|
|
}}
|
|
>
|
|
{section.icon}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex h-full w-[260px] shrink-0 flex-col border-r border-gray-200 bg-white">
|
|
<div className="flex h-11 items-center justify-between border-b border-gray-200 px-3">
|
|
<span className="text-sm font-bold text-gray-800">도구상자</span>
|
|
<Button
|
|
size="icon"
|
|
variant="ghost"
|
|
className="h-7 w-7"
|
|
onClick={() => setIsLeftPanelCollapsed(true)}
|
|
title="도구상자 접기"
|
|
>
|
|
<PanelLeftClose className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
<ScrollArea className="flex-1">
|
|
{SECTIONS.map((section) => {
|
|
const isExpanded = expandedSection === section.id;
|
|
return (
|
|
<div key={section.id} className="border-b-2 border-gray-100">
|
|
<button
|
|
onClick={() => toggleSection(section.id)}
|
|
className={`flex h-14 w-full items-center justify-between px-5 transition-colors ${
|
|
isExpanded
|
|
? "bg-linear-to-r from-blue-600 to-indigo-600 text-white shadow-sm"
|
|
: "bg-white text-gray-900 hover:bg-gray-50"
|
|
}`}
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<span className={isExpanded ? "" : "text-blue-600"}>{section.icon}</span>
|
|
<span className="text-base font-bold">{section.label}</span>
|
|
</div>
|
|
<ChevronRight
|
|
className={`h-5 w-5 transition-transform ${isExpanded ? "rotate-90" : "text-gray-400"}`}
|
|
/>
|
|
</button>
|
|
{isExpanded && (
|
|
<div className="border-t border-blue-100 bg-linear-to-b from-blue-50/30 to-white p-5">
|
|
{section.id === "templates" && <TemplatePalette />}
|
|
{section.id === "components" && <ComponentPalette />}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</ScrollArea>
|
|
</div>
|
|
);
|
|
}
|