diff --git a/packages/core/src/serialization/mxCodec.js b/packages/core/src/serialization/mxCodec.js index ffa961225..317b280a2 100644 --- a/packages/core/src/serialization/mxCodec.js +++ b/packages/core/src/serialization/mxCodec.js @@ -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 = []; } diff --git a/packages/core/src/util/mxGestureUtils.js b/packages/core/src/util/mxGestureUtils.js index 15e48d334..889d84acb 100644 --- a/packages/core/src/util/mxGestureUtils.js +++ b/packages/core/src/util/mxGestureUtils.js @@ -1,5 +1,6 @@ import mxDragSource from './drag_pan/mxDragSource'; import mxPoint from './datatypes/mxPoint'; +import mxConstants from './mxConstants'; /** * Function: makeDraggable diff --git a/packages/html/.storybook/preview.js b/packages/html/.storybook/preview.js index 645f52de9..51c36b80f 100644 --- a/packages/html/.storybook/preview.js +++ b/packages/html/.storybook/preview.js @@ -7,4 +7,15 @@ export const parameters = { date: /Date$/, }, }, -} \ No newline at end of file +} + +export const defaultArgTypes = { + width: { + type: 'number', + defaultValue: 800 + }, + height: { + type: 'number', + defaultValue: 600 + } +}; diff --git a/packages/html/public/images/add.png b/packages/html/public/images/add.png new file mode 100644 index 000000000..bf5f8edb1 Binary files /dev/null and b/packages/html/public/images/add.png differ diff --git a/packages/html/public/images/camera.png b/packages/html/public/images/camera.png new file mode 100644 index 000000000..aecc94d66 Binary files /dev/null and b/packages/html/public/images/camera.png differ diff --git a/packages/html/public/images/check.png b/packages/html/public/images/check.png new file mode 100644 index 000000000..ce81bceb7 Binary files /dev/null and b/packages/html/public/images/check.png differ diff --git a/packages/html/public/images/close.png b/packages/html/public/images/close.png new file mode 100644 index 000000000..4de4396d4 Binary files /dev/null and b/packages/html/public/images/close.png differ diff --git a/packages/html/public/images/connector.gif b/packages/html/public/images/connector.gif new file mode 100644 index 000000000..326e061ea Binary files /dev/null and b/packages/html/public/images/connector.gif differ diff --git a/packages/html/public/images/copy.png b/packages/html/public/images/copy.png new file mode 100644 index 000000000..a987d4397 Binary files /dev/null and b/packages/html/public/images/copy.png differ diff --git a/packages/html/public/images/cut.png b/packages/html/public/images/cut.png new file mode 100644 index 000000000..52bf94431 Binary files /dev/null and b/packages/html/public/images/cut.png differ diff --git a/packages/html/public/images/delete2.png b/packages/html/public/images/delete2.png new file mode 100644 index 000000000..be78c614b Binary files /dev/null and b/packages/html/public/images/delete2.png differ diff --git a/packages/html/public/images/dot.gif b/packages/html/public/images/dot.gif new file mode 100644 index 000000000..08b994764 Binary files /dev/null and b/packages/html/public/images/dot.gif differ diff --git a/packages/html/public/images/export1.png b/packages/html/public/images/export1.png new file mode 100644 index 000000000..b8a01b873 Binary files /dev/null and b/packages/html/public/images/export1.png differ diff --git a/packages/html/public/images/fit_to_size.png b/packages/html/public/images/fit_to_size.png new file mode 100644 index 000000000..4de46b015 Binary files /dev/null and b/packages/html/public/images/fit_to_size.png differ diff --git a/packages/html/public/images/gradient_background.jpg b/packages/html/public/images/gradient_background.jpg new file mode 100644 index 000000000..7dbf35b3d Binary files /dev/null and b/packages/html/public/images/gradient_background.jpg differ diff --git a/packages/html/public/images/green-dot.gif b/packages/html/public/images/green-dot.gif new file mode 100644 index 000000000..acaf7b298 Binary files /dev/null and b/packages/html/public/images/green-dot.gif differ diff --git a/packages/html/public/images/group.png b/packages/html/public/images/group.png new file mode 100644 index 000000000..585ad790d Binary files /dev/null and b/packages/html/public/images/group.png differ diff --git a/packages/html/public/images/handle-connect.png b/packages/html/public/images/handle-connect.png new file mode 100644 index 000000000..513ab2b41 Binary files /dev/null and b/packages/html/public/images/handle-connect.png differ diff --git a/packages/html/public/images/handle-main.png b/packages/html/public/images/handle-main.png new file mode 100644 index 000000000..ee067ff81 Binary files /dev/null and b/packages/html/public/images/handle-main.png differ diff --git a/packages/html/public/images/icons48/column.png b/packages/html/public/images/icons48/column.png new file mode 100644 index 000000000..5ae2c2439 Binary files /dev/null and b/packages/html/public/images/icons48/column.png differ diff --git a/packages/html/public/images/icons48/earth.png b/packages/html/public/images/icons48/earth.png new file mode 100644 index 000000000..4493880c2 Binary files /dev/null and b/packages/html/public/images/icons48/earth.png differ diff --git a/packages/html/public/images/icons48/gear.png b/packages/html/public/images/icons48/gear.png new file mode 100644 index 000000000..647d897fd Binary files /dev/null and b/packages/html/public/images/icons48/gear.png differ diff --git a/packages/html/public/images/icons48/keys.png b/packages/html/public/images/icons48/keys.png new file mode 100644 index 000000000..41828e429 Binary files /dev/null and b/packages/html/public/images/icons48/keys.png differ diff --git a/packages/html/public/images/icons48/mail_new.png b/packages/html/public/images/icons48/mail_new.png new file mode 100644 index 000000000..16c666280 Binary files /dev/null and b/packages/html/public/images/icons48/mail_new.png differ diff --git a/packages/html/public/images/icons48/server.png b/packages/html/public/images/icons48/server.png new file mode 100644 index 000000000..9621c6e46 Binary files /dev/null and b/packages/html/public/images/icons48/server.png differ diff --git a/packages/html/public/images/icons48/table.png b/packages/html/public/images/icons48/table.png new file mode 100644 index 000000000..d4df646fe Binary files /dev/null and b/packages/html/public/images/icons48/table.png differ diff --git a/packages/html/public/images/key.png b/packages/html/public/images/key.png new file mode 100644 index 000000000..e66758a83 Binary files /dev/null and b/packages/html/public/images/key.png differ diff --git a/packages/html/public/images/loading.gif b/packages/html/public/images/loading.gif new file mode 100644 index 000000000..118f4b0df Binary files /dev/null and b/packages/html/public/images/loading.gif differ diff --git a/packages/html/public/images/navigate_minus.png b/packages/html/public/images/navigate_minus.png new file mode 100644 index 000000000..71edaf999 Binary files /dev/null and b/packages/html/public/images/navigate_minus.png differ diff --git a/packages/html/public/images/navigate_plus.png b/packages/html/public/images/navigate_plus.png new file mode 100644 index 000000000..b5b7e8706 Binary files /dev/null and b/packages/html/public/images/navigate_plus.png differ diff --git a/packages/html/public/images/paste.png b/packages/html/public/images/paste.png new file mode 100644 index 000000000..fd628d9db Binary files /dev/null and b/packages/html/public/images/paste.png differ diff --git a/packages/html/public/images/plus.png b/packages/html/public/images/plus.png new file mode 100644 index 000000000..24a84bb49 Binary files /dev/null and b/packages/html/public/images/plus.png differ diff --git a/packages/html/public/images/press32.png b/packages/html/public/images/press32.png new file mode 100644 index 000000000..f00e3f707 Binary files /dev/null and b/packages/html/public/images/press32.png differ diff --git a/packages/html/public/images/print32.png b/packages/html/public/images/print32.png new file mode 100644 index 000000000..0cca86cc1 Binary files /dev/null and b/packages/html/public/images/print32.png differ diff --git a/packages/html/public/images/printer.png b/packages/html/public/images/printer.png new file mode 100644 index 000000000..6004816bc Binary files /dev/null and b/packages/html/public/images/printer.png differ diff --git a/packages/html/public/images/redo.png b/packages/html/public/images/redo.png new file mode 100644 index 000000000..3eae59c51 Binary files /dev/null and b/packages/html/public/images/redo.png differ diff --git a/packages/html/public/images/sidebar_bg.gif b/packages/html/public/images/sidebar_bg.gif new file mode 100644 index 000000000..67e824486 Binary files /dev/null and b/packages/html/public/images/sidebar_bg.gif differ diff --git a/packages/html/public/images/spacer.gif b/packages/html/public/images/spacer.gif new file mode 100644 index 000000000..35d42e808 Binary files /dev/null and b/packages/html/public/images/spacer.gif differ diff --git a/packages/html/public/images/toolbar_bg.gif b/packages/html/public/images/toolbar_bg.gif new file mode 100644 index 000000000..87b937402 Binary files /dev/null and b/packages/html/public/images/toolbar_bg.gif differ diff --git a/packages/html/public/images/undo.png b/packages/html/public/images/undo.png new file mode 100644 index 000000000..4ba0ffb16 Binary files /dev/null and b/packages/html/public/images/undo.png differ diff --git a/packages/html/public/images/view_1_1.png b/packages/html/public/images/view_1_1.png new file mode 100644 index 000000000..88657a1ed Binary files /dev/null and b/packages/html/public/images/view_1_1.png differ diff --git a/packages/html/public/images/view_1_132.png b/packages/html/public/images/view_1_132.png new file mode 100644 index 000000000..e9a1b7299 Binary files /dev/null and b/packages/html/public/images/view_1_132.png differ diff --git a/packages/html/public/images/view_next.png b/packages/html/public/images/view_next.png new file mode 100644 index 000000000..b4094f04e Binary files /dev/null and b/packages/html/public/images/view_next.png differ diff --git a/packages/html/public/images/view_previous.png b/packages/html/public/images/view_previous.png new file mode 100644 index 000000000..b385b44c3 Binary files /dev/null and b/packages/html/public/images/view_previous.png differ diff --git a/packages/html/public/images/wires-grid.gif b/packages/html/public/images/wires-grid.gif new file mode 100644 index 000000000..ad888a2a6 Binary files /dev/null and b/packages/html/public/images/wires-grid.gif differ diff --git a/packages/html/public/images/zoom_in.png b/packages/html/public/images/zoom_in.png new file mode 100644 index 000000000..ad6abb93a Binary files /dev/null and b/packages/html/public/images/zoom_in.png differ diff --git a/packages/html/public/images/zoom_in32.png b/packages/html/public/images/zoom_in32.png new file mode 100644 index 000000000..438ff0ff0 Binary files /dev/null and b/packages/html/public/images/zoom_in32.png differ diff --git a/packages/html/public/images/zoom_out.png b/packages/html/public/images/zoom_out.png new file mode 100644 index 000000000..0566f26bf Binary files /dev/null and b/packages/html/public/images/zoom_out.png differ diff --git a/packages/html/public/images/zoom_out32.png b/packages/html/public/images/zoom_out32.png new file mode 100644 index 000000000..8edb7655b Binary files /dev/null and b/packages/html/public/images/zoom_out32.png differ diff --git a/packages/html/stories/Anchors.stories.js b/packages/html/stories/Anchors.stories.js index 7728b9be0..8c69750ed 100644 --- a/packages/html/stories/Anchors.stories.js +++ b/packages/html/stories/Anchors.stories.js @@ -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 + } } }; diff --git a/packages/html/stories/Clipboard.stories.js b/packages/html/stories/Clipboard.stories.js new file mode 100644 index 000000000..360e57d26 --- /dev/null +++ b/packages/html/stories/Clipboard.stories.js @@ -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) === '') { + 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({}); \ No newline at end of file diff --git a/packages/html/stories/DragSource.stories.js b/packages/html/stories/DragSource.stories.js new file mode 100644 index 000000000..46d4c995b --- /dev/null +++ b/packages/html/stories/DragSource.stories.js @@ -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({}); \ No newline at end of file diff --git a/packages/html/stories/Drop.stories.js b/packages/html/stories/Drop.stories.js new file mode 100644 index 000000000..ca3d6c728 --- /dev/null +++ b/packages/html/stories/Drop.stories.js @@ -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:
'; + + 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({}); \ No newline at end of file diff --git a/packages/html/stories/EdgeTolerance.stories.js b/packages/html/stories/EdgeTolerance.stories.js index e14ad4bc2..f10e4d218 100644 --- a/packages/html/stories/EdgeTolerance.stories.js +++ b/packages/html/stories/EdgeTolerance.stories.js @@ -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 } }; diff --git a/packages/html/stories/Editing.stories.js b/packages/html/stories/Editing.stories.js new file mode 100644 index 000000000..223a28781 --- /dev/null +++ b/packages/html/stories/Editing.stories.js @@ -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 = ` +

Editing

+ 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. +
+
+ 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({}); \ No newline at end of file diff --git a/packages/html/stories/ExtendCanvas.stories.js b/packages/html/stories/ExtendCanvas.stories.js index 41d9cc3e0..f4d975356 100644 --- a/packages/html/stories/ExtendCanvas.stories.js +++ b/packages/html/stories/ExtendCanvas.stories.js @@ -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; diff --git a/packages/html/stories/FixedPoints.stories.js b/packages/html/stories/FixedPoints.stories.js new file mode 100644 index 000000000..a387a8a80 --- /dev/null +++ b/packages/html/stories/FixedPoints.stories.js @@ -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({}); \ No newline at end of file diff --git a/packages/html/stories/Grid.stories.js b/packages/html/stories/Grid.stories.js index 8efa60d75..d02f66faa 100644 --- a/packages/html/stories/Grid.stories.js +++ b/packages/html/stories/Grid.stories.js @@ -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 + } } }; diff --git a/packages/html/stories/HelloPort.stories.js b/packages/html/stories/HelloPort.stories.js new file mode 100644 index 000000000..09e04b0a7 --- /dev/null +++ b/packages/html/stories/HelloPort.stories.js @@ -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({}); \ No newline at end of file diff --git a/packages/html/stories/HelloWorld.stories.js b/packages/html/stories/HelloWorld.stories.js index 2d4570c6c..d9a8d2c39 100644 --- a/packages/html/stories/HelloWorld.stories.js +++ b/packages/html/stories/HelloWorld.stories.js @@ -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) diff --git a/packages/html/stories/Orthogonal.stories.js b/packages/html/stories/Orthogonal.stories.js new file mode 100644 index 000000000..3554e43fd --- /dev/null +++ b/packages/html/stories/Orthogonal.stories.js @@ -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({}); \ No newline at end of file diff --git a/packages/html/stories/PortRefs.stories.js b/packages/html/stories/PortRefs.stories.js new file mode 100644 index 000000000..5f7f26c49 --- /dev/null +++ b/packages/html/stories/PortRefs.stories.js @@ -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({}); \ No newline at end of file diff --git a/src/pages/backgrounds/ExtendCanvas.js b/src/pages/backgrounds/ExtendCanvas.js deleted file mode 100644 index b937da038..000000000 --- a/src/pages/backgrounds/ExtendCanvas.js +++ /dev/null @@ -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 ( - <> -

Extend canvas

- This example demonstrates implementing an infinite canvas with - scrollbars. -
{ - 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; diff --git a/src/pages/backgrounds/Grid.js b/src/pages/backgrounds/Grid.js deleted file mode 100644 index 89782fa67..000000000 --- a/src/pages/backgrounds/Grid.js +++ /dev/null @@ -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 ( - <> -

Grid

- This example demonstrates drawing a grid dynamically using HTML 5 - canvas. -
{ - this.el = el; - }} - style={{ - overflow: 'hidden', - height: '481px', - cursor: 'default', - }} - /> -
{ - 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; diff --git a/src/pages/backgrounds/index.js b/src/pages/backgrounds/index.js deleted file mode 100644 index 118a86b10..000000000 --- a/src/pages/backgrounds/index.js +++ /dev/null @@ -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 ( - - } /> - } /> - - ); -} diff --git a/src/pages/basic/HelloWorld.js b/src/pages/basic/HelloWorld.js deleted file mode 100644 index 8266ab50e..000000000 --- a/src/pages/basic/HelloWorld.js +++ /dev/null @@ -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 ( - <> -

Hello, World!

- This example demonstrates using a DOM node to create a graph and adding - vertices and edges. -
{ - 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; diff --git a/src/pages/basic/Template.js b/src/pages/basic/Template.js deleted file mode 100644 index 576cddd88..000000000 --- a/src/pages/basic/Template.js +++ /dev/null @@ -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 ( - <> -

Hello, World!

-
{ - 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; diff --git a/src/pages/basic/index.js b/src/pages/basic/index.js deleted file mode 100644 index d306b4814..000000000 --- a/src/pages/basic/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import HelloWorld from './HelloWorld'; -import Preview from '../Previews'; -import PageTabs from '../PageTabs'; - -export default function _Basic() { - { - /* } /> */ - } - return ( - - } /> - - ); -} diff --git a/src/pages/connections/Anchors.js b/src/pages/connections/Anchors.js deleted file mode 100644 index b7ff28ae9..000000000 --- a/src/pages/connections/Anchors.js +++ /dev/null @@ -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 ( - <> -

Anchors

- This example demonstrates defining fixed connection points for all - shapes. -
{ - 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; diff --git a/src/pages/connections/Orthogonal.js b/src/pages/connections/Orthogonal.js deleted file mode 100644 index 2dd2d7bb9..000000000 --- a/src/pages/connections/Orthogonal.js +++ /dev/null @@ -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 ( - <> -

Orthogonal

- This example demonstrates the use of port constraints and orthogonal - edge styles and handlers. -
{ - 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; diff --git a/src/pages/connections/index.js b/src/pages/connections/index.js deleted file mode 100644 index eb2aec2ab..000000000 --- a/src/pages/connections/index.js +++ /dev/null @@ -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 ( - - } /> - } /> - } /> - } /> - } /> - } /> - - ); -} diff --git a/src/pages/dnd_copypaste/DragSource.js b/src/pages/dnd_copypaste/DragSource.js deleted file mode 100644 index af80bfc9c..000000000 --- a/src/pages/dnd_copypaste/DragSource.js +++ /dev/null @@ -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 ( - <> -

Dragsource

- This example demonstrates using one drag source for multiple graphs and - changing the drag icon. -
{ - 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; diff --git a/src/pages/dnd_copypaste/Drop.js b/src/pages/dnd_copypaste/Drop.js deleted file mode 100644 index 6de89beca..000000000 --- a/src/pages/dnd_copypaste/Drop.js +++ /dev/null @@ -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 ( - <> -

Drop

- This example demonstrates handling native drag and drop of images - (requires modern browser). -
{ - 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; diff --git a/src/pages/dnd_copypaste/index.js b/src/pages/dnd_copypaste/index.js deleted file mode 100644 index ea1efd7b3..000000000 --- a/src/pages/dnd_copypaste/index.js +++ /dev/null @@ -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 ( - - } /> - } /> - } /> - - ); -} diff --git a/src/pages/editing/Editing.js b/src/pages/editing/Editing.js deleted file mode 100644 index 9564f43d9..000000000 --- a/src/pages/editing/Editing.js +++ /dev/null @@ -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 ( - <> -

Editing

- 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. -
-
- Double-click the upper/lower half of the cell to edit different fields - of the user object. -
{ - 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; diff --git a/src/pages/editing/index.js b/src/pages/editing/index.js deleted file mode 100644 index 7426d674b..000000000 --- a/src/pages/editing/index.js +++ /dev/null @@ -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 ( - - } /> - - ); -}