import { Graph, RubberBand, InternalEvent, CellRenderer, EdgeHandler, mxHierarchicalLayout, Constants, CellOverlay, ImageBox, mxClient, mxMorphing, EventObject, EventUtils, } from '@maxgraph/core'; import { globalTypes } from '../.storybook/preview'; export default { title: 'Layouts/AutoLayout', argTypes: { ...globalTypes, contextMenu: { type: 'boolean', defaultValue: false, }, rubberBand: { type: 'boolean', defaultValue: true, }, }, }; const Template = ({ label, ...args }) => { 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'; if (!args.contextMenu) InternalEvent.disableContextMenu(container); class MyCustomCellRenderer extends CellRenderer { installCellOverlayListeners(state, overlay, shape) { super.installCellOverlayListeners(state, overlay, shape); InternalEvent.addListener( shape.node, mxClient.IS_POINTER ? 'pointerdown' : 'mousedown', (evt) => { overlay.fireEvent(new EventObject('pointerdown', 'event', evt, 'state', state)); } ); if (!mxClient.IS_POINTER && mxClient.IS_TOUCH) { InternalEvent.addListener(shape.node, 'touchstart', (evt) => { overlay.fireEvent(new EventObject('pointerdown', 'event', evt, 'state', state)); }); } } } class MyCustomEdgeHandler extends EdgeHandler { connect(edge, terminal, isSource, isClone, me) { super.connect(edge, terminal, isSource, isClone, me); executeLayout(); } } class MyCustomGraph extends Graph { createEdgeHandler(state, edgeStyle) { return new MyCustomEdgeHandler(state, edgeStyle); } createCellRenderer() { return new MyCustomCellRenderer(); } } // Creates the graph inside the given this.el const graph = new MyCustomGraph(container); graph.setPanning(true); const panningHandler = graph.getPlugin('PanningHandler'); panningHandler.useLeftButtonForPanning = true; graph.setAllowDanglingEdges(false); const connectionHandler = graph.getPlugin('ConnectionHandler'); connectionHandler.select = false; graph.view.setTranslate(20, 20); // Enables rubberband selection if (args.rubberBand) new RubberBand(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(); const layout = new mxHierarchicalLayout(graph, Constants.DIRECTION_WEST); let v1; const executeLayout = (change, post) => { graph.getModel().beginUpdate(); try { if (change != null) { change(); } layout.execute(graph.getDefaultParent(), v1); } catch (e) { throw e; } finally { // New API for animating graph layout results asynchronously const morph = new mxMorphing(graph); morph.addListener(InternalEvent.DONE, () => { graph.getModel().endUpdate(); if (post != null) { post(); } }); morph.startAnimation(); } }; const addOverlay = (cell) => { // Creates a new overlay with an image and a tooltip const overlay = new CellOverlay( new ImageBox('images/add.png', 24, 24), 'Add outgoing' ); overlay.cursor = 'hand'; // Installs a handler for clicks on the overlay overlay.addListener(InternalEvent.CLICK, (sender, evt2) => { graph.clearSelection(); const geo = graph.getCellGeometry(cell); let v2; executeLayout( () => { v2 = graph.insertVertex({ parent, value: 'World!', position: [geo.x, geo.y], size: [80, 30], }); addOverlay(v2); graph.view.refresh(v2); const e1 = graph.insertEdge({ parent, source: cell, target: v2, }); }, () => { graph.scrollCellToVisible(v2); } ); }); // Special CMS event overlay.addListener('pointerdown', (sender, eo) => { const evt2 = eo.getProperty('event'); const state = eo.getProperty('state'); const popupMenuHandler = graph.getPlugin('PopupMenuHandler'); popupMenuHandler.hideMenu(); graph.stopEditing(false); const pt = utils.convertPoint( graph.container, EventUtils.getClientX(evt2), EventUtils.getClientY(evt2) ); connectionHandler.start(state, pt.x, pt.y); graph.isMouseDown = true; graph.isMouseTrigger = EventUtils.isMouseEvent(evt2); InternalEvent.consume(evt2); }); // Sets the overlay for the cell in the graph graph.addCellOverlay(cell, overlay); }; // Adds cells to the model in a single step graph.batchUpdate(() => { v1 = graph.insertVertex({ parent, value: 'Hello,', position: [0, 0], size: [80, 30], }); addOverlay(v1); }); graph.resizeCell = function () { Graph.prototype.resizeCell.apply(this, arguments); executeLayout(); }; connectionHandler.addListener(InternalEvent.CONNECT, function () { executeLayout(); }); return container; }; export const Default = Template.bind({});