mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-22 00:40:22 +02:00
store and reference entity by unique id, not name (elements, graph, etc.)
This commit is contained in:

parent
481b5cde0f
commit
91091c614f
@@ -7,9 +7,17 @@ import erMarkers from './erMarkers';
|
|||||||
import { configureSvgSize } from '../../setupGraphViewbox';
|
import { configureSvgSize } from '../../setupGraphViewbox';
|
||||||
import addSVGAccessibilityFields from '../../accessibility';
|
import addSVGAccessibilityFields from '../../accessibility';
|
||||||
import { parseGenericTypes } from '../common/common';
|
import { parseGenericTypes } from '../common/common';
|
||||||
|
import { v4 as uuid4 } from 'uuid';
|
||||||
|
|
||||||
|
/** Regex used to remove chars from the entity name so the result can be used in an id */
|
||||||
|
const BAD_ID_CHARS_REGEXP = /[^A-Za-z0-9]([\W])*/g;
|
||||||
|
|
||||||
|
// Configuration
|
||||||
let conf = {};
|
let conf = {};
|
||||||
|
|
||||||
|
// Map so we can look up the id of an entity based on the name
|
||||||
|
let entityNameIds = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows the top-level API module to inject config specific to this renderer, storing it in the
|
* Allows the top-level API module to inject config specific to this renderer, storing it in the
|
||||||
* local conf object. Note that generic config still needs to be retrieved using getConfig()
|
* local conf object. Note that generic config still needs to be retrieved using getConfig()
|
||||||
@@ -313,15 +321,18 @@ const drawEntities = function (svgNode, entities, graph) {
|
|||||||
const keys = Object.keys(entities);
|
const keys = Object.keys(entities);
|
||||||
let firstOne;
|
let firstOne;
|
||||||
|
|
||||||
keys.forEach(function (id) {
|
keys.forEach(function (entityName) {
|
||||||
// Create a group for each entity
|
const entityId = generateId(entityName, 'entity');
|
||||||
const groupNode = svgNode.append('g').attr('id', id);
|
entityNameIds.set(entityName, entityId);
|
||||||
|
|
||||||
firstOne = firstOne === undefined ? id : firstOne;
|
// Create a group for each entity
|
||||||
|
const groupNode = svgNode.append('g').attr('id', entityId);
|
||||||
|
|
||||||
|
firstOne = firstOne === undefined ? entityId : firstOne;
|
||||||
|
|
||||||
// Label the entity - this is done first so that we can get the bounding box
|
// Label the entity - this is done first so that we can get the bounding box
|
||||||
// which then determines the size of the rectangle
|
// which then determines the size of the rectangle
|
||||||
const textId = 'entity-' + id;
|
const textId = 'text-' + entityId;
|
||||||
const textNode = groupNode
|
const textNode = groupNode
|
||||||
.append('text')
|
.append('text')
|
||||||
.attr('class', 'er entityLabel')
|
.attr('class', 'er entityLabel')
|
||||||
@@ -334,12 +345,12 @@ const drawEntities = function (svgNode, entities, graph) {
|
|||||||
'style',
|
'style',
|
||||||
'font-family: ' + getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px'
|
'font-family: ' + getConfig().fontFamily + '; font-size: ' + conf.fontSize + 'px'
|
||||||
)
|
)
|
||||||
.text(id);
|
.text(entityName);
|
||||||
|
|
||||||
const { width: entityWidth, height: entityHeight } = drawAttributes(
|
const { width: entityWidth, height: entityHeight } = drawAttributes(
|
||||||
groupNode,
|
groupNode,
|
||||||
textNode,
|
textNode,
|
||||||
entities[id].attributes
|
entities[entityName].attributes
|
||||||
);
|
);
|
||||||
|
|
||||||
// Draw the rectangle - insert it before the text so that the text is not obscured
|
// Draw the rectangle - insert it before the text so that the text is not obscured
|
||||||
@@ -356,12 +367,12 @@ const drawEntities = function (svgNode, entities, graph) {
|
|||||||
|
|
||||||
const rectBBox = rectNode.node().getBBox();
|
const rectBBox = rectNode.node().getBBox();
|
||||||
|
|
||||||
// Add the entity to the graph
|
// Add the entity to the graph using the entityId
|
||||||
graph.setNode(id, {
|
graph.setNode(entityId, {
|
||||||
width: rectBBox.width,
|
width: rectBBox.width,
|
||||||
height: rectBBox.height,
|
height: rectBBox.height,
|
||||||
shape: 'rect',
|
shape: 'rect',
|
||||||
id: id,
|
id: entityId,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return firstOne;
|
return firstOne;
|
||||||
@@ -405,7 +416,12 @@ const getEdgeName = function (rel) {
|
|||||||
*/
|
*/
|
||||||
const addRelationships = function (relationships, g) {
|
const addRelationships = function (relationships, g) {
|
||||||
relationships.forEach(function (r) {
|
relationships.forEach(function (r) {
|
||||||
g.setEdge(r.entityA, r.entityB, { relationship: r }, getEdgeName(r));
|
g.setEdge(
|
||||||
|
entityNameIds.get(r.entityA),
|
||||||
|
entityNameIds.get(r.entityB),
|
||||||
|
{ relationship: r },
|
||||||
|
getEdgeName(r)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
return relationships;
|
return relationships;
|
||||||
}; // addRelationships
|
}; // addRelationships
|
||||||
@@ -425,7 +441,11 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
|
|||||||
relCnt++;
|
relCnt++;
|
||||||
|
|
||||||
// Find the edge relating to this relationship
|
// Find the edge relating to this relationship
|
||||||
const edge = g.edge(rel.entityA, rel.entityB, getEdgeName(rel));
|
const edge = g.edge(
|
||||||
|
entityNameIds.get(rel.entityA),
|
||||||
|
entityNameIds.get(rel.entityB),
|
||||||
|
getEdgeName(rel)
|
||||||
|
);
|
||||||
|
|
||||||
// Get a function that will generate the line path
|
// Get a function that will generate the line path
|
||||||
const lineFunction = line()
|
const lineFunction = line()
|
||||||
@@ -652,6 +672,35 @@ export const draw = function (text, id, _version, diagObj) {
|
|||||||
addSVGAccessibilityFields(diagObj.db, svg, id);
|
addSVGAccessibilityFields(diagObj.db, svg, id);
|
||||||
}; // draw
|
}; // draw
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a unique id based on the given string. Start with the prefix, then a hyphen, then the
|
||||||
|
* simplified str, then a hyphen, then a unique uuid. (Hyphens are only included if needed.)
|
||||||
|
* Although the official XML standard for ids says that many more characters are valid in the id,
|
||||||
|
* this keeps things simple by accepting only A-Za-z0-9.
|
||||||
|
*
|
||||||
|
* @param {string} [str?=''] Given string to use as the basis for the id. Default is `''`
|
||||||
|
* @param {string} [prefix?=''] String to put at the start, followed by '-'. Default is `''`
|
||||||
|
* @param str
|
||||||
|
* @param prefix
|
||||||
|
* @returns {string}
|
||||||
|
* @see https://www.w3.org/TR/xml/#NT-Name
|
||||||
|
*/
|
||||||
|
export function generateId(str = '', prefix = '') {
|
||||||
|
const simplifiedStr = str.replace(BAD_ID_CHARS_REGEXP, '');
|
||||||
|
return `${strWithHyphen(prefix)}${strWithHyphen(simplifiedStr)}${uuid4()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a hyphen to a string only if the string isn't empty
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {string}
|
||||||
|
* @todo This could be moved into a string utility file/class.
|
||||||
|
*/
|
||||||
|
function strWithHyphen(str = '') {
|
||||||
|
return str.length > 0 ? `${str}-` : '';
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setConf,
|
setConf,
|
||||||
draw,
|
draw,
|
||||||
|
Reference in New Issue
Block a user