테이블 기반 방식으로 변경
This commit is contained in:
98
frontend/components/dataflow/TableNode.tsx
Normal file
98
frontend/components/dataflow/TableNode.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { Handle, Position, NodeResizer } from "@xyflow/react";
|
||||
|
||||
interface TableColumn {
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface Table {
|
||||
tableName: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
columns: TableColumn[];
|
||||
}
|
||||
|
||||
interface TableNodeData {
|
||||
table: Table;
|
||||
onColumnClick: (tableName: string, columnName: string) => void;
|
||||
onScrollAreaEnter?: () => void;
|
||||
onScrollAreaLeave?: () => void;
|
||||
selected?: boolean;
|
||||
selectedColumns?: string[]; // 선택된 컬럼 목록
|
||||
}
|
||||
|
||||
export const TableNode: React.FC<{ data: TableNodeData; selected?: boolean }> = ({ data, selected }) => {
|
||||
const { table, onColumnClick, onScrollAreaEnter, onScrollAreaLeave, selectedColumns = [] } = data;
|
||||
|
||||
return (
|
||||
<div className="relative min-h-[200px] min-w-[280px] rounded-lg border-2 border-gray-300 bg-white shadow-lg">
|
||||
{/* NodeResizer for resizing functionality */}
|
||||
<NodeResizer
|
||||
color="#ff0071"
|
||||
isVisible={selected}
|
||||
minWidth={280}
|
||||
minHeight={200}
|
||||
handleStyle={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
backgroundColor: "#ff0071",
|
||||
border: "1px solid white",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 테이블 헤더 */}
|
||||
<div className="rounded-t-lg bg-blue-600 p-3 text-white">
|
||||
<h3 className="truncate text-sm font-semibold">{table.displayName}</h3>
|
||||
<p className="truncate text-xs opacity-90">{table.tableName}</p>
|
||||
{table.description && <p className="mt-1 truncate text-xs opacity-75">{table.description}</p>}
|
||||
</div>
|
||||
|
||||
{/* 컬럼 목록 */}
|
||||
<div
|
||||
className="max-h-[300px] overflow-y-auto p-2"
|
||||
onMouseEnter={onScrollAreaEnter}
|
||||
onMouseLeave={onScrollAreaLeave}
|
||||
>
|
||||
<div className="space-y-1">
|
||||
{table.columns.map((column) => {
|
||||
const isSelected = selectedColumns.includes(column.name);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={column.name}
|
||||
onClick={() => onColumnClick(table.tableName, column.name)}
|
||||
className={`cursor-pointer rounded px-2 py-1 text-xs transition-colors ${
|
||||
isSelected ? "bg-blue-100 text-blue-800 ring-2 ring-blue-500" : "text-gray-700 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-mono font-medium">{column.name}</span>
|
||||
<span className="text-gray-500">{column.type}</span>
|
||||
</div>
|
||||
{column.description && <div className="mt-0.5 text-gray-500">{column.description}</div>}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* React Flow Handles */}
|
||||
<Handle
|
||||
type="target"
|
||||
position={Position.Left}
|
||||
className="h-3 w-3 border-2 border-gray-400 bg-white"
|
||||
isConnectable={false}
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
className="h-3 w-3 border-2 border-gray-400 bg-white"
|
||||
isConnectable={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user