216 lines
6.8 KiB
TypeScript
216 lines
6.8 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import React from "react";
|
||
|
|
import { cn } from "@/lib/utils";
|
||
|
|
import { PopComponentDefinition, PopComponentConfig } from "../types/pop-layout";
|
||
|
|
import { Settings, Database, Link2 } from "lucide-react";
|
||
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// Props 정의
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
interface ComponentEditorPanelProps {
|
||
|
|
/** 선택된 컴포넌트 (없으면 null) */
|
||
|
|
component: PopComponentDefinition | null;
|
||
|
|
/** 컴포넌트 설정 변경 시 호출 */
|
||
|
|
onConfigChange?: (config: Partial<PopComponentConfig>) => void;
|
||
|
|
/** 컴포넌트 라벨 변경 시 호출 */
|
||
|
|
onLabelChange?: (label: string) => void;
|
||
|
|
/** 추가 className */
|
||
|
|
className?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 컴포넌트 편집 패널
|
||
|
|
//
|
||
|
|
// 역할:
|
||
|
|
// - 선택된 컴포넌트의 설정을 편집
|
||
|
|
// - 3개 탭: 기본 설정 / 데이터 바인딩 / 데이터 연결
|
||
|
|
//
|
||
|
|
// TODO:
|
||
|
|
// - 타입별 상세 설정 UI 구현
|
||
|
|
// - 데이터 바인딩 UI 구현
|
||
|
|
// - 데이터 플로우 UI 구현
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
export function ComponentEditorPanel({
|
||
|
|
component,
|
||
|
|
onConfigChange,
|
||
|
|
onLabelChange,
|
||
|
|
className,
|
||
|
|
}: ComponentEditorPanelProps) {
|
||
|
|
// 컴포넌트가 선택되지 않은 경우
|
||
|
|
if (!component) {
|
||
|
|
return (
|
||
|
|
<div className={cn("flex h-full flex-col", className)}>
|
||
|
|
<div className="border-b px-4 py-3">
|
||
|
|
<h3 className="text-sm font-medium">컴포넌트 편집</h3>
|
||
|
|
</div>
|
||
|
|
<div className="flex flex-1 items-center justify-center p-4 text-sm text-muted-foreground">
|
||
|
|
컴포넌트를 선택하세요
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className={cn("flex h-full flex-col", className)}>
|
||
|
|
{/* 헤더 */}
|
||
|
|
<div className="border-b px-4 py-3">
|
||
|
|
<h3 className="text-sm font-medium">
|
||
|
|
{component.label || getComponentTypeLabel(component.type)}
|
||
|
|
</h3>
|
||
|
|
<p className="text-xs text-muted-foreground">{component.type}</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 탭 컨텐츠 */}
|
||
|
|
<Tabs defaultValue="settings" className="flex-1">
|
||
|
|
<TabsList className="w-full justify-start rounded-none border-b bg-transparent px-2">
|
||
|
|
<TabsTrigger value="settings" className="gap-1 text-xs">
|
||
|
|
<Settings className="h-3 w-3" />
|
||
|
|
설정
|
||
|
|
</TabsTrigger>
|
||
|
|
<TabsTrigger value="data" className="gap-1 text-xs">
|
||
|
|
<Database className="h-3 w-3" />
|
||
|
|
데이터
|
||
|
|
</TabsTrigger>
|
||
|
|
<TabsTrigger value="flow" className="gap-1 text-xs">
|
||
|
|
<Link2 className="h-3 w-3" />
|
||
|
|
연결
|
||
|
|
</TabsTrigger>
|
||
|
|
</TabsList>
|
||
|
|
|
||
|
|
{/* 기본 설정 탭 */}
|
||
|
|
<TabsContent value="settings" className="flex-1 overflow-auto p-4">
|
||
|
|
<ComponentSettingsForm
|
||
|
|
component={component}
|
||
|
|
onConfigChange={onConfigChange}
|
||
|
|
onLabelChange={onLabelChange}
|
||
|
|
/>
|
||
|
|
</TabsContent>
|
||
|
|
|
||
|
|
{/* 데이터 바인딩 탭 (뼈대) */}
|
||
|
|
<TabsContent value="data" className="flex-1 overflow-auto p-4">
|
||
|
|
<DataBindingPlaceholder />
|
||
|
|
</TabsContent>
|
||
|
|
|
||
|
|
{/* 데이터 연결 탭 (뼈대) */}
|
||
|
|
<TabsContent value="flow" className="flex-1 overflow-auto p-4">
|
||
|
|
<DataFlowPlaceholder />
|
||
|
|
</TabsContent>
|
||
|
|
</Tabs>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 컴포넌트 설정 폼
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
interface ComponentSettingsFormProps {
|
||
|
|
component: PopComponentDefinition;
|
||
|
|
onConfigChange?: (config: Partial<PopComponentConfig>) => void;
|
||
|
|
onLabelChange?: (label: string) => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
function ComponentSettingsForm({
|
||
|
|
component,
|
||
|
|
onConfigChange,
|
||
|
|
onLabelChange,
|
||
|
|
}: ComponentSettingsFormProps) {
|
||
|
|
return (
|
||
|
|
<div className="space-y-4">
|
||
|
|
{/* 라벨 입력 */}
|
||
|
|
<div className="space-y-1.5">
|
||
|
|
<label className="text-xs font-medium">라벨</label>
|
||
|
|
<input
|
||
|
|
type="text"
|
||
|
|
className="h-8 w-full rounded border border-input bg-background px-2 text-sm"
|
||
|
|
value={component.label || ""}
|
||
|
|
onChange={(e) => onLabelChange?.(e.target.value)}
|
||
|
|
placeholder="컴포넌트 라벨"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 타입별 설정 (TODO: 상세 구현) */}
|
||
|
|
<div className="rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4">
|
||
|
|
<p className="text-center text-xs text-muted-foreground">
|
||
|
|
{getComponentTypeLabel(component.type)} 상세 설정
|
||
|
|
</p>
|
||
|
|
<p className="mt-1 text-center text-xs text-muted-foreground">
|
||
|
|
(추후 구현 예정)
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 데이터 바인딩 플레이스홀더 (뼈대)
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
function DataBindingPlaceholder() {
|
||
|
|
return (
|
||
|
|
<div className="space-y-4">
|
||
|
|
<div className="rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4">
|
||
|
|
<div className="flex flex-col items-center gap-2">
|
||
|
|
<Database className="h-8 w-8 text-gray-400" />
|
||
|
|
<p className="text-center text-xs text-muted-foreground">
|
||
|
|
데이터 바인딩 설정
|
||
|
|
</p>
|
||
|
|
<p className="text-center text-xs text-muted-foreground">
|
||
|
|
테이블 선택 → 칼럼 선택 → 조인 설정
|
||
|
|
</p>
|
||
|
|
<p className="mt-2 text-center text-xs text-gray-400">
|
||
|
|
(추후 구현 예정)
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 데이터 플로우 플레이스홀더 (뼈대)
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
function DataFlowPlaceholder() {
|
||
|
|
return (
|
||
|
|
<div className="space-y-4">
|
||
|
|
<div className="rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4">
|
||
|
|
<div className="flex flex-col items-center gap-2">
|
||
|
|
<Link2 className="h-8 w-8 text-gray-400" />
|
||
|
|
<p className="text-center text-xs text-muted-foreground">
|
||
|
|
데이터 연결 설정
|
||
|
|
</p>
|
||
|
|
<p className="text-center text-xs text-muted-foreground">
|
||
|
|
컴포넌트 간 / 섹션 간 / 화면 간 연결
|
||
|
|
</p>
|
||
|
|
<p className="mt-2 text-center text-xs text-gray-400">
|
||
|
|
(추후 구현 예정)
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ========================================
|
||
|
|
// 헬퍼 함수
|
||
|
|
// ========================================
|
||
|
|
|
||
|
|
function getComponentTypeLabel(type: string): string {
|
||
|
|
const labels: Record<string, string> = {
|
||
|
|
"pop-field": "필드",
|
||
|
|
"pop-button": "버튼",
|
||
|
|
"pop-list": "리스트",
|
||
|
|
"pop-indicator": "인디케이터",
|
||
|
|
"pop-scanner": "스캐너",
|
||
|
|
"pop-numpad": "넘패드",
|
||
|
|
};
|
||
|
|
return labels[type] || type;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default ComponentEditorPanel;
|