import * as React from 'react'; import { LayoutProps, ModalSettings } from '../layout/layout'; import Layout from '../layout/layout'; import { IVisjsOptions } from './interfaces/IVisjsOptions'; import { INetwork } from './interfaces/INetwork'; import { IUndoRedoGraph } from './interfaces/IGraph'; // Graph related imports: import { UndoRedoGraph } from './graph'; import { generateGraphOptions } from './defaults/default.graph-options'; // Import Addons: import { makeMeMultiSelect } from './addons/selectionbox.extension'; import DynamicRenderer from '../dynamic/dynamicRenderer'; import { rgetattr, rsetattr } from '../../../lib/helpers/objectMethods'; import { Button } from 'react-bootstrap'; export interface GraphicalEditorComponentProps extends Partial>{ options?: IVisjsOptions network?: INetwork } export interface GraphicalEditorComponentState { update: null } class GraphicalEditorComponent extends React.Component, GraphicalEditorComponentState> { /** * The Element containing the Network. */ public network: IUndoRedoGraph; public layout: Layout; constructor(props) { super(props); } public tabs: {[index: string]: INetwork} = {}; public addGraphAddons(network: IUndoRedoGraph){ makeMeMultiSelect(network); } // public async initEditor() { // const _this = this; // /** Generate the Default Callback for adding a Node */ // rsetattr( // this._visjsOptions, // 'manipulation.addEdge', // generateAddFunction(_this, (data, callback) => { // /** Test if an Edge-Template exists */ // if (_this.template.type === 'elements' && _this.template.edges.length === 1 && _this.template.nodes.length === 0) { // const edge = Object.assign( // _this.template.edges[0], // data // ); // if (typeof _this.options.addEdgeCallback === 'function') { // _this.options.addEdgeCallback(edge, callback); // } else { // callback(edge); // } // } // } // ) // ); // if (!rgetattr(this._visjsOptions, 'manipulation.enabled', false)) { // rsetattr(this._visjsOptions, 'manipulation.enabled', false); // } // const editor = initEditor>({ // component: this, // element: this.layout.panels.main, // networkOptions: this.visjsOptions, // renderMinimap: false // }); // const editItemIfPossible = (params) => { // if (params.nodes.length === 1) { // // A Node was Selected. Figure out, whether it is a cluster or not. // const nodeID = params.nodes[0]; // if (_this.network.network.isCluster(nodeID)) { // // Uncluster the Nodes // _this.network.network.openCluster(nodeID); // } else { // // Open up the Settings Tab // const selection = getSubElements(this.network, false); // _this.updateNode(selection); // } // } else if (params.edges.length === 1) { // // A Node was Selected. Figure out, whether it is a cluster or not. // const edges = _this.network.data.edges.get(params.edges[0]); // _this.updateEdges(edges); // } // } // // Make the Default behaviour adding new Nodes // this.network.on('doubleClick', (params) => { // if (this.options.enableEditing){ // // Test if no Element was Selected // if (params.nodes.length === 0 && params.edges.length === 0) { // // If so, just add the new Element // _this.addNode(params.pointer.canvas); // } else { // editItemIfPossible(params); // } // } // }); // this.network.on('oncopy', event => { // try { // } catch (e) { // } // }); // this.network.on('onpaste', async event => { // console.log(event) // try { // const data = await _this.readDataFromClipboard() // _this.paste(parseWithFunctions(data), _this.network.network.DOMtoCanvas(_this.mousePos), false); // _this.zemaService.showToast('success', 'Clipboard', 'Copied Content to Clipboard'); // } catch (e) { // _this.zemaService.showToast('warning', 'Clipboard', 'Failed Copying To Clipboard'); // } // }); // let _mousePosOnContextMenu = this.mousePos; // let rightClickActions: IRigthClickActions> = []; // const _contextHandler = (params) => { // let nodeID = null; // if (params.nodes.length === 1) { // nodeID = _this.network.network.getNodeAt(params.pointer.DOM); // if (!nodeID) // nodeID = params.nodes[0]; // } // let edgeID = null; // if (params.edges.length === 1) { // edgeID = params.edges[0]; // } // // If nothing is selected, try to select the element, // // which is underneath the Pointer // if (params.nodes.length === 0) { // nodeID = _this.network.network.getNodeAt(params.pointer.DOM); // // Test if a Node is underneath the Pointer // if (nodeID) { // // Make shure the Element is selected // _this.network.network.selectNodes([nodeID]); // } // } // // If nothing is selected, try to select the element, // // which is underneath the Pointer // if (params.edges.length === 0) { // edgeID = _this.network.network.getEdgeAt(params.pointer.DOM); // // Test if a Node is underneath the Pointer // if (edgeID) { // // Make shure the Element is selected // _this.network.network.selectEdges([edgeID]); // } // } // // Test whether multiple Nodes or just a single Node is selected. // if (nodeID) { // // A single Node is selected // rightClickActions = _this.contextMenuGenerator.node(_this, params.pointer.DOM, nodeID); // } else if (params.nodes.length > 1) { // // The Default Right-Clickmenu must be selected // rightClickActions = _this.contextMenuGenerator.default(_this, params.pointer.DOM, params.nodes, params.edges); // } else if (edgeID) { // // Only 1 Edge is selected // rightClickActions = _this.contextMenuGenerator.edge(_this, params.pointer.DOM, edgeID); // } else { // rightClickActions = _this.contextMenuGenerator.background(_this, params.pointer.DOM); // } // } // this.network.on('oncontext', (params) => { // if (_this.options.enableContextMenu){ // // Make shure the Context-Menu is only Opened, if // // The event isnt prevented // if (params.event.prevent) { // return; // } // // Call creating the Menu Entries. // _contextHandler(params) // // Decide, whether an Element is selected or not // params.event.preventDefault(); // // Store the current Position // _mousePosOnContextMenu = _this.mousePos; // // Check after a View seconds whether the Mouse has been move // // setTimeout(_contextHandler, 200, params); // setTimeout(() => { // if (Math.abs(_this.mousePos.x - _mousePosOnContextMenu.x) < 5 && Math.abs(_this.mousePos.y - _mousePosOnContextMenu.y) < 5) { // if (rightClickActions.length > 0) { // _this.layout.openContextMenu(params.event, rightClickActions); // } else { // _this.network.network.unselectAll(); // } // } else { // rightClickActions = []; // } // }, 200) // } // }); // // Make shure the Sidepanel is working correctly // this.network.on('select', (params) => { // if (_this.options.enableEditing){ // if (_this.options.editOnSelect && _this.options.hidePanelOnDeselect && params.nodes.length === 0 && params.edges.length === 0){ // _this.layout.panels.right.hide(); // } else if (_this.options.editOnSelect){ // editItemIfPossible(params); // } // } // }); // enableClusterPreview(this); // this.network.addNode(this._nodes); // this.network.addEdge(this._edges); // editor.resize(); // } public initializeGraph(){ const _this = this; // Assign a default Network: const networkToRender = this.props.network || { nodes: [], edges: [], clusters: [], version: "20200713", name: "new", id: "new" } // Initally store the Network to Render this.tabs[networkToRender.id] = networkToRender; // Define the Options const visjsOptions = this.props.options || generateGraphOptions(); const settings: GraphicalEditorComponentProps = { allowUserSelect: true, network: networkToRender, onItemSelected(item){ }, onMount(ref, layout){ // Define the Network. _this.network = new UndoRedoGraph(ref, visjsOptions); // Load the Networ: _this.network.loadData(networkToRender); _this.layout = layout; // Resize the Network. // _this.network.resize(); }, onResize(){ if (_this.network) { _this.network.resize(); } }, async onNewTab(){ // Function to create a new tab. try { const label = await _this.getDialogData({ header: 'Enter Network-Name', content: { component: 'DynamicForm', props: { schema: { type: "string", description: "Name of the Element" } } } }) const id = Date.now().toString(); // const label = id; _this.tabs[id] = { nodes: [], edges: [], clusters: [], version: "20200713", name: label, id: id }; return { id, label, delteable: true } } catch (error) { return false; } }, async onTabSelect(oldTabId: string, newTabId: string){ // Contains the Main Logic for selecting another tab // Therefore _this.tabs[oldTabId] = _this.network.getData(); // Use the content of the new Tab as rendering: _this.network.loadData(_this.tabs[newTabId]); return true; }, async onTabDelete(tabId: string) { // A Tab should be deleted try { const saveData = await _this.getDialogData({ header: 'Close Tab', content: { component: (props) => { return (<>

You are about to close the tab. Do you want to save changes?

{' '} {' '} {' '} ) }, props: {} } }); // The variable save Data contains the info, whether the user wants to save or discard the changes return true; } catch(error) { return false } }, async onNoTabSelected() { // Nothing to Display! // Use the content of the new Tab as rendering: _this.network.loadData({ nodes: [], edges: [], clusters: [], version: "20200713", name: 'empty', id: 'empty' }); }, onUnmount(){ if (_this.network){ // Destroy the Network. _this.network.destroy(); } }, tabs: { active: 'start', items: [{ id: networkToRender.id, label: networkToRender.name, delteable: true }], allowNewTabs: true } }; return settings; } public getDialogData(settings: ModalSettings) { const _this = this; let close: () => void; return new Promise((resolve, reject) => { // Adapt the Settings: const _onSubmit = rgetattr<(data) => Promise>(settings, 'content.props.onSubmit', async (data) => { }); const _onCancel = rgetattr<(err) => Promise>(settings, 'content.props.onCancel', async (err) => { }); const onSubmit = async (data) => { close(); _onSubmit(data); resolve(data); } const onCancel = async (data) => { close(); _onCancel(data); reject(data); } rsetattr(settings, 'content.props.onSubmit', onSubmit); rsetattr(settings, 'content.props.onCancel', onCancel); close = this.layout.openPopup(settings) }); } public requestRerender() { this.setState({ update: null }) } public componentDidMount() { const _this = this; console.log('editor',this.props.children) } public render() { const settings = this.initializeGraph(); const template: { groupName: string, items: { label: string, keywords: string, item: string, id: string | number, }[] }[] = [ { groupName: 'GRP 1', items: [ { item: 'hello', keywords: '1', label: 'Hello', id: 1, }, { item: 'world', keywords: '2', label: 'world', id: 2, }, { item: '!', keywords: '3', label: '!', id: 3, } ] }, { groupName: 'GRP 2', items: [ { item: 'world', keywords: '4', label: 'world', id: 4, } ] } ]; return ( <> ) } } export default GraphicalEditorComponent;