nope/resources/ui/graph/editor.tsx
2020-10-26 08:39:34 +01:00

498 lines
17 KiB
TypeScript

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';
export interface GraphicalEditorComponentProps<N,E> extends Partial<LayoutProps<any>>{
options?: IVisjsOptions
network?: INetwork<N,E>
}
export interface GraphicalEditorComponentState {
update: null
}
class GraphicalEditorComponent<N,E> extends React.Component<GraphicalEditorComponentProps<N,E>, GraphicalEditorComponentState> {
/**
* The Element containing the Network.
*/
public network: IUndoRedoGraph<N,E>;
public layout: Layout<any>;
constructor(props) {
super(props);
}
public tabs: {[index: string]: INetwork<N,E>} = {};
public addGraphAddons(network: IUndoRedoGraph<N,E>){
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<N,E,D, BaseGraphEditor<N,E,D>>({
// 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<ICallbackData<N,E>> = [];
// 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<N,E> = {
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: [{
id: 'test',
shape: 'ellipse',
label
}],
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
return true;
},
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<T = any>(settings: ModalSettings) {
const _this = this;
let close: () => void;
return new Promise<T>((resolve, reject) => {
// Adapt the Settings:
const _onSubmit = rgetattr<(data) => Promise<void>>(settings, 'content.props.onSubmit', async (data) => { });
const _onCancel = rgetattr<(err) => Promise<void>>(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 (
<>
<Layout
selection={template}
allowUserSelect
onMount={settings.onMount}
onResize={settings.onResize}
onNewTab={settings.onNewTab}
onTabSelect={settings.onTabSelect}
onUnmount={settings.onUnmount}
onNoTabSelected={settings.onNoTabSelected}
onTabDelete={settings.onTabDelete}
tabs={settings.tabs}
></Layout>
</>
)
}
}
export default GraphicalEditorComponent;