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');
|
||||
groupElem.attr('class', 'architecture-groups');
|
||||
|
||||
drawServices(db, servicesElem, services);
|
||||
await drawServices(db, servicesElem, services);
|
||||
drawJunctions(db, servicesElem, junctions);
|
||||
|
||||
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
|
||||
|
||||
drawEdges(edgesElem, cy);
|
||||
drawGroups(groupElem, cy);
|
||||
await drawEdges(edgesElem, cy);
|
||||
await drawGroups(groupElem, cy);
|
||||
positionNodes(db, cy);
|
||||
|
||||
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 { 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 iconSize = getConfigField('iconSize');
|
||||
const halfIconSize = iconSize / 2;
|
||||
const arrowSize = iconSize / 6;
|
||||
const halfArrowSize = arrowSize / 2;
|
||||
|
||||
cy.edges().map((edge) => {
|
||||
const {
|
||||
source,
|
||||
sourceDir,
|
||||
sourceArrow,
|
||||
sourceGroup,
|
||||
target,
|
||||
targetDir,
|
||||
targetArrow,
|
||||
targetGroup,
|
||||
label,
|
||||
} = edgeData(edge);
|
||||
let { x: startX, y: startY } = edge[0].sourceEndpoint();
|
||||
const { x: midX, y: midY } = edge[0].midpoint();
|
||||
let { x: endX, y: endY } = edge[0].targetEndpoint();
|
||||
await Promise.all(
|
||||
cy.edges().map(async (edge) => {
|
||||
const {
|
||||
source,
|
||||
sourceDir,
|
||||
sourceArrow,
|
||||
sourceGroup,
|
||||
target,
|
||||
targetDir,
|
||||
targetArrow,
|
||||
targetGroup,
|
||||
label,
|
||||
} = edgeData(edge);
|
||||
let { x: startX, y: startY } = edge[0].sourceEndpoint();
|
||||
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
|
||||
const groupEdgeShift = padding + 4;
|
||||
// +18 comes from the service label height that extends the padding on the bottom side of each group
|
||||
if (sourceGroup) {
|
||||
if (isArchitectureDirectionX(sourceDir)) {
|
||||
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;
|
||||
// Adjust the edge distance if it has the {group} modifier
|
||||
const groupEdgeShift = padding + 4;
|
||||
// +18 comes from the service label height that extends the padding on the bottom side of each group
|
||||
if (sourceGroup) {
|
||||
if (isArchitectureDirectionX(sourceDir)) {
|
||||
startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift;
|
||||
} 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');
|
||||
createText(
|
||||
textElem,
|
||||
label,
|
||||
{
|
||||
useHtmlLabels: false,
|
||||
width,
|
||||
classes: 'architecture-service-label',
|
||||
},
|
||||
getConfig()
|
||||
);
|
||||
if (label) {
|
||||
const axis = !isArchitectureDirectionXY(sourceDir, targetDir)
|
||||
? isArchitectureDirectionX(sourceDir)
|
||||
? 'X'
|
||||
: 'Y'
|
||||
: 'XY';
|
||||
|
||||
textElem
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle');
|
||||
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 {
|
||||
width = Math.abs(startX - endX) / 2;
|
||||
}
|
||||
|
||||
if (axis === 'X') {
|
||||
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')');
|
||||
} else if (axis === 'Y') {
|
||||
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);
|
||||
const textElem = g.append('g');
|
||||
await createText(
|
||||
textElem,
|
||||
label,
|
||||
{
|
||||
useHtmlLabels: false,
|
||||
width,
|
||||
classes: 'architecture-service-label',
|
||||
},
|
||||
getConfig()
|
||||
);
|
||||
|
||||
textElem
|
||||
.attr('dominant-baseline', 'auto')
|
||||
.attr('transform', `rotate(${-1 * x * y * 45})`);
|
||||
textElem
|
||||
.attr('dy', '1em')
|
||||
.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
|
||||
const bboxNew = textElem.node().getBoundingClientRect();
|
||||
textElem.attr(
|
||||
'transform',
|
||||
`
|
||||
if (axis === 'X') {
|
||||
textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')');
|
||||
} else if (axis === 'Y') {
|
||||
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(${(x * bboxNew.width) / 2}, ${(y * bboxNew.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 groupIconSize = padding * 0.75;
|
||||
|
||||
@@ -191,82 +193,84 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
||||
const iconSize = getConfigField('iconSize');
|
||||
const halfIconSize = iconSize / 2;
|
||||
|
||||
cy.nodes().map((node) => {
|
||||
const data = nodeData(node);
|
||||
if (data.type === 'group') {
|
||||
const { h, w, x1, y1 } = node.boundingBox();
|
||||
console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`);
|
||||
await Promise.all(
|
||||
cy.nodes().map(async (node) => {
|
||||
const data = nodeData(node);
|
||||
if (data.type === 'group') {
|
||||
const { h, w, x1, y1 } = node.boundingBox();
|
||||
console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`);
|
||||
|
||||
groupsEl
|
||||
.append('rect')
|
||||
.attr('x', x1 + halfIconSize)
|
||||
.attr('y', y1 + halfIconSize)
|
||||
.attr('width', w)
|
||||
.attr('height', h)
|
||||
.attr('class', 'node-bkg');
|
||||
groupsEl
|
||||
.append('rect')
|
||||
.attr('x', x1 + halfIconSize)
|
||||
.attr('y', y1 + halfIconSize)
|
||||
.attr('width', w)
|
||||
.attr('height', h)
|
||||
.attr('class', 'node-bkg');
|
||||
|
||||
const groupLabelContainer = groupsEl.append('g');
|
||||
let shiftedX1 = x1;
|
||||
let shiftedY1 = y1;
|
||||
if (data.icon) {
|
||||
const bkgElem = groupLabelContainer.append('g');
|
||||
getIcon(data.icon)?.(bkgElem, groupIconSize);
|
||||
bkgElem.attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(shiftedX1 + halfIconSize + 1) +
|
||||
', ' +
|
||||
(shiftedY1 + halfIconSize + 1) +
|
||||
')'
|
||||
);
|
||||
shiftedX1 += groupIconSize;
|
||||
// TODO: test with more values
|
||||
// - 1 - 2 comes from the Y axis transform of the icon and label
|
||||
shiftedY1 += fontSize / 2 - 1 - 2;
|
||||
const groupLabelContainer = groupsEl.append('g');
|
||||
let shiftedX1 = x1;
|
||||
let shiftedY1 = y1;
|
||||
if (data.icon) {
|
||||
const bkgElem = groupLabelContainer.append('g');
|
||||
getIcon(data.icon)?.(bkgElem, groupIconSize);
|
||||
bkgElem.attr(
|
||||
'transform',
|
||||
'translate(' +
|
||||
(shiftedX1 + halfIconSize + 1) +
|
||||
', ' +
|
||||
(shiftedY1 + halfIconSize + 1) +
|
||||
')'
|
||||
);
|
||||
shiftedX1 += groupIconSize;
|
||||
// TODO: test with more values
|
||||
// - 1 - 2 comes from the Y axis transform of the icon and label
|
||||
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,
|
||||
elem: D3Element,
|
||||
services: ArchitectureService[]
|
||||
): number {
|
||||
services.forEach((service) => {
|
||||
): Promise<number> {
|
||||
for (const service of services) {
|
||||
const serviceElem = elem.append('g');
|
||||
const iconSize = getConfigField('iconSize');
|
||||
|
||||
if (service.title) {
|
||||
const textElem = serviceElem.append('g');
|
||||
createText(
|
||||
await createText(
|
||||
textElem,
|
||||
service.title,
|
||||
{
|
||||
@@ -331,7 +335,7 @@ export const drawServices = function (
|
||||
service.width = width;
|
||||
service.height = height;
|
||||
db.setElementForId(service.id, serviceElem);
|
||||
});
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user