2020-10-29 18:20:42 +00:00
/ * *
* @author Martin Karkowski
* @email m . karkowski @zema . de
* @create date 2019 - 04 - 29 11 :30 : 22
* @modify date 2020 - 10 - 29 12 :44 : 40
* @desc [ description ]
* /
import { faArrowLeft , faArrowRight , faClone , faDownload , faEyeSlash , faFile , faPlusSquare , faPrint , faUpload } from '@fortawesome/free-solid-svg-icons' ;
import React from 'react' ;
import { Button , Form } from 'react-bootstrap' ;
import { IToolbar } from '../../layout/toolbar' ;
2020-10-30 18:30:59 +00:00
import { IGraphCallbackData } from '../interfaces/IGraphCallbackData' ;
import { writeToClipboard } from '../../helpers/clipboard'
2020-10-29 18:20:42 +00:00
async function openFile ( data ) {
return await data . layout . getDialogData ( {
content : {
props : { } ,
component ( props : {
onSubmit : ( data : string ) = > void ;
onCancel : ( error : any ) = > void
} ) {
const showFile = ( e ) = > {
e . preventDefault ( )
const reader = new FileReader ( )
reader . onload = async ( event ) = > {
const text = ( ( event . target ) . result as string ) ;
// Use the Submit Function
props . onSubmit ( text ) ;
} ;
// Tell the Reader to load the File.
reader . readAsText ( e . target . files [ 0 ] )
}
return (
< >
< p >
Select the File :
< / p >
< Form >
< Form.File
id = "file"
label = "Custom file input"
custom
onChange = { ( e ) = > showFile ( e ) }
/ >
< / Form >
< / >
)
} ,
} ,
header : 'Do you want to discard all unstored changes?' ,
} ) ;
}
/ * *
* Function to extract the Default Toolbar Menu
* /
2020-10-30 18:30:59 +00:00
export function defaultToolbar < N = any , E = any > ( ) {
2020-10-29 18:20:42 +00:00
2020-10-30 18:30:59 +00:00
const toolbar : IToolbar < IGraphCallbackData < N , E > > = {
2020-10-29 18:20:42 +00:00
items : [
{
type : 'action' ,
label : '' ,
icon : faPlusSquare ,
onClick : ( data ) = > {
/ * *
* Add a new Node in the Center of the Screen .
* Therefore extract the Center - Position of the
* Graph and call the addNode Function .
*
* This will result in opening up the corresponding
* Editor .
* /
data . network . network . addNodeMode ( ) ;
// if (( data.component.template as ITemplate<N,E>).nodes && (data.component.template as ITemplate<N,E>).nodes.length > 0){
// const pos = data.network.network.getViewPosition();
// data.component.addNode(pos);
// } else {
// data.network.showMessage('info', 'No Template Selected!', 'Please select a template for a Node in the Sidebar!', 5000);
// }
}
} ,
{
type : 'action' ,
label : '' ,
icon : faArrowRight ,
onClick : ( data ) = > {
/ * *
* Set the graph in the Add - Edge Mode
* /
data . network . network . addEdgeMode ( ) ;
// if ((data.eid as ITemplate<N,E>).nodes && (data.component.template as ITemplate<N,E>).nodes.length === 0 && (data.component.template as ITemplate<N,E>).edges.length === 1){
// data.network.network.addEdgeMode();
// } else {
// data.network.showMessage('info', 'No Template Selected!', 'Please select a template for an Edge in the Sidebar!', 5000);
// }
}
} ,
{
type : 'menu' ,
id : 'fileMenu' ,
items : [
{
type : 'action' ,
label : 'New File' ,
icon : faFile ,
onClick : async ( data ) = > {
/** Clearout the Graph and Delete the History */
data . network . clear ( ) ;
data . network . resetHistory ( ) ;
2020-10-30 18:30:59 +00:00
if ( data . tabs . tabs . length > 0 ) {
2020-10-29 18:20:42 +00:00
const clearAll = await data . layout . getDialogData ( {
content : {
props : { } ,
component ( props : {
onSubmit : ( data : boolean ) = > void ;
onCancel : ( error : any ) = > void
} ) {
return (
< >
< Button variant = "danger" onClick = { e = > props . onSubmit ( true ) } > Yes < / Button > { ' ' }
< Button variant = "success" onClick = { e = > props . onSubmit ( false ) } > No < / Button >
< / >
)
} ,
} ,
header : 'Do you want to discard all unstored changes?' ,
} ) ;
// Iterate over the Tabs and delete every.
2020-10-30 18:30:59 +00:00
for ( const tab of data . tabs . tabs ) {
2020-10-29 18:20:42 +00:00
// Force to delete the Tabs.
2020-10-30 18:30:59 +00:00
await data . tabs . delteTab ( tab , clearAll ) ;
2020-10-29 18:20:42 +00:00
}
}
2020-10-30 18:30:59 +00:00
await data . tabs . requestTab ( ) ;
2020-10-29 18:20:42 +00:00
}
} ,
{
type : 'divider' ,
} ,
{
type : 'action' ,
label : 'Open File' ,
icon : faUpload ,
onClick : async ( data ) = > {
2020-10-30 18:30:59 +00:00
let file = ''
while ( file == '' ) {
// Load the File
file = await openFile ( data ) ;
2020-10-29 18:20:42 +00:00
2020-10-30 18:30:59 +00:00
// Try to load the File till the User aborts, or the
// File was correct.
try {
data . network . loadJSON ( file , true ) ;
data . layout . showToast ( 'Loaded Graph' , 'success' ) ;
} catch ( e ) {
data . layout . showToast ( 'Failed to Load the Graph' , 'error' ) ;
console . error ( e )
file = '' ;
}
}
}
2020-10-29 18:20:42 +00:00
} ,
{
type : 'action' ,
label : 'Import File' ,
icon : faUpload ,
onClick : async ( data ) = > {
2020-10-30 18:30:59 +00:00
let file = ''
while ( file == '' ) {
// Load the File
file = await openFile ( data ) ;
// Try to load the File till the User aborts, or the
// File was correct.
try {
setTimeout ( ( ) = > {
data . network . loadJSON ( file , false ) ;
} , 1000 ) ;
data . layout . showToast ( 'Loaded Graph' , 'success' ) ;
} catch ( e ) {
data . layout . showToast ( 'Failed to Load the Graph' , 'error' ) ;
file = '' ;
}
}
2020-10-29 18:20:42 +00:00
}
} ,
{
type : 'action' ,
label : 'Save File' ,
icon : faDownload ,
onClick : async ( data ) = > {
// Get the Name
const name = await data . layout . getDialogData ( {
header : 'Enter File-Name' ,
content : {
component : 'DynamicForm' ,
props : {
schema : {
type : "string" ,
description : "Name of the File"
}
}
}
} ) ;
// Store the content
}
} ,
{
type : 'action' ,
label : 'print' ,
icon : faPrint ,
onClick : ( data ) = > {
/** Function, which should render the CANVAS as PDF */
/** Therefore extract the real Canvas-Object */
const canvas = data . network . network . body . container . getElementsByTagName ( 'canvas' ) [ 0 ] ;
const imgData = canvas . toDataURL ( 'image/jpeg' , 1.0 ) ;
// // const pdf = new jsPDF();
// pdf.addImage(imgData, 'JPEG', 0, 0);
// pdf.save('download.pdf');
}
}
] ,
label : 'File'
} ,
{
type : 'menu' ,
id : 'editMenu' ,
label : 'edit' ,
items : [
{
type : 'action' ,
label : 'Undo' ,
icon : faArrowLeft ,
onClick : ( data ) = > {
/** Undo the Last Action if Possible */
data . network . undo ( ) ;
}
} ,
{
type : 'action' ,
label : 'Redo' ,
icon : faArrowRight ,
onClick : ( data ) = > {
/** Redo the Last Action if Possible */
data . network . redo ( ) ;
}
} ,
{
type : 'divider'
} ,
{
type : 'action' ,
label : 'add node' ,
icon : faPlusSquare ,
onClick : ( data ) = > {
/ * *
* Add a new Node in the Center of the Screen .
* Therefore extract the Center - Position of the
* Graph and call the addNode Function .
*
* This will result in opening up the corresponding
* Editor .
* /
2020-10-30 18:30:59 +00:00
data . layout . showToast ( 'Shortcut for adding a node: "Left-Control + Mouse-Click"' , 'light' ) ;
2020-10-29 18:20:42 +00:00
data . network . network . addNodeMode ( ) ;
}
} ,
{
type : 'action' ,
label : 'add edge' ,
icon : faArrowRight ,
onClick : ( data ) = > {
/ * *
* Set the graph in the Add - Edge Mode
* /
2020-10-30 18:30:59 +00:00
data . layout . showToast ( 'Shortcut for adding a node: "Left-Shift + Mouse-Click"' , 'info' ) ;
2020-10-29 18:20:42 +00:00
data . network . network . addEdgeMode ( ) ;
}
} ,
{
type : 'divider' ,
} ,
{
type : 'action' ,
label : 'export to clipboard' ,
icon : faClone ,
onClick : ( data ) = > {
2020-10-30 18:30:59 +00:00
writeToClipboard ( data . network . getJSON ( ) )
2020-10-29 18:20:42 +00:00
}
} ,
{
type : 'action' ,
label : 'import from clipboard' ,
icon : faClone ,
onClick : ( data ) = > {
data . component . pasteFromClipboard ( ) ;
}
} ,
{
type : 'action' ,
label : 'upload project-file' ,
icon : faClone ,
onClick : ( data ) = > {
}
} ,
{
type : 'divider' ,
} ,
{
type : 'action' ,
label : 'hide selected' ,
icon : faEyeSlash ,
onClick : ( data ) = > {
/ * *
* Callback which whill hide the selected Elements .
* Therefore disable storingContent on Updates , cause
* every data . options of the Selected Elements are updated .
* /
2020-10-30 18:30:59 +00:00
data . network . disableUndoRedo = true ;
2020-10-29 18:20:42 +00:00
data . network . network . setVisibilityOfNodes ( data . selectedNodes , false ) ;
data . network . network . storePositions ( ) ;
/** Iterate over the selected Elements and hide the Node */
for ( const id of data . selectedNodes ) {
const node = data . network . getNode ( id ) ;
node . hidden = true ;
/** Update the data.options */
data . network . updateNode ( node ) ;
}
/** Iterate over the selected Elements and hide the Edge */
for ( const id of data . selectedEdges ) {
const edge = data . network . getEdge ( id ) ;
edge . hidden = true ;
/** Update the data.options */
data . network . updateEdge ( edge ) ;
}
/** Activate Redo/Undo Tracking again */
2020-10-30 18:30:59 +00:00
data . network . disableUndoRedo = false ;
2020-10-29 18:20:42 +00:00
/** Store the current state */
data . network . save ( ) ;
}
} ,
]
}
]
}
// const config: IToolbar<D> = {
// /** Set the File-Menu as initally active */
// activeTab: 'fileMenu',
// /** Define the Tabs of the Toolbar */
// tabs: {
// view: {
// label: 'View',
// tooltip: 'Manipulates the View of the Graph',
// menu: {
// items: [
// {
// type: 'action',
// label: 'fit',
// tooltip: 'Fits the Graph',
// icon: 'fa fa-expand',
// onClick: (data) => {
// data.network.network.fit({
// animation: true
// });
// }
// },
// {
// type: 'action',
// label: 'zoom in',
// tooltip: 'Zoom in the Graph',
// icon: 'fa fa-search-plus',
// onClick: (data) => {
// const position = data.network.network.getViewPosition();
// const currentScale = data.network.network.getScale();
// data.network.network.moveTo({
// position,
// scale: currentScale + 0.1,
// animation: {
// duration: 100,
// }
// });
// }
// },
// {
// type: 'action',
// label: 'zoom out',
// tooltip: 'Zooms out the Graph',
// icon: 'fa fa-search-minus',
// onClick: (data) => {
// const position = data.network.network.getViewPosition();
// const currentScale = data.network.network.getScale();
// data.network.network.moveTo({
// position,
// scale: currentScale - 0.1,
// animation: {
// duration: 100,
// }
// });
// }
// }, {
// type: 'divider'
// },
// {
// type: 'action',
// label: 'view all',
// tooltip: 'Show all hidden Elements',
// icon: 'fa fa-eye',
// onClick: (data) => {
// const nodes = data.network.nodes;
// const edges = data.network.edges;
// for (const node of nodes){
// node.hidden = false;
// }
// for (const edge of edges){
// edge.hidden = false;
// }
// data.network.updateNode(nodes);
// data.network.updateEdge(edges);
// }
// },
// ]
// }
// },
// layout: {
// label: 'Layout',
// tooltip: 'Options to Manipulate the Layout of the Graph',
// // disabled: true,
// menu: {
// items: [
// {
// id: 'item2',
// type: 'menu-radio',
// text(item) {
// const text = item.selected;
// const el = this.get('item2:' + item.selected);
// return el.text;
// },
// selected: getCurrentLayout(),
// items: [
// { id: 'h_ud', label: 'Hierarchical: UD', icon: 'fa fa-arrow-down' },
// { id: 'h_du', label: 'Hierarchical: DU', icon: 'fa fa-arrow-up' },
// { id: 'h_lr', label: 'Hierarchical: LR', icon: 'fa fa-arrow-right' },
// { id: 'h_rl', label: 'Hierarchical: RL', icon: 'fa fa-arrow-left' },
// { id: 'normal', label: 'Normal View-Mode' }
// ],
// onRefresh(data) {
// data.event.done(() => {
// const before = getCurrentLayout();
// if (data.event.item.selected != before) {
// // Update the data.options object
// if (typeof rgetattr(data.options, 'layout.hierarchical', {}) === 'boolean') {
// rsetattr(data.options, 'layout.hierarchical', {})
// }
// // Based on the Selection, update the Elements
// switch (data.event.item.selected) {
// case 'normal':
// rsetattr(data.options, 'layout.hierarchical.enabled', false)
// break;
// default:
// const direction = (data.event.item.selected as string).slice(2).toUpperCase();
// rsetattr(data.options, 'layout.hierarchical.enabled', true);
// rsetattr(data.options, 'layout.hierarchical.direction', direction);
// break;
// }
// data.component.visjsOptions = data.options;
// // Store the New data.options
// data.network.network.storePositions();
// const nodes = data.network.nodes;
// const edges = data.network.edges;
// const clusters = data.network.getClusters();
// data.network.clear();
// data.network.network.setOptions(data.options);
// data.network.addNode(nodes);
// data.network.addEdge(edges);
// data.network.readinClusters(clusters);
// }
// });
// }
// },
// ]
// }
// },
// cluster: {
// label: 'Clusters',
// tooltip: 'Add Clusters to the Node',
// // disabled: true,
// menu: {
// items: [
// {
// type: 'action',
// label: 'cluster selected',
// tooltip: 'Clusterize the Selected Elements',
// icon: 'fa fa-cog',
// onClick: (data) => {
// clusterSelected(data.component, false)
// }
// },
// {
// type: 'action',
// label: 'cluster outliners',
// tooltip: 'Clusterize the Selected Elements',
// icon: 'fa fa-cog',
// onClick: (data) => {
// data.network.network.clusterOutliers();
// data.network.save();
// }
// },
// {
// type: 'divider'
// },
// {
// type: 'action',
// label: 'uncluster selected',
// tooltip: '',
// icon: 'fa fa-cog',
// onClick: (data) => {
// for (const node of data.selectedNodes) {
// if (data.network.network.isCluster(node)) {
// data.network.network.openCluster(node)
// }
// }
// data.network.save();
// }
// },
// {
// type: 'action',
// label: 'view all',
// tooltip: 'Show all hidden Elements',
// icon: 'fa fa-eye',
// onClick: (data) => {
// const nodesToUpdate = data.network.nodes;
// for (const node of nodesToUpdate){
// node.hidden = false;
// }
// data.network.updateNode(nodesToUpdate);
// }
// },
// ]
// }
// },
// settings: {
// label: 'Settings',
// tooltip: 'User Settings',
// // disabled: true,
// menu: {
// items: [
// {
// type: 'check',
// label: 'Multiselect',
// icon: 'fa fa-check-square',
// checked: rgetattr(graphOptions, 'interaction.multiselect', false),
// onClick: (data) => {
// data.event.done(() => {
// rsetattr(data.options, 'interaction.multiselect', data.event.item.checked);
// data.component.visjsOptions = data.options;
// data.network.network.setOptions(data.options);
// })
// }
// },
// {
// type: 'divider'
// },
// {
// type: 'check',
// label: 'Navigation Buttons',
// icon: 'fa fa-check-square',
// checked: rgetattr(graphOptions, 'interaction.navigationButtons', false),
// onClick: (data) => {
// data.event.done(() => {
// rsetattr(data.options, 'interaction.navigationButtons', data.event.item.checked);
// data.component.visjsOptions = data.options;
// data.network.network.setOptions(data.options);
// })
// }
// },
// {
// type: 'divider'
// },
// {
// id: 'physics',
// type: 'check',
// label: 'physics',
// icon: 'fa fa-check-square',
// checked: rgetattr(graphOptions, 'physics', false) === true || rgetattr(graphOptions, 'physics.enabled', false) === true,
// onClick: (data) => {
// data.event.done(() => {
// if (typeof data.options.physics == 'boolean') {
// data.options.physics = data.event.item.checked;
// rsetattr(data.options, 'physics', data.event.item.checked);
// data.component.visjsOptions = data.options;
// data.network.network.setOptions(data.options);
// } else {
// rsetattr(data.options, 'physics.enabled', data.event.item.checked);
// data.component.visjsOptions = data.options;
// data.network.network.setOptions(data.options);
// }
// })
// }
// },
// {
// type: 'divider'
// },
// {
// id: 'shadows',
// type: 'check',
// label: 'shadows',
// icon: 'fa fa-check-square',
// checked: rgetattr(graphOptions, 'nodes.shadow', false) === true || rgetattr(graphOptions, 'nodes.shadow.enabled', false) === true,
// onClick: (data) => {
// data.event.done(() => {
// if (data.options.nodes && typeof data.options.nodes.shadow == 'boolean') {
// data.options.nodes.shadow = data.event.item.checked;
// rsetattr(data.options, 'nodes.shadow', data.event.item.checked);
// } else {
// rsetattr(data.options, 'nodes.shadow.enabled', data.event.item.checked);
// }
// if (data.options.edges && typeof data.options.edges.shadow == 'boolean') {
// data.options.edges.shadow = data.event.item.checked;
// rsetattr(data.options, 'edges.shadow', data.event.item.checked);
// } else {
// rsetattr(data.options, 'edges.shadow.enabled', data.event.item.checked);
// }
// data.component.visjsOptions = data.options;
// data.network.network.setOptions(data.options);
// })
// }
// },{
// type: 'divider'
// },
// {
// type: 'check',
// label: 'Edit on Select',
// icon: 'fa fa-check-square',
// checked: additionalOptions.editOnSelect || false,
// onClick: (data) => {
// data.event.done(() => {
// data.component.options.editOnSelect = data.event.item.checked;
// })
// }
// },
// {
// type: 'check',
// label: 'Hide Panel on Deselect',
// icon: 'fa fa-check-square',
// checked: additionalOptions.hidePanelOnDeselect || false,
// onClick: (data) => {
// data.event.done(() => {
// data.component.options.hidePanelOnDeselect = data.event.item.checked;
// })
// }
// },
// ]
// }
// },
// developer: {
// label: 'Developer',
// tooltip: 'Extra Tools for a Developer',
// // disabled: true,
// menu: {
// items: [
// {
// type: 'action',
// label: 'json',
// tooltip: 'Shows the current data as JSON',
// icon: 'fa fa-code',
// onClick: (data) => {
// let editor: any = null;
// const speed = 0.3;
// w2popup.open({
// id: 'test',
// title: 'Debug window',
// maximized: true,
// body: '<div id="jsonviewer" style="width:100%;height:100%"></div>',
// showMax: true,
// speed,
// width: 500,
// height: 500,
// onOpen(event) {
// setTimeout(() => {
// // Generate the Editor
// const editorOptions = {
// // modes: ['tree', 'view', 'code', 'text']
// mode: 'view',
// search: 'true',
// mainMenuBar: false
// };
// editor = new JSONEditor(document.getElementById('jsonviewer'), editorOptions, {
// nodes: data.network.nodes,
// edges: data.network.edges
// });
// }, speed * 1000 + 0.050)
// },
// onClose(event) {
// if (editor) {
// editor.destroy();
// }
// }
// });
// }
// },
// {
// type: 'action',
// label: 'generate Template',
// tooltip: 'Creates a Template Structure',
// icon: 'fa fa-code',
// onClick: (data) => {
// writeToClipboard(stringifyWithFunctions(data.component.generateTemplateData()));
// }
// },
// {
// type: 'divider'
// },
// {
// type: 'check',
// label: 'Use Version Control',
// icon: 'fa fa-check-square',
// checked: rgetattr(additionalOptions, 'useVersionControl', true),
// onClick: (data) => {
// data.event.done(() => {
// data.component.network.useVersionControl = data.event.item.checked;
// })
// }
// },
// ]
// }
// },
// help: {
// label: 'Help',
// menu: {
// items: [
// {
// type: 'action',
// label: 'Show Help',
// tooltip: 'Clusterize the Selected Elements',
// icon: 'fa fa-info',
// onClick: (data) => {
// // Load the Help Component.
// data.component.layout.openDialogComponent<HelpComponent>({
// title: 'help',
// component: {
// component: HelpComponent
// },
// buttons: [
// {
// label: 'Close',
// callback(instance, close){
// close();
// },
// status: 'danger'
// }
// ],
// closeOnBackdropClick: true,
// closeOnEsc: true,
// });
// }
// },
// ]
// }
// }
// }
// };
return toolbar ;
}