mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-23 17:29:54 +02:00
fix(arch): async/await fixes for drawText changes
This commit is contained in:
@@ -455,13 +455,13 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram)
|
|||||||
const groupElem = svg.append('g');
|
const groupElem = svg.append('g');
|
||||||
groupElem.attr('class', 'architecture-groups');
|
groupElem.attr('class', 'architecture-groups');
|
||||||
|
|
||||||
drawServices(db, servicesElem, services);
|
await drawServices(db, servicesElem, services);
|
||||||
drawJunctions(db, servicesElem, junctions);
|
drawJunctions(db, servicesElem, junctions);
|
||||||
|
|
||||||
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
|
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
|
||||||
|
|
||||||
drawEdges(edgesElem, cy);
|
await drawEdges(edgesElem, cy);
|
||||||
drawGroups(groupElem, cy);
|
await drawGroups(groupElem, cy);
|
||||||
positionNodes(db, cy);
|
positionNodes(db, cy);
|
||||||
|
|
||||||
setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth'));
|
setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth'));
|
||||||
|
@@ -22,167 +22,169 @@ import { getIcon } from '../../rendering-util/svgRegister.js';
|
|||||||
import { db, getConfigField } from './architectureDb.js';
|
import { db, getConfigField } from './architectureDb.js';
|
||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) {
|
||||||
const padding = getConfigField('padding');
|
const padding = getConfigField('padding');
|
||||||
const iconSize = getConfigField('iconSize');
|
const iconSize = getConfigField('iconSize');
|
||||||
const halfIconSize = iconSize / 2;
|
const halfIconSize = iconSize / 2;
|
||||||
const arrowSize = iconSize / 6;
|
const arrowSize = iconSize / 6;
|
||||||
const halfArrowSize = arrowSize / 2;
|
const halfArrowSize = arrowSize / 2;
|
||||||
|
|
||||||
cy.edges().map((edge) => {
|
await Promise.all(
|
||||||
const {
|
cy.edges().map(async (edge) => {
|
||||||
source,
|
const {
|
||||||
sourceDir,
|
source,
|
||||||
sourceArrow,
|
sourceDir,
|
||||||
sourceGroup,
|
sourceArrow,
|
||||||
target,
|
sourceGroup,
|
||||||
targetDir,
|
target,
|
||||||
targetArrow,
|
targetDir,
|
||||||
targetGroup,
|
targetArrow,
|
||||||
label,
|
targetGroup,
|
||||||
} = edgeData(edge);
|
label,
|
||||||
let { x: startX, y: startY } = edge[0].sourceEndpoint();
|
} = edgeData(edge);
|
||||||
const { x: midX, y: midY } = edge[0].midpoint();
|
let { x: startX, y: startY } = edge[0].sourceEndpoint();
|
||||||
let { x: endX, y: endY } = edge[0].targetEndpoint();
|
const { x: midX, y: midY } = edge[0].midpoint();
|
||||||
|
let { x: endX, y: endY } = edge[0].targetEndpoint();
|
||||||
|
|
||||||
// Adjust the edge distance if it has the {group} modifier
|
// Adjust the edge distance if it has the {group} modifier
|
||||||
const groupEdgeShift = padding + 4;
|
const groupEdgeShift = padding + 4;
|
||||||
// +18 comes from the service label height that extends the padding on the bottom side of each group
|
// +18 comes from the service label height that extends the padding on the bottom side of each group
|
||||||
if (sourceGroup) {
|
if (sourceGroup) {
|
||||||
if (isArchitectureDirectionX(sourceDir)) {
|
if (isArchitectureDirectionX(sourceDir)) {
|
||||||
startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift;
|
startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift;
|
||||||
} else {
|
|
||||||
startY += sourceDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetGroup) {
|
|
||||||
if (isArchitectureDirectionX(targetDir)) {
|
|
||||||
endX += targetDir === 'L' ? -groupEdgeShift : groupEdgeShift;
|
|
||||||
} else {
|
|
||||||
endY += targetDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust the edge distance if it doesn't have the {group} modifier and the endpoint is a junction node
|
|
||||||
if (!sourceGroup && db.getNode(source)?.type === 'junction') {
|
|
||||||
if (isArchitectureDirectionX(sourceDir)) {
|
|
||||||
startX += sourceDir === 'L' ? halfIconSize : -halfIconSize;
|
|
||||||
} else {
|
|
||||||
startY += sourceDir === 'T' ? halfIconSize : -halfIconSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!targetGroup && db.getNode(target)?.type === 'junction') {
|
|
||||||
if (isArchitectureDirectionX(targetDir)) {
|
|
||||||
endX += targetDir === 'L' ? halfIconSize : -halfIconSize;
|
|
||||||
} else {
|
|
||||||
endY += targetDir === 'T' ? halfIconSize : -halfIconSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (edge[0]._private.rscratch) {
|
|
||||||
// const bounds = edge[0]._private.rscratch;
|
|
||||||
|
|
||||||
const g = edgesEl.insert('g');
|
|
||||||
|
|
||||||
g.insert('path')
|
|
||||||
.attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `)
|
|
||||||
.attr('class', 'edge');
|
|
||||||
|
|
||||||
if (sourceArrow) {
|
|
||||||
const xShift = isArchitectureDirectionX(sourceDir)
|
|
||||||
? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize)
|
|
||||||
: startX - halfArrowSize;
|
|
||||||
const yShift = isArchitectureDirectionY(sourceDir)
|
|
||||||
? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize)
|
|
||||||
: startY - halfArrowSize;
|
|
||||||
|
|
||||||
g.insert('polygon')
|
|
||||||
.attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize))
|
|
||||||
.attr('transform', `translate(${xShift},${yShift})`)
|
|
||||||
.attr('class', 'arrow');
|
|
||||||
}
|
|
||||||
if (targetArrow) {
|
|
||||||
const xShift = isArchitectureDirectionX(targetDir)
|
|
||||||
? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize)
|
|
||||||
: endX - halfArrowSize;
|
|
||||||
const yShift = isArchitectureDirectionY(targetDir)
|
|
||||||
? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize)
|
|
||||||
: endY - halfArrowSize;
|
|
||||||
|
|
||||||
g.insert('polygon')
|
|
||||||
.attr('points', ArchitectureDirectionArrow[targetDir](arrowSize))
|
|
||||||
.attr('transform', `translate(${xShift},${yShift})`)
|
|
||||||
.attr('class', 'arrow');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (label) {
|
|
||||||
const axis = !isArchitectureDirectionXY(sourceDir, targetDir)
|
|
||||||
? isArchitectureDirectionX(sourceDir)
|
|
||||||
? 'X'
|
|
||||||
: 'Y'
|
|
||||||
: 'XY';
|
|
||||||
|
|
||||||
let width = 0;
|
|
||||||
if (axis === 'X') {
|
|
||||||
width = Math.abs(startX - endX);
|
|
||||||
} else if (axis === 'Y') {
|
|
||||||
// Reduce width by a factor of 1.5 to avoid overlapping service labels
|
|
||||||
width = Math.abs(startY - endY) / 1.5;
|
|
||||||
} else {
|
} else {
|
||||||
width = Math.abs(startX - endX) / 2;
|
startY += sourceDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetGroup) {
|
||||||
|
if (isArchitectureDirectionX(targetDir)) {
|
||||||
|
endX += targetDir === 'L' ? -groupEdgeShift : groupEdgeShift;
|
||||||
|
} else {
|
||||||
|
endY += targetDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the edge distance if it doesn't have the {group} modifier and the endpoint is a junction node
|
||||||
|
if (!sourceGroup && db.getNode(source)?.type === 'junction') {
|
||||||
|
if (isArchitectureDirectionX(sourceDir)) {
|
||||||
|
startX += sourceDir === 'L' ? halfIconSize : -halfIconSize;
|
||||||
|
} else {
|
||||||
|
startY += sourceDir === 'T' ? halfIconSize : -halfIconSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!targetGroup && db.getNode(target)?.type === 'junction') {
|
||||||
|
if (isArchitectureDirectionX(targetDir)) {
|
||||||
|
endX += targetDir === 'L' ? halfIconSize : -halfIconSize;
|
||||||
|
} else {
|
||||||
|
endY += targetDir === 'T' ? halfIconSize : -halfIconSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edge[0]._private.rscratch) {
|
||||||
|
// const bounds = edge[0]._private.rscratch;
|
||||||
|
|
||||||
|
const g = edgesEl.insert('g');
|
||||||
|
|
||||||
|
g.insert('path')
|
||||||
|
.attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `)
|
||||||
|
.attr('class', 'edge');
|
||||||
|
|
||||||
|
if (sourceArrow) {
|
||||||
|
const xShift = isArchitectureDirectionX(sourceDir)
|
||||||
|
? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize)
|
||||||
|
: startX - halfArrowSize;
|
||||||
|
const yShift = isArchitectureDirectionY(sourceDir)
|
||||||
|
? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize)
|
||||||
|
: startY - halfArrowSize;
|
||||||
|
|
||||||
|
g.insert('polygon')
|
||||||
|
.attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize))
|
||||||
|
.attr('transform', `translate(${xShift},${yShift})`)
|
||||||
|
.attr('class', 'arrow');
|
||||||
|
}
|
||||||
|
if (targetArrow) {
|
||||||
|
const xShift = isArchitectureDirectionX(targetDir)
|
||||||
|
? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize)
|
||||||
|
: endX - halfArrowSize;
|
||||||
|
const yShift = isArchitectureDirectionY(targetDir)
|
||||||
|
? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize)
|
||||||
|
: endY - halfArrowSize;
|
||||||
|
|
||||||
|
g.insert('polygon')
|
||||||
|
.attr('points', ArchitectureDirectionArrow[targetDir](arrowSize))
|
||||||
|
.attr('transform', `translate(${xShift},${yShift})`)
|
||||||
|
.attr('class', 'arrow');
|
||||||
}
|
}
|
||||||
|
|
||||||
const textElem = g.append('g');
|
if (label) {
|
||||||
createText(
|
const axis = !isArchitectureDirectionXY(sourceDir, targetDir)
|
||||||
textElem,
|
? isArchitectureDirectionX(sourceDir)
|
||||||
label,
|
? 'X'
|
||||||
{
|
: 'Y'
|
||||||
useHtmlLabels: false,
|
: 'XY';
|
||||||
width,
|
|
||||||
classes: 'architecture-service-label',
|
|
||||||
},
|
|
||||||
getConfig()
|
|
||||||
);
|
|
||||||
|
|
||||||
textElem
|
let width = 0;
|
||||||
.attr('dy', '1em')
|
if (axis === 'X') {
|
||||||
.attr('alignment-baseline', 'middle')
|
width = Math.abs(startX - endX);
|
||||||
.attr('dominant-baseline', 'middle')
|
} else if (axis === 'Y') {
|
||||||
.attr('text-anchor', 'middle');
|
// Reduce width by a factor of 1.5 to avoid overlapping service labels
|
||||||
|
width = Math.abs(startY - endY) / 1.5;
|
||||||
|
} else {
|
||||||
|
width = Math.abs(startX - endX) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (axis === 'X') {
|
const textElem = g.append('g');
|
||||||
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')');
|
await createText(
|
||||||
} else if (axis === 'Y') {
|
textElem,
|
||||||
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ') rotate(-90)');
|
label,
|
||||||
} else if (axis === 'XY') {
|
{
|
||||||
const pair = getArchitectureDirectionPair(sourceDir, targetDir);
|
useHtmlLabels: false,
|
||||||
if (pair && isArchitecturePairXY(pair)) {
|
width,
|
||||||
const bboxOrig = textElem.node().getBoundingClientRect();
|
classes: 'architecture-service-label',
|
||||||
const [x, y] = getArchitectureDirectionXYFactors(pair);
|
},
|
||||||
|
getConfig()
|
||||||
|
);
|
||||||
|
|
||||||
textElem
|
textElem
|
||||||
.attr('dominant-baseline', 'auto')
|
.attr('dy', '1em')
|
||||||
.attr('transform', `rotate(${-1 * x * y * 45})`);
|
.attr('alignment-baseline', 'middle')
|
||||||
|
.attr('dominant-baseline', 'middle')
|
||||||
|
.attr('text-anchor', 'middle');
|
||||||
|
|
||||||
// Calculate the new width/height with the rotation applied, and transform to the proper position
|
if (axis === 'X') {
|
||||||
const bboxNew = textElem.node().getBoundingClientRect();
|
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')');
|
||||||
textElem.attr(
|
} else if (axis === 'Y') {
|
||||||
'transform',
|
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ') rotate(-90)');
|
||||||
`
|
} else if (axis === 'XY') {
|
||||||
|
const pair = getArchitectureDirectionPair(sourceDir, targetDir);
|
||||||
|
if (pair && isArchitecturePairXY(pair)) {
|
||||||
|
const bboxOrig = textElem.node().getBoundingClientRect();
|
||||||
|
const [x, y] = getArchitectureDirectionXYFactors(pair);
|
||||||
|
|
||||||
|
textElem
|
||||||
|
.attr('dominant-baseline', 'auto')
|
||||||
|
.attr('transform', `rotate(${-1 * x * y * 45})`);
|
||||||
|
|
||||||
|
// Calculate the new width/height with the rotation applied, and transform to the proper position
|
||||||
|
const bboxNew = textElem.node().getBoundingClientRect();
|
||||||
|
textElem.attr(
|
||||||
|
'transform',
|
||||||
|
`
|
||||||
translate(${midX}, ${midY - bboxOrig.height / 2})
|
translate(${midX}, ${midY - bboxOrig.height / 2})
|
||||||
translate(${(x * bboxNew.width) / 2}, ${(y * bboxNew.height) / 2})
|
translate(${(x * bboxNew.width) / 2}, ${(y * bboxNew.height) / 2})
|
||||||
rotate(${-1 * x * y * 45}, 0, ${bboxOrig.height / 2})
|
rotate(${-1 * x * y * 45}, 0, ${bboxOrig.height / 2})
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Core) {
|
||||||
const padding = getConfigField('padding');
|
const padding = getConfigField('padding');
|
||||||
const groupIconSize = padding * 0.75;
|
const groupIconSize = padding * 0.75;
|
||||||
|
|
||||||
@@ -191,82 +193,84 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
|||||||
const iconSize = getConfigField('iconSize');
|
const iconSize = getConfigField('iconSize');
|
||||||
const halfIconSize = iconSize / 2;
|
const halfIconSize = iconSize / 2;
|
||||||
|
|
||||||
cy.nodes().map((node) => {
|
await Promise.all(
|
||||||
const data = nodeData(node);
|
cy.nodes().map(async (node) => {
|
||||||
if (data.type === 'group') {
|
const data = nodeData(node);
|
||||||
const { h, w, x1, y1 } = node.boundingBox();
|
if (data.type === 'group') {
|
||||||
console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`);
|
const { h, w, x1, y1 } = node.boundingBox();
|
||||||
|
console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`);
|
||||||
|
|
||||||
groupsEl
|
groupsEl
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('x', x1 + halfIconSize)
|
.attr('x', x1 + halfIconSize)
|
||||||
.attr('y', y1 + halfIconSize)
|
.attr('y', y1 + halfIconSize)
|
||||||
.attr('width', w)
|
.attr('width', w)
|
||||||
.attr('height', h)
|
.attr('height', h)
|
||||||
.attr('class', 'node-bkg');
|
.attr('class', 'node-bkg');
|
||||||
|
|
||||||
const groupLabelContainer = groupsEl.append('g');
|
const groupLabelContainer = groupsEl.append('g');
|
||||||
let shiftedX1 = x1;
|
let shiftedX1 = x1;
|
||||||
let shiftedY1 = y1;
|
let shiftedY1 = y1;
|
||||||
if (data.icon) {
|
if (data.icon) {
|
||||||
const bkgElem = groupLabelContainer.append('g');
|
const bkgElem = groupLabelContainer.append('g');
|
||||||
getIcon(data.icon)?.(bkgElem, groupIconSize);
|
getIcon(data.icon)?.(bkgElem, groupIconSize);
|
||||||
bkgElem.attr(
|
bkgElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' +
|
'translate(' +
|
||||||
(shiftedX1 + halfIconSize + 1) +
|
(shiftedX1 + halfIconSize + 1) +
|
||||||
', ' +
|
', ' +
|
||||||
(shiftedY1 + halfIconSize + 1) +
|
(shiftedY1 + halfIconSize + 1) +
|
||||||
')'
|
')'
|
||||||
);
|
);
|
||||||
shiftedX1 += groupIconSize;
|
shiftedX1 += groupIconSize;
|
||||||
// TODO: test with more values
|
// TODO: test with more values
|
||||||
// - 1 - 2 comes from the Y axis transform of the icon and label
|
// - 1 - 2 comes from the Y axis transform of the icon and label
|
||||||
shiftedY1 += fontSize / 2 - 1 - 2;
|
shiftedY1 += fontSize / 2 - 1 - 2;
|
||||||
|
}
|
||||||
|
if (data.label) {
|
||||||
|
const textElem = groupLabelContainer.append('g');
|
||||||
|
await createText(
|
||||||
|
textElem,
|
||||||
|
data.label,
|
||||||
|
{
|
||||||
|
useHtmlLabels: false,
|
||||||
|
width: w,
|
||||||
|
classes: 'architecture-service-label',
|
||||||
|
},
|
||||||
|
getConfig()
|
||||||
|
);
|
||||||
|
textElem
|
||||||
|
.attr('dy', '1em')
|
||||||
|
.attr('alignment-baseline', 'middle')
|
||||||
|
.attr('dominant-baseline', 'start')
|
||||||
|
.attr('text-anchor', 'start');
|
||||||
|
|
||||||
|
textElem.attr(
|
||||||
|
'transform',
|
||||||
|
'translate(' +
|
||||||
|
(shiftedX1 + halfIconSize + 4) +
|
||||||
|
', ' +
|
||||||
|
(shiftedY1 + halfIconSize + 2) +
|
||||||
|
')'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (data.label) {
|
})
|
||||||
const textElem = groupLabelContainer.append('g');
|
);
|
||||||
createText(
|
|
||||||
textElem,
|
|
||||||
data.label,
|
|
||||||
{
|
|
||||||
useHtmlLabels: false,
|
|
||||||
width: w,
|
|
||||||
classes: 'architecture-service-label',
|
|
||||||
},
|
|
||||||
getConfig()
|
|
||||||
);
|
|
||||||
textElem
|
|
||||||
.attr('dy', '1em')
|
|
||||||
.attr('alignment-baseline', 'middle')
|
|
||||||
.attr('dominant-baseline', 'start')
|
|
||||||
.attr('text-anchor', 'start');
|
|
||||||
|
|
||||||
textElem.attr(
|
|
||||||
'transform',
|
|
||||||
'translate(' +
|
|
||||||
(shiftedX1 + halfIconSize + 4) +
|
|
||||||
', ' +
|
|
||||||
(shiftedY1 + halfIconSize + 2) +
|
|
||||||
')'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawServices = function (
|
export const drawServices = async function (
|
||||||
db: ArchitectureDB,
|
db: ArchitectureDB,
|
||||||
elem: D3Element,
|
elem: D3Element,
|
||||||
services: ArchitectureService[]
|
services: ArchitectureService[]
|
||||||
): number {
|
): Promise<number> {
|
||||||
services.forEach((service) => {
|
for (const service of services) {
|
||||||
const serviceElem = elem.append('g');
|
const serviceElem = elem.append('g');
|
||||||
const iconSize = getConfigField('iconSize');
|
const iconSize = getConfigField('iconSize');
|
||||||
|
|
||||||
if (service.title) {
|
if (service.title) {
|
||||||
const textElem = serviceElem.append('g');
|
const textElem = serviceElem.append('g');
|
||||||
createText(
|
await createText(
|
||||||
textElem,
|
textElem,
|
||||||
service.title,
|
service.title,
|
||||||
{
|
{
|
||||||
@@ -331,7 +335,7 @@ export const drawServices = function (
|
|||||||
service.width = width;
|
service.width = width;
|
||||||
service.height = height;
|
service.height = height;
|
||||||
db.setElementForId(service.id, serviceElem);
|
db.setElementForId(service.id, serviceElem);
|
||||||
});
|
}
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user