|
@ -5,7 +5,6 @@
|
|||
* Type definitions from the typed-mxgraph project
|
||||
*/
|
||||
|
||||
import mxUtils from '../util/mxUtils';
|
||||
import mxCellPath from '../view/cell/mxCellPath';
|
||||
import mxCodecRegistry from './mxCodecRegistry';
|
||||
import mxConstants from '../util/mxConstants';
|
||||
|
@ -13,6 +12,7 @@ import mxCell from '../view/cell/mxCell';
|
|||
import mxLog from '../util/gui/mxLog';
|
||||
import { getFunctionName } from '../util/mxStringUtils';
|
||||
import { importNode, isNode } from '../util/mxDomUtils';
|
||||
import { createXmlDocument } from '../util/mxXmlUtils';
|
||||
|
||||
/**
|
||||
* XML codec for JavaScript object graphs. See {@link mxObjectCodec} for a
|
||||
|
@ -120,7 +120,7 @@ import { importNode, isNode } from '../util/mxDomUtils';
|
|||
*/
|
||||
class mxCodec {
|
||||
constructor(document) {
|
||||
this.document = document || mxUtils.createXmlDocument();
|
||||
this.document = document || createXmlDocument();
|
||||
this.objects = [];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import mxDragSource from './drag_pan/mxDragSource';
|
||||
import mxPoint from './datatypes/mxPoint';
|
||||
import mxConstants from './mxConstants';
|
||||
|
||||
/**
|
||||
* Function: makeDraggable
|
||||
|
|
|
@ -7,4 +7,15 @@ export const parameters = {
|
|||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultArgTypes = {
|
||||
width: {
|
||||
type: 'number',
|
||||
defaultValue: 800
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
defaultValue: 600
|
||||
}
|
||||
};
|
||||
|
|
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 887 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 954 B |
After Width: | Height: | Size: 728 B |
After Width: | Height: | Size: 781 B |
After Width: | Height: | Size: 914 B |
After Width: | Height: | Size: 517 B |
After Width: | Height: | Size: 857 B |
After Width: | Height: | Size: 529 B |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 326 B |
After Width: | Height: | Size: 899 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 379 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 300 B |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 485 B |
After Width: | Height: | Size: 709 B |
After Width: | Height: | Size: 783 B |
After Width: | Height: | Size: 236 B |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 896 B |
After Width: | Height: | Size: 895 B |
After Width: | Height: | Size: 80 B |
After Width: | Height: | Size: 43 B |
After Width: | Height: | Size: 155 B |
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 849 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 918 B |
After Width: | Height: | Size: 912 B |
After Width: | Height: | Size: 50 B |
After Width: | Height: | Size: 858 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 847 B |
After Width: | Height: | Size: 2.1 KiB |
|
@ -1,10 +1,15 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
import HelloWorld from './HelloWorld.stories';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Connections/Anchors',
|
||||
argTypes: {
|
||||
...HelloWorld.argTypes
|
||||
...defaultArgTypes.argTypes,
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,359 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'DnD_CopyPaste/Clipboard',
|
||||
argTypes: {
|
||||
...defaultArgTypes,
|
||||
contextMenu: {
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxEvent,
|
||||
mxRubberband,
|
||||
mxClipboard,
|
||||
mxUtils,
|
||||
mxEventUtils,
|
||||
mxClient,
|
||||
mxCodec,
|
||||
mxGraphModel,
|
||||
mxStringUtils
|
||||
} = 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';
|
||||
|
||||
// Disables the built-in context menu
|
||||
if (!args.contextMenu)
|
||||
mxEvent.disableContextMenu(container);
|
||||
|
||||
// Creates the graph inside the given this.el
|
||||
const graph = new mxGraph(container);
|
||||
|
||||
// Public helper method for shared clipboard.
|
||||
mxClipboard.cellsToString = function(cells) {
|
||||
const codec = new mxCodec();
|
||||
const model = new mxGraphModel();
|
||||
const parent = model.getChildAt(model.getRoot(), 0);
|
||||
|
||||
for (let i = 0; i < cells.length; i++) {
|
||||
model.add(parent, cells[i]);
|
||||
}
|
||||
|
||||
return mxUtils.getXml(codec.encode(model));
|
||||
};
|
||||
|
||||
// Focused but invisible textarea during control or meta key events
|
||||
const textInput = document.createElement('textarea');
|
||||
mxUtils.setOpacity(textInput, 0);
|
||||
textInput.style.width = '1px';
|
||||
textInput.style.height = '1px';
|
||||
let restoreFocus = false;
|
||||
const gs = graph.gridSize;
|
||||
let lastPaste = null;
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
|
||||
// Workaround for no copy event in IE/FF if empty
|
||||
textInput.value = ' ';
|
||||
|
||||
// Shows a textare when control/cmd is pressed to handle native clipboard actions
|
||||
mxEvent.addListener(document, 'keydown', function(evt) {
|
||||
// No dialog visible
|
||||
const source = mxEventUtils.getSource(evt);
|
||||
|
||||
if (
|
||||
graph.isEnabled() &&
|
||||
!graph.isMouseDown &&
|
||||
!graph.isEditing() &&
|
||||
source.nodeName !== 'INPUT'
|
||||
) {
|
||||
if (
|
||||
evt.keyCode === 224 /* FF */ ||
|
||||
(!mxClient.IS_MAC && evt.keyCode === 17) /* Control */ ||
|
||||
(mxClient.IS_MAC &&
|
||||
(evt.keyCode === 91 || evt.keyCode === 93)) /* Left/Right Meta */
|
||||
) {
|
||||
// Cannot use parentNode for check in IE
|
||||
if (!restoreFocus) {
|
||||
// Avoid autoscroll but allow handling of events
|
||||
textInput.style.position = 'absolute';
|
||||
textInput.style.left = `${graph.container.scrollLeft + 10}px`;
|
||||
textInput.style.top = `${graph.container.scrollTop + 10}px`;
|
||||
graph.container.appendChild(textInput);
|
||||
|
||||
restoreFocus = true;
|
||||
textInput.focus();
|
||||
textInput.select();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Restores focus on graph this.el and removes text input from DOM
|
||||
mxEvent.addListener(document, 'keyup', function(evt) {
|
||||
if (
|
||||
restoreFocus &&
|
||||
(evt.keyCode === 224 /* FF */ ||
|
||||
evt.keyCode === 17 /* Control */ ||
|
||||
evt.keyCode === 91 ||
|
||||
evt.keyCode === 93) /* Meta */
|
||||
) {
|
||||
restoreFocus = false;
|
||||
|
||||
if (!graph.isEditing()) {
|
||||
graph.container.focus();
|
||||
}
|
||||
|
||||
textInput.parentNode.removeChild(textInput);
|
||||
}
|
||||
});
|
||||
|
||||
// Inserts the XML for the given cells into the text input for copy
|
||||
const copyCells = function(graph, cells) {
|
||||
if (cells.length > 0) {
|
||||
const clones = graph.cloneCells(cells);
|
||||
|
||||
// Checks for orphaned relative children and makes absolute
|
||||
for (let i = 0; i < clones.length; i++) {
|
||||
const state = graph.view.getState(cells[i]);
|
||||
|
||||
if (state != null) {
|
||||
const geo = graph.getCellGeometry(clones[i]);
|
||||
|
||||
if (geo != null && geo.relative) {
|
||||
geo.relative = false;
|
||||
geo.x = state.x / state.view.scale - state.view.translate.x;
|
||||
geo.y = state.y / state.view.scale - state.view.translate.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textInput.value = mxClipboard.cellsToString(clones);
|
||||
}
|
||||
|
||||
textInput.select();
|
||||
lastPaste = textInput.value;
|
||||
};
|
||||
|
||||
// Handles copy event by putting XML for current selection into text input
|
||||
mxEvent.addListener(
|
||||
textInput,
|
||||
'copy',
|
||||
mxUtils.bind(this, function(evt) {
|
||||
if (graph.isEnabled() && !graph.isSelectionEmpty()) {
|
||||
copyCells(
|
||||
graph,
|
||||
mxUtils.sortCells(
|
||||
graph.model.getTopmostCells(graph.getSelectionCells())
|
||||
)
|
||||
);
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Handles cut event by removing cells putting XML into text input
|
||||
mxEvent.addListener(
|
||||
textInput,
|
||||
'cut',
|
||||
mxUtils.bind(this, function(evt) {
|
||||
if (graph.isEnabled() && !graph.isSelectionEmpty()) {
|
||||
copyCells(graph, graph.removeCells());
|
||||
dx = -gs;
|
||||
dy = -gs;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Merges XML into existing graph and layers
|
||||
const importXml = function(xml, dx, dy) {
|
||||
dx = dx != null ? dx : 0;
|
||||
dy = dy != null ? dy : 0;
|
||||
let cells = [];
|
||||
|
||||
try {
|
||||
const doc = mxUtils.parseXml(xml);
|
||||
const node = doc.documentElement;
|
||||
|
||||
if (node != null) {
|
||||
const model = new mxGraphModel();
|
||||
const codec = new mxCodec(node.ownerDocument);
|
||||
codec.decode(node, model);
|
||||
|
||||
const childCount = model.getChildCount(model.getRoot());
|
||||
const targetChildCount = graph.model.getChildCount(
|
||||
graph.model.getRoot()
|
||||
);
|
||||
|
||||
// Merges existing layers and adds new layers
|
||||
graph.model.beginUpdate();
|
||||
try {
|
||||
for (let i = 0; i < childCount; i++) {
|
||||
let parent = model.getChildAt(model.getRoot(), i);
|
||||
|
||||
// Adds cells to existing layers if not locked
|
||||
if (targetChildCount > i) {
|
||||
// Inserts into active layer if only one layer is being pasted
|
||||
const target =
|
||||
childCount === 1
|
||||
? graph.getDefaultParent()
|
||||
: graph.model.getChildAt(graph.model.getRoot(), i);
|
||||
|
||||
if (!graph.isCellLocked(target)) {
|
||||
const children = model.getChildren(parent);
|
||||
cells = cells.concat(
|
||||
graph.importCells(children, dx, dy, target)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Delta is non cascading, needs separate move for layers
|
||||
parent = graph.importCells(
|
||||
[parent],
|
||||
0,
|
||||
0,
|
||||
graph.model.getRoot()
|
||||
)[0];
|
||||
const children = graph.model.getChildren(parent);
|
||||
graph.moveCells(children, dx, dy);
|
||||
cells = cells.concat(children);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
graph.model.endUpdate();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
return cells;
|
||||
};
|
||||
|
||||
// Parses and inserts XML into graph
|
||||
const pasteText = function(text) {
|
||||
const xml = mxStringUtils.trim(text);
|
||||
const x =
|
||||
graph.container.scrollLeft / graph.view.scale - graph.view.translate.x;
|
||||
const y =
|
||||
graph.container.scrollTop / graph.view.scale - graph.view.translate.y;
|
||||
|
||||
if (xml.length > 0) {
|
||||
if (lastPaste !== xml) {
|
||||
lastPaste = xml;
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
} else {
|
||||
dx += gs;
|
||||
dy += gs;
|
||||
}
|
||||
|
||||
// Standard paste via control-v
|
||||
if (xml.substring(0, 14) === '<mxGraphModel>') {
|
||||
graph.setSelectionCells(importXml(xml, dx, dy));
|
||||
graph.scrollCellToVisible(graph.getSelectionCell());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Function to fetch text from paste events
|
||||
const extractGraphModelFromEvent = function(evt) {
|
||||
let data = null;
|
||||
|
||||
if (evt != null) {
|
||||
const provider =
|
||||
evt.dataTransfer != null ? evt.dataTransfer : evt.clipboardData;
|
||||
|
||||
if (provider != null) {
|
||||
data =
|
||||
mxUtils.indexOf(provider.types, 'text/html') >= 0
|
||||
? provider.getData('text/html')
|
||||
: null;
|
||||
|
||||
if (
|
||||
mxUtils.indexOf(
|
||||
provider.types,
|
||||
'text/plain' && (data == null || data.length === 0)
|
||||
)
|
||||
) {
|
||||
data = provider.getData('text/plain');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
// Handles paste event by parsing and inserting XML
|
||||
mxEvent.addListener(textInput, 'paste', function(evt) {
|
||||
// Clears existing contents before paste - should not be needed
|
||||
// because all text is selected, but doesn't hurt since the
|
||||
// actual pasting of the new text is delayed in all cases.
|
||||
textInput.value = '';
|
||||
|
||||
if (graph.isEnabled()) {
|
||||
const xml = extractGraphModelFromEvent(evt);
|
||||
|
||||
if (xml != null && xml.length > 0) {
|
||||
pasteText(xml);
|
||||
} else {
|
||||
// Timeout for new value to appear
|
||||
window.setTimeout(
|
||||
mxUtils.bind(this, function() {
|
||||
pasteText(textInput.value);
|
||||
}),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
textInput.select();
|
||||
});
|
||||
|
||||
// Enables rubberband selection
|
||||
if (args.rubberBand)
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello,',
|
||||
position: [20, 20],
|
||||
size: [80, 30],
|
||||
});
|
||||
const v2 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'World!',
|
||||
position: [200, 150],
|
||||
size: [80, 30],
|
||||
});
|
||||
const e1 = graph.insertEdge({ parent, source: v1, target: v2 });
|
||||
});
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -0,0 +1,189 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'DnD_CopyPaste/DragSource',
|
||||
argTypes: {
|
||||
...defaultArgTypes,
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxDomUtils,
|
||||
mxRubberband,
|
||||
mxDragSource,
|
||||
mxUtils,
|
||||
mxGestureUtils,
|
||||
mxEdgeHandler,
|
||||
mxGraphHandler,
|
||||
mxGuide,
|
||||
mxEventUtils,
|
||||
mxCell,
|
||||
mxGeometry
|
||||
} = 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.cursor = 'default';
|
||||
|
||||
class MyCustomGuide extends mxGuide {
|
||||
isEnabledForEvent(evt) {
|
||||
// Alt disables guides
|
||||
return !mxEventUtils.isAltDown(evt);
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomGraphHandler extends mxGraphHandler {
|
||||
// Enables guides
|
||||
guidesEnabled = true;
|
||||
|
||||
createGuide() {
|
||||
return new MyCustomGuide(this.graph, this.getGuideStates());
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomEdgeHandler extends mxEdgeHandler {
|
||||
// Enables snapping waypoints to terminals
|
||||
snapToTerminals = true;
|
||||
}
|
||||
|
||||
class MyCustomGraph extends mxGraph {
|
||||
createGraphHandler() {
|
||||
return new MyCustomGraphHandler(this);
|
||||
}
|
||||
|
||||
createEdgeHandler(state, edgeStyle) {
|
||||
return new MyCustomEdgeHandler(state, edgeStyle);
|
||||
}
|
||||
}
|
||||
|
||||
const graphs = [];
|
||||
|
||||
// Creates the graph inside the given container
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const subContainer = document.createElement('div');
|
||||
subContainer.style.overflow = 'hidden';
|
||||
subContainer.style.position = 'relative';
|
||||
subContainer.style.width = '321px';
|
||||
subContainer.style.height = '241px';
|
||||
subContainer.style.background = "url('/images/grid.gif')";
|
||||
subContainer.style.cursor = 'default';
|
||||
|
||||
container.appendChild(subContainer);
|
||||
|
||||
const graph = new MyCustomGraph(subContainer);
|
||||
graph.gridSize = 30;
|
||||
|
||||
// Uncomment the following if you want the container
|
||||
// to fit the size of the graph
|
||||
// graph.setResizeContainer(true);
|
||||
|
||||
// Enables rubberband selection
|
||||
if (args.rubberBand)
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello,',
|
||||
position: [20, 20],
|
||||
size: [80, 30],
|
||||
});
|
||||
const v2 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'World!',
|
||||
position: [200, 150],
|
||||
size: [80, 30],
|
||||
});
|
||||
const e1 = graph.insertEdge({
|
||||
parent,
|
||||
source: v1,
|
||||
target: v2,
|
||||
});
|
||||
});
|
||||
|
||||
graphs.push(graph);
|
||||
}
|
||||
|
||||
// Returns the graph under the mouse
|
||||
const graphF = evt => {
|
||||
const x = mxEventUtils.getClientX(evt);
|
||||
const y = mxEventUtils.getClientY(evt);
|
||||
const elt = document.elementFromPoint(x, y);
|
||||
|
||||
for (const graph of graphs) {
|
||||
if (mxDomUtils.isAncestorNode(graph.container, elt)) {
|
||||
return graph;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Inserts a cell at the given location
|
||||
const funct = (graph, evt, target, x, y) => {
|
||||
const cell = new mxCell('Test', new mxGeometry(0, 0, 120, 40));
|
||||
cell.vertex = true;
|
||||
const cells = graph.importCells([cell], x, y, target);
|
||||
|
||||
if (cells != null && cells.length > 0) {
|
||||
graph.scrollCellToVisible(cells[0]);
|
||||
graph.setSelectionCells(cells);
|
||||
}
|
||||
};
|
||||
|
||||
// Creates a DOM node that acts as the drag source
|
||||
const img = mxUtils.createImage('images/icons48/gear.png');
|
||||
img.style.width = '48px';
|
||||
img.style.height = '48px';
|
||||
container.appendChild(img);
|
||||
|
||||
// Creates the element that is being for the actual preview.
|
||||
const dragElt = document.createElement('div');
|
||||
dragElt.style.border = 'dashed black 1px';
|
||||
dragElt.style.width = '120px';
|
||||
dragElt.style.height = '40px';
|
||||
|
||||
// Drag source is configured to use dragElt for preview and as drag icon
|
||||
// if scalePreview (last) argument is true. Dx and dy are null to force
|
||||
// the use of the defaults. Note that dx and dy are only used for the
|
||||
// drag icon but not for the preview.
|
||||
const ds = mxGestureUtils.makeDraggable(
|
||||
img,
|
||||
graphF,
|
||||
funct,
|
||||
dragElt,
|
||||
null,
|
||||
null,
|
||||
graphs[0].autoscroll,
|
||||
true
|
||||
);
|
||||
|
||||
// Redirects feature to global switch. Note that this feature should only be used
|
||||
// if the the x and y arguments are used in funct to insert the cell.
|
||||
ds.isGuidesEnabled = () => {
|
||||
return graphs[0].graphHandler.guidesEnabled;
|
||||
};
|
||||
|
||||
// Restores original drag icon while outside of graph
|
||||
ds.createDragElement = mxDragSource.prototype.createDragElement;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -0,0 +1,193 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'DnD_CopyPaste/Drop',
|
||||
argTypes: {
|
||||
...defaultArgTypes,
|
||||
contextMenu: {
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxDomUtils,
|
||||
mxRubberband,
|
||||
mxDragSource,
|
||||
mxUtils,
|
||||
mxGestureUtils,
|
||||
mxEdgeHandler,
|
||||
mxGraphHandler,
|
||||
mxGuide,
|
||||
mxEventUtils,
|
||||
mxEvent,
|
||||
mxClient
|
||||
} = mxgraph;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = 'Drag & drop your images below:<br>';
|
||||
|
||||
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';
|
||||
div.appendChild(container);
|
||||
|
||||
// Checks if the browser is supported
|
||||
const fileSupport =
|
||||
window.File != null &&
|
||||
window.FileReader != null &&
|
||||
window.FileList != null;
|
||||
|
||||
if (!fileSupport || !mxClient.isBrowserSupported()) {
|
||||
// Displays an error message if the browser is not supported.
|
||||
mxUtils.error('Browser is not supported!', 200, false);
|
||||
} else {
|
||||
// Disables the built-in context menu
|
||||
if (!args.contextMenu)
|
||||
mxEvent.disableContextMenu(container);
|
||||
|
||||
// Creates the graph inside the given this.el
|
||||
const graph = new mxGraph(container);
|
||||
|
||||
// Enables rubberband selection
|
||||
if (args.rubberBand)
|
||||
new mxRubberband(graph);
|
||||
|
||||
mxEvent.addListener(container, 'dragover', function(evt) {
|
||||
if (graph.isEnabled()) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
mxEvent.addListener(container, 'drop', evt => {
|
||||
if (graph.isEnabled()) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
||||
// Gets drop location point for vertex
|
||||
const pt = mxUtils.convertPoint(
|
||||
graph.container,
|
||||
mxEventUtils.getClientX(evt),
|
||||
mxEventUtils.getClientY(evt)
|
||||
);
|
||||
const tr = graph.view.translate;
|
||||
const { scale } = graph.view;
|
||||
const x = pt.x / scale - tr.x;
|
||||
const y = pt.y / scale - tr.y;
|
||||
|
||||
// Converts local images to data urls
|
||||
const filesArray = evt.dataTransfer.files;
|
||||
|
||||
for (let i = 0; i < filesArray.length; i++) {
|
||||
handleDrop(graph, filesArray[i], x + i * 10, y + i * 10);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
function handleDrop(graph, file, x, y) {
|
||||
// Handles each file as a separate insert for simplicity.
|
||||
// Use barrier to handle multiple files as a single insert.
|
||||
|
||||
if (file.type.substring(0, 5) === 'image') {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function(e) {
|
||||
// Gets size of image for vertex
|
||||
let data = e.target.result;
|
||||
|
||||
// SVG needs special handling to add viewbox if missing and
|
||||
// find initial size from SVG attributes (only for IE11)
|
||||
if (file.type.substring(0, 9) === 'image/svg') {
|
||||
const comma = data.indexOf(',');
|
||||
const svgText = atob(data.substring(comma + 1));
|
||||
const root = mxUtils.parseXml(svgText);
|
||||
|
||||
// Parses SVG to find width and height
|
||||
if (root != null) {
|
||||
const svgs = root.getElementsByTagName('svg');
|
||||
|
||||
if (svgs.length > 0) {
|
||||
const svgRoot = svgs[0];
|
||||
let w = parseFloat(svgRoot.getAttribute('width'));
|
||||
let h = parseFloat(svgRoot.getAttribute('height'));
|
||||
|
||||
// Check if viewBox attribute already exists
|
||||
const vb = svgRoot.getAttribute('viewBox');
|
||||
|
||||
if (vb == null || vb.length === 0) {
|
||||
svgRoot.setAttribute('viewBox', `0 0 ${w} ${h}`);
|
||||
}
|
||||
// Uses width and height from viewbox for
|
||||
// missing width and height attributes
|
||||
else if (Number.isNaN(w) || Number.isNaN(h)) {
|
||||
const tokens = vb.split(' ');
|
||||
|
||||
if (tokens.length > 3) {
|
||||
w = parseFloat(tokens[2]);
|
||||
h = parseFloat(tokens[3]);
|
||||
}
|
||||
}
|
||||
|
||||
w = Math.max(1, Math.round(w));
|
||||
h = Math.max(1, Math.round(h));
|
||||
|
||||
data = `data:image/svg+xml,${btoa(
|
||||
mxUtils.getXml(svgs[0], '\n')
|
||||
)}`;
|
||||
graph.insertVertex({
|
||||
position: [x, y],
|
||||
size: [w, h],
|
||||
style: `shape=image;image=${data};`,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
const w = Math.max(1, img.width);
|
||||
const h = Math.max(1, img.height);
|
||||
|
||||
// Converts format of data url to cell style value for use in vertex
|
||||
const semi = data.indexOf(';');
|
||||
|
||||
if (semi > 0) {
|
||||
data =
|
||||
data.substring(0, semi) +
|
||||
data.substring(data.indexOf(',', semi + 1));
|
||||
}
|
||||
|
||||
graph.insertVertex({
|
||||
position: [x, y],
|
||||
size: [w, h],
|
||||
style: `shape=image;image=${data};`,
|
||||
});
|
||||
};
|
||||
|
||||
img.src = data;
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -1,16 +1,11 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Connections/EdgeTolerance',
|
||||
argTypes: {
|
||||
width: {
|
||||
type: 'number',
|
||||
defaultValue: 800
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
defaultValue: 600
|
||||
}
|
||||
...defaultArgTypes.argTypes
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Editing/Editing',
|
||||
argTypes: {
|
||||
...defaultArgTypes,
|
||||
contextMenu: {
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxEvent,
|
||||
mxKeyHandler,
|
||||
mxUtils,
|
||||
mxDomUtils,
|
||||
mxCloneUtils,
|
||||
mxEventUtils
|
||||
} = mxgraph;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `
|
||||
<h1>Editing</h1>
|
||||
This example demonstrates using the in-place editor trigger to specify
|
||||
the editing value and write the new value into a specific field of the
|
||||
user object. Wrapping and DOM nodes as labels are also demonstrated
|
||||
here.
|
||||
<br />
|
||||
<br />
|
||||
Double-click the upper/lower half of the cell to edit different fields
|
||||
of the user object.
|
||||
`;
|
||||
|
||||
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';
|
||||
div.appendChild(container);
|
||||
|
||||
class MyCustomGraph extends mxGraph {
|
||||
getLabel(cell) {
|
||||
// Returns a HTML representation of the cell where the
|
||||
// upper half is the first value, lower half is second
|
||||
// value
|
||||
|
||||
const table = document.createElement('table');
|
||||
table.style.height = '100%';
|
||||
table.style.width = '100%';
|
||||
|
||||
const body = document.createElement('tbody');
|
||||
const tr1 = document.createElement('tr');
|
||||
const td1 = document.createElement('td');
|
||||
td1.style.textAlign = 'center';
|
||||
td1.style.fontSize = '12px';
|
||||
td1.style.color = '#774400';
|
||||
mxDomUtils.write(td1, cell.value.first);
|
||||
|
||||
const tr2 = document.createElement('tr');
|
||||
const td2 = document.createElement('td');
|
||||
td2.style.textAlign = 'center';
|
||||
td2.style.fontSize = '12px';
|
||||
td2.style.color = '#774400';
|
||||
mxDomUtils.write(td2, cell.value.second);
|
||||
|
||||
tr1.appendChild(td1);
|
||||
tr2.appendChild(td2);
|
||||
body.appendChild(tr1);
|
||||
body.appendChild(tr2);
|
||||
table.appendChild(body);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
getEditingValue(cell, evt) {
|
||||
// Returns the editing value for the given cell and event
|
||||
evt.fieldname = this.__getFieldnameForEvent(cell, evt);
|
||||
return cell.value[evt.fieldname] || '';
|
||||
}
|
||||
|
||||
__getFieldnameForEvent(cell, evt) {
|
||||
// Helper method that returns the fieldname to be used for
|
||||
// a mouse event
|
||||
if (evt != null) {
|
||||
// Finds the relative coordinates inside the cell
|
||||
const point = mxUtils.convertPoint(
|
||||
this.container,
|
||||
mxEventUtils.getClientX(evt),
|
||||
mxEventUtils.getClientY(evt)
|
||||
);
|
||||
const state = this.getView().getState(cell);
|
||||
|
||||
if (state != null) {
|
||||
point.x -= state.x;
|
||||
point.y -= state.y;
|
||||
|
||||
// Returns second if mouse in second half of cell
|
||||
if (point.y > state.height / 2) {
|
||||
return 'second';
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'first';
|
||||
}
|
||||
|
||||
labelChanged(cell, newValue, trigger) {
|
||||
// Sets the new value for the given cell and trigger
|
||||
const name = trigger != null ? trigger.fieldname : null;
|
||||
|
||||
if (name != null) {
|
||||
// Clones the user object for correct undo and puts
|
||||
// the new value in the correct field.
|
||||
const value = mxCloneUtils.clone(cell.value);
|
||||
value[name] = newValue;
|
||||
newValue = value;
|
||||
|
||||
super.labelChanged(cell, newValue, trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new MyCustomGraph(container);
|
||||
graph.setHtmlLabels(true);
|
||||
|
||||
// Adds handling of return and escape keystrokes for editing
|
||||
const keyHandler = new mxKeyHandler(graph);
|
||||
|
||||
// Sample user objects with 2 fields
|
||||
const value = {};
|
||||
value.first = 'First value';
|
||||
value.second = 'Second value';
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value,
|
||||
position: [100, 60],
|
||||
size: [120, 80],
|
||||
style: 'overflow=fill;',
|
||||
});
|
||||
});
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -1,11 +1,19 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import HelloWorld from './HelloWorld.stories';
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Backgrounds/ExtendCanvas',
|
||||
argTypes: {
|
||||
...HelloWorld.argTypes
|
||||
...defaultArgTypes.argTypes,
|
||||
contextMenu: {
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -15,9 +23,7 @@ const Template = ({ label, ...args }) => {
|
|||
mxEvent,
|
||||
mxRubberband,
|
||||
mxRectangle,
|
||||
mxGraphView,
|
||||
mxPoint,
|
||||
mxDomHelpers,
|
||||
mxUtils
|
||||
} = mxgraph;
|
||||
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Connections/FixedPoints',
|
||||
argTypes: {
|
||||
...defaultArgTypes.argTypes,
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxRubberband,
|
||||
mxConnectionHandler,
|
||||
mxConnectionConstraint,
|
||||
mxConstraintHandler,
|
||||
mxPoint,
|
||||
mxCellState,
|
||||
mxEdgeHandler
|
||||
} = 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';
|
||||
|
||||
class MyCustomConstraintHandler extends mxConstraintHandler {
|
||||
// Snaps to fixed points
|
||||
intersects(icon, point, source, existingEdge) {
|
||||
return (
|
||||
!source || existingEdge || mxUtils.intersects(icon.bounds, point)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomConnectionHandler extends mxConnectionHandler {
|
||||
// connectImage = new mxImage('images/connector.gif', 16, 16);
|
||||
|
||||
isConnectableCell(cell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special case: Snaps source of new connections to fixed points
|
||||
* Without a connect preview in connectionHandler.createEdgeState mouseMove
|
||||
* and getSourcePerimeterPoint should be overriden by setting sourceConstraint
|
||||
* sourceConstraint to null in mouseMove and updating it and returning the
|
||||
* nearest point (cp) in getSourcePerimeterPoint (see below)
|
||||
*/
|
||||
updateEdgeState(pt, constraint) {
|
||||
if (pt != null && this.previous != null) {
|
||||
const constraints = this.graph.getAllConnectionConstraints(
|
||||
this.previous
|
||||
);
|
||||
let nearestConstraint = null;
|
||||
let dist = null;
|
||||
|
||||
for (let i = 0; i < constraints.length; i++) {
|
||||
const cp = this.graph.getConnectionPoint(
|
||||
this.previous,
|
||||
constraints[i]
|
||||
);
|
||||
|
||||
if (cp != null) {
|
||||
const tmp =
|
||||
(cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y);
|
||||
|
||||
if (dist == null || tmp < dist) {
|
||||
nearestConstraint = constraints[i];
|
||||
dist = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestConstraint != null) {
|
||||
this.sourceConstraint = nearestConstraint;
|
||||
}
|
||||
|
||||
// In case the edge style must be changed during the preview:
|
||||
// this.edgeState.style['edgeStyle'] = 'orthogonalEdgeStyle';
|
||||
// And to use the new edge style in the new edge inserted into the graph,
|
||||
// update the cell style as follows:
|
||||
// this.edgeState.cell.style = mxUtils.setStyle(this.edgeState.cell.style, 'edgeStyle', this.edgeState.style['edgeStyle']);
|
||||
}
|
||||
return super.updateEdgeState(pt, constraint);
|
||||
}
|
||||
|
||||
createEdgeState(me) {
|
||||
// Connect preview
|
||||
const edge = this.graph.createEdge(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
'edgeStyle=orthogonalEdgeStyle'
|
||||
);
|
||||
|
||||
return new mxCellState(
|
||||
this.graph.view,
|
||||
edge,
|
||||
this.graph.getCellStyle(edge)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomEdgeHandler extends mxEdgeHandler {
|
||||
// Disables floating connections (only use with no connect image)
|
||||
isConnectableCell(cell) {
|
||||
return graph.connectionHandler.isConnectableCell(cell);
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomGraph extends mxGraph {
|
||||
createConnectionHandler() {
|
||||
const r = new MyCustomConnectionHandler();
|
||||
r.constraintHandler = new MyCustomConstraintHandler(this);
|
||||
return r;
|
||||
}
|
||||
|
||||
createEdgeHandler(state, edgeStyle) {
|
||||
const r = new MyCustomEdgeHandler(state, edgeStyle);
|
||||
r.constraintHandler = new MyCustomConstraintHandler(this);
|
||||
return r;
|
||||
}
|
||||
|
||||
getAllConnectionConstraints(terminal) {
|
||||
if (terminal != null && this.model.isVertex(terminal.cell)) {
|
||||
return [
|
||||
new mxConnectionConstraint(new mxPoint(0, 0), true),
|
||||
new mxConnectionConstraint(new mxPoint(0.5, 0), true),
|
||||
new mxConnectionConstraint(new mxPoint(1, 0), true),
|
||||
new mxConnectionConstraint(new mxPoint(0, 0.5), true),
|
||||
new mxConnectionConstraint(new mxPoint(1, 0.5), true),
|
||||
new mxConnectionConstraint(new mxPoint(0, 1), true),
|
||||
new mxConnectionConstraint(new mxPoint(0.5, 1), true),
|
||||
new mxConnectionConstraint(new mxPoint(1, 1), true),
|
||||
];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new MyCustomGraph(container);
|
||||
graph.setConnectable(true);
|
||||
|
||||
// Enables rubberband selection
|
||||
if (args.rubberBand)
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello,',
|
||||
position: [20, 20],
|
||||
size: [80, 60],
|
||||
style: 'shape=triangle;perimeter=trianglePerimeter',
|
||||
});
|
||||
const v2 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'World!',
|
||||
position: [200, 150],
|
||||
size: [80, 60],
|
||||
style: 'shape=ellipse;perimeter=ellipsePerimeter',
|
||||
});
|
||||
const v3 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello,',
|
||||
position: [200, 20],
|
||||
size: [80, 30],
|
||||
});
|
||||
const e1 = graph.insertEdge({
|
||||
parent,
|
||||
value: '',
|
||||
source: v1,
|
||||
target: v2,
|
||||
style:
|
||||
'edgeStyle=elbowEdgeStyle;elbow=horizontal;' +
|
||||
'exitX=0.5;exitY=1;exitPerimeter=1;entryX=0;entryY=0;entryPerimeter=1;',
|
||||
});
|
||||
const e2 = graph.insertEdge({
|
||||
parent,
|
||||
value: '',
|
||||
source: v3,
|
||||
target: v2,
|
||||
style:
|
||||
'edgeStyle=elbowEdgeStyle;elbow=horizontal;orthogonal=0;' +
|
||||
'entryX=0;entryY=0;entryPerimeter=1;',
|
||||
});
|
||||
});
|
||||
|
||||
// Use this code to snap the source point for new connections without a connect preview,
|
||||
// ie. without an overridden graph.connectionHandler.createEdgeState
|
||||
/*
|
||||
let mxConnectionHandlerMouseMove = mxConnectionHandler.prototype.mouseMove;
|
||||
mxConnectionHandler.prototype.mouseMove = function(sender, me)
|
||||
{
|
||||
this.sourceConstraint = null;
|
||||
|
||||
mxConnectionHandlerMouseMove.apply(this, arguments);
|
||||
};
|
||||
|
||||
let mxConnectionHandlerGetSourcePerimeterPoint = mxConnectionHandler.prototype.getSourcePerimeterPoint;
|
||||
mxConnectionHandler.prototype.getSourcePerimeterPoint = function(state, pt, me)
|
||||
{
|
||||
let result = null;
|
||||
|
||||
if (this.previous != null && pt != null)
|
||||
{
|
||||
let constraints = this.graph.getAllConnectionConstraints(this.previous);
|
||||
let nearestConstraint = null;
|
||||
let nearest = null;
|
||||
let dist = null;
|
||||
|
||||
for (let i = 0; i < constraints.length; i++)
|
||||
{
|
||||
let cp = this.graph.getConnectionPoint(this.previous, constraints[i]);
|
||||
|
||||
if (cp != null)
|
||||
{
|
||||
let tmp = (cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y);
|
||||
|
||||
if (dist == null || tmp < dist)
|
||||
{
|
||||
nearestConstraint = constraints[i];
|
||||
nearest = cp;
|
||||
dist = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestConstraint != null)
|
||||
{
|
||||
this.sourceConstraint = nearestConstraint;
|
||||
result = nearest;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = mxConnectionHandlerGetSourcePerimeterPoint.apply(this, arguments);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
*/
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -1,11 +1,19 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import HelloWorld from './HelloWorld.stories';
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Backgrounds/Grid',
|
||||
argTypes: {
|
||||
...HelloWorld.argTypes
|
||||
...defaultArgTypes.argTypes,
|
||||
contextMenu: {
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
},
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Connections/HelloPort',
|
||||
argTypes: {
|
||||
...defaultArgTypes.argTypes,
|
||||
rubberBand: {
|
||||
type: 'boolean',
|
||||
defaultValue: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxRubberband,
|
||||
mxEdgeStyle,
|
||||
mxPoint,
|
||||
mxConstants,
|
||||
mxDomHelpers
|
||||
} = mxgraph;
|
||||
|
||||
const div = document.createElement('div');
|
||||
|
||||
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';
|
||||
div.appendChild(container);
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new mxGraph(container);
|
||||
graph.setConnectable(true);
|
||||
graph.setTooltips(true);
|
||||
|
||||
// Sets the default edge style
|
||||
const style = graph.getStylesheet().getDefaultEdgeStyle();
|
||||
style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
|
||||
|
||||
// Ports are not used as terminals for edges, they are
|
||||
// only used to compute the graphical connection point
|
||||
graph.isPort = function(cell) {
|
||||
const geo = this.getCellGeometry(cell);
|
||||
|
||||
return geo != null ? geo.relative : false;
|
||||
};
|
||||
|
||||
// Implements a tooltip that shows the actual
|
||||
// source and target of an edge
|
||||
graph.getTooltipForCell = function(cell) {
|
||||
if (this.model.isEdge(cell)) {
|
||||
return `${this.convertValueToString(
|
||||
this.model.getTerminal(cell, true)
|
||||
)} => ${this.convertValueToString(
|
||||
this.model.getTerminal(cell, false)
|
||||
)}`;
|
||||
}
|
||||
|
||||
return mxGraph.prototype.getTooltipForCell.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Removes the folding icon and disables any folding
|
||||
graph.isCellFoldable = function(cell) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Enables rubberband selection
|
||||
if (args.rubberBand)
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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 v1 = graph.insertVertex(parent, null, 'Hello', 20, 80, 80, 30);
|
||||
v1.setConnectable(false);
|
||||
const v11 = graph.insertVertex(v1, null, '', 1, 1, 10, 10);
|
||||
v11.geometry.offset = new mxPoint(-5, -5);
|
||||
v11.geometry.relative = true;
|
||||
const v12 = graph.insertVertex(v1, null, '', 1, 0, 10, 10);
|
||||
v12.geometry.offset = new mxPoint(-5, -5);
|
||||
v12.geometry.relative = true;
|
||||
const v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
|
||||
const v3 = graph.insertVertex(parent, null, 'World2', 200, 20, 80, 30);
|
||||
var e1 = graph.insertEdge(parent, null, '', v11, v2);
|
||||
var e1 = graph.insertEdge(parent, null, '', v12, v3);
|
||||
} finally {
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
}
|
||||
|
||||
const controller = document.createElement('div');
|
||||
div.appendChild(controller);
|
||||
|
||||
const button = mxDomHelpers.button('View XML', function() {
|
||||
const encoder = new mxCodec();
|
||||
const node = encoder.encode(graph.getModel());
|
||||
mxUtils.popup(mxUtils.getPrettyXml(node), true);
|
||||
});
|
||||
|
||||
controller.appendChild(button);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -1,16 +1,11 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Basic/HelloWorld',
|
||||
argTypes: {
|
||||
width: {
|
||||
type: 'number',
|
||||
defaultValue: 800
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
defaultValue: 600
|
||||
},
|
||||
...defaultArgTypes,
|
||||
contextMenu: {
|
||||
type: 'boolean',
|
||||
defaultValue: false
|
||||
|
@ -30,6 +25,7 @@ const Template = ({ label, ...args }) => {
|
|||
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';
|
||||
|
||||
if (!args.contextMenu)
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Connections/Orthogonal',
|
||||
argTypes: {
|
||||
...defaultArgTypes.argTypes
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxRubberband,
|
||||
mxConnectionHandler,
|
||||
mxGraphHandler,
|
||||
mxGuide,
|
||||
mxPoint,
|
||||
mxCellState,
|
||||
mxEdgeHandler,
|
||||
mxGraphView
|
||||
} = 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';
|
||||
|
||||
// Enables guides
|
||||
mxGraphHandler.prototype.guidesEnabled = true;
|
||||
|
||||
// Alt disables guides
|
||||
mxGuide.prototype.isEnabledForEvent = function(evt) {
|
||||
return !mxEvent.isAltDown(evt);
|
||||
};
|
||||
|
||||
// Enables snapping waypoints to terminals
|
||||
mxEdgeHandler.prototype.snapToTerminals = true;
|
||||
|
||||
// Enables orthogonal connect preview in IE
|
||||
mxConnectionHandler.prototype.movePreviewAway = false;
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new mxGraph(container);
|
||||
graph.disconnectOnMove = false;
|
||||
graph.foldingEnabled = false;
|
||||
graph.cellsResizable = false;
|
||||
graph.extendParents = false;
|
||||
graph.setConnectable(true);
|
||||
|
||||
// Implements perimeter-less connection points as fixed points (computed before the edge style).
|
||||
graph.view.updateFixedTerminalPoint = function(
|
||||
edge,
|
||||
terminal,
|
||||
source,
|
||||
constraint
|
||||
) {
|
||||
mxGraphView.prototype.updateFixedTerminalPoint.apply(this, arguments);
|
||||
|
||||
const pts = edge.absolutePoints;
|
||||
const pt = pts[source ? 0 : pts.length - 1];
|
||||
|
||||
if (
|
||||
terminal != null &&
|
||||
pt == null &&
|
||||
this.getPerimeterFunction(terminal) == null
|
||||
) {
|
||||
edge.setAbsoluteTerminalPoint(
|
||||
new mxPoint(
|
||||
this.getRoutingCenterX(terminal),
|
||||
this.getRoutingCenterY(terminal)
|
||||
),
|
||||
source
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Changes the default edge style
|
||||
graph.getStylesheet().getDefaultEdgeStyle().edgeStyle =
|
||||
'orthogonalEdgeStyle';
|
||||
delete graph.getStylesheet().getDefaultEdgeStyle().endArrow;
|
||||
|
||||
// Implements the connect preview
|
||||
graph.connectionHandler.createEdgeState = function(me) {
|
||||
const edge = graph.createEdge(null, null, null, null, null);
|
||||
|
||||
return new mxCellState(
|
||||
this.graph.view,
|
||||
edge,
|
||||
this.graph.getCellStyle(edge)
|
||||
);
|
||||
};
|
||||
|
||||
// Uncomment the following if you want the container
|
||||
// to fit the size of the graph
|
||||
// graph.setResizeContainer(true);
|
||||
|
||||
// Enables rubberband selection
|
||||
if (args.rubberBand)
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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 v1 = graph.insertVertex(parent, null, '', 40, 40, 40, 30);
|
||||
v1.setConnectable(false);
|
||||
const v11 = graph.insertVertex(
|
||||
v1,
|
||||
null,
|
||||
'',
|
||||
0.5,
|
||||
0,
|
||||
10,
|
||||
40,
|
||||
'portConstraint=northsouth;',
|
||||
true
|
||||
);
|
||||
v11.geometry.offset = new mxPoint(-5, -5);
|
||||
const v12 = graph.insertVertex(
|
||||
v1,
|
||||
null,
|
||||
'',
|
||||
0,
|
||||
0.5,
|
||||
10,
|
||||
10,
|
||||
'portConstraint=west;shape=triangle;direction=west;perimeter=none;' +
|
||||
'routingCenterX=-0.5;routingCenterY=0;',
|
||||
true
|
||||
);
|
||||
v12.geometry.offset = new mxPoint(-10, -5);
|
||||
const v13 = graph.insertVertex(
|
||||
v1,
|
||||
null,
|
||||
'',
|
||||
1,
|
||||
0.5,
|
||||
10,
|
||||
10,
|
||||
'portConstraint=east;shape=triangle;direction=east;perimeter=none;' +
|
||||
'routingCenterX=0.5;routingCenterY=0;',
|
||||
true
|
||||
);
|
||||
v13.geometry.offset = new mxPoint(0, -5);
|
||||
|
||||
const v2 = graph.addCell(graph.getModel().cloneCell(v1));
|
||||
v2.geometry.x = 200;
|
||||
v2.geometry.y = 60;
|
||||
|
||||
const v3 = graph.addCell(graph.getModel().cloneCell(v1));
|
||||
v3.geometry.x = 40;
|
||||
v3.geometry.y = 150;
|
||||
|
||||
const v4 = graph.addCell(graph.getModel().cloneCell(v1));
|
||||
v4.geometry.x = 200;
|
||||
v4.geometry.y = 170;
|
||||
|
||||
graph.insertEdge(parent, null, '', v1.getChildAt(2), v2.getChildAt(1));
|
||||
graph.insertEdge(parent, null, '', v2.getChildAt(2), v3.getChildAt(1));
|
||||
graph.insertEdge(parent, null, '', v3.getChildAt(2), v4.getChildAt(1));
|
||||
} finally {
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -0,0 +1,279 @@
|
|||
import mxgraph from '@mxgraph/core';
|
||||
|
||||
import { defaultArgTypes } from '../.storybook/preview';
|
||||
|
||||
export default {
|
||||
title: 'Connections/PortRefs',
|
||||
argTypes: {
|
||||
...defaultArgTypes.argTypes
|
||||
}
|
||||
};
|
||||
|
||||
const Template = ({ label, ...args }) => {
|
||||
const {
|
||||
mxGraph,
|
||||
mxRubberband,
|
||||
mxPoint,
|
||||
mxEdgeHandler,
|
||||
mxConstraintHandler,
|
||||
mxImage,
|
||||
mxShape,
|
||||
mxTriangle,
|
||||
mxConstants,
|
||||
mxConnectionConstraint
|
||||
} = 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';
|
||||
|
||||
// Replaces the port image
|
||||
mxConstraintHandler.prototype.pointImage = new mxImage(
|
||||
'/images/dot.gif',
|
||||
10,
|
||||
10
|
||||
);
|
||||
|
||||
const graph = new mxGraph(container);
|
||||
graph.setConnectable(true);
|
||||
|
||||
// Disables automatic handling of ports. This disables the reset of the
|
||||
// respective style in mxGraph.cellConnected. Note that this feature may
|
||||
// be useful if floating and fixed connections are combined.
|
||||
graph.setPortsEnabled(false);
|
||||
|
||||
// Enables rubberband selection
|
||||
new mxRubberband(graph);
|
||||
|
||||
// Gets the default parent for inserting new cells. This
|
||||
// is normally the first child of the root (ie. layer 0).
|
||||
const parent = graph.getDefaultParent();
|
||||
|
||||
// Ports are equal for all shapes...
|
||||
const ports = new Array();
|
||||
|
||||
// NOTE: Constraint is used later for orthogonal edge routing (currently ignored)
|
||||
ports.w = { x: 0, y: 0.5, perimeter: true, constraint: 'west' };
|
||||
ports.e = { x: 1, y: 0.5, perimeter: true, constraint: 'east' };
|
||||
ports.n = { x: 0.5, y: 0, perimeter: true, constraint: 'north' };
|
||||
ports.s = { x: 0.5, y: 1, perimeter: true, constraint: 'south' };
|
||||
ports.nw = { x: 0, y: 0, perimeter: true, constraint: 'north west' };
|
||||
ports.ne = { x: 1, y: 0, perimeter: true, constraint: 'north east' };
|
||||
ports.sw = { x: 0, y: 1, perimeter: true, constraint: 'south west' };
|
||||
ports.se = { x: 1, y: 1, perimeter: true, constraint: 'south east' };
|
||||
|
||||
// ... except for triangles
|
||||
const ports2 = new Array();
|
||||
|
||||
// NOTE: Constraint is used later for orthogonal edge routing (currently ignored)
|
||||
ports2.in1 = { x: 0, y: 0, perimeter: true, constraint: 'west' };
|
||||
ports2.in2 = { x: 0, y: 0.25, perimeter: true, constraint: 'west' };
|
||||
ports2.in3 = { x: 0, y: 0.5, perimeter: true, constraint: 'west' };
|
||||
ports2.in4 = { x: 0, y: 0.75, perimeter: true, constraint: 'west' };
|
||||
ports2.in5 = { x: 0, y: 1, perimeter: true, constraint: 'west' };
|
||||
|
||||
ports2.out1 = {
|
||||
x: 0.5,
|
||||
y: 0,
|
||||
perimeter: true,
|
||||
constraint: 'north east',
|
||||
};
|
||||
ports2.out2 = { x: 1, y: 0.5, perimeter: true, constraint: 'east' };
|
||||
ports2.out3 = {
|
||||
x: 0.5,
|
||||
y: 1,
|
||||
perimeter: true,
|
||||
constraint: 'south east',
|
||||
};
|
||||
|
||||
// Extends shapes classes to return their ports
|
||||
mxShape.prototype.getPorts = function() {
|
||||
return ports;
|
||||
};
|
||||
|
||||
mxTriangle.prototype.getPorts = function() {
|
||||
return ports2;
|
||||
};
|
||||
|
||||
// Disables floating connections (only connections via ports allowed)
|
||||
graph.connectionHandler.isConnectableCell = function(cell) {
|
||||
return false;
|
||||
};
|
||||
mxEdgeHandler.prototype.isConnectableCell = function(cell) {
|
||||
return graph.connectionHandler.isConnectableCell(cell);
|
||||
};
|
||||
|
||||
// Disables existing port functionality
|
||||
graph.view.getTerminalPort = function(state, terminal, source) {
|
||||
return terminal;
|
||||
};
|
||||
|
||||
// Returns all possible ports for a given terminal
|
||||
graph.getAllConnectionConstraints = function(terminal, source) {
|
||||
if (
|
||||
terminal != null &&
|
||||
terminal.shape != null &&
|
||||
terminal.shape.stencil != null
|
||||
) {
|
||||
// for stencils with existing constraints...
|
||||
if (terminal.shape.stencil != null) {
|
||||
return terminal.shape.stencil.constraints;
|
||||
}
|
||||
} else if (terminal != null && this.model.isVertex(terminal.cell)) {
|
||||
if (terminal.shape != null) {
|
||||
const ports = terminal.shape.getPorts();
|
||||
const cstrs = new Array();
|
||||
|
||||
for (const id in ports) {
|
||||
const port = ports[id];
|
||||
|
||||
const cstr = new mxConnectionConstraint(
|
||||
new mxPoint(port.x, port.y),
|
||||
port.perimeter
|
||||
);
|
||||
cstr.id = id;
|
||||
cstrs.push(cstr);
|
||||
}
|
||||
|
||||
return cstrs;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Sets the port for the given connection
|
||||
graph.setConnectionConstraint = function(
|
||||
edge,
|
||||
terminal,
|
||||
source,
|
||||
constraint
|
||||
) {
|
||||
if (constraint != null) {
|
||||
const key = source
|
||||
? mxConstants.STYLE_SOURCE_PORT
|
||||
: mxConstants.STYLE_TARGET_PORT;
|
||||
|
||||
if (constraint == null || constraint.id == null) {
|
||||
this.setCellStyles(key, null, [edge]);
|
||||
} else if (constraint.id != null) {
|
||||
this.setCellStyles(key, constraint.id, [edge]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Returns the port for the given connection
|
||||
graph.getConnectionConstraint = function(edge, terminal, source) {
|
||||
const key = source
|
||||
? mxConstants.STYLE_SOURCE_PORT
|
||||
: mxConstants.STYLE_TARGET_PORT;
|
||||
const id = edge.style[key];
|
||||
|
||||
if (id != null) {
|
||||
const c = new mxConnectionConstraint(null, null);
|
||||
c.id = id;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Returns the actual point for a port by redirecting the constraint to the port
|
||||
const graphGetConnectionPoint = graph.getConnectionPoint;
|
||||
graph.getConnectionPoint = function(vertex, constraint) {
|
||||
if (constraint.id != null && vertex != null && vertex.shape != null) {
|
||||
const port = vertex.shape.getPorts()[constraint.id];
|
||||
|
||||
if (port != null) {
|
||||
constraint = new mxConnectionConstraint(
|
||||
new mxPoint(port.x, port.y),
|
||||
port.perimeter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return graphGetConnectionPoint.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Adds cells to the model in a single step
|
||||
graph.getModel().beginUpdate();
|
||||
try {
|
||||
const v1 = graph.insertVertex(parent, null, 'A', 20, 20, 100, 40);
|
||||
const v2 = graph.insertVertex(
|
||||
parent,
|
||||
null,
|
||||
'B',
|
||||
80,
|
||||
100,
|
||||
100,
|
||||
100,
|
||||
'shape=ellipse;perimeter=ellipsePerimeter'
|
||||
);
|
||||
const v3 = graph.insertVertex(
|
||||
parent,
|
||||
null,
|
||||
'C',
|
||||
190,
|
||||
30,
|
||||
100,
|
||||
60,
|
||||
'shape=triangle;perimeter=trianglePerimeter;direction=south'
|
||||
);
|
||||
const e1 = graph.insertEdge(
|
||||
parent,
|
||||
null,
|
||||
'',
|
||||
v1,
|
||||
v2,
|
||||
'sourcePort=s;targetPort=nw'
|
||||
);
|
||||
const e2 = graph.insertEdge(
|
||||
parent,
|
||||
null,
|
||||
'',
|
||||
v1,
|
||||
v3,
|
||||
'sourcePort=e;targetPort=out3'
|
||||
);
|
||||
} finally {
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
}
|
||||
|
||||
// Comming soon... Integration with orthogonal edge style
|
||||
// Sets default edge style to use port constraints (needs to be moved up when uncommented)
|
||||
// graph.getStylesheet().getDefaultEdgeStyle()['edgeStyle'] = 'orthogonalEdgeStyle';
|
||||
/* let mxUtilsGetPortConstraints = mxUtils.getPortConstraints;
|
||||
mxUtils.getPortConstraints = function(terminal, edge, source, defaultValue)
|
||||
{
|
||||
let key = (source) ? mxConstants.STYLE_SOURCE_PORT : mxConstants.STYLE_TARGET_PORT;
|
||||
let id = edge.style[key];
|
||||
|
||||
let port = terminal.shape.getPorts()[id];
|
||||
|
||||
// TODO: Add support for rotation, direction
|
||||
if (port != null)
|
||||
{
|
||||
return port.constraint;
|
||||
}
|
||||
|
||||
return mxUtilsGetPortConstraints.apply(this, arguments);
|
||||
};
|
||||
// Connect preview
|
||||
graph.connectionHandler.createEdgeState = function(me)
|
||||
{
|
||||
let edge = graph.createEdge(null, null, null, null, null);
|
||||
|
||||
return new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge));
|
||||
};
|
||||
*/
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
export const Default = Template.bind({});
|
|
@ -1,270 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
import mxRectangle from '../../mxgraph/util/datatypes/mxRectangle';
|
||||
import mxUtils from '../../mxgraph/util/mxUtils';
|
||||
import mxPoint from '../../mxgraph/util/datatypes/mxPoint';
|
||||
|
||||
class ExtendCanvas extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Extend canvas</h1>
|
||||
This example demonstrates implementing an infinite canvas with
|
||||
scrollbars.
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
height: '241px',
|
||||
background: "url('editors/images/grid.gif')",
|
||||
cursor: 'default',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Disables the built-in context menu
|
||||
mxEvent.disableContextMenu(this.el);
|
||||
|
||||
/**
|
||||
* Specifies the size of the size for "tiles" to be used for a graph with
|
||||
* scrollbars but no visible background page. A good value is large
|
||||
* enough to reduce the number of repaints that is caused for auto-
|
||||
* translation, which depends on this value, and small enough to give
|
||||
* a small empty buffer around the graph. Default is 400x400.
|
||||
*/
|
||||
const scrollTileSize = new mxRectangle(0, 0, 400, 400);
|
||||
|
||||
class MyCustomGraph extends mxGraph {
|
||||
/**
|
||||
* Returns the padding for pages in page view with scrollbars.
|
||||
*/
|
||||
getPagePadding() {
|
||||
return new mxPoint(
|
||||
Math.max(0, Math.round(this.container.offsetWidth - 34)),
|
||||
Math.max(0, Math.round(this.container.offsetHeight - 34))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the page format scaled with the page size.
|
||||
*/
|
||||
getPageSize() {
|
||||
return this.pageVisible
|
||||
? new mxRectangle(
|
||||
0,
|
||||
0,
|
||||
this.pageFormat.width * this.pageScale,
|
||||
this.pageFormat.height * this.pageScale
|
||||
)
|
||||
: scrollTileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rectangle describing the position and count of the
|
||||
* background pages, where x and y are the position of the top,
|
||||
* left page and width and height are the vertical and horizontal
|
||||
* page count.
|
||||
*/
|
||||
getPageLayout() {
|
||||
const size = this.pageVisible ? this.getPageSize() : scrollTileSize;
|
||||
const bounds = this.getGraphBounds();
|
||||
|
||||
if (bounds.width === 0 || bounds.height === 0) {
|
||||
return new mxRectangle(0, 0, 1, 1);
|
||||
}
|
||||
|
||||
// Computes untransformed graph bounds
|
||||
const x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x);
|
||||
const y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y);
|
||||
const w = Math.floor(bounds.width / this.view.scale);
|
||||
const h = Math.floor(bounds.height / this.view.scale);
|
||||
|
||||
const x0 = Math.floor(x / size.width);
|
||||
const y0 = Math.floor(y / size.height);
|
||||
const w0 = Math.ceil((x + w) / size.width) - x0;
|
||||
const h0 = Math.ceil((y + h) / size.height) - y0;
|
||||
|
||||
return new mxRectangle(x0, y0, w0, h0);
|
||||
}
|
||||
|
||||
getPreferredPageSize(bounds, width, height) {
|
||||
const pages = this.getPageLayout();
|
||||
const size = this.getPageSize();
|
||||
|
||||
return new mxRectangle(
|
||||
0,
|
||||
0,
|
||||
pages.width * size.width,
|
||||
pages.height * size.height
|
||||
);
|
||||
}
|
||||
|
||||
sizeDidChange() {
|
||||
if (this.container != null && mxUtils.hasScrollbars(this.container)) {
|
||||
const pages = this.getPageLayout();
|
||||
const pad = this.getPagePadding();
|
||||
const size = this.getPageSize();
|
||||
|
||||
// Updates the minimum graph size
|
||||
const minw = Math.ceil(
|
||||
(2 * pad.x) / this.view.scale + pages.width * size.width
|
||||
);
|
||||
const minh = Math.ceil(
|
||||
(2 * pad.y) / this.view.scale + pages.height * size.height
|
||||
);
|
||||
|
||||
const min = this.minimumGraphSize;
|
||||
|
||||
// LATER: Fix flicker of scrollbar size in IE quirks mode
|
||||
// after delayed call in window.resize event handler
|
||||
if (min == null || min.width !== minw || min.height !== minh) {
|
||||
this.minimumGraphSize = new mxRectangle(0, 0, minw, minh);
|
||||
}
|
||||
|
||||
// Updates auto-translate to include padding and graph size
|
||||
const dx = pad.x / this.view.scale - pages.x * size.width;
|
||||
const dy = pad.y / this.view.scale - pages.y * size.height;
|
||||
|
||||
if (
|
||||
!this.autoTranslate &&
|
||||
(this.view.translate.x !== dx || this.view.translate.y !== dy)
|
||||
) {
|
||||
this.autoTranslate = true;
|
||||
this.view.x0 = pages.x;
|
||||
this.view.y0 = pages.y;
|
||||
|
||||
// NOTE: THIS INVOKES THIS METHOD AGAIN. UNFORTUNATELY THERE IS NO WAY AROUND THIS SINCE THE
|
||||
// BOUNDS ARE KNOWN AFTER THE VALIDATION AND SETTING THE TRANSLATE TRIGGERS A REVALIDATION.
|
||||
// SHOULD MOVE TRANSLATE/SCALE TO VIEW.
|
||||
const tx = this.view.translate.x;
|
||||
const ty = this.view.translate.y;
|
||||
|
||||
this.view.setTranslate(dx, dy);
|
||||
this.container.scrollLeft += (dx - tx) * this.view.scale;
|
||||
this.container.scrollTop += (dy - ty) * this.view.scale;
|
||||
|
||||
this.autoTranslate = false;
|
||||
return;
|
||||
}
|
||||
super.sizeDidChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = (this.graph = new MyCustomGraph(this.el));
|
||||
graph.panningHandler.ignoreCell = true;
|
||||
graph.setPanning(true);
|
||||
|
||||
// Fits the number of background pages to the graph
|
||||
graph.view.getBackgroundPageBounds = function() {
|
||||
const layout = this.graph.getPageLayout();
|
||||
const page = this.graph.getPageSize();
|
||||
|
||||
return new mxRectangle(
|
||||
this.scale * (this.translate.x + layout.x * page.width),
|
||||
this.scale * (this.translate.y + layout.y * page.height),
|
||||
this.scale * layout.width * page.width,
|
||||
this.scale * layout.height * page.height
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Guesses autoTranslate to avoid another repaint (see below).
|
||||
* Works if only the scale of the graph changes or if pages
|
||||
* are visible and the visible pages do not change.
|
||||
*/
|
||||
const graphViewValidate = graph.view.validate;
|
||||
graph.view.validate = function() {
|
||||
if (
|
||||
this.graph.container != null &&
|
||||
mxUtils.hasScrollbars(this.graph.container)
|
||||
) {
|
||||
const pad = this.graph.getPagePadding();
|
||||
const size = this.graph.getPageSize();
|
||||
|
||||
// Updating scrollbars here causes flickering in quirks and is not needed
|
||||
// if zoom method is always used to set the current scale on the graph.
|
||||
const tx = this.translate.x;
|
||||
const ty = this.translate.y;
|
||||
this.translate.x = pad.x / this.scale - (this.x0 || 0) * size.width;
|
||||
this.translate.y = pad.y / this.scale - (this.y0 || 0) * size.height;
|
||||
}
|
||||
|
||||
graphViewValidate.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Enables rubberband selection
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello,',
|
||||
position: [20, 20],
|
||||
size: [80, 30],
|
||||
});
|
||||
const v2 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'World!',
|
||||
position: [200, 150],
|
||||
size: [80, 30],
|
||||
});
|
||||
const e1 = graph.insertEdge({
|
||||
parent,
|
||||
source: v1,
|
||||
target: v2,
|
||||
});
|
||||
});
|
||||
|
||||
// Sets initial scrollbar positions
|
||||
window.setTimeout(() => {
|
||||
const bounds = graph.getGraphBounds();
|
||||
const width = Math.max(
|
||||
bounds.width,
|
||||
scrollTileSize.width * graph.view.scale
|
||||
);
|
||||
const height = Math.max(
|
||||
bounds.height,
|
||||
scrollTileSize.height * graph.view.scale
|
||||
);
|
||||
graph.container.scrollTop = Math.floor(
|
||||
Math.max(
|
||||
0,
|
||||
bounds.y - Math.max(20, (graph.container.clientHeight - height) / 4)
|
||||
)
|
||||
);
|
||||
graph.container.scrollLeft = Math.floor(
|
||||
Math.max(
|
||||
0,
|
||||
bounds.x - Math.max(0, (graph.container.clientWidth - width) / 2)
|
||||
)
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExtendCanvas;
|
|
@ -1,218 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
import mxPoint from '../../mxgraph/util/datatypes/mxPoint';
|
||||
import mxLog from '../../mxgraph/util/gui/mxLog';
|
||||
import mxUtils from '../../mxgraph/util/mxUtils';
|
||||
import mxGraphView from '../../mxgraph/view/graph/mxGraphView';
|
||||
|
||||
class Grid extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Grid</h1>
|
||||
This example demonstrates drawing a grid dynamically using HTML 5
|
||||
canvas.
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
height: '481px',
|
||||
cursor: 'default',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
ref={el => {
|
||||
this.el2 = el;
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
mxEvent.disableContextMenu(this.el);
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new mxGraph(this.el);
|
||||
graph.graphHandler.scaleGrid = true;
|
||||
graph.setPanning(true);
|
||||
|
||||
// Enables rubberband selection
|
||||
new mxRubberband(graph);
|
||||
|
||||
let repaintGrid;
|
||||
|
||||
// Create grid dynamically (requires canvas)
|
||||
(function() {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.style.position = 'absolute';
|
||||
canvas.style.top = '0px';
|
||||
canvas.style.left = '0px';
|
||||
canvas.style.zIndex = -1;
|
||||
graph.container.appendChild(canvas);
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Modify event filtering to accept canvas as container
|
||||
const mxGraphViewIsContainerEvent =
|
||||
mxGraphView.prototype.isContainerEvent;
|
||||
mxGraphView.prototype.isContainerEvent = function(evt) {
|
||||
return (
|
||||
mxGraphViewIsContainerEvent.apply(this, arguments) ||
|
||||
mxEvent.getSource(evt) === canvas
|
||||
);
|
||||
};
|
||||
|
||||
let s = 0;
|
||||
let gs = 0;
|
||||
let tr = new mxPoint();
|
||||
let w = 0;
|
||||
let h = 0;
|
||||
|
||||
repaintGrid = function() {
|
||||
if (ctx != null) {
|
||||
const bounds = graph.getGraphBounds();
|
||||
const width = Math.max(
|
||||
bounds.x + bounds.width,
|
||||
graph.container.clientWidth
|
||||
);
|
||||
const height = Math.max(
|
||||
bounds.y + bounds.height,
|
||||
graph.container.clientHeight
|
||||
);
|
||||
const sizeChanged = width !== w || height !== h;
|
||||
|
||||
if (
|
||||
graph.view.scale !== s ||
|
||||
graph.view.translate.x !== tr.x ||
|
||||
graph.view.translate.y !== tr.y ||
|
||||
gs !== graph.gridSize ||
|
||||
sizeChanged
|
||||
) {
|
||||
tr = graph.view.translate.clone();
|
||||
s = graph.view.scale;
|
||||
gs = graph.gridSize;
|
||||
w = width;
|
||||
h = height;
|
||||
|
||||
// Clears the background if required
|
||||
if (!sizeChanged) {
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
} else {
|
||||
canvas.setAttribute('width', w);
|
||||
canvas.setAttribute('height', h);
|
||||
}
|
||||
|
||||
const tx = tr.x * s;
|
||||
const ty = tr.y * s;
|
||||
|
||||
// Sets the distance of the grid lines in pixels
|
||||
const minStepping = graph.gridSize;
|
||||
let stepping = minStepping * s;
|
||||
|
||||
if (stepping < minStepping) {
|
||||
const count =
|
||||
Math.round(Math.ceil(minStepping / stepping) / 2) * 2;
|
||||
stepping = count * stepping;
|
||||
}
|
||||
|
||||
const xs = Math.floor((0 - tx) / stepping) * stepping + tx;
|
||||
let xe = Math.ceil(w / stepping) * stepping;
|
||||
const ys = Math.floor((0 - ty) / stepping) * stepping + ty;
|
||||
let ye = Math.ceil(h / stepping) * stepping;
|
||||
|
||||
xe += Math.ceil(stepping);
|
||||
ye += Math.ceil(stepping);
|
||||
|
||||
const ixs = Math.round(xs);
|
||||
const ixe = Math.round(xe);
|
||||
const iys = Math.round(ys);
|
||||
const iye = Math.round(ye);
|
||||
|
||||
// Draws the actual grid
|
||||
ctx.strokeStyle = '#f6f6f6';
|
||||
ctx.beginPath();
|
||||
|
||||
for (let x = xs; x <= xe; x += stepping) {
|
||||
x = Math.round((x - tx) / stepping) * stepping + tx;
|
||||
const ix = Math.round(x);
|
||||
|
||||
ctx.moveTo(ix + 0.5, iys + 0.5);
|
||||
ctx.lineTo(ix + 0.5, iye + 0.5);
|
||||
}
|
||||
|
||||
for (let y = ys; y <= ye; y += stepping) {
|
||||
y = Math.round((y - ty) / stepping) * stepping + ty;
|
||||
const iy = Math.round(y);
|
||||
|
||||
ctx.moveTo(ixs + 0.5, iy + 0.5);
|
||||
ctx.lineTo(ixe + 0.5, iy + 0.5);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
mxLog.show();
|
||||
mxLog.debug('Using background image');
|
||||
|
||||
this.el.style.backgroundImage = "url('editors/images/grid.gif')";
|
||||
}
|
||||
|
||||
const mxGraphViewValidateBackground =
|
||||
mxGraphView.prototype.validateBackground;
|
||||
mxGraphView.prototype.validateBackground = function() {
|
||||
mxGraphViewValidateBackground.apply(this, arguments);
|
||||
repaintGrid();
|
||||
};
|
||||
})();
|
||||
|
||||
// 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 v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);
|
||||
const v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
|
||||
const e1 = graph.insertEdge(parent, null, '', v1, v2);
|
||||
} finally {
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
}
|
||||
|
||||
graph.centerZoom = false;
|
||||
|
||||
this.el2.appendChild(
|
||||
mxUtils.button('+', function() {
|
||||
graph.zoomIn();
|
||||
})
|
||||
);
|
||||
|
||||
this.el2.appendChild(
|
||||
mxUtils.button('-', function() {
|
||||
graph.zoomOut();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Grid;
|
|
@ -1,14 +0,0 @@
|
|||
import React from 'react';
|
||||
import Grid from './Grid';
|
||||
import Preview from '../Previews';
|
||||
import ExtendCanvas from './ExtendCanvas';
|
||||
import PageTabs from '../PageTabs';
|
||||
|
||||
export default function _Backgrounds() {
|
||||
return (
|
||||
<PageTabs curPageURL="/backgrounds">
|
||||
<Preview sourceKey="ExtendCanvas" content={<ExtendCanvas />} />
|
||||
<Preview sourceKey="Grid" content={<Grid />} />
|
||||
</PageTabs>
|
||||
);
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2006-2018, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
|
||||
class HelloWorld extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph with a grid wallpaper
|
||||
return (
|
||||
<>
|
||||
<h1>Hello, World!</h1>
|
||||
This example demonstrates using a DOM node to create a graph and adding
|
||||
vertices and edges.
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
height: '241px',
|
||||
background: "url('editors/images/grid.gif')",
|
||||
cursor: 'default',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Create a sample graph in the DOM node with the specified ID.
|
||||
mxEvent.disableContextMenu(this.el); // Disable the built-in context menu
|
||||
const graph = new mxGraph(this.el); // Create the graph inside the given container
|
||||
new mxRubberband(graph); // Enable rubberband selection
|
||||
|
||||
// Get the default parent for inserting new cells. This
|
||||
// is normally the first child of the root (ie. layer 0).
|
||||
const parent = graph.getDefaultParent();
|
||||
|
||||
graph.batchUpdate(() => {
|
||||
// Add cells to the model in a single step
|
||||
const vertex1 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello',
|
||||
position: [20, 20],
|
||||
size: [80, 30],
|
||||
relative: false,
|
||||
});
|
||||
const vertex2 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'World!',
|
||||
position: [200, 150],
|
||||
size: [80, 30],
|
||||
relative: false,
|
||||
});
|
||||
const edge = graph.insertEdge({
|
||||
parent,
|
||||
// value: 'to the',
|
||||
source: vertex1,
|
||||
target: vertex2,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default HelloWorld;
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
|
||||
Template. This is used as a template HTML file by the
|
||||
backends to demonstrate the deployment of the client with a graph embedded
|
||||
in the page as XML data (see graph variable in the onload-handler).
|
||||
|
||||
*** THIS FILE MUST BE DEPLOYED BY ONE OF THE BACKENDS! ***
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
import mxUtils from '../../mxgraph/util/mxUtils';
|
||||
import mxCodec from '../../mxgraph/serialization/mxCodec';
|
||||
|
||||
class Template extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Hello, World!</h1>
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
height: '241px',
|
||||
background:
|
||||
"url('/mxgraph/javascript/examples/editors/images/grid.gif')",
|
||||
cursor: 'default',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Creates the graph inside the given container
|
||||
const graph = new mxGraph(this.el);
|
||||
|
||||
// Adds rubberband selection to the graph
|
||||
new mxRubberband(graph);
|
||||
|
||||
const doc = mxUtils.parseXml(xml);
|
||||
const codec = new mxCodec(doc);
|
||||
codec.decode(doc.documentElement, graph.getModel());
|
||||
}
|
||||
}
|
||||
|
||||
export default Template;
|
|
@ -1,15 +0,0 @@
|
|||
import React from 'react';
|
||||
import HelloWorld from './HelloWorld';
|
||||
import Preview from '../Previews';
|
||||
import PageTabs from '../PageTabs';
|
||||
|
||||
export default function _Basic() {
|
||||
{
|
||||
/* <Preview sourceKey="Template" content={<Template />} /> */
|
||||
}
|
||||
return (
|
||||
<PageTabs curPageURL="/basic">
|
||||
<Preview sourceKey="HelloWorld" content={<HelloWorld />} />
|
||||
</PageTabs>
|
||||
);
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
import mxShape from '../../mxgraph/shape/mxShape';
|
||||
import mxConnectionConstraint from '../../mxgraph/view/connection/mxConnectionConstraint';
|
||||
import mxPoint from '../../mxgraph/util/datatypes/mxPoint';
|
||||
import mxPolyline from '../../mxgraph/shape/edge/mxPolyline';
|
||||
import mxCellState from '../../mxgraph/view/cell/mxCellState';
|
||||
import mxGeometry from '../../mxgraph/util/datatypes/mxGeometry';
|
||||
import mxConnectionHandler from '../../mxgraph/handler/mxConnectionHandler';
|
||||
|
||||
class Anchors extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Anchors</h1>
|
||||
This example demonstrates defining fixed connection points for all
|
||||
shapes.
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
height: '241px',
|
||||
background: 'url("editors/images/grid.gif")',
|
||||
cursor: 'default',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Disables the built-in context menu
|
||||
mxEvent.disableContextMenu(this.el);
|
||||
|
||||
class MyCustomConnectionHandler extends mxConnectionHandler {
|
||||
// Enables connect preview for the default edge style
|
||||
createEdgeState(me) {
|
||||
const edge = graph.createEdge(null, null, null, null, null);
|
||||
return new mxCellState(
|
||||
this.graph.view,
|
||||
edge,
|
||||
this.graph.getCellStyle(edge)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomGraph extends mxGraph {
|
||||
getAllConnectionConstraints(terminal, source) {
|
||||
// Overridden to define per-shape connection points
|
||||
if (terminal != null && terminal.shape != null) {
|
||||
if (terminal.shape.stencil != null) {
|
||||
if (terminal.shape.stencil.constraints != null) {
|
||||
return terminal.shape.stencil.constraints;
|
||||
}
|
||||
} else if (terminal.shape.constraints != null) {
|
||||
return terminal.shape.constraints;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
createConnectionHandler() {
|
||||
return new MyCustomConnectionHandler(this);
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomGeometryClass extends mxGeometry {
|
||||
// Defines the default constraints for the vertices
|
||||
constraints = [
|
||||
new mxConnectionConstraint(new mxPoint(0.25, 0), true),
|
||||
new mxConnectionConstraint(new mxPoint(0.5, 0), true),
|
||||
new mxConnectionConstraint(new mxPoint(0.75, 0), true),
|
||||
new mxConnectionConstraint(new mxPoint(0, 0.25), true),
|
||||
new mxConnectionConstraint(new mxPoint(0, 0.5), true),
|
||||
new mxConnectionConstraint(new mxPoint(0, 0.75), true),
|
||||
new mxConnectionConstraint(new mxPoint(1, 0.25), true),
|
||||
new mxConnectionConstraint(new mxPoint(1, 0.5), true),
|
||||
new mxConnectionConstraint(new mxPoint(1, 0.75), true),
|
||||
new mxConnectionConstraint(new mxPoint(0.25, 1), true),
|
||||
new mxConnectionConstraint(new mxPoint(0.5, 1), true),
|
||||
new mxConnectionConstraint(new mxPoint(0.75, 1), true),
|
||||
];
|
||||
}
|
||||
|
||||
// Edges have no connection points
|
||||
mxPolyline.prototype.constraints = null;
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new MyCustomGraph(this.el);
|
||||
graph.setConnectable(true);
|
||||
|
||||
// Specifies the default edge style
|
||||
graph.getStylesheet().getDefaultEdgeStyle().edgeStyle =
|
||||
'orthogonalEdgeStyle';
|
||||
|
||||
// Enables rubberband selection
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello,',
|
||||
position: [20, 20],
|
||||
size: [80, 30],
|
||||
geometryClass: MyCustomGeometryClass,
|
||||
});
|
||||
const v2 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'World!',
|
||||
position: [200, 150],
|
||||
size: [80, 30],
|
||||
geometryClass: MyCustomGeometryClass,
|
||||
});
|
||||
const e1 = graph.insertEdge({
|
||||
parent,
|
||||
value: '',
|
||||
position: v1,
|
||||
size: v2,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Anchors;
|
|
@ -1,189 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
import mxGraphHandler from '../../mxgraph/handler/mxGraphHandler';
|
||||
import mxGuide from '../../mxgraph/util/mxGuide';
|
||||
import mxEdgeHandler from '../../mxgraph/handler/mxEdgeHandler';
|
||||
import mxConnectionHandler from '../../mxgraph/handler/mxConnectionHandler';
|
||||
import mxGraphView from '../../mxgraph/view/graph/mxGraphView';
|
||||
import mxPoint from '../../mxgraph/util/datatypes/mxPoint';
|
||||
import mxCellState from '../../mxgraph/view/cell/mxCellState';
|
||||
|
||||
class Orthogonal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Orthogonal</h1>
|
||||
This example demonstrates the use of port constraints and orthogonal
|
||||
edge styles and handlers.
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
height: '241px',
|
||||
background: "url('editors/images/grid.gif')",
|
||||
cursor: 'default',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Enables guides
|
||||
mxGraphHandler.prototype.guidesEnabled = true;
|
||||
|
||||
// Alt disables guides
|
||||
mxGuide.prototype.isEnabledForEvent = function(evt) {
|
||||
return !mxEvent.isAltDown(evt);
|
||||
};
|
||||
|
||||
// Enables snapping waypoints to terminals
|
||||
mxEdgeHandler.prototype.snapToTerminals = true;
|
||||
|
||||
// Enables orthogonal connect preview in IE
|
||||
mxConnectionHandler.prototype.movePreviewAway = false;
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new mxGraph(this.el);
|
||||
graph.disconnectOnMove = false;
|
||||
graph.foldingEnabled = false;
|
||||
graph.cellsResizable = false;
|
||||
graph.extendParents = false;
|
||||
graph.setConnectable(true);
|
||||
|
||||
// Implements perimeter-less connection points as fixed points (computed before the edge style).
|
||||
graph.view.updateFixedTerminalPoint = function(
|
||||
edge,
|
||||
terminal,
|
||||
source,
|
||||
constraint
|
||||
) {
|
||||
mxGraphView.prototype.updateFixedTerminalPoint.apply(this, arguments);
|
||||
|
||||
const pts = edge.absolutePoints;
|
||||
const pt = pts[source ? 0 : pts.length - 1];
|
||||
|
||||
if (
|
||||
terminal != null &&
|
||||
pt == null &&
|
||||
this.getPerimeterFunction(terminal) == null
|
||||
) {
|
||||
edge.setAbsoluteTerminalPoint(
|
||||
new mxPoint(
|
||||
this.getRoutingCenterX(terminal),
|
||||
this.getRoutingCenterY(terminal)
|
||||
),
|
||||
source
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Changes the default edge style
|
||||
graph.getStylesheet().getDefaultEdgeStyle().edgeStyle =
|
||||
'orthogonalEdgeStyle';
|
||||
delete graph.getStylesheet().getDefaultEdgeStyle().endArrow;
|
||||
|
||||
// Implements the connect preview
|
||||
graph.connectionHandler.createEdgeState = function(me) {
|
||||
const edge = graph.createEdge(null, null, null, null, null);
|
||||
|
||||
return new mxCellState(
|
||||
this.graph.view,
|
||||
edge,
|
||||
this.graph.getCellStyle(edge)
|
||||
);
|
||||
};
|
||||
|
||||
// Uncomment the following if you want the container
|
||||
// to fit the size of the graph
|
||||
// graph.setResizeContainer(true);
|
||||
|
||||
// Enables rubberband selection
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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 v1 = graph.insertVertex(parent, null, '', 40, 40, 40, 30);
|
||||
v1.setConnectable(false);
|
||||
const v11 = graph.insertVertex(
|
||||
v1,
|
||||
null,
|
||||
'',
|
||||
0.5,
|
||||
0,
|
||||
10,
|
||||
40,
|
||||
'portConstraint=northsouth;',
|
||||
true
|
||||
);
|
||||
v11.geometry.offset = new mxPoint(-5, -5);
|
||||
const v12 = graph.insertVertex(
|
||||
v1,
|
||||
null,
|
||||
'',
|
||||
0,
|
||||
0.5,
|
||||
10,
|
||||
10,
|
||||
'portConstraint=west;shape=triangle;direction=west;perimeter=none;' +
|
||||
'routingCenterX=-0.5;routingCenterY=0;',
|
||||
true
|
||||
);
|
||||
v12.geometry.offset = new mxPoint(-10, -5);
|
||||
const v13 = graph.insertVertex(
|
||||
v1,
|
||||
null,
|
||||
'',
|
||||
1,
|
||||
0.5,
|
||||
10,
|
||||
10,
|
||||
'portConstraint=east;shape=triangle;direction=east;perimeter=none;' +
|
||||
'routingCenterX=0.5;routingCenterY=0;',
|
||||
true
|
||||
);
|
||||
v13.geometry.offset = new mxPoint(0, -5);
|
||||
|
||||
const v2 = graph.addCell(graph.getModel().cloneCell(v1));
|
||||
v2.geometry.x = 200;
|
||||
v2.geometry.y = 60;
|
||||
|
||||
const v3 = graph.addCell(graph.getModel().cloneCell(v1));
|
||||
v3.geometry.x = 40;
|
||||
v3.geometry.y = 150;
|
||||
|
||||
const v4 = graph.addCell(graph.getModel().cloneCell(v1));
|
||||
v4.geometry.x = 200;
|
||||
v4.geometry.y = 170;
|
||||
|
||||
graph.insertEdge(parent, null, '', v1.getChildAt(2), v2.getChildAt(1));
|
||||
graph.insertEdge(parent, null, '', v2.getChildAt(2), v3.getChildAt(1));
|
||||
graph.insertEdge(parent, null, '', v3.getChildAt(2), v4.getChildAt(1));
|
||||
} finally {
|
||||
// Updates the display
|
||||
graph.getModel().endUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Orthogonal;
|
|
@ -1,22 +0,0 @@
|
|||
import React from 'react';
|
||||
import Preview from '../Previews';
|
||||
import Anchors from './Anchors';
|
||||
import EdgeTolerance from './EdgeTolerance';
|
||||
import FixedPoints from './FixedPoints';
|
||||
import HelloPort from './HelloPort';
|
||||
import Orthogonal from './Orthogonal';
|
||||
import PortRefs from './PortRefs';
|
||||
import PageTabs from '../PageTabs';
|
||||
|
||||
export default function _Connections() {
|
||||
return (
|
||||
<PageTabs curPageURL="/connections">
|
||||
<Preview sourceKey="Anchors" content={<Anchors />} />
|
||||
<Preview sourceKey="EdgeTolerance" content={<EdgeTolerance />} />
|
||||
<Preview sourceKey="FixedPoints" content={<FixedPoints />} />
|
||||
<Preview sourceKey="HelloPort" content={<HelloPort />} />
|
||||
<Preview sourceKey="Orthogonal" content={<Orthogonal />} />
|
||||
<Preview sourceKey="PortRefs" content={<PortRefs />} />
|
||||
</PageTabs>
|
||||
);
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
import mxCell from '../../mxgraph/view/cell/mxCell';
|
||||
import mxGeometry from '../../mxgraph/util/datatypes/mxGeometry';
|
||||
import mxUtils from '../../mxgraph/util/mxUtils';
|
||||
import mxDragSource from '../../mxgraph/util/drag_pan/mxDragSource';
|
||||
import mxGraphHandler from '../../mxgraph/handler/mxGraphHandler';
|
||||
import mxGuide from '../../mxgraph/util/mxGuide';
|
||||
import mxEdgeHandler from '../../mxgraph/handler/mxEdgeHandler';
|
||||
|
||||
class DragSource extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Dragsource</h1>
|
||||
This example demonstrates using one drag source for multiple graphs and
|
||||
changing the drag icon.
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
class MyCustomGuide extends mxGuide {
|
||||
isEnabledForEvent(evt) {
|
||||
// Alt disables guides
|
||||
return !mxEvent.isAltDown(evt);
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomGraphHandler extends mxGraphHandler {
|
||||
// Enables guides
|
||||
guidesEnabled = true;
|
||||
|
||||
createGuide() {
|
||||
return new MyCustomGuide(this.graph, this.getGuideStates());
|
||||
}
|
||||
}
|
||||
|
||||
class MyCustomEdgeHandler extends mxEdgeHandler {
|
||||
// Enables snapping waypoints to terminals
|
||||
snapToTerminals = true;
|
||||
}
|
||||
|
||||
class MyCustomGraph extends mxGraph {
|
||||
createGraphHandler() {
|
||||
return new MyCustomGraphHandler(this);
|
||||
}
|
||||
|
||||
createEdgeHandler(state, edgeStyle) {
|
||||
return new MyCustomEdgeHandler(state, edgeStyle);
|
||||
}
|
||||
}
|
||||
|
||||
const graphs = [];
|
||||
|
||||
// Creates the graph inside the given container
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const container = document.createElement('div');
|
||||
container.style.overflow = 'hidden';
|
||||
container.style.position = 'relative';
|
||||
container.style.width = '321px';
|
||||
container.style.height = '241px';
|
||||
container.style.background = "url('editors/images/grid.gif')";
|
||||
container.style.cursor = 'default';
|
||||
|
||||
this.el.appendChild(container);
|
||||
|
||||
const graph = new MyCustomGraph(container);
|
||||
graph.gridSize = 30;
|
||||
|
||||
// Uncomment the following if you want the container
|
||||
// to fit the size of the graph
|
||||
// graph.setResizeContainer(true);
|
||||
|
||||
// Enables rubberband selection
|
||||
new mxRubberband(graph);
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'Hello,',
|
||||
position: [20, 20],
|
||||
size: [80, 30],
|
||||
});
|
||||
const v2 = graph.insertVertex({
|
||||
parent,
|
||||
value: 'World!',
|
||||
position: [200, 150],
|
||||
size: [80, 30],
|
||||
});
|
||||
const e1 = graph.insertEdge({
|
||||
parent,
|
||||
source: v1,
|
||||
target: v2,
|
||||
});
|
||||
});
|
||||
|
||||
graphs.push(graph);
|
||||
}
|
||||
|
||||
// Returns the graph under the mouse
|
||||
const graphF = evt => {
|
||||
const x = mxEvent.getClientX(evt);
|
||||
const y = mxEvent.getClientY(evt);
|
||||
const elt = document.elementFromPoint(x, y);
|
||||
|
||||
for (const graph of graphs) {
|
||||
if (mxUtils.isAncestorNode(graph.container, elt)) {
|
||||
return graph;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Inserts a cell at the given location
|
||||
const funct = (graph, evt, target, x, y) => {
|
||||
const cell = new mxCell('Test', new mxGeometry(0, 0, 120, 40));
|
||||
cell.vertex = true;
|
||||
const cells = graph.importCells([cell], x, y, target);
|
||||
|
||||
if (cells != null && cells.length > 0) {
|
||||
graph.scrollCellToVisible(cells[0]);
|
||||
graph.setSelectionCells(cells);
|
||||
}
|
||||
};
|
||||
|
||||
// Creates a DOM node that acts as the drag source
|
||||
const img = mxUtils.createImage('images/icons48/gear.png');
|
||||
img.style.width = '48px';
|
||||
img.style.height = '48px';
|
||||
this.el.appendChild(img);
|
||||
|
||||
// Creates the element that is being for the actual preview.
|
||||
const dragElt = document.createElement('div');
|
||||
dragElt.style.border = 'dashed black 1px';
|
||||
dragElt.style.width = '120px';
|
||||
dragElt.style.height = '40px';
|
||||
|
||||
// Drag source is configured to use dragElt for preview and as drag icon
|
||||
// if scalePreview (last) argument is true. Dx and dy are null to force
|
||||
// the use of the defaults. Note that dx and dy are only used for the
|
||||
// drag icon but not for the preview.
|
||||
const ds = mxUtils.makeDraggable(
|
||||
img,
|
||||
graphF,
|
||||
funct,
|
||||
dragElt,
|
||||
null,
|
||||
null,
|
||||
graphs[0].autoscroll,
|
||||
true
|
||||
);
|
||||
|
||||
// Redirects feature to global switch. Note that this feature should only be used
|
||||
// if the the x and y arguments are used in funct to insert the cell.
|
||||
ds.isGuidesEnabled = () => {
|
||||
return graphs[0].graphHandler.guidesEnabled;
|
||||
};
|
||||
|
||||
// Restores original drag icon while outside of graph
|
||||
ds.createDragElement = mxDragSource.prototype.createDragElement;
|
||||
}
|
||||
|
||||
// NOTE: To enable cross-document DnD (eg. between frames),
|
||||
// the following methods need to be overridden:
|
||||
/* mxDragSourceMouseUp = mxDragSource.prototype.mouseUp;
|
||||
mxDragSource.prototype.mouseUp = function(evt)
|
||||
{
|
||||
let doc = this.element.ownerDocument;
|
||||
|
||||
if (doc != document)
|
||||
{
|
||||
let mu = (mxClient.IS_TOUCH) ? 'touchend' : 'mouseup';
|
||||
|
||||
if (this.mouseUpHandler != null)
|
||||
{
|
||||
mxEvent.removeListener(doc, mu, this.mouseUpHandler);
|
||||
}
|
||||
}
|
||||
|
||||
mxDragSourceMouseUp.apply(this, arguments);
|
||||
}; */
|
||||
|
||||
/* mxDragSourceMouseDown = mxDragSource.prototype.mouseDown;
|
||||
mxDragSource.prototype.mouseDown = function(evt)
|
||||
{
|
||||
if (this.enabled && !mxEvent.isConsumed(evt))
|
||||
{
|
||||
mxDragSourceMouseDown.apply(this, arguments);
|
||||
let doc = this.element.ownerDocument;
|
||||
|
||||
if (doc != document)
|
||||
{
|
||||
let mu = (mxClient.IS_TOUCH) ? 'touchend' : 'mouseup';
|
||||
mxEvent.addListener(doc, mu, this.mouseUpHandler);
|
||||
}
|
||||
}
|
||||
}; */
|
||||
}
|
||||
|
||||
export default DragSource;
|
|
@ -1,184 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxRubberband from '../../mxgraph/handler/mxRubberband';
|
||||
import mxUtils from '../../mxgraph/util/mxUtils';
|
||||
import mxClient from '../../mxgraph/mxClient';
|
||||
|
||||
class Drop extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Drop</h1>
|
||||
This example demonstrates handling native drag and drop of images
|
||||
(requires modern browser).
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
height: '441px',
|
||||
background: `url('editors/images/grid.gif')`,
|
||||
cursor: 'default',
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Checks if the browser is supported
|
||||
const fileSupport =
|
||||
window.File != null &&
|
||||
window.FileReader != null &&
|
||||
window.FileList != null;
|
||||
|
||||
if (!fileSupport || !mxClient.isBrowserSupported()) {
|
||||
// Displays an error message if the browser is not supported.
|
||||
mxUtils.error('Browser is not supported!', 200, false);
|
||||
} else {
|
||||
// Disables the built-in context menu
|
||||
mxEvent.disableContextMenu(this.el);
|
||||
|
||||
// Creates the graph inside the given this.el
|
||||
const graph = new mxGraph(this.el);
|
||||
|
||||
// Enables rubberband selection
|
||||
new mxRubberband(graph);
|
||||
|
||||
mxEvent.addListener(this.el, 'dragover', function(evt) {
|
||||
if (graph.isEnabled()) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
mxEvent.addListener(this.el, 'drop', evt => {
|
||||
if (graph.isEnabled()) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
|
||||
// Gets drop location point for vertex
|
||||
const pt = mxUtils.convertPoint(
|
||||
graph.container,
|
||||
mxEvent.getClientX(evt),
|
||||
mxEvent.getClientY(evt)
|
||||
);
|
||||
const tr = graph.view.translate;
|
||||
const { scale } = graph.view;
|
||||
const x = pt.x / scale - tr.x;
|
||||
const y = pt.y / scale - tr.y;
|
||||
|
||||
// Converts local images to data urls
|
||||
const filesArray = evt.dataTransfer.files;
|
||||
|
||||
for (let i = 0; i < filesArray.length; i++) {
|
||||
this.handleDrop(graph, filesArray[i], x + i * 10, y + i * 10);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleDrop(graph, file, x, y) {
|
||||
// Handles each file as a separate insert for simplicity.
|
||||
// Use barrier to handle multiple files as a single insert.
|
||||
|
||||
if (file.type.substring(0, 5) === 'image') {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function(e) {
|
||||
// Gets size of image for vertex
|
||||
let data = e.target.result;
|
||||
|
||||
// SVG needs special handling to add viewbox if missing and
|
||||
// find initial size from SVG attributes (only for IE11)
|
||||
if (file.type.substring(0, 9) === 'image/svg') {
|
||||
const comma = data.indexOf(',');
|
||||
const svgText = atob(data.substring(comma + 1));
|
||||
const root = mxUtils.parseXml(svgText);
|
||||
|
||||
// Parses SVG to find width and height
|
||||
if (root != null) {
|
||||
const svgs = root.getElementsByTagName('svg');
|
||||
|
||||
if (svgs.length > 0) {
|
||||
const svgRoot = svgs[0];
|
||||
let w = parseFloat(svgRoot.getAttribute('width'));
|
||||
let h = parseFloat(svgRoot.getAttribute('height'));
|
||||
|
||||
// Check if viewBox attribute already exists
|
||||
const vb = svgRoot.getAttribute('viewBox');
|
||||
|
||||
if (vb == null || vb.length === 0) {
|
||||
svgRoot.setAttribute('viewBox', `0 0 ${w} ${h}`);
|
||||
}
|
||||
// Uses width and height from viewbox for
|
||||
// missing width and height attributes
|
||||
else if (Number.isNaN(w) || Number.isNaN(h)) {
|
||||
const tokens = vb.split(' ');
|
||||
|
||||
if (tokens.length > 3) {
|
||||
w = parseFloat(tokens[2]);
|
||||
h = parseFloat(tokens[3]);
|
||||
}
|
||||
}
|
||||
|
||||
w = Math.max(1, Math.round(w));
|
||||
h = Math.max(1, Math.round(h));
|
||||
|
||||
data = `data:image/svg+xml,${btoa(
|
||||
mxUtils.getXml(svgs[0], '\n')
|
||||
)}`;
|
||||
graph.insertVertex({
|
||||
position: [x, y],
|
||||
size: [w, h],
|
||||
style: `shape=image;image=${data};`,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
const w = Math.max(1, img.width);
|
||||
const h = Math.max(1, img.height);
|
||||
|
||||
// Converts format of data url to cell style value for use in vertex
|
||||
const semi = data.indexOf(';');
|
||||
|
||||
if (semi > 0) {
|
||||
data =
|
||||
data.substring(0, semi) +
|
||||
data.substring(data.indexOf(',', semi + 1));
|
||||
}
|
||||
|
||||
graph.insertVertex({
|
||||
position: [x, y],
|
||||
size: [w, h],
|
||||
style: `shape=image;image=${data};`,
|
||||
});
|
||||
};
|
||||
|
||||
img.src = data;
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Drop;
|
|
@ -1,16 +0,0 @@
|
|||
import React from 'react';
|
||||
import Preview from '../Previews';
|
||||
import Clipboard from './Clipboard';
|
||||
import DragSource from './DragSource';
|
||||
import Drop from './Drop';
|
||||
import PageTabs from '../PageTabs';
|
||||
|
||||
export default function _DnDCopyPaste() {
|
||||
return (
|
||||
<PageTabs curPageURL="/dnd_copypaste">
|
||||
<Preview sourceKey="Clipboard" content={<Clipboard />} />
|
||||
<Preview sourceKey="DragSource" content={<DragSource />} />
|
||||
<Preview sourceKey="Drop" content={<Drop />} />
|
||||
</PageTabs>
|
||||
);
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2006-2013, JGraph Ltd
|
||||
* Converted to ES9 syntax/React by David Morrissey 2021
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import mxEvent from '../../mxgraph/util/event/mxEvent';
|
||||
import mxGraph from '../../mxgraph/view/graph/mxGraph';
|
||||
import mxUtils from '../../mxgraph/util/mxUtils';
|
||||
import mxKeyHandler from '../../mxgraph/handler/mxKeyHandler';
|
||||
|
||||
class Editing extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
// A container for the graph
|
||||
return (
|
||||
<>
|
||||
<h1>Editing</h1>
|
||||
This example demonstrates using the in-place editor trigger to specify
|
||||
the editing value and write the new value into a specific field of the
|
||||
user object. Wrapping and DOM nodes as labels are also demonstrated
|
||||
here.
|
||||
<br />
|
||||
<br />
|
||||
Double-click the upper/lower half of the cell to edit different fields
|
||||
of the user object.
|
||||
<div
|
||||
ref={el => {
|
||||
this.el = el;
|
||||
}}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
height: '241px',
|
||||
background: "url('editors/images/grid.gif')",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
class MyCustomGraph extends mxGraph {
|
||||
getLabel(cell) {
|
||||
// Returns a HTML representation of the cell where the
|
||||
// upper half is the first value, lower half is second
|
||||
// value
|
||||
|
||||
const table = document.createElement('table');
|
||||
table.style.height = '100%';
|
||||
table.style.width = '100%';
|
||||
|
||||
const body = document.createElement('tbody');
|
||||
const tr1 = document.createElement('tr');
|
||||
const td1 = document.createElement('td');
|
||||
td1.style.textAlign = 'center';
|
||||
td1.style.fontSize = '12px';
|
||||
td1.style.color = '#774400';
|
||||
mxUtils.write(td1, cell.value.first);
|
||||
|
||||
const tr2 = document.createElement('tr');
|
||||
const td2 = document.createElement('td');
|
||||
td2.style.textAlign = 'center';
|
||||
td2.style.fontSize = '12px';
|
||||
td2.style.color = '#774400';
|
||||
mxUtils.write(td2, cell.value.second);
|
||||
|
||||
tr1.appendChild(td1);
|
||||
tr2.appendChild(td2);
|
||||
body.appendChild(tr1);
|
||||
body.appendChild(tr2);
|
||||
table.appendChild(body);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
getEditingValue(cell, evt) {
|
||||
// Returns the editing value for the given cell and event
|
||||
evt.fieldname = this.__getFieldnameForEvent(cell, evt);
|
||||
return cell.value[evt.fieldname] || '';
|
||||
}
|
||||
|
||||
__getFieldnameForEvent(cell, evt) {
|
||||
// Helper method that returns the fieldname to be used for
|
||||
// a mouse event
|
||||
if (evt != null) {
|
||||
// Finds the relative coordinates inside the cell
|
||||
const point = mxUtils.convertPoint(
|
||||
this.container,
|
||||
mxEvent.getClientX(evt),
|
||||
mxEvent.getClientY(evt)
|
||||
);
|
||||
const state = this.getView().getState(cell);
|
||||
|
||||
if (state != null) {
|
||||
point.x -= state.x;
|
||||
point.y -= state.y;
|
||||
|
||||
// Returns second if mouse in second half of cell
|
||||
if (point.y > state.height / 2) {
|
||||
return 'second';
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'first';
|
||||
}
|
||||
|
||||
labelChanged(cell, newValue, trigger) {
|
||||
// Sets the new value for the given cell and trigger
|
||||
const name = trigger != null ? trigger.fieldname : null;
|
||||
|
||||
if (name != null) {
|
||||
// Clones the user object for correct undo and puts
|
||||
// the new value in the correct field.
|
||||
const value = mxUtils.clone(cell.value);
|
||||
value[name] = newValue;
|
||||
newValue = value;
|
||||
|
||||
super.labelChanged(cell, newValue, trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the graph inside the given container
|
||||
const graph = new MyCustomGraph(this.el);
|
||||
graph.setHtmlLabels(true);
|
||||
|
||||
// Adds handling of return and escape keystrokes for editing
|
||||
const keyHandler = new mxKeyHandler(graph);
|
||||
|
||||
// Sample user objects with 2 fields
|
||||
const value = {};
|
||||
value.first = 'First value';
|
||||
value.second = 'Second value';
|
||||
|
||||
// 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.batchUpdate(() => {
|
||||
const v1 = graph.insertVertex({
|
||||
parent,
|
||||
value,
|
||||
position: [100, 60],
|
||||
size: [120, 80],
|
||||
style: 'overflow=fill;',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Editing;
|
|
@ -1,12 +0,0 @@
|
|||
import React from 'react';
|
||||
import Preview from '../Previews';
|
||||
import Editing from './Editing';
|
||||
import PageTabs from '../PageTabs';
|
||||
|
||||
export default function _Editing() {
|
||||
return (
|
||||
<PageTabs curPageURL="/editing">
|
||||
<Preview sourceKey="Editing" content={<Editing />} />
|
||||
</PageTabs>
|
||||
);
|
||||
}
|