fix(arch): async/await fixes for drawText changes

This commit is contained in:
NicolasNewman
2024-08-14 10:32:56 -05:00
parent 1df90b4a05
commit d36522648f
2 changed files with 209 additions and 205 deletions

View File

@@ -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'));

View File

@@ -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;
}; };