nope/resources/ui/graph/editor.tsx

856 lines
33 KiB
TypeScript
Raw Normal View History

2020-10-25 20:14:51 +00:00
import * as React from 'react';
2020-10-29 18:20:42 +00:00
import { Button, ListGroup } from 'react-bootstrap';
import { v4 as generateID } from 'uuid';
import { deepClone, rgetattr, rsetattr } from '../../../lib/helpers/objectMethods';
2020-10-30 18:30:59 +00:00
import DynamicLayout, { IDynamicLayoutProps } from '../layout/dynamicLayout';
2020-10-29 18:20:42 +00:00
import { ILayout } from '../layout/interfaces/ILayout';
2020-10-30 18:30:59 +00:00
import { ISelection } from '../layout/interfaces/ISelection';
2020-10-29 18:20:42 +00:00
import { ITab } from '../layout/interfaces/ITab';
2020-10-30 18:30:59 +00:00
import Layout from '../layout/layout';
2020-10-25 20:14:51 +00:00
// Import Addons:
import { makeMeMultiSelect } from './addons/selectionbox.extension';
2020-10-29 18:20:42 +00:00
import { generateDefaultSelection } from './defaults/default.elements';
import { generateGraphOptions } from './defaults/default.graph-options';
import { defaultHotkeys } from './defaults/default.hotkeys';
import { defaultToolbar } from './defaults/default.toolbar';
// Graph related imports:
import { UndoRedoGraph } from './graph';
2020-10-30 18:30:59 +00:00
import { adaptIDS, adaptPositions, getSelectedElements } from './helpers/data.handlers';
2020-10-29 18:20:42 +00:00
import { IBaseEdgeOptions } from './interfaces/IBaseEdgeOptions';
import { IBaseNodeOptions } from './interfaces/IBaseNodeOptions';
2020-10-30 18:30:59 +00:00
import { IBasicTemplate } from './interfaces/IBasicTemplate';
2020-10-29 18:20:42 +00:00
import { IEdgeTypeDefinition } from './interfaces/IEdgeTypeDefinition';
import { IUndoRedoGraph } from './interfaces/IGraph';
import { IGraphCallbackData } from './interfaces/IGraphCallbackData';
import { IGraphTemplate } from './interfaces/IGraphTemplate';
import { DEFAULT_NETWORK, INetwork } from './interfaces/INetwork';
import { IVisjsOptions } from './interfaces/IVisjsOptions';
2020-10-30 18:30:59 +00:00
import Selection, { SelectionProps } from '../layout/selection';
import DynamicRenderer from '../dynamic/dynamicRenderer';
import TabEntry, { ITabProps } from '../layout/tabs';
import { IJsonSchema } from '../../types/IJSONSchema';
import { ITabEntry } from '../layout/interfaces/ITabEntry';
import { parseWithFunctions, stringifyWithFunctions } from '../../../lib/helpers/jsonMethods';
import { readDataFromClipboard, writeToClipboard } from '../helpers/clipboard';
import { Network, __esModule } from '../../visjs/vis';
import { DH_UNABLE_TO_CHECK_GENERATOR } from 'constants';
import {getCurrentThemeColors} from '../helpers/colors';
import {generateColors } from './helpers/color.theme';
export interface GraphicalEditorComponentProps<N, E> {
2020-10-29 18:20:42 +00:00
graphOptions?: IVisjsOptions;
network?: INetwork<N, E>;
addEdgeCallback?: (edgeData: E, callback: (edgeData: E) => void) => void;
editOnSelect?: boolean;
editOnChange?: boolean;
parseFunctions?: boolean;
enableContextMenu?: boolean;
enableEditing?: boolean;
hidePanelOnDeselect?: boolean;
hideToolbar?: boolean;
hideRightPanel?: boolean;
allowSelfConnection?: boolean;
allowDoubleConnections?: boolean;
edgeTypes?: IEdgeTypeDefinition<N, E>[];
showEdgeSelction?: boolean
2020-10-25 20:14:51 +00:00
}
export interface GraphicalEditorComponentState {
2020-10-26 07:39:34 +00:00
update: null
2020-10-25 20:14:51 +00:00
}
2020-10-29 18:20:42 +00:00
class GraphicalEditorComponent<N, E> extends React.Component<GraphicalEditorComponentProps<N, E>, GraphicalEditorComponentState> {
2020-10-25 20:14:51 +00:00
/**
* The Element containing the Network.
*/
2020-10-30 18:30:59 +00:00
public Network: IUndoRedoGraph<N, E>;
public LayoutHandler: ILayout;
public TabHandler: ITabEntry;
2020-10-29 18:20:42 +00:00
public graphOptions: IVisjsOptions;
2020-10-30 18:30:59 +00:00
public theme: {
colors: {
primary: string;
secondary: string;
success: string;
info: string;
warning: string;
danger: string;
light: string;
dark: string;
};
font: {
size: number;
type: string;
};
}
2020-10-25 20:14:51 +00:00
constructor(props) {
super(props);
2020-10-30 18:30:59 +00:00
this.theme = getCurrentThemeColors();
2020-10-25 20:14:51 +00:00
}
2020-10-29 18:20:42 +00:00
public tabs: { [index: string]: INetwork<N, E> } = {};
2020-10-25 20:14:51 +00:00
2020-10-29 18:20:42 +00:00
/**
* Function to add the Addons of the Graph:
* @param network
*/
public addGraphAddons(network: IUndoRedoGraph<N, E>) {
2020-10-25 20:14:51 +00:00
makeMeMultiSelect(network);
}
2020-10-29 18:20:42 +00:00
/**
* Function to add an Edge.
* @param edgeData
* @param callback
*/
protected async _addEdge(edgeData: IBaseEdgeOptions, callback: (edge: IBaseEdgeOptions) => void) {
// Defaulty prevent self connections.
if (this.props.allowSelfConnection || edgeData.from !== edgeData.to) {
let addEdge = true;
// If multiple Connections from one node to the same node are
// for bidden, test if there already exists such an edge.
if (!this.props.allowDoubleConnections) {
2020-10-30 18:30:59 +00:00
this.Network.edges.forEach((edge) => {
2020-10-29 18:20:42 +00:00
if (edge.from === edgeData.from && edge.to === edgeData.to) {
addEdge = false;
}
});
}
// If adding an Edge is allowed => Add the Edge.
if (addEdge) {
// Manually add an ID to the Edge
edgeData.id = generateID();
// Extract the From and To Node.
2020-10-30 18:30:59 +00:00
const from = this.Network.getNode(edgeData.from);
const to = this.Network.getNode(edgeData.to);
2020-10-29 18:20:42 +00:00
const _this = this;
const _addEdge = (type: IEdgeTypeDefinition<N, E>) => {
if (typeof type.customAddFunction == 'function') {
2020-10-30 18:30:59 +00:00
type.customAddFunction(from, to, _this.Network);
2020-10-29 18:20:42 +00:00
} else {
// Define the Edge Object
let edge = Object.assign(deepClone(type.visual), {
from: edgeData.from,
to: edgeData.to
});
// Add the Edge.
callback(edge);
}
}
switch (edgeData.type) {
case 'automatic':
// Search for Possible Connection Types.
const possibleEdges: IEdgeTypeDefinition<N, E>[] = [];
2020-10-30 18:30:59 +00:00
for (const edgeType of this.props.edgeTypes || []) {
2020-10-29 18:20:42 +00:00
if ((edgeType.fromTypes.includes(from.type) || edgeType.fromTypes.length === 0) && (edgeType.toTypes.includes(to.type) || edgeType.toTypes.length === 0)) {
possibleEdges.push(edgeType);
}
}
// Test if Edges has been found
switch (possibleEdges.length) {
case 0:
2020-10-30 18:30:59 +00:00
this.LayoutHandler.showToast('No Matching Edge Type has been found. Please select the corresponding Edge Type manually', 'info');
2020-10-29 18:20:42 +00:00
return;
case 1:
// Define the New Edge.
return _addEdge(possibleEdges[0]);
default:
if (this.props.showEdgeSelction) {
// Create a Popup, on which the corresponding edge can be selected:
try {
2020-10-30 18:30:59 +00:00
const type: IEdgeTypeDefinition<N, E> = await this.LayoutHandler.getDialogData({
2020-10-29 18:20:42 +00:00
content: {
props: {
possibleEdges
},
component(props: {
possibleEdges: IEdgeTypeDefinition<N, E>[],
onSubmit: (data: IEdgeTypeDefinition<N, E>) => void;
onCancel: (error: any) => void
}) {
return (
<>
Select a possible edge by clicking on the item
<ListGroup>
{
props.possibleEdges.map((item, idx) => {
<ListGroup.Item key={idx} onClick={_ => props.onSubmit(item)}>{item.type}</ListGroup.Item>
})
}
</ListGroup>
<Button variant="danger" onClick={e => props.onCancel(e)}>Cancel </Button>
</>
)
},
},
header: 'Please select an edge-type!',
closeButton: true
});
// Add the Edge itself.
return _addEdge(type);
} catch (e) {
// The User has aborted the selection.
return;
}
}
2020-10-30 18:30:59 +00:00
this.LayoutHandler.showToast('Mulitple Valid Edge Type has been found. Please select the corresponding Edge Type manually', 'info');
2020-10-29 18:20:42 +00:00
}
return;
default:
2020-10-30 18:30:59 +00:00
for (const type of this.props.edgeTypes || []) {
2020-10-29 18:20:42 +00:00
if ((edgeData.type === type.type) && (type.fromTypes.includes(from.type) || type.fromTypes.length === 0) && (type.toTypes.includes(to.type) || type.toTypes.length === 0)) {
// Quit the Method
return _addEdge(type);
}
}
2020-10-30 18:30:59 +00:00
this.LayoutHandler.showToast('You are not allowed to connect the desired Nodes with this edges', 'error');
2020-10-29 18:20:42 +00:00
break;
}
} else {
2020-10-30 18:30:59 +00:00
this.LayoutHandler.showToast('Double Connections not allowed! You can not connect these elements again', 'error');
2020-10-29 18:20:42 +00:00
}
} else {
2020-10-30 18:30:59 +00:00
this.LayoutHandler.showToast('Self-Connections forbidden! You can not connect elements to itself', 'error');
2020-10-29 18:20:42 +00:00
}
}
2020-10-30 18:30:59 +00:00
protected async _editNodes(_template: IBasicTemplate<N,E>, mode: 'add' | 'update' = 'update') {
// Defaulty prevent self connections.
if (_template?.nodes.length > 0) {
try {
if (_template.nodes[0].editorComponentSelector && false){
} else {
// Open up the corresponding Node Panel:
_template.nodes[0] = await this.LayoutHandler.getDialogData({
content: {
component: 'DynamicForm',
props: {
schema: {
type: 'object',
properties:{
label: {
description: 'Label of the Node',
type: 'string'
},
title: {
description: 'Tooltip, presented on Hovering',
type: 'string'
},
// color: {
// description: 'Color of the Node.',
// type: 'string'
// }
},
required: [
"label"
]
} as IJsonSchema,
uiSchema: {
// color: {
// "ui:widget": "color"
// }
},
data: Object.assign(generateColors(_template.nodes[0].shape, this.theme), _template.nodes[0])
}
}
});
}
switch (mode) {
case 'add':
this.Network.addNode(_template.nodes);
this.Network.addEdge(_template.edges);
break;
case 'update':
this.Network.updateNode(_template.nodes);
this.Network.updateEdge(_template.edges);
break;
}
// Tabs
this.tabs[this.TabSettings.tabs.active] = this.Network.getData();
} catch(error) {
console.log(error)
}
} else {
this.LayoutHandler.showToast('Please Select a Node Before.', 'error');
}
}
public initEditor(ref: React.RefObject<any>) {
2020-10-29 18:20:42 +00:00
if (process.browser){
const _this = this;
// Based on the Provided Options =>
// select the corresponding graph options,
// either use th provided one or use the
// default options.
if (this.props.graphOptions) {
this.graphOptions = this.props.graphOptions;
} else {
this.graphOptions = generateGraphOptions();
}
if (!rgetattr(this.graphOptions, 'manipulation.enabled', false)) {
rsetattr(this.graphOptions, 'manipulation.enabled', false);
}
2020-10-30 18:30:59 +00:00
// Generate the Default Callback for adding an Edge
rsetattr(
this.graphOptions,
'manipulation.addEdge',
(data,callback) => {
_this._addEdge(data,callback).catch(console.error)
}
);
// Generate the Default Callback for adding a Node
rsetattr(
this.graphOptions,
'manipulation.addNode',
(data,callback) => {
_this._addNode(data).catch(console.error)
}
);
2020-10-29 18:20:42 +00:00
// Create the Network
2020-10-30 18:30:59 +00:00
this.Network = new UndoRedoGraph(ref, this.graphOptions);
2020-10-29 18:20:42 +00:00
// Add a new item if possible.
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];
2020-10-30 18:30:59 +00:00
if (_this.Network.network.isCluster(nodeID)) {
2020-10-29 18:20:42 +00:00
// Uncluster the Nodes
2020-10-30 18:30:59 +00:00
_this.Network.network.openCluster(nodeID);
2020-10-29 18:20:42 +00:00
} else {
// Open up the Settings Tab
2020-10-30 18:30:59 +00:00
const nodes = getSelectedElements(_this.Network, false);
_this._editNodes({
edges: [],
nodes,
type: 'element'
});
2020-10-29 18:20:42 +00:00
}
} else if (params.edges.length === 1) {
// A Node was Selected. Figure out, whether it is a cluster or not.
2020-10-30 18:30:59 +00:00
const edges = _this.Network.data.edges.get(params.edges[0]);
2020-10-29 18:20:42 +00:00
_this.updateEdges(edges);
}
}
2020-10-30 18:30:59 +00:00
if (!this.Network){
throw Error('Something went wrong')
}
2020-10-29 18:20:42 +00:00
// Make the Default behaviour adding new Nodes
2020-10-30 18:30:59 +00:00
this.Network.on('doubleClick', (params) => {
if (_this.props.enableEditing || true) {
2020-10-29 18:20:42 +00:00
// Test if no Element was Selected
if (params.nodes.length === 0 && params.edges.length === 0) {
// If so, just add the new Element
2020-10-30 18:30:59 +00:00
_this._addNode(params.pointer.canvas);
2020-10-29 18:20:42 +00:00
} else {
editItemIfPossible(params);
}
}
});
2020-10-30 18:30:59 +00:00
this.Network.on('oncopy', event => {
2020-10-29 18:20:42 +00:00
try {
2020-10-30 18:30:59 +00:00
_this.LayoutHandler.showToast('Copied');
writeToClipboard(stringifyWithFunctions(_this.createTemplateOfSelectedElements()))
2020-10-29 18:20:42 +00:00
} catch (e) {
}
});
2020-10-30 18:30:59 +00:00
this.Network.on('onpaste', async event => {
2020-10-29 18:20:42 +00:00
try {
2020-10-30 18:30:59 +00:00
const data = await readDataFromClipboard();
const mousePosition = _this.Network.network.DOMtoCanvas({
x: _this.LayoutHandler.currentMousePosition.offsetX,
y: _this.LayoutHandler.currentMousePosition.offsetY
})
_this.paste(parseWithFunctions(data), mousePosition, false);
_this.LayoutHandler.showToast('Pasted');
2020-10-29 18:20:42 +00:00
} catch (e) {
2020-10-30 18:30:59 +00:00
_this.LayoutHandler.showToast('Failed Pasting','error');
console.error(e)
2020-10-29 18:20:42 +00:00
}
2020-10-30 18:30:59 +00:00
});
2020-10-29 18:20:42 +00:00
// // Make shure the Sidepanel is working correctly
2020-10-30 18:30:59 +00:00
this.Network.on('select', (params) => {
// if (_this.graphOptions.enableEditing){
// if (_this.graphOptions.editOnSelect && _this.graphOptions.hidePanelOnDeselect && params.nodes.length === 0 && params.edges.length === 0){
// _this.layout.panels.right.hide();
// } else if (_this.graphOptions.editOnSelect){
// editItemIfPossible(params);
// }
// }
});
2020-10-29 18:20:42 +00:00
// enableClusterPreview(this);
// this.network.addNode(this._nodes);
// this.network.addEdge(this._edges);
// editor.resize();
2020-10-30 18:30:59 +00:00
console.log('TABS',this.tabs)
this.Network.loadData(this.tabs[this.TabSettings?.tabs?.active] || DEFAULT_NETWORK())
2020-10-29 18:20:42 +00:00
// Add all Addons as required.
2020-10-30 18:30:59 +00:00
this.addGraphAddons(this.Network);
2020-10-29 18:20:42 +00:00
}
2020-10-30 18:30:59 +00:00
return this.Network;
}
/**
* Function, which is used to provide a
*/
public createTemplateOfSelectedElements() {
const selected: IBasicTemplate<N,E> = {
nodes: [],
edges: [],
type: 'element'
};
const selection = this.Network.network.getSelection(true);
selected.nodes = deepClone(this.Network.nodes.filter(item => selection.nodes.indexOf(item.id) !== -1));
selected.edges = deepClone(this.Network.edges.filter(item => selection.edges.indexOf(item.id) !== -1));
return selected;
}
public paste(
template: IBasicTemplate<N,E>,
position: {
x: number,
y: number
},
useExistingNodesForEdges: boolean) {
const data = adaptPositions(adaptIDS(template, useExistingNodesForEdges), position);
this.Network.addNode(data.nodes);
this.Network.addEdge(data.edges);
2020-10-29 18:20:42 +00:00
}
/**
* Function to Update the Data of a Node.
* @param selection The Selected Node.
*/
public async updateNode(selection: Array<IBaseNodeOptions<N>>) {
const _self = this;
/** Extract the Component, which should be used in the Prompt */
let componentSelector = 'default';
if (selection.length > 0 && selection[0].editorComponentSelector) {
componentSelector = selection[0].editorComponentSelector;
}
// const comp = this.editPanelDict.nodes[componentSelector];
// if (comp) {
// /** Open the Window, with the Edit-Prompt */
// this.openEditInterface(this.editPanelDict.nodes[componentSelector], {
// inputTemplate: {
// nodes: selection,
// edges: [],
// type: 'elements'
// },
// },
// 'Edit Node',
// (data) => {
// _self.network.updateNode(data.template.nodes);
// });
// } else {
// this.layout.showToast('Editor is Trying to open an Unkown Edit-Component', 'error');
// }
}
2020-10-30 18:30:59 +00:00
protected async _addNode(pos: { x: number, y: number }) {
// Defaulty prevent self connections.
if (this._template?.nodes.length > 0) {
try {
// Adapt its IDs. (Creates a Copy)
let _template = adaptIDS(this._template);
// Adapt its Position
_template = adaptPositions(_template, pos);
// Remove the Initial Label:
delete _template.nodes[0].label;
await this._editNodes(_template, 'add');
} catch(error) {
console.error(error)
}
} else {
this.LayoutHandler.showToast('Please Select a Node Before.', 'error');
}
2020-10-29 18:20:42 +00:00
}
2020-10-30 18:30:59 +00:00
public EditorSettings: GraphicalEditorComponentProps<N, E>
public LayoutSettings: IDynamicLayoutProps<IGraphCallbackData<N,E>>
public TabSettings: ITabProps;
2020-10-29 18:20:42 +00:00
2020-10-30 18:30:59 +00:00
protected _template: IBasicTemplate<N,E>
public selectTemplate(template: IBasicTemplate<N,E>){
this._template = template;
}
2020-10-29 18:20:42 +00:00
public initializeGraph() {
2020-10-25 20:14:51 +00:00
const _this = this;
// Assign a default Network:
2020-10-30 18:30:59 +00:00
const networkToRender: INetwork<N,E> = this.props.network || this.tabs[this.TabSettings?.tabs?.active] || DEFAULT_NETWORK();
2020-10-29 18:20:42 +00:00
2020-10-25 20:14:51 +00:00
// Initally store the Network to Render
this.tabs[networkToRender.id] = networkToRender;
2020-10-30 18:30:59 +00:00
this.TabSettings = {
onMount(item){
_this.TabHandler = item;
2020-10-25 20:14:51 +00:00
},
2020-10-29 18:20:42 +00:00
async onNewTab() {
2020-10-25 20:14:51 +00:00
// Function to create a new tab.
2020-10-26 07:39:34 +00:00
try {
2020-10-30 18:30:59 +00:00
if (_this.TabHandler.tabs.length == 0) {
const tab = {
delteable: true,
id: generateID(),
label: 'Unsaved Content'
};
await _this.TabHandler.createTab(tab);
// Store the Content of the tab:
_this.tabs[tab.id] = _this.Network.getData();
}
const label = await _this.LayoutHandler.getDialogData({
2020-10-26 07:39:34 +00:00
header: 'Enter Network-Name',
content: {
component: 'DynamicForm',
props: {
schema: {
type: "string",
description: "Name of the Element"
}
}
}
})
2020-10-29 18:20:42 +00:00
2020-10-26 07:39:34 +00:00
const id = Date.now().toString();
// const label = id;
2020-10-29 18:20:42 +00:00
_this.tabs[id] = DEFAULT_NETWORK();
2020-10-26 07:39:34 +00:00
return {
id,
label,
delteable: true
}
} catch (error) {
return false;
2020-10-25 20:14:51 +00:00
}
},
2020-10-29 18:20:42 +00:00
async onTabSelect(oldTab: ITab, newTab: ITab) {
2020-10-30 18:30:59 +00:00
if (_this.Network) {
// Contains the Main Logic for selecting another tab
// Therefore
if (oldTab) {
_this.tabs[oldTab.id] = _this.Network.getData();
}
// Use the content of the new Tab as rendering:
_this.Network.loadData(_this.tabs[newTab.id]);
2020-10-29 18:20:42 +00:00
}
return true;
2020-10-25 20:14:51 +00:00
},
2020-10-29 18:20:42 +00:00
async onTabDelete(tab: ITab, force: boolean) {
2020-10-25 20:14:51 +00:00
// A Tab should be deleted
2020-10-30 18:30:59 +00:00
if (force) {
2020-10-29 18:20:42 +00:00
return true;
}
2020-10-26 08:54:30 +00:00
try {
2020-10-30 18:30:59 +00:00
const saveData = await _this.LayoutHandler.getDialogData<boolean>({
2020-10-26 08:54:30 +00:00
header: 'Close Tab',
content: {
component: (props) => {
return (<>
<p>
2020-10-29 18:20:42 +00:00
You are about to close the tab "{tab.label}". Do you want to save changes?
2020-10-30 18:30:59 +00:00
</p>
2020-10-29 18:20:42 +00:00
<Button variant="success" onClick={_ => props.onSubmit(true)}>Yes</Button>{' '}
<Button variant="danger" onClick={_ => props.onSubmit(false)}>No</Button>{' '}
<Button variant="secondary" onClick={_ => props.onCancel(new Error("Canceled"))}>Cancel</Button>{' '}
2020-10-26 08:54:30 +00:00
</>)
},
props: {}
}
});
// The variable save Data contains the info, whether the user wants to save or discard the changes
return true;
2020-10-29 18:20:42 +00:00
} catch (error) {
2020-10-26 08:54:30 +00:00
return false
}
2020-10-25 20:14:51 +00:00
},
async onNoTabSelected() {
// Nothing to Display!
2020-10-30 18:30:59 +00:00
if (_this.Network) {
console.log('No Tab')
// Use the content of the new Tab as rendering:
_this.Network.loadData(DEFAULT_NETWORK());
}
2020-10-25 20:14:51 +00:00
},
2020-10-30 18:30:59 +00:00
async onTabEdit(tab){
const label = await _this.LayoutHandler.getDialogData<string>({
header: 'Enter Network-Name',
content: {
component: 'DynamicForm',
props: {
schema: {
type: "string",
description: "Name of the Element"
},
data: tab.label
}
}
});
tab.label = label;
return tab;
2020-10-25 20:14:51 +00:00
},
tabs: {
2020-10-30 18:30:59 +00:00
active: '-1',
allowNewTabs: true,
2020-10-29 18:20:42 +00:00
items: [],
2020-10-30 18:30:59 +00:00
}
};
this.LayoutSettings = {
components: [
{
component() {
const graph = React.useRef(null);
// Similar to componentDidMount and componentDidUpdate:
// Createa a Function, that if mounted defines the Network.
React.useEffect(() => {
// Define the Network.
_this.initEditor(graph)
// Return a Function, that will destroy the network.
return () => {
_this.tabs[_this.TabSettings.tabs.active] = _this.Network.getData();
// Destroy the Network.
_this.Network.destroy();
_this.Network = null;
}
});
2020-10-26 07:39:34 +00:00
2020-10-30 18:30:59 +00:00
return (<>
{React.createElement(TabEntry, { ..._this.TabSettings })}
<div ref={graph} style={{ height: '100%' }} onMouseEnter={e => {
// Enable Hotkeys
_this.LayoutHandler.hotkeysEnabled = true;
}} onMouseLeave={e => {
// Disable Hotkeys
_this.LayoutHandler.hotkeysEnabled = false;
}}></div>
</>
)
},
gridSettings: {
h: 5,
w: 10,
x: 5,
y: 0,
minW: 3,
minH: 3,
},
id: 'main',
label: 'Editor',
props: {
},
visible: true,
bg: 'light',
text: 'dark'
},
{
component(props: {selection: ISelection<IBasicTemplate<N,E>>}){
return (<Selection<IBasicTemplate<N,E>> selection={props.selection} allowUserSelect={true} onItemSelected={template =>_this.selectTemplate(template)}></Selection>)
},
gridSettings: {
h: 5,
w: 4,
x: 0,
y: 0,
maxW: 5,
},
id: 'selection',
label: 'Selection',
props: {
selection: generateDefaultSelection()
},
hideable: true,
bg:'light',
visible: true,
text: 'dark',
showLabel: true,
},
{
component(){
return (<>Minimap</>)
},
gridSettings: {
h: 3,
w: 3,
x: 0,
y: 5,
maxW: 5,
},
id: 'minimap',
label: 'Minimap',
props: {},
hideable: true,
bg:'light',
visible: false,
text: 'dark',
showLabel: true,
},
{
component(){
return (<>Preview</>)
},
gridSettings: {
h: 3,
w: 3,
x: 3,
y: 5,
},
id: 'preview',
label: 'Preview',
props: {},
hideable: true,
bg:'light',
visible: false,
text: 'dark'
},
],
layoutSettings: {
width: process.browser ? window.innerWidth : 1920,
autoSize: false,
preventCollision: false,
cols: 15,
compactType: 'horizontal',
onLayoutChange(){
if (_this.Network) {
_this.Network.resize();
}
}
},
generateData(){
2020-10-29 18:20:42 +00:00
// Get the Current Selection.
2020-10-30 18:30:59 +00:00
const selection = _this.Network.network.getSelection(true);
2020-10-26 07:39:34 +00:00
2020-10-30 18:30:59 +00:00
const ret: IGraphCallbackData<N,E> = {
network: _this.Network,
layout: _this.LayoutHandler,
2020-10-29 18:20:42 +00:00
selectedEdges: selection.edges,
2020-10-30 18:30:59 +00:00
selectedNodes: selection.nodes,
tabs: _this.TabHandler
};
Object.defineProperty(ret, 'network', {
get(){
return _this.Network;
},
});
Object.defineProperty(ret, 'layout', {
get(){
return _this.LayoutHandler;
},
});
return ret
},
onResize() {
if (_this.Network) {
_this.Network.resize();
2020-10-29 18:20:42 +00:00
}
},
2020-10-30 18:30:59 +00:00
onMount(layout){
_this.LayoutHandler = layout;
},
2020-10-29 18:20:42 +00:00
toolbar: defaultToolbar(),
hotkeys: defaultHotkeys()
2020-10-30 18:30:59 +00:00
}
2020-10-26 07:39:34 +00:00
2020-10-30 18:30:59 +00:00
return this.LayoutSettings;
2020-10-26 07:39:34 +00:00
}
public requestRerender() {
this.setState({
update: null
})
}
public componentDidMount() {
const _this = this;
}
public render() {
2020-10-30 18:30:59 +00:00
this.initializeGraph();
return React.createElement(DynamicLayout, {... this.LayoutSettings});
2020-10-25 20:14:51 +00:00
}
}
export default GraphicalEditorComponent;