179 lines
5.8 KiB
TypeScript
179 lines
5.8 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import { BaseLayoutRenderer, LayoutRendererProps } from "./BaseLayoutRenderer";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { X } from "lucide-react";
|
|
|
|
export default class TabsLayoutRenderer extends BaseLayoutRenderer {
|
|
render(): React.ReactElement {
|
|
const { layout, isDesignMode, isSelected, onClick, className } = this.props;
|
|
|
|
if (!layout.layoutConfig.tabs) {
|
|
return <div className="error-layout">탭 레이아웃 설정이 없습니다.</div>;
|
|
}
|
|
|
|
return (
|
|
<TabsLayoutComponent
|
|
layout={layout}
|
|
isDesignMode={isDesignMode}
|
|
isSelected={isSelected}
|
|
onClick={onClick}
|
|
className={className}
|
|
renderer={this}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
interface TabsLayoutComponentProps {
|
|
layout: any;
|
|
isDesignMode?: boolean;
|
|
isSelected?: boolean;
|
|
onClick?: (e: React.MouseEvent) => void;
|
|
className?: string;
|
|
renderer: TabsLayoutRenderer;
|
|
}
|
|
|
|
const TabsLayoutComponent: React.FC<TabsLayoutComponentProps> = ({
|
|
layout,
|
|
isDesignMode,
|
|
isSelected,
|
|
onClick,
|
|
className,
|
|
renderer,
|
|
}) => {
|
|
const tabsConfig = layout.layoutConfig.tabs;
|
|
const [activeTab, setActiveTab] = useState(tabsConfig.defaultTab || layout.zones[0]?.id || "");
|
|
|
|
const containerStyle = renderer.getLayoutContainerStyle();
|
|
|
|
// 탭 컨테이너 스타일
|
|
const tabsStyle: React.CSSProperties = {
|
|
...containerStyle,
|
|
display: "flex",
|
|
flexDirection: tabsConfig.position === "left" || tabsConfig.position === "right" ? "row" : "column",
|
|
};
|
|
|
|
// 디자인 모드 스타일
|
|
if (isDesignMode) {
|
|
tabsStyle.border = isSelected ? "2px solid #3b82f6" : "1px solid #e2e8f0";
|
|
tabsStyle.borderRadius = "8px";
|
|
}
|
|
|
|
// 탭 사이즈 클래스
|
|
const sizeClass = tabsConfig.size === "sm" ? "text-sm" : tabsConfig.size === "lg" ? "text-lg" : "";
|
|
|
|
return (
|
|
<div
|
|
className={`tabs-layout ${isDesignMode ? "design-mode" : ""} ${className || ""}`}
|
|
style={tabsStyle}
|
|
onClick={onClick}
|
|
draggable={isDesignMode}
|
|
onDragStart={renderer.props.onDragStart}
|
|
onDragEnd={renderer.props.onDragEnd}
|
|
>
|
|
{layout.zones.length === 0 ? (
|
|
/* 디자인 모드에서 존이 없을 때 안내 메시지 */
|
|
isDesignMode && (
|
|
<div
|
|
className="empty-tabs-container"
|
|
style={{
|
|
flex: 1,
|
|
border: "2px dashed #cbd5e1",
|
|
borderRadius: "8px",
|
|
backgroundColor: "rgba(148, 163, 184, 0.05)",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
fontSize: "14px",
|
|
color: "#64748b",
|
|
minHeight: "200px",
|
|
padding: "20px",
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
탭 레이아웃에 존을 추가하세요
|
|
</div>
|
|
)
|
|
) : (
|
|
<Tabs
|
|
value={activeTab}
|
|
onValueChange={setActiveTab}
|
|
orientation={tabsConfig.position === "left" || tabsConfig.position === "right" ? "vertical" : "horizontal"}
|
|
className="flex h-full w-full flex-col"
|
|
>
|
|
{/* 탭 목록 */}
|
|
<TabsList
|
|
className={` ${sizeClass} ${tabsConfig.position === "bottom" ? "order-2" : ""} ${tabsConfig.position === "right" ? "order-2" : ""} ${tabsConfig.variant === "pills" ? "bg-gray-100" : ""} ${tabsConfig.variant === "underline" ? "border-b" : ""} `}
|
|
style={{
|
|
flexShrink: 0,
|
|
justifyContent:
|
|
tabsConfig.position === "left" || tabsConfig.position === "right" ? "flex-start" : "center",
|
|
}}
|
|
>
|
|
{layout.zones.map((zone: any) => (
|
|
<div key={zone.id} className="flex items-center">
|
|
<TabsTrigger
|
|
value={zone.id}
|
|
className={` ${sizeClass} ${tabsConfig.variant === "pills" ? "rounded-full" : ""} ${tabsConfig.variant === "underline" ? "border-b-2 border-transparent data-[state=active]:border-blue-500" : ""} `}
|
|
>
|
|
{zone.name}
|
|
</TabsTrigger>
|
|
|
|
{/* 닫기 버튼 (설정에서 허용한 경우) */}
|
|
{tabsConfig.closable && isDesignMode && layout.zones.length > 1 && (
|
|
<button
|
|
className="ml-1 rounded p-1 hover:bg-gray-200"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
// 탭 닫기 로직 (실제 구현 시 필요)
|
|
console.log("탭 닫기:", zone.id);
|
|
}}
|
|
>
|
|
<X className="h-3 w-3" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
))}
|
|
</TabsList>
|
|
|
|
{/* 탭 컨텐츠 */}
|
|
<div className="flex-1 overflow-auto">
|
|
{layout.zones.map((zone: any) => {
|
|
const zoneChildren = renderer.getZoneChildren(zone.id);
|
|
|
|
return (
|
|
<TabsContent
|
|
key={zone.id}
|
|
value={zone.id}
|
|
className="h-full p-2"
|
|
style={{
|
|
margin: 0,
|
|
borderRadius: "6px",
|
|
backgroundColor: "rgba(248, 250, 252, 0.3)",
|
|
}}
|
|
>
|
|
{renderer.renderZone(zone, zoneChildren, {
|
|
style: {
|
|
height: "100%",
|
|
minHeight: "100px",
|
|
},
|
|
className: "tab-panel",
|
|
})}
|
|
</TabsContent>
|
|
);
|
|
})}
|
|
</div>
|
|
</Tabs>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// React 컴포넌트로 래핑
|
|
export const TabsLayout: React.FC<LayoutRendererProps> = (props) => {
|
|
const renderer = new TabsLayoutRenderer(props);
|
|
return renderer.render();
|
|
};
|