File size: 3,304 Bytes
7ed5764
 
 
 
 
 
7fda361
7ed5764
7fda361
 
483664b
7fda361
 
 
 
 
 
 
483664b
7fda361
7ed5764
 
 
 
 
 
7fda361
 
7ed5764
7fda361
 
7ed5764
7fda361
 
 
 
 
 
 
7ed5764
 
 
 
 
7fda361
 
 
 
 
 
483664b
7fda361
483664b
7fda361
483664b
 
 
7ed5764
 
 
 
 
 
7fda361
 
7ed5764
8ec0e19
7ed5764
 
 
 
 
7fda361
4f7c2bb
8023f1f
4f7c2bb
 
7fda361
 
 
 
7ed5764
 
 
 
 
 
 
 
 
 
 
 
 
 
d983a3c
 
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
105
106
107
108
109
110
111
112
113
114
115
116
117
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>
  );
}