mxGraph-Flowchart echo label text super long processing, to achieve the effect of mouse hovering to display the complete text

Write custom directory title here

  • foreword
  • environment
  • npm introduces mxgraph
  • Build a flowchart
  • Flowchart initialization
  • page effect

Foreword

When using mxGraph to echo the flow chart, there is a problem that the label is too long and overlapped. It is necessary to add a tooltip to display the complete code when the mouse hovers over it.

Environment

vue:^3.2.36
mxgraph:^4.2.2

npm introduces mxgraph

// package.json
"dependencies": {<!-- -->
"mxgraph": "^4.2.2",
// .... other dependencies
}

Construction flowchart

const loadDate = (data, graph) => {<!-- -->
    const parent = graph. getDefaultParent();
    const obVer = new Map();

    graph.getStylesheet().putCellStyle('e1', {<!-- -->
        strokeColor: 'green',
        strokeWidth: 2
    });

    graph.getStylesheet().putCellStyle('e2', {<!-- -->
        strokeColor: '#ccc',
        strokeWidth: 2
    });
    // Open the tooltip property
    graph.setTooltips(true);
    // set the mouseover style
    graph.getCursorForCell = function (cell) {<!-- -->
        if (cell !== null & amp; & amp; cell.value !== null & amp; & amp; cell.vertex === true) {<!-- -->
            return 'pointer';
        }
    };
    graph.setConnectable(false); // disable drag and drop connection
    graph.getSelectionModel().cellsSelectable = false; // disable selection
    graph.getModel().beginUpdate();
    try {<!-- -->
        data.forEach((item, index) => {<!-- -->
            item.forEach((itemSub, indexSub) => {<!-- -->
                const style1: any = {<!-- -->};
                style1[mx.mxConstants.STYLE_SHAPE] = mx.mxConstants.SHAPE_IMAGE;
                style1[mx.mxConstants.STYLE_IMAGE] = itemSub.icon;
                style1[mx.mxConstants.STYLE_FONTCOLOR] = '#666';
                style1.verticalLabelPosition = 'bottom';
                graph.getStylesheet().putCellStyle('setp' + index + '-' + indexSub, style1);
                const indexItem = graph.insertVertex(
                    parent,
                    null,
                    itemSub.label,
                    20 + index * 250,
                    parseInt(totalHeight.value) / 2 + 80 * indexSub - item.length * 40,
                    80,
                    40,
                    'setp' + index + '-' + indexSub
                );
                // Override the getLabel function
                graph.getLabel = function (cell) {<!-- -->
                    if (this. getModel(). isVertex(cell)) {<!-- -->
                        const label = mx.mxGraph.prototype.getLabel.apply(this, arguments);
                        return this. ellipseText(label, cell);
                    }
                    return mx.mxGraph.prototype.getLabel.apply(this, arguments);
                };
                // Override the ellipseText function
                graph.ellipseText = function (label, cell) {<!-- -->
                    const vertexWidth = this.view.getState(cell).width;
                    const div = document. createElement('div');
                    div.style.position = 'absolute';
                    div.style.height = '20px';
                    div.style.overflow = 'hidden';
                    div.style.whiteSpace = 'nowrap';
                    div. innerHTML = label;
                    document.body.appendChild(div);
                    let textWidth = div. offsetWidth;
                    document.body.removeChild(div);
                    // The length of the label is less than or equal to the width of the element, so there is no need to omit it
                    if (textWidth <= vertexWidth) {<!-- -->
                        return label;
                    }
                    const dots = '...';
                    // The maximum number of characters that an element can display (can also be dynamically set by custom rules)
                    let maxChars = Math. floor((label. length * vertexWidth) / textWidth);
                    // You can adjust the pixels of the ellipsis position
                    const minDiff = 5;
                    while (textWidth > vertexWidth - 2 * (dots. length + minDiff) & amp; & amp; maxChars > 1) {<!-- -->
                        maxChars -= 1;
                        div.textContent = label.substring(0, maxChars);
                        textWidth = div. offsetWidth;
                    }
                    const result = label. substring(0, maxChars) + dots;
                    return result;
                };
                obVer.set(index + '-' + indexSub, {<!-- -->
                    graphItem: indexItem,
                    itemData: itemSub
                });
                // arrow
                if (index > 0 & amp; & amp; index < data.length) {<!-- -->
                    obVer.forEach((obVeritem, key) => {<!-- -->
                        // traverse the previous
                        if (key. indexOf(index - 1 + '-') > -1) {<!-- -->
                            // debugger;
                            const getGlItem = obVer. get(key);
                            const e1 = graph. insertEdge(
                                parent,
                                null,
                                '',
                                getGlItem.graphItem,
                                indexItem,
                                itemSub.isGrayArrow ? 'e2' : 'e1'
                            );
                        }
                    });
                }
            });
        });
    } finally {<!-- -->
        graph.getModel().endUpdate();
    }
};

Flowchart initialization

const init = async (container: HTMLDivElement) => {<!-- -->
    const graph = new mx.mxGraph(container);
    const style = graph. getStylesheet(). getDefaultEdgeStyle();
    stye[mx.mxConstants.STYLE_EDGE] = mx.mxEdgeStyle.ElbowConnector;
    style[mx.mxConstants.STYLE_ROUNDED] = true;
    graph.alternateEdgeStyle = 'elbow=vertical';
    // can edit
    graph. setEnabled(false);
    // Whether it can be moved as a whole
    graph.panningHandler.useLeftButtonForPanning = true;
    loadDate(props. JSDate, graph);
    // Get the tooltip component
    var tooltip = new mx.mxTooltipHandler(graph);
    tooltip.init();
    const tool:any = document.getElementsByClassName('mxTooltip')[0];
    tool.style.cssText = "position:absolute;width:240px;boxShadow: 3px 3px 12px #C0C0C0;background: #FFFFCC;border-style: solid;border-width: 1px;border-color: black;font-family: Arial;font-size: 8pt;cursor: default;padding: 4px;color: black;"
   
    // Define the mouse hover to display the full text
    graph.getTooltipForCell = function (cell) {<!-- -->
        if (this. getModel(). isVertex(cell)) {<!-- -->
            return cell. value;
        }
    };
};

Page effect


At present, this implementation method is problematic
The first div whose className is mxTooltip is generated by tooltip.init(); code; the second div is generated when the mouse hovers. The prompt text seen on the page is the first div, and the second div is not displayed on the page (positioning problem).

In order to avoid more dom nodes whose className is mxTooltip on the page, the dom must be cleared every time the pop-up window is closed.

 // Get all elements whose class is mxTooltip
    const elements:any = document.getElementsByClassName('mxTooltip');

    // Traverse the list of obtained elements and remove each element in turn
    while (elements. length > 0) {<!-- -->
        elements[0].parentNode.removeChild(elements[0]);
    }