File size: 4,894 Bytes
a112474
16682aa
cedd3c4
16682aa
 
98f8f03
d460634
cedd3c4
16682aa
 
dfaccc7
16682aa
cedd3c4
d7943fc
91961e7
98f8f03
 
dfd9216
98f8f03
 
 
 
 
91961e7
98f8f03
 
114fd1c
98f8f03
d460634
 
 
 
 
 
98f8f03
114fd1c
d460634
98f8f03
114fd1c
d460634
98f8f03
 
 
 
 
 
1ca8050
 
 
 
98f8f03
d460634
98f8f03
 
 
 
 
cedd3c4
dfd9216
98f8f03
dfd9216
114fd1c
dfd9216
 
 
d460634
 
 
 
 
 
454a221
6565904
91961e7
454a221
98f8f03
d460634
ef8a15c
d460634
 
 
 
 
98f8f03
454a221
502f722
454a221
 
 
d7943fc
16682aa
d7943fc
16682aa
d7943fc
16682aa
 
d7943fc
16682aa
d7943fc
16682aa
d7943fc
 
 
98f8f03
d460634
 
 
cedd3c4
44b2aaf
cedd3c4
 
 
 
 
 
 
fd3b7ca
cedd3c4
d460634
 
 
 
 
 
 
 
 
 
f366c41
454a221
d460634
 
 
 
a112474
d460634
 
 
a112474
d460634
 
f366c41
98f8f03
 
 
 
cedd3c4
 
 
 
 
 
 
 
 
 
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { Handle, NodeResizeControl, type Position, useReactFlow } from "@xyflow/react";
import type React from "react";
import { ErrorBoundary } from "react-error-boundary";
// @ts-ignore
import AlertTriangle from "~icons/tabler/alert-triangle-filled.jsx";
// @ts-ignore
import ChevronDownRight from "~icons/tabler/chevron-down-right.jsx";
// @ts-ignore
import Dots from "~icons/tabler/dots.jsx";
// @ts-ignore
import Help from "~icons/tabler/question-mark.jsx";
// @ts-ignore
import Skull from "~icons/tabler/skull.jsx";
import Tooltip from "../../Tooltip";
import { COLORS } from "../../common.ts";

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

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

function LynxKiteNodeComponent(props: LynxKiteNodeProps) {
  const reactFlow = useReactFlow();
  const data = props.data;
  const expanded = !data.collapsed;
  const handles = getHandles(data.meta?.value?.inputs || [], data.meta?.value?.outputs || []);
  function titleClicked() {
    reactFlow.updateNodeData(props.id, { collapsed: expanded });
  }
  const handleOffsetDirection = {
    top: "left",
    bottom: "left",
    left: "top",
    right: "top",
  };
  const titleStyle: { backgroundColor?: string } = {};
  if (data.meta?.value?.color) {
    titleStyle.backgroundColor = COLORS[data.meta.value.color] || data.meta.value.color;
  }
  return (
    <div
      className={`node-container ${expanded ? "expanded" : "collapsed"} ${props.parentId ? "in-group" : ""}`}
      style={{
        width: props.width || 200,
        height: expanded ? props.height || 200 : undefined,
      }}
    >
      <div className="lynxkite-node" style={props.nodeStyle}>
        <div
          className={`title bg-primary drag-handle ${data.status}`}
          style={titleStyle}
          onClick={titleClicked}
        >
          <span className="title-title">{data.title}</span>
          {data.error && (
            <Tooltip doc={`Error: ${data.error}`}>
              <AlertTriangle />
            </Tooltip>
          )}
          {expanded || (
            <Tooltip doc="Click to expand node">
              <Dots />
            </Tooltip>
          )}
          <Tooltip doc={data.meta?.value?.doc}>
            <Help />
          </Tooltip>
        </div>
        {expanded && (
          <>
            {data.error && <div className="error">{data.error}</div>}
            <ErrorBoundary
              resetKeys={[props]}
              fallback={
                <p className="error" style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <Skull style={{ fontSize: 20 }} />
                  Failed to display this node.
                </p>
              }
            >
              <div className="node-content">{props.children}</div>
            </ErrorBoundary>
            <NodeResizeControl
              minWidth={100}
              minHeight={50}
              style={{ background: "transparent", border: "none" }}
            >
              <ChevronDownRight className="node-resizer" />
            </NodeResizeControl>
          </>
        )}
        {handles.map((handle) => (
          <Handle
            key={`${handle.name} on ${handle.position}`}
            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>
  );
}

export default function LynxKiteNode(Component: React.ComponentType<any>) {
  return (props: any) => {
    return (
      <LynxKiteNodeComponent {...props}>
        <Component {...props} />
      </LynxKiteNodeComponent>
    );
  };
}