[agent-pipeline] pipe-20260315091327-kxyf round-3
This commit is contained in:
@@ -14,6 +14,22 @@ import {
|
||||
} from "lucide-react";
|
||||
import { ScreenLayoutSummary } from "@/lib/api/screenGroup";
|
||||
|
||||
// 글로우 펄스 애니메이션 CSS 주입
|
||||
if (typeof document !== "undefined") {
|
||||
const styleId = "glow-pulse-animation";
|
||||
if (!document.getElementById(styleId)) {
|
||||
const style = document.createElement("style");
|
||||
style.id = styleId;
|
||||
style.textContent = `
|
||||
@keyframes glow-pulse {
|
||||
from { filter: drop-shadow(0 0 6px hsl(221.2 83.2% 53.3% / 0.4)) drop-shadow(0 0 14px hsl(221.2 83.2% 53.3% / 0.2)); }
|
||||
to { filter: drop-shadow(0 0 10px hsl(221.2 83.2% 53.3% / 0.6)) drop-shadow(0 0 22px hsl(221.2 83.2% 53.3% / 0.3)); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 타입 정의 ==========
|
||||
|
||||
// 화면 노드 데이터 인터페이스
|
||||
@@ -181,14 +197,19 @@ export const ScreenNode: React.FC<{ data: ScreenNodeData }> = ({ data }) => {
|
||||
<div
|
||||
className={`group relative flex h-[240px] w-[240px] flex-col overflow-hidden rounded-xl border border-border/50 bg-card/80 backdrop-blur-sm shadow-lg transition-all cursor-pointer ${
|
||||
isFocused
|
||||
? "border-2 border-primary ring-4 ring-primary/30 shadow-[0_0_30px_-5px_hsl(var(--primary)/0.4)] scale-[1.03]"
|
||||
? "border-2 border-primary scale-[1.03]"
|
||||
: isFaded
|
||||
? "opacity-50"
|
||||
: "hover:shadow-xl hover:border-border hover:-translate-y-0.5"
|
||||
? "opacity-40"
|
||||
: "hover:shadow-xl hover:border-border hover:-translate-y-0.5 hover:shadow-[0_0_15px_-3px_hsl(var(--primary)/0.2)]"
|
||||
}`}
|
||||
style={{
|
||||
filter: isFaded ? "grayscale(100%)" : "none",
|
||||
filter: isFaded
|
||||
? "grayscale(100%)"
|
||||
: isFocused
|
||||
? "drop-shadow(0 0 8px hsl(var(--primary) / 0.5)) drop-shadow(0 0 20px hsl(var(--primary) / 0.25))"
|
||||
: "none",
|
||||
transition: "all 0.3s ease",
|
||||
animation: isFocused ? "glow-pulse 2s ease-in-out infinite alternate" : "none",
|
||||
}}
|
||||
>
|
||||
{/* Handles */}
|
||||
@@ -196,19 +217,19 @@ export const ScreenNode: React.FC<{ data: ScreenNodeData }> = ({ data }) => {
|
||||
type="target"
|
||||
position={Position.Left}
|
||||
id="left"
|
||||
className="!h-2 !w-2 !border-2 !border-background !bg-primary opacity-0 transition-opacity group-hover:opacity-100"
|
||||
className="!h-2.5 !w-2.5 !border-2 !border-background !bg-primary opacity-0 transition-all duration-300 group-hover:opacity-100 group-hover:shadow-[0_0_6px_hsl(var(--primary)/0.5)]"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
id="right"
|
||||
className="!h-2 !w-2 !border-2 !border-background !bg-primary opacity-0 transition-opacity group-hover:opacity-100"
|
||||
className="!h-2.5 !w-2.5 !border-2 !border-background !bg-primary opacity-0 transition-all duration-300 group-hover:opacity-100 group-hover:shadow-[0_0_6px_hsl(var(--primary)/0.5)]"
|
||||
/>
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
id="bottom"
|
||||
className="!h-2 !w-2 !border-2 !border-background !bg-primary opacity-0 transition-opacity group-hover:opacity-100"
|
||||
className="!h-2.5 !w-2.5 !border-2 !border-background !bg-primary opacity-0 transition-all duration-300 group-hover:opacity-100 group-hover:shadow-[0_0_6px_hsl(var(--primary)/0.5)]"
|
||||
/>
|
||||
|
||||
{/* 헤더 (컬러) */}
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
import { getTableColumns, ColumnTypeInfo } from "@/lib/api/tableManagement";
|
||||
import { ScreenSettingModal } from "./ScreenSettingModal";
|
||||
import { TableSettingModal } from "./TableSettingModal";
|
||||
import { AnimatedFlowEdge } from "./AnimatedFlowEdge";
|
||||
import { Monitor, Database, FolderOpen } from "lucide-react";
|
||||
|
||||
// 관계 유형별 색상 정의 (CSS 변수 기반 - 다크모드 자동 대응)
|
||||
@@ -51,6 +52,10 @@ const nodeTypes = {
|
||||
tableNode: TableNode,
|
||||
};
|
||||
|
||||
const edgeTypes = {
|
||||
animatedFlow: AnimatedFlowEdge,
|
||||
};
|
||||
|
||||
// 레이아웃 상수
|
||||
const SCREEN_Y = 50; // 화면 노드 Y 위치 (상단)
|
||||
const TABLE_Y = 420; // 메인 테이블 노드 Y 위치 (중단)
|
||||
@@ -688,7 +693,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: `screen-${nextScreen.screenId}`,
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
label: `${i + 1}`,
|
||||
labelStyle: { fontSize: 11, fill: "hsl(var(--info))", fontWeight: 600 },
|
||||
labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
|
||||
@@ -710,7 +715,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: `table-${scr.tableName}`,
|
||||
sourceHandle: "bottom",
|
||||
targetHandle: "top",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
animated: true, // 모든 메인 테이블 연결은 애니메이션
|
||||
style: {
|
||||
stroke: "hsl(var(--primary))",
|
||||
@@ -749,7 +754,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: targetNodeId,
|
||||
sourceHandle: "bottom",
|
||||
targetHandle: "top",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: "hsl(var(--primary))",
|
||||
@@ -794,7 +799,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: refTargetNodeId,
|
||||
sourceHandle: "bottom",
|
||||
targetHandle: "bottom_target",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
animated: false,
|
||||
style: {
|
||||
stroke: RELATION_COLORS.join.strokeLight, // 초기값 (연한색)
|
||||
@@ -902,7 +907,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: `table-${referencedTable}`, // 참조당하는 테이블
|
||||
sourceHandle: "bottom", // 하단에서 나감 (서브테이블 구간으로)
|
||||
targetHandle: "bottom_target", // 하단으로 들어감
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
animated: false,
|
||||
style: {
|
||||
stroke: relationColor.strokeLight, // 관계 유형별 연한 색상
|
||||
@@ -945,7 +950,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: `subtable-${subTable.tableName}`,
|
||||
sourceHandle: "bottom",
|
||||
targetHandle: "top",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
markerEnd: {
|
||||
type: MarkerType.ArrowClosed,
|
||||
color: relationColor.strokeLight
|
||||
@@ -974,7 +979,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: `table-${join.join_table}`,
|
||||
sourceHandle: "bottom",
|
||||
targetHandle: "bottom_target",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
markerEnd: {
|
||||
type: MarkerType.ArrowClosed,
|
||||
color: RELATION_COLORS.join.strokeLight
|
||||
@@ -1006,7 +1011,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: `table-${rel.table_name}`,
|
||||
sourceHandle: "bottom",
|
||||
targetHandle: "top",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
label: rel.relation_type === "join" ? "조인" : rel.crud_operations || "",
|
||||
labelStyle: { fontSize: 9, fill: "hsl(var(--success))" },
|
||||
labelBgStyle: { fill: "hsl(var(--card))", stroke: "hsl(var(--border))", strokeWidth: 1 },
|
||||
@@ -1028,7 +1033,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: `screen-${flow.target_screen_id}`,
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
type: "smoothstep",
|
||||
type: "animatedFlow",
|
||||
animated: true,
|
||||
label: flow.flow_label || flow.flow_type || "이동",
|
||||
labelStyle: { fontSize: 10, fill: "hsl(var(--primary))", fontWeight: 500 },
|
||||
@@ -1994,7 +1999,7 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
target: targetNodeId,
|
||||
sourceHandle: 'bottom', // 고정: 서브테이블 구간 통과
|
||||
targetHandle: 'bottom_target', // 고정: 서브테이블 구간 통과
|
||||
type: 'smoothstep',
|
||||
type: "animatedFlow",
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: relationColor.stroke, // 관계 유형별 색상
|
||||
@@ -2372,10 +2377,22 @@ function ScreenRelationFlowInner({ screen, selectedGroup, initialFocusedScreenId
|
||||
onNodeClick={handleNodeClick}
|
||||
onNodeContextMenu={handleNodeContextMenu}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
minZoom={0.3}
|
||||
maxZoom={1.5}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
>
|
||||
<svg style={{ position: "absolute", width: 0, height: 0 }}>
|
||||
<defs>
|
||||
<filter id="edge-glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="3" result="blur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="blur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
<Background variant={BackgroundVariant.Dots} gap={20} size={1} color="hsl(var(--border))" />
|
||||
<Controls position="bottom-right" />
|
||||
{/* 관계 범례 */}
|
||||
|
||||
Reference in New Issue
Block a user