@hitachivantara/uikit-react-lab
Version:
Contributed React components to UI Kit by the community.
141 lines (140 loc) • 5.5 kB
JavaScript
import { useFlowInstance } from "./hooks/useFlowInstance.js";
import { useNodeMetaRegistry } from "./FlowContext/NodeMetaContext.js";
import { flowStyles } from "./base.js";
import { useClasses } from "./Flow.styles.js";
import { useCallback, useRef, useState } from "react";
import { useUniqueId } from "@hitachivantara/uikit-react-core";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { Global } from "@emotion/react";
import { MarkerType, ReactFlow, addEdge, applyEdgeChanges, applyNodeChanges } from "reactflow";
import { uid } from "uid";
import { useDndMonitor, useDroppable } from "@dnd-kit/core";
//#region src/Flow/DroppableFlow.tsx
var getNode = (nodes, nodeId) => {
return nodes.find((n) => n.id === nodeId);
};
var validateEdge = (nodes, edges, connection, nodeMetaRegistry) => {
const { source: sourceId, sourceHandle, target: targetId, targetHandle } = connection;
if (!sourceHandle || !targetHandle || !sourceId || !targetId) return false;
const sourceNode = getNode(nodes, sourceId);
const targetNode = getNode(nodes, targetId);
if (!sourceNode || !targetNode) return false;
const sourceType = sourceNode.type;
const targetType = targetNode.type;
if (!sourceType || !targetType) return false;
const inputs = nodeMetaRegistry[targetId]?.inputs || [];
const source = (nodeMetaRegistry[sourceId]?.outputs || []).flatMap((out) => out.outputs || out).find((out) => out.id === sourceHandle);
const target = inputs.flatMap((inp) => inp.inputs || inp).find((inp) => inp.id === targetHandle);
const sourceProvides = source?.provides || "";
const targetAccepts = target?.accepts || [];
const sourceMaxConnections = source?.maxConnections;
const targetMaxConnections = target?.maxConnections;
let isValid = targetAccepts.length === 0 || targetAccepts.includes(sourceProvides);
if (isValid && targetMaxConnections != null) isValid = edges.filter((edg) => edg.target === targetId && edg.targetHandle === targetHandle).length < targetMaxConnections;
if (isValid && sourceMaxConnections != null) isValid = edges.filter((edg) => edg.source === sourceId && edg.sourceHandle === sourceHandle).length < sourceMaxConnections;
return isValid;
};
var HvDroppableFlow = ({ id, className, children, onFlowChange, onDndDrop, classes: classesProp, nodes: initialNodes = [], edges: initialEdges = [], onConnect: onConnectProp, onNodesChange: onNodesChangeProp, onEdgesChange: onEdgesChangeProp, defaultEdgeOptions: defaultEdgeOptionsProp, nodeTypes, ...others }) => {
const { classes, cx } = useClasses(classesProp);
const elementId = useUniqueId(id);
const reactFlowInstance = useFlowInstance();
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState(initialEdges);
const nodesRef = useRef(initialNodes);
const edgesRef = useRef(initialEdges);
const updateNodes = (nds) => {
setNodes(nds);
nodesRef.current = nds;
};
const updateEdges = (eds) => {
setEdges(eds);
edgesRef.current = eds;
};
const { setNodeRef } = useDroppable({ id: elementId });
useDndMonitor({ onDragEnd: useCallback((event) => {
if (event.over?.id !== elementId) return;
const hvFlow = event.active.data.current?.hvFlow;
const type = hvFlow?.type;
if (!type || !nodeTypes?.[type]) return;
const position = reactFlowInstance.screenToFlowPosition({
x: hvFlow?.x || 0,
y: hvFlow?.y || 0
});
const data = {
nodeLabel: hvFlow?.label,
...hvFlow?.data
};
const newNode = {
id: uid(),
position,
data,
type
};
if (onDndDrop) {
onDndDrop(event, newNode);
return;
}
updateNodes(nodes.concat(newNode));
}, [
elementId,
nodeTypes,
nodes,
onDndDrop,
reactFlowInstance
]) });
const handleFlowChange = useCallback((nds, eds) => {
if (!nds.find((node) => node.dragging)) onFlowChange?.(nds, eds);
}, [onFlowChange]);
const handleConnect = useCallback((connection) => {
const eds = addEdge(connection, edgesRef.current);
updateEdges(eds);
handleFlowChange(nodesRef.current, eds);
onConnectProp?.(connection);
}, [handleFlowChange, onConnectProp]);
const handleNodesChange = useCallback((changes) => {
const nds = applyNodeChanges(changes, nodesRef.current);
updateNodes(nds);
handleFlowChange(nds, edgesRef.current);
onNodesChangeProp?.(changes);
}, [handleFlowChange, onNodesChangeProp]);
const handleEdgesChange = useCallback((changes) => {
const eds = applyEdgeChanges(changes, edgesRef.current);
updateEdges(eds);
handleFlowChange(nodesRef.current, eds);
onEdgesChangeProp?.(changes);
}, [handleFlowChange, onEdgesChangeProp]);
const { registry } = useNodeMetaRegistry();
const isValidConnection = (connection) => validateEdge(nodes, edges, connection, registry);
const defaultEdgeOptions = {
markerEnd: {
type: MarkerType.ArrowClosed,
height: 20,
width: 20
},
type: "smoothstep",
pathOptions: { borderRadius: 40 },
...defaultEdgeOptionsProp
};
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Global, { styles: flowStyles }), /* @__PURE__ */ jsx("div", {
id: elementId,
ref: setNodeRef,
className: cx(classes.root, className),
children: /* @__PURE__ */ jsx(ReactFlow, {
nodes,
edges,
nodeTypes,
onNodesChange: handleNodesChange,
onEdgesChange: handleEdgesChange,
onConnect: handleConnect,
isValidConnection,
defaultEdgeOptions,
snapGrid: [1, 1],
snapToGrid: true,
onError: (code, message) => {},
...others,
children
})
})] });
};
//#endregion
export { HvDroppableFlow };