schyma
Version:
JSON Schemas Visualizer React component
179 lines • 8.92 kB
JavaScript
import { __awaiter } from "tslib";
import React, { useCallback, useEffect } from 'react';
import { SmartBezierEdge } from '@tisoap/react-flow-smart-edge';
import { ReactFlow, MiniMap, Controls, Background, useReactFlow, MarkerType, useNodesState, useEdgesState, addEdge, Position, ReactFlowProvider, ConnectionLineType, } from 'reactflow';
import { propMerge, removeEdgesByParent, removeElementsByParent, resolveRef } from '../utils/reusables';
import { getLayoutedElements } from '../utils/dagreUtils';
const position = { x: 0, y: 0, zoom: 0.2 };
const initialEdges = [
{
id: 'edges-e5-7',
source: '0',
target: '1',
label: '+',
labelBgPadding: [8, 4],
labelBgBorderRadius: 4,
animated: true,
type: 'smart',
markerEnd: {
type: MarkerType.ArrowClosed,
},
},
];
function Flow({ initialNode, nNodes, setnNodes, setCurrentNode, schema }) {
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements([initialNode], initialEdges);
const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges);
const { setCenter } = useReactFlow();
const onConnect = useCallback((connection) => setEdges((eds) => addEdge(Object.assign(Object.assign({}, connection), { type: ConnectionLineType.SmoothStep, animated: true }), eds)),
// eslint-disable-next-line react-hooks/exhaustive-deps
[]);
const extractChildren = (props, parent) => __awaiter(this, void 0, void 0, function* () {
const children = [];
for (const prop in props) {
const id = String(Math.floor(Math.random() * 1000000));
if (props[prop].$ref) {
const res = yield resolveRef(props[prop].$ref, schema);
children.push({
id,
type: 'input',
data: Object.assign(Object.assign(Object.assign(Object.assign({}, props[prop]), { label: prop, parent: parent.id, relations: Object.assign(Object.assign({}, parent.relations), { [parent.id]: 'node' }) }), res), { children: [] }),
position: position,
sourcePosition: Position.Right,
targetPosition: Position.Left,
});
}
else {
children.push({
id,
type: 'input',
data: Object.assign(Object.assign({}, props[prop]), { label: prop, id, parent: parent.id, relations: Object.assign(Object.assign({}, parent.relations), { [parent.id]: 'node' }), children: [] }),
position: position,
sourcePosition: Position.Right,
targetPosition: Position.Left,
});
}
}
return children;
});
useEffect(() => {
const fetchInitialChildren = () => __awaiter(this, void 0, void 0, function* () {
const newNodes = [];
const properties = initialNode.data.properties;
const children = yield extractChildren(properties, initialNode);
newNodes.push({
id: initialNode.id,
type: 'input',
data: {
children,
label: initialNode.data.label,
description: initialNode.data.description,
properties: initialNode.data.properties,
relations: initialNode.data.relations,
},
position: { x: 0, y: 0 },
sourcePosition: Position.Right,
targetPosition: Position.Left,
});
setNodes(newNodes);
});
fetchInitialChildren();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Node focus when clicked
const focusNode = (children, zoom) => {
if (children.length === 0)
return;
let middleChild = children[Math.floor(children.length / 2)];
const middleChildWithLatestPosition = nodes.filter((a) => a.id == middleChild.id)[0];
if (middleChildWithLatestPosition) {
middleChild = middleChildWithLatestPosition;
}
setCenter(middleChild.position.x, middleChild.position.y, { zoom, duration: 1000 });
};
// On Node Click
const nodeClick = (_event, node) => __awaiter(this, void 0, void 0, function* () {
const findChildren = nodes.filter((item) => { var _a; return ((_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.parent) === node.id; });
if (!findChildren.length) {
const itemChildren = node.data.children;
const newEdges = [
...edges,
...itemChildren.map((item) => {
var _a;
return {
id: String(Math.floor(Math.random() * 1000000)),
source: (_a = item === null || item === void 0 ? void 0 : item.data) === null || _a === void 0 ? void 0 : _a.parent,
target: item === null || item === void 0 ? void 0 : item.id,
markerEnd: {
type: MarkerType.ArrowClosed,
},
};
}),
];
//TODO: Fix nodes type error
const newNodes = nodes.concat(itemChildren);
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(newNodes, newEdges, 'LR');
setNodes([...layoutedNodes]);
setEdges([...layoutedEdges]);
if (itemChildren.length > 0) {
focusNode(itemChildren, 0.9);
}
}
else {
const newNodes = removeElementsByParent(nodes, node.id);
const newEdges = removeEdgesByParent(edges, node.id);
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(newNodes, newEdges, 'LR');
setNodes([...layoutedNodes]);
setEdges([...layoutedEdges]);
focusNode([node], 0.9);
}
});
//On Node Hover
function handleMouseEnter(_e, node) {
return __awaiter(this, void 0, void 0, function* () {
if (!nNodes[node.id]) {
const itemChildren = [];
const nodeChildren = node.data.children;
yield Promise.all(nodeChildren.map((item) => __awaiter(this, void 0, void 0, function* () {
let children = [];
const label = item.data.label;
const extractProps = propMerge(item.data, label);
if (Object.keys(extractProps).length > 0) {
const res = yield extractChildren(extractProps, item);
children = res;
}
const relations = Object.assign(Object.assign({}, node.data.relations), item.data.relations);
itemChildren.push({
id: item.id,
type: (children === null || children === void 0 ? void 0 : children.length) > 0 ? 'default' : 'output',
data: {
label: item.data.label,
children: children,
parent: item.data.parent,
examples: item.data.examples,
description: item.data.description,
relations: relations,
},
position: position,
sourcePosition: Position.Right,
targetPosition: Position.Left,
});
})));
node.data.children = itemChildren;
nNodes[node.id] = node;
setnNodes(nNodes);
}
setCurrentNode(node);
});
}
const edgeTypes = {
smart: SmartBezierEdge,
};
return (React.createElement(ReactFlow, { nodes: nodes, edges: edges, edgeTypes: edgeTypes, onNodesChange: onNodesChange, connectionLineType: ConnectionLineType.SmoothStep, onEdgesChange: onEdgesChange, onConnect: onConnect, onNodeMouseEnter: handleMouseEnter, onNodeClick: nodeClick, fitView: true, defaultViewport: { x: 1, y: 1, zoom: 0.9 } },
React.createElement(MiniMap, null),
React.createElement(Controls, null),
React.createElement(Background, null)));
}
export default ({ setCurrentNode, setnNodes, nNodes, initialNode, schema }) => (React.createElement(ReactFlowProvider, null,
React.createElement(Flow, { setnNodes: setnNodes, nNodes: nNodes, setCurrentNode: setCurrentNode, initialNode: initialNode, schema: schema })));
//# sourceMappingURL=Nodes.js.map