UNPKG

schyma

Version:

JSON Schemas Visualizer React component

179 lines 8.92 kB
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