mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-15 21:39:40 +02:00
feat(arch): implemented node labels
This commit is contained in:
@@ -48,7 +48,7 @@ const clear = (): void => {
|
|||||||
commonClear();
|
commonClear();
|
||||||
};
|
};
|
||||||
|
|
||||||
const addService = function ({ id, icon, in: parent, title }: Omit<ArchitectureService, "edges">) {
|
const addService = function ({ id, icon, in: parent, title, iconText }: Omit<ArchitectureService, "edges">) {
|
||||||
if (state.records.registeredIds[id] !== undefined) {
|
if (state.records.registeredIds[id] !== undefined) {
|
||||||
throw new Error(`The service id [${id}] is already in use by another ${state.records.registeredIds[id]}`);
|
throw new Error(`The service id [${id}] is already in use by another ${state.records.registeredIds[id]}`);
|
||||||
}
|
}
|
||||||
@@ -71,6 +71,7 @@ const addService = function ({ id, icon, in: parent, title }: Omit<ArchitectureS
|
|||||||
state.records.services[id] = {
|
state.records.services[id] = {
|
||||||
id,
|
id,
|
||||||
icon,
|
icon,
|
||||||
|
iconText,
|
||||||
title,
|
title,
|
||||||
edges: [],
|
edges: [],
|
||||||
in: parent,
|
in: parent,
|
||||||
|
@@ -19,6 +19,20 @@ const getStyles: DiagramStylesProvider = (options: ArchitectureStyleOptions) =>
|
|||||||
stroke-width: ${options.archGroupBorderStrokeWidth};
|
stroke-width: ${options.archGroupBorderStrokeWidth};
|
||||||
stroke-dasharray: 8;
|
stroke-dasharray: 8;
|
||||||
}
|
}
|
||||||
|
.node-icon-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-icon-text > div {
|
||||||
|
color: #fff;
|
||||||
|
margin: 1px;
|
||||||
|
height: fit-content;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default getStyles;
|
export default getStyles;
|
||||||
|
@@ -183,6 +183,7 @@ export interface ArchitectureService {
|
|||||||
id: string;
|
id: string;
|
||||||
edges: ArchitectureEdge[];
|
edges: ArchitectureEdge[];
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
iconText?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
in?: string;
|
in?: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
@@ -118,15 +118,16 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) {
|
|||||||
|
|
||||||
// Calculate the new width/height with the rotation applied, and transform to the proper position
|
// Calculate the new width/height with the rotation applied, and transform to the proper position
|
||||||
const bboxNew = textElem.node().getBoundingClientRect();
|
const bboxNew = textElem.node().getBoundingClientRect();
|
||||||
textElem
|
textElem.attr(
|
||||||
.attr('transform', `
|
'transform',
|
||||||
translate(${midX}, ${midY - (bboxOrig.height / 2)})
|
`
|
||||||
translate(${x * bboxNew.width / 2}, ${y * bboxNew.height / 2})
|
translate(${midX}, ${midY - bboxOrig.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})
|
||||||
`);
|
`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -163,12 +164,16 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
|||||||
getIcon(data.icon)?.(bkgElem, groupIconSize);
|
getIcon(data.icon)?.(bkgElem, groupIconSize);
|
||||||
bkgElem.attr(
|
bkgElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' + (shiftedX1 + halfIconSize + 1) + ', ' + (shiftedY1 + halfIconSize + 1) + ')'
|
'translate(' +
|
||||||
|
(shiftedX1 + 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) {
|
if (data.label) {
|
||||||
const textElem = groupLabelContainer.append('g');
|
const textElem = groupLabelContainer.append('g');
|
||||||
@@ -190,7 +195,11 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) {
|
|||||||
|
|
||||||
textElem.attr(
|
textElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
'translate(' + (shiftedX1 + halfIconSize + 4) + ', ' + (shiftedY1 + halfIconSize + 2) + ')'
|
'translate(' +
|
||||||
|
(shiftedX1 + halfIconSize + 4) +
|
||||||
|
', ' +
|
||||||
|
(shiftedY1 + halfIconSize + 2) +
|
||||||
|
')'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,6 +227,7 @@ export const drawServices = function (
|
|||||||
},
|
},
|
||||||
getConfig()
|
getConfig()
|
||||||
);
|
);
|
||||||
|
|
||||||
textElem
|
textElem
|
||||||
.attr('dy', '1em')
|
.attr('dy', '1em')
|
||||||
.attr('alignment-baseline', 'middle')
|
.attr('alignment-baseline', 'middle')
|
||||||
@@ -234,6 +244,17 @@ export const drawServices = function (
|
|||||||
// throw new Error(`Invalid SVG Icon name: "${service.icon}"`);
|
// throw new Error(`Invalid SVG Icon name: "${service.icon}"`);
|
||||||
// }
|
// }
|
||||||
bkgElem = getIcon(service.icon)?.(bkgElem, iconSize);
|
bkgElem = getIcon(service.icon)?.(bkgElem, iconSize);
|
||||||
|
} else if (service.iconText) {
|
||||||
|
bkgElem = getIcon('blank')?.(bkgElem, iconSize);
|
||||||
|
const textElemContainer = bkgElem.append('g');
|
||||||
|
const fo = textElemContainer.append('foreignObject').attr('width', iconSize).attr('height', iconSize);
|
||||||
|
const divElem = fo
|
||||||
|
.append('div')
|
||||||
|
.attr('class', 'node-icon-text')
|
||||||
|
.attr('style', `height: ${iconSize}px;`)
|
||||||
|
.append('div').html(service.iconText);
|
||||||
|
const fontSize = parseInt(window.getComputedStyle(divElem.node(), null).getPropertyValue("font-size").replace(/[^\d]/g, '')) ?? 16;
|
||||||
|
divElem.attr('style', `-webkit-line-clamp: ${Math.floor((iconSize - 2) / fontSize)};`)
|
||||||
} else {
|
} else {
|
||||||
bkgElem
|
bkgElem
|
||||||
.append('path')
|
.append('path')
|
||||||
|
11
packages/mermaid/src/rendering-util/svg/blank.ts
Normal file
11
packages/mermaid/src/rendering-util/svg/blank.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Designer: Nicolas Newman
|
||||||
|
*/
|
||||||
|
import { createIcon } from '../svgRegister.js';
|
||||||
|
|
||||||
|
export default createIcon(
|
||||||
|
`<g>
|
||||||
|
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
|
||||||
|
</g>`,
|
||||||
|
80
|
||||||
|
);
|
@@ -5,6 +5,7 @@ import disk from './disk.js';
|
|||||||
import internet from './internet.js';
|
import internet from './internet.js';
|
||||||
import cloud from './cloud.js';
|
import cloud from './cloud.js';
|
||||||
import unknown from './unknown.js';
|
import unknown from './unknown.js';
|
||||||
|
import blank from './blank.js';
|
||||||
|
|
||||||
const defaultIconLibrary: IconLibrary = {
|
const defaultIconLibrary: IconLibrary = {
|
||||||
database: database,
|
database: database,
|
||||||
@@ -13,6 +14,7 @@ const defaultIconLibrary: IconLibrary = {
|
|||||||
internet: internet,
|
internet: internet,
|
||||||
cloud: cloud,
|
cloud: cloud,
|
||||||
unknown: unknown,
|
unknown: unknown,
|
||||||
|
blank: blank,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defaultIconLibrary;
|
export default defaultIconLibrary;
|
||||||
|
@@ -26,7 +26,7 @@ Group:
|
|||||||
;
|
;
|
||||||
|
|
||||||
Service:
|
Service:
|
||||||
'service' id=ARCH_ID icon=ARCH_ICON? title=ARCH_TITLE? ('in' in=ARCH_ID)? EOL
|
'service' id=ARCH_ID (iconText=ARCH_TEXT_ICON | icon=ARCH_ICON)? title=ARCH_TITLE? ('in' in=ARCH_ID)? EOL
|
||||||
;
|
;
|
||||||
|
|
||||||
Edge:
|
Edge:
|
||||||
@@ -35,6 +35,7 @@ Edge:
|
|||||||
|
|
||||||
terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B';
|
terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B';
|
||||||
terminal ARCH_ID: /[\w]+/;
|
terminal ARCH_ID: /[\w]+/;
|
||||||
|
terminal ARCH_TEXT_ICON: /\("[^"]+"\)/;
|
||||||
terminal ARCH_ICON: /\([\w]+\)/;
|
terminal ARCH_ICON: /\([\w]+\)/;
|
||||||
terminal ARCH_TITLE: /\[[\w ]+\]/;
|
terminal ARCH_TITLE: /\[[\w ]+\]/;
|
||||||
terminal ARROW_INTO: /\(|\)/;
|
terminal ARROW_INTO: /\(|\)/;
|
@@ -11,6 +11,8 @@ export class ArchitectureValueConverter extends AbstractMermaidValueConverter {
|
|||||||
): ValueType | undefined {
|
): ValueType | undefined {
|
||||||
if (rule.name === 'ARCH_ICON') {
|
if (rule.name === 'ARCH_ICON') {
|
||||||
return input.replace(/[()]/g, '').trim();
|
return input.replace(/[()]/g, '').trim();
|
||||||
|
} else if (rule.name === 'ARCH_TEXT_ICON') {
|
||||||
|
return input.replace(/[()"]/g, '');
|
||||||
} else if (rule.name === 'ARCH_TITLE') {
|
} else if (rule.name === 'ARCH_TITLE') {
|
||||||
return input.replace(/[[\]]/g, '').trim();
|
return input.replace(/[[\]]/g, '').trim();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user