File size: 3,304 Bytes
8fe4e41
 
 
 
 
 
be095f5
8fe4e41
be095f5
 
a55eb17
be095f5
 
 
 
 
 
 
a55eb17
be095f5
8fe4e41
 
 
 
 
 
be095f5
 
8fe4e41
be095f5
 
8fe4e41
be095f5
 
 
 
 
 
 
8fe4e41
 
 
 
 
be095f5
 
 
 
 
 
a55eb17
be095f5
a55eb17
be095f5
a55eb17
 
 
8fe4e41
 
 
 
 
 
be095f5
 
8fe4e41
01db704
8fe4e41
 
 
 
 
be095f5
408ef2b
64d244a
408ef2b
 
be095f5
 
 
 
8fe4e41
 
 
 
 
 
 
 
 
 
 
 
 
 
9a98e24
 
8fe4e41
 
 
 
 
 
 
 
 
 
 
 
 
 
9a98e24
be095f5
 
 
 
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>
  );
}