File size: 3,206 Bytes
1270bff
7fda361
7ed5764
7fda361
 
483664b
7fda361
 
 
 
 
 
 
483664b
7fda361
7ed5764
 
 
 
 
 
7fda361
 
7ed5764
7fda361
 
7ed5764
7fda361
 
 
 
 
 
 
7ed5764
1270bff
 
7fda361
 
 
 
 
 
483664b
7fda361
483664b
7fda361
483664b
 
 
7ed5764
 
 
 
 
 
7fda361
 
7ed5764
8ec0e19
7ed5764
 
 
 
 
7fda361
1270bff
7fda361
 
 
 
7ed5764
 
 
 
 
 
 
 
 
 
 
 
 
 
d983a3c
 
7ed5764
 
 
 
1270bff
7ed5764
 
 
1270bff
7ed5764
 
d983a3c
7fda361
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { Handle, NodeResizeControl, type Position, useReactFlow } from "@xyflow/react";
// @ts-ignore
import ChevronDownRight from "~icons/tabler/chevron-down-right.jsx";

interface LynxKiteNodeProps {
  id: string;
  width: number;
  height: number;
  nodeStyle: any;
  data: any;
  children: any;
}

function getHandles(inputs: object, outputs: object) {
  const handles: {
    position: "top" | "bottom" | "left" | "right";
    name: string;
    index: number;
    offsetPercentage: number;
    showLabel: boolean;
    type: "source" | "target";
  }[] = [];
  for (const e of Object.values(inputs)) {
    handles.push({ ...e, type: "target" });
  }
  for (const e of Object.values(outputs)) {
    handles.push({ ...e, type: "source" });
  }
  const counts = { top: 0, bottom: 0, left: 0, right: 0 };
  for (const e of handles) {
    e.index = counts[e.position];
    counts[e.position]++;
  }
  for (const e of handles) {
    e.offsetPercentage = (100 * (e.index + 1)) / (counts[e.position] + 1);
    const simpleHorizontal = counts.top === 0 && counts.bottom === 0 && handles.length <= 2;
    const simpleVertical = counts.left === 0 && counts.right === 0 && handles.length <= 2;
    e.showLabel = !simpleHorizontal && !simpleVertical;
  }
  return handles;
}

export default function LynxKiteNode(props: LynxKiteNodeProps) {
  const reactFlow = useReactFlow();
  const data = props.data;
  const expanded = !data.collapsed;
  const handles = getHandles(data.meta?.inputs || {}, data.meta?.outputs || {});
  function titleClicked() {
    reactFlow.updateNodeData(props.id, { collapsed: expanded });
  }
  const handleOffsetDirection = {
    top: "left",
    bottom: "left",
    left: "top",
    right: "top",
  };

  return (
    <div
      className={`node-container ${expanded ? "expanded" : "collapsed"} `}
      style={{
        width: props.width || 200,
        height: expanded ? props.height || 200 : undefined,
      }}
    >
      <div className="lynxkite-node" style={props.nodeStyle}>
        <div className={`title bg-primary ${data.status}`} onClick={titleClicked}>
          {data.title}
          {data.error && <span className="title-icon">⚠️</span>}
          {expanded || <span className="title-icon"></span>}
        </div>
        {expanded && (
          <>
            {data.error && <div className="error">{data.error}</div>}
            {props.children}
            <NodeResizeControl
              minWidth={100}
              minHeight={50}
              style={{ background: "transparent", border: "none" }}
            >
              <ChevronDownRight className="node-resizer" />
            </NodeResizeControl>
          </>
        )}
        {handles.map((handle) => (
          <Handle
            key={handle.name}
            id={handle.name}
            type={handle.type}
            position={handle.position as Position}
            style={{
              [handleOffsetDirection[handle.position]]: `${handle.offsetPercentage}% `,
            }}
          >
            {handle.showLabel && (
              <span className="handle-name">{handle.name.replace(/_/g, " ")}</span>
            )}
          </Handle>
        ))}
      </div>
    </div>
  );
}