2021-04-25 03:39:40 +00:00
|
|
|
import mxgraph from '@mxgraph/core';
|
|
|
|
|
|
|
|
import { globalTypes } from '../.storybook/preview';
|
|
|
|
|
|
|
|
export default {
|
|
|
|
title: 'Misc/DynamicLoading',
|
|
|
|
argTypes: {
|
|
|
|
...globalTypes
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const Template = ({ label, ...args }) => {
|
|
|
|
const {
|
|
|
|
mxGraph,
|
|
|
|
mxText,
|
2021-04-25 10:47:53 +00:00
|
|
|
mxEffects,
|
2021-04-25 03:39:40 +00:00
|
|
|
mxEvent,
|
|
|
|
mxConstants,
|
|
|
|
mxPerimeter,
|
|
|
|
mxCodec,
|
|
|
|
mxUtils,
|
|
|
|
mxXmlUtils
|
|
|
|
} = mxgraph;
|
|
|
|
|
|
|
|
const container = document.createElement('div');
|
|
|
|
container.style.position = 'relative';
|
|
|
|
container.style.overflow = 'hidden';
|
|
|
|
container.style.width = `${args.width}px`;
|
|
|
|
container.style.height = `${args.height}px`;
|
|
|
|
container.style.background = 'url(/images/grid.gif)';
|
|
|
|
container.style.cursor = 'default';
|
|
|
|
|
|
|
|
let requestId = 0;
|
|
|
|
|
|
|
|
// Speedup the animation
|
|
|
|
mxText.prototype.enableBoundingBox = false;
|
|
|
|
|
|
|
|
// Creates the graph inside the given container
|
|
|
|
const graph = new mxGraph(container);
|
|
|
|
|
|
|
|
// Disables all built-in interactions
|
|
|
|
graph.setEnabled(false);
|
|
|
|
|
|
|
|
// Handles clicks on cells
|
|
|
|
graph.addListener(mxEvent.CLICK, function(sender, evt) {
|
|
|
|
const cell = evt.getProperty('cell');
|
|
|
|
|
|
|
|
if (cell != null) {
|
|
|
|
load(graph, cell);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Changes the default vertex style in-place
|
|
|
|
const style = graph.getStylesheet().getDefaultVertexStyle();
|
2021-05-02 06:04:34 +00:00
|
|
|
style.shape = mxConstants.SHAPE_ELLIPSE;
|
|
|
|
style.perimiter = mxPerimeter.EllipsePerimeter;
|
|
|
|
style.gradientColor = 'white';
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Gets the default parent for inserting new cells. This
|
|
|
|
// is normally the first child of the root (ie. layer 0).
|
|
|
|
const parent = graph.getDefaultParent();
|
|
|
|
|
|
|
|
const cx = graph.container.clientWidth / 2;
|
|
|
|
const cy = graph.container.clientHeight / 2;
|
|
|
|
|
|
|
|
const cell = graph.insertVertex(
|
|
|
|
parent,
|
|
|
|
'0-0',
|
|
|
|
'0-0',
|
|
|
|
cx - 20,
|
|
|
|
cy - 15,
|
|
|
|
60,
|
|
|
|
40
|
|
|
|
);
|
|
|
|
|
|
|
|
// Animates the changes in the graph model
|
|
|
|
graph.getModel().addListener(mxEvent.CHANGE, function(sender, evt) {
|
|
|
|
const { changes } = evt.getProperty('edit');
|
|
|
|
mxEffects.animateChanges(graph, changes);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Loads the links for the given cell into the given graph
|
|
|
|
// by requesting the respective data in the server-side
|
|
|
|
// (implemented for this demo using the server-function)
|
|
|
|
function load(graph, cell) {
|
|
|
|
if (cell.isVertex()) {
|
|
|
|
const cx = graph.container.clientWidth / 2;
|
|
|
|
const cy = graph.container.clientHeight / 2;
|
|
|
|
|
|
|
|
// Gets the default parent for inserting new cells. This
|
|
|
|
// is normally the first child of the root (ie. layer 0).
|
|
|
|
const parent = graph.getDefaultParent();
|
|
|
|
|
|
|
|
// Adds cells to the model in a single step
|
|
|
|
graph.getModel().beginUpdate();
|
|
|
|
try {
|
|
|
|
const xml = server(cell.id);
|
|
|
|
const doc = mxXmlUtils.parseXml(xml);
|
|
|
|
const dec = new mxCodec(doc);
|
|
|
|
const model = dec.decode(doc.documentElement);
|
|
|
|
|
|
|
|
// Removes all cells which are not in the response
|
|
|
|
for (var key in graph.getModel().cells) {
|
|
|
|
const tmp = graph.getModel().getCell(key);
|
|
|
|
|
|
|
|
if (tmp != cell && tmp.isVertex()) {
|
|
|
|
graph.removeCells([tmp]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merges the response model with the client model
|
2021-04-25 10:47:53 +00:00
|
|
|
graph.getModel().mergeChildren(graph.getModel().getRoot().getChildAt(0), parent);
|
2021-04-25 03:39:40 +00:00
|
|
|
|
|
|
|
// Moves the given cell to the center
|
|
|
|
let geo = cell.getGeometry();
|
|
|
|
|
|
|
|
if (geo != null) {
|
|
|
|
geo = geo.clone();
|
|
|
|
geo.x = cx - geo.width / 2;
|
|
|
|
geo.y = cy - geo.height / 2;
|
|
|
|
|
|
|
|
graph.getModel().setGeometry(cell, geo);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a list of the new vertices, if there is more
|
|
|
|
// than the center vertex which might have existed
|
|
|
|
// previously, then this needs to be changed to analyze
|
|
|
|
// the target model before calling mergeChildren above
|
|
|
|
const vertices = [];
|
|
|
|
|
|
|
|
for (var key in graph.getModel().cells) {
|
|
|
|
const tmp = graph.getModel().getCell(key);
|
|
|
|
|
|
|
|
if (tmp != cell && tmp.isVertex()) {
|
|
|
|
vertices.push(tmp);
|
|
|
|
|
|
|
|
// Changes the initial location "in-place"
|
|
|
|
// to get a nice animation effect from the
|
|
|
|
// center to the radius of the circle
|
|
|
|
const geo = tmp.getGeometry();
|
|
|
|
|
|
|
|
if (geo != null) {
|
|
|
|
geo.x = cx - geo.width / 2;
|
|
|
|
geo.y = cy - geo.height / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Arranges the response in a circle
|
|
|
|
const cellCount = vertices.length;
|
|
|
|
const phi = (2 * Math.PI) / cellCount;
|
|
|
|
const r = Math.min(
|
|
|
|
graph.container.clientWidth / 4,
|
|
|
|
graph.container.clientHeight / 4
|
|
|
|
);
|
|
|
|
|
|
|
|
for (let i = 0; i < cellCount; i++) {
|
|
|
|
let geo = vertices[i].getGeometry();
|
|
|
|
|
|
|
|
if (geo != null) {
|
|
|
|
geo = geo.clone();
|
|
|
|
geo.x += r * Math.sin(i * phi);
|
|
|
|
geo.y += r * Math.cos(i * phi);
|
|
|
|
|
|
|
|
graph.getModel().setGeometry(vertices[i], geo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
// Updates the display
|
|
|
|
graph.getModel().endUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simulates the existence of a server that can crawl the
|
|
|
|
// big graph with a certain depth and create a graph model
|
|
|
|
// for the traversed cells, which is then sent to the client
|
|
|
|
function server(cellId) {
|
|
|
|
// Increments the request ID as a prefix for the cell IDs
|
|
|
|
requestId++;
|
|
|
|
|
|
|
|
// Creates a local graph with no display
|
|
|
|
const graph = new mxGraph();
|
|
|
|
|
|
|
|
// Gets the default parent for inserting new cells. This
|
|
|
|
// is normally the first child of the root (ie. layer 0).
|
|
|
|
const parent = graph.getDefaultParent();
|
|
|
|
|
|
|
|
// Adds cells to the model in a single step
|
|
|
|
graph.getModel().beginUpdate();
|
|
|
|
try {
|
|
|
|
const v0 = graph.insertVertex(parent, cellId, 'Dummy', 0, 0, 60, 40);
|
|
|
|
const cellCount = parseInt(Math.random() * 16) + 4;
|
|
|
|
|
|
|
|
// Creates the random links and cells for the response
|
|
|
|
for (let i = 0; i < cellCount; i++) {
|
|
|
|
const id = `${requestId}-${i}`;
|
|
|
|
const v = graph.insertVertex(parent, id, id, 0, 0, 60, 40);
|
|
|
|
const e = graph.insertEdge(parent, null, `Link ${i}`, v0, v);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
// Updates the display
|
|
|
|
graph.getModel().endUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
const enc = new mxCodec();
|
|
|
|
const node = enc.encode(graph.getModel());
|
|
|
|
|
|
|
|
return mxUtils.getXml(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
load(graph, cell);
|
|
|
|
|
|
|
|
return container;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const Default = Template.bind({});
|