Spaces:
Running
Running
File size: 4,342 Bytes
a112474 cedd3c4 98f8f03 d460634 cedd3c4 98f8f03 dfd9216 98f8f03 114fd1c 98f8f03 d460634 98f8f03 114fd1c d460634 98f8f03 114fd1c d460634 98f8f03 d460634 a112474 98f8f03 454a221 cedd3c4 dfd9216 98f8f03 dfd9216 114fd1c dfd9216 d460634 454a221 6565904 454a221 98f8f03 d460634 8bef796 d460634 98f8f03 454a221 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 |
import { Handle, NodeResizeControl, type Position, useReactFlow } from "@xyflow/react";
import { ErrorBoundary } from "react-error-boundary";
// @ts-ignore
import ChevronDownRight from "~icons/tabler/chevron-down-right.jsx";
// @ts-ignore
import Skull from "~icons/tabler/skull.jsx";
interface LynxKiteNodeProps {
id: string;
width: number;
height: number;
nodeStyle: any;
data: any;
children: any;
}
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]++;
}
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;
}
const OP_COLORS: { [key: string]: string } = {
orange: "oklch(75% 0.2 55)",
blue: "oklch(75% 0.2 230)",
green: "oklch(75% 0.2 130)",
};
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 = OP_COLORS[data.meta.value.color] || data.meta.value.color;
}
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}`}
style={titleStyle}
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>}
<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>
);
};
}
|