import { Graph, EdgeStyle, mxDomHelpers, XmlUtils, Perimeter, utils, Constants, CloneUtils, mxCodec, } from '@maxgraph/core'; import { globalTypes } from '../.storybook/preview'; export default { title: 'Misc/Monitor', argTypes: { ...globalTypes, }, }; const Template = ({ label, ...args }) => { 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); // Should we allow overriding constants? // Constants.SHADOWCOLOR = '#e0e0e0'; // Creates the graph inside the given container const graph = createGraph(container); // Creates a process display using the activity names as IDs to refer to the elements const xml = '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + ''; const doc = XmlUtils.parseXml(xml); const codec = new mxCodec(doc); codec.decode(doc.documentElement, graph.getModel()); const buttons = document.createElement('div'); div.appendChild(buttons); // Creates a button to invoke the refresh function buttons.appendChild( mxDomHelpers.button('Update', function (evt) { // XML is normally fetched from URL at server using utils.get - this is a client-side // string with randomized states to demonstrate the idea of the workflow monitor const xml = `` + `` + ``; update(graph, xml); }) ); /** * Updates the display of the given graph using the XML data */ function update(graph, xml) { if (xml != null && xml.length > 0) { const doc = XmlUtils.parseXml(xml); if (doc != null && doc.documentElement != null) { const model = graph.getModel(); const nodes = doc.documentElement.getElementsByTagName('update'); if (nodes != null && nodes.length > 0) { model.beginUpdate(); try { for (let i = 0; i < nodes.length; i++) { // Processes the activity nodes inside the process node const id = nodes[i].getAttribute('id'); const state = nodes[i].getAttribute('state'); // Gets the cell for the given activity name from the model const cell = model.getCell(id); // Updates the cell color and adds some tooltip information if (cell != null) { // Resets the fillcolor and the overlay graph.setCellStyles('fillColor', 'white', [cell]); graph.removeCellOverlays(cell); // Changes the cell color for the known states if (state == 'Running') { graph.setCellStyles('fillColor', '#f8cecc', [cell]); } else if (state == 'Waiting') { graph.setCellStyles('fillColor', '#fff2cc', [cell]); } else if (state == 'Completed') { graph.setCellStyles('fillColor', '#d4e1f5', [cell]); } // Adds tooltip information using an overlay icon if (state != 'Init') { // Sets the overlay for the cell in the graph graph.addCellOverlay( cell, createOverlay(graph.warningImage, `State: ${state}`) ); } } } // for } finally { model.endUpdate(); } } } } } /** * Creates an overlay object using the given tooltip and text for the alert window * which is being displayed on click. */ function createOverlay(image, tooltip) { const overlay = new CellOverlay(image, tooltip); // Installs a handler for clicks on the overlay overlay.addListener(InternalEvent.CLICK, function (sender, evt) { utils.alert(`${tooltip}\nLast update: ${new Date()}`); }); return overlay; } /** * Creates and returns an empty graph inside the given container. */ function createGraph(container) { const graph = new Graph(container); graph.setTooltips(true); graph.setEnabled(false); // Disables folding graph.isCellFoldable = function (cell, collapse) { return false; }; // Creates the stylesheet for the process display let style = graph.getStylesheet().getDefaultVertexStyle(); style.fontSize = 11; style.fontColor = 'black'; style.strokeColor = '#808080'; style.fillColor = 'white'; style.gradientColor = 'white'; style.gradientDirection = Constants.DIRECTION_EAST; style.rounded = true; style.shadow = true; style.fontStyle = 1; style = graph.getStylesheet().getDefaultEdgeStyle(); style.edge = EdgeStyle.ElbowConnector; style.strokeColor = '#808080'; style.rounded = true; style.shadow = true; style = []; style.shape = Constants.SHAPE_SWIMLANE; style.perimiter = Perimeter.RectanglePerimeter; style.strokeColor = '#a0a0a0'; style.fontColor = '#606060'; style.fillColor = '#E0E0DF'; style.gradientColor = 'white'; style.startSize = 30; style.rounded = false; style.fontSize = 12; style.fontStyle = 0; style.horizontal = false; // To improve text quality for vertical labels in some old IE versions... style.labelBackgroundColor = '#efefef'; graph.getStylesheet().putCellStyle('swimlane', style); style = []; style.shape = Constants.SHAPE_RHOMBUS; style.perimiter = Perimeter.RhombusPerimeter; style.strokeColor = '#91BCC0'; style.fontColor = 'gray'; style.fillColor = '#91BCC0'; style.gradientColor = 'white'; style.align = Constants.ALIGN_CENTER; style.verticalAlign = Constants.ALIGN_MIDDLE; style.fontSize = 16; graph.getStylesheet().putCellStyle('step', style); style = []; style.shape = Constants.SHAPE_ELLIPSE; style.perimiter = Perimeter.EllipsePerimeter; style.fontColor = 'gray'; style.fillColor = '#A0C88F'; style.gradientColor = 'white'; style.strokeColor = '#A0C88F'; style.align = Constants.ALIGN_CENTER; style.verticalAlign = Constants.ALIGN_MIDDLE; style.fontSize = 16; graph.getStylesheet().putCellStyle('start', style); style = CloneUtils.clone(style); style.fillColor = '#DACCBC'; style.strokeColor = '#AF7F73'; graph.getStylesheet().putCellStyle('end', style); return graph; } /** * Returns a random state. */ function getState() { let state = 'Init'; const rnd = Math.random() * 4; if (rnd > 3) { state = 'Completed'; } else if (rnd > 2) { state = 'Running'; } else if (rnd > 1) { state = 'Waiting'; } return state; } return div; }; export const Default = Template.bind({});