100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
import {
|
|
CalendarClock,
|
|
CheckCircle2,
|
|
CirclePlay,
|
|
CircleX,
|
|
ClipboardCheck,
|
|
GitFork,
|
|
PlugZap,
|
|
Radio,
|
|
Split,
|
|
SquareCheckBig,
|
|
Timer,
|
|
Waypoints,
|
|
type LucideIcon
|
|
} from "lucide-react";
|
|
import {
|
|
Handle,
|
|
Position,
|
|
type Node,
|
|
type NodeProps
|
|
} from "@xyflow/react";
|
|
import type { WorkflowNodeType } from "../../api/workflow";
|
|
|
|
export type WorkflowFlowNodeData = {
|
|
label: string;
|
|
workflowType: string;
|
|
definition: WorkflowNodeType;
|
|
hasError: boolean;
|
|
};
|
|
|
|
export type WorkflowFlowNode = Node<WorkflowFlowNodeData, "workflow">;
|
|
|
|
const iconByName: Record<string, LucideIcon> = {
|
|
"calendar-clock": CalendarClock,
|
|
"circle-check-big": CheckCircle2,
|
|
"circle-play": CirclePlay,
|
|
"circle-x": CircleX,
|
|
"clipboard-check": ClipboardCheck,
|
|
"plug-zap": PlugZap,
|
|
radio: Radio,
|
|
split: Split,
|
|
"square-check-big": SquareCheckBig,
|
|
timer: Timer,
|
|
waypoints: Waypoints
|
|
};
|
|
|
|
export default function WorkflowNode({
|
|
data,
|
|
selected,
|
|
isConnectable
|
|
}: NodeProps<WorkflowFlowNode>) {
|
|
const Icon = iconByName[data.definition.icon] ?? GitFork;
|
|
const inputPorts = data.definition.input_ports;
|
|
const outputPorts = data.definition.output_ports;
|
|
return (
|
|
<div
|
|
className={[
|
|
"workflow-node",
|
|
`workflow-node-${data.definition.category}`,
|
|
selected ? "is-selected" : "",
|
|
data.hasError ? "has-error" : ""
|
|
].filter(Boolean).join(" ")}
|
|
>
|
|
{inputPorts.map((port, index) => (
|
|
<Handle
|
|
key={port.id}
|
|
id={port.id}
|
|
type="target"
|
|
position={Position.Left}
|
|
className="workflow-node-handle"
|
|
style={{ top: portPosition(index, inputPorts.length) }}
|
|
isConnectable={isConnectable}
|
|
title={port.label}
|
|
/>
|
|
))}
|
|
<span className="workflow-node-icon"><Icon size={17} /></span>
|
|
<span className="workflow-node-copy">
|
|
<strong>{data.label}</strong>
|
|
<small>{data.definition.label}</small>
|
|
</span>
|
|
{outputPorts.map((port, index) => (
|
|
<Handle
|
|
key={port.id}
|
|
id={port.id}
|
|
type="source"
|
|
position={Position.Right}
|
|
className="workflow-node-handle"
|
|
style={{ top: portPosition(index, outputPorts.length) }}
|
|
isConnectable={isConnectable}
|
|
title={port.label}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function portPosition(index: number, count: number): string {
|
|
return `${((index + 1) / (count + 1)) * 100}%`;
|
|
}
|