Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor fontawesome icon usage. #6282

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
5 changes: 5 additions & 0 deletions .changeset/proud-seahorses-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': patch
---

Registered icons are now embedded as SVGs inside diagram. If an icon is not available in the registered icons it will still use <i> tag
32 changes: 32 additions & 0 deletions cypress/integration/rendering/flowchart-icon.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { imgSnapshotTest } from '../../helpers/util.ts';

const themes = ['default', 'forest', 'dark', 'base', 'neutral'];

themes.forEach((theme, index) => {
describe('Flowchart Icon', () => {
it(`${index + 1}-icon: verify if icons are working from fontawesome library ${theme} theme`, () => {
imgSnapshotTest(
`flowchart TD
A("fab:fa-twitter Twitter") --> B("fab:fa-facebook Facebook")
B --> C("fa:fa-coffee Coffee")
C --> D("fa:fa-car Car")
D --> E("fab:fa-github GitHub")
`,
{ theme }
);
});
});
});

themes.forEach((theme, index) => {
describe('Flowchart Icon', () => {
it(`${index + 1}-icon: verify if registered icons are working on ${theme} theme`, () => {
imgSnapshotTest(
`flowchart TD
A("fa:fa-bell Bell")
`,
{ theme }
);
});
});
});
4 changes: 4 additions & 0 deletions cypress/platform/e2e.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
/>
<style>
svg {
border: 2px solid darkred;
Expand Down
2 changes: 1 addition & 1 deletion cypress/platform/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const contentLoaded = async function () {
mermaid.registerLayoutLoaders(layouts);
mermaid.initialize(graphObj.mermaid);
const staticBellIconPack = {
prefix: 'fa6-regular',
prefix: 'fa',
icons: {
bell: {
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',
Expand Down
8 changes: 6 additions & 2 deletions docs/syntax/flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -1916,9 +1916,13 @@ If a class is named default it will be assigned to all classes without specific

## Basic support for fontawesome

It is possible to add icons from fontawesome.
It is possible to add icons from fontawesome and registered icon pack.

The icons are accessed via the syntax fa:#icon class name#.
Mermaid supports icons from registered icon packs. Follow the instructions provided [here](../config/icons.md) to register your icon packs.

The registered icons can be accessed via the syntax #registered icon pack name#:#icon name#.

The fontawesome icons are accessed via the syntax fa:#icon class name#.

```mermaid-example
flowchart TD
Expand Down
18 changes: 10 additions & 8 deletions packages/mermaid/src/dagre-wrapper/clusters.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getConfig } from '../diagram-api/diagramAPI.js';
import { evaluate } from '../diagrams/common/common.js';
import { getSubGraphTitleMargins } from '../utils/subGraphTitleMargins.js';

const rect = (parent, node) => {
const rect = async (parent, node) => {
log.info('Creating subgraph rect for ', node.id, node);
const siteConfig = getConfig();

Expand All @@ -31,7 +31,9 @@ const rect = (parent, node) => {
const text =
node.labelType === 'markdown'
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels }, siteConfig)
: label.node().appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
: label
.node()
.appendChild(await createLabel(node.labelText, node.labelStyle, undefined, true));

// Get the size of the label
let bbox = text.getBBox();
Expand Down Expand Up @@ -129,7 +131,7 @@ const noteGroup = (parent, node) => {

return shapeSvg;
};
const roundedWithTitle = (parent, node) => {
const roundedWithTitle = async (parent, node) => {
const siteConfig = getConfig();

// Add outer g element
Expand All @@ -144,7 +146,7 @@ const roundedWithTitle = (parent, node) => {

const text = label
.node()
.appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
.appendChild(await createLabel(node.labelText, node.labelStyle, undefined, true));

// Get the size of the label
let bbox = text.getBBox();
Expand Down Expand Up @@ -236,13 +238,13 @@ const shapes = { rect, roundedWithTitle, noteGroup, divider };

let clusterElems = {};

export const insertCluster = (elem, node) => {
export const insertCluster = async (elem, node) => {
log.trace('Inserting cluster');
const shape = node.shape || 'rect';
clusterElems[node.id] = shapes[shape](elem, node);
clusterElems[node.id] = await shapes[shape](elem, node);
};
export const getClusterTitleWidth = (elem, node) => {
const label = createLabel(node.labelText, node.labelStyle, undefined, true);
export const getClusterTitleWidth = async (elem, node) => {
const label = await createLabel(node.labelText, node.labelStyle, undefined, true);
elem.node().appendChild(label);
const width = label.getBBox().width;
elem.node().removeChild(label);
Expand Down
5 changes: 3 additions & 2 deletions packages/mermaid/src/dagre-wrapper/createLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function addHtmlLabel(node) {
* @param isNode
* @deprecated svg-util/createText instead
*/
const createLabel = (_vertexText, style, isTitle, isNode) => {
const createLabel = async (_vertexText, style, isTitle, isNode) => {
let vertexText = _vertexText || '';
if (typeof vertexText === 'object') {
vertexText = vertexText[0];
Expand All @@ -53,9 +53,10 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
log.debug('vertexText' + vertexText);
const label = await replaceIconSubstring(decodeEntities(vertexText));
const node = {
isNode,
label: replaceIconSubstring(decodeEntities(vertexText)),
label,
labelStyle: style.replace('fill:', 'color:'),
};
let vertexNode = addHtmlLabel(node);
Expand Down
12 changes: 6 additions & 6 deletions packages/mermaid/src/dagre-wrapper/edges.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const clear = () => {
terminalLabels = {};
};

export const insertEdgeLabel = (elem, edge) => {
export const insertEdgeLabel = async (elem, edge) => {
const config = getConfig();
const useHtmlLabels = evaluate(config.flowchart.htmlLabels);
// Create the actual text element
Expand All @@ -33,7 +33,7 @@ export const insertEdgeLabel = (elem, edge) => {
},
config
)
: createLabel(edge.label, edge.labelStyle);
: await createLabel(edge.label, edge.labelStyle);

// Create outer g, edgeLabel, this will be positioned after graph layout
const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
Expand Down Expand Up @@ -63,7 +63,7 @@ export const insertEdgeLabel = (elem, edge) => {
let fo;
if (edge.startLabelLeft) {
// Create the actual text element
const startLabelElement = createLabel(edge.startLabelLeft, edge.labelStyle);
const startLabelElement = await createLabel(edge.startLabelLeft, edge.labelStyle);
const startEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
const inner = startEdgeLabelLeft.insert('g').attr('class', 'inner');
fo = inner.node().appendChild(startLabelElement);
Expand All @@ -77,7 +77,7 @@ export const insertEdgeLabel = (elem, edge) => {
}
if (edge.startLabelRight) {
// Create the actual text element
const startLabelElement = createLabel(edge.startLabelRight, edge.labelStyle);
const startLabelElement = await createLabel(edge.startLabelRight, edge.labelStyle);
const startEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
const inner = startEdgeLabelRight.insert('g').attr('class', 'inner');
fo = startEdgeLabelRight.node().appendChild(startLabelElement);
Expand All @@ -93,7 +93,7 @@ export const insertEdgeLabel = (elem, edge) => {
}
if (edge.endLabelLeft) {
// Create the actual text element
const endLabelElement = createLabel(edge.endLabelLeft, edge.labelStyle);
const endLabelElement = await createLabel(edge.endLabelLeft, edge.labelStyle);
const endEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
const inner = endEdgeLabelLeft.insert('g').attr('class', 'inner');
fo = inner.node().appendChild(endLabelElement);
Expand All @@ -110,7 +110,7 @@ export const insertEdgeLabel = (elem, edge) => {
}
if (edge.endLabelRight) {
// Create the actual text element
const endLabelElement = createLabel(edge.endLabelRight, edge.labelStyle);
const endLabelElement = await createLabel(edge.endLabelRight, edge.labelStyle);
const endEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
const inner = endEdgeLabelRight.insert('g').attr('class', 'inner');

Expand Down
6 changes: 3 additions & 3 deletions packages/mermaid/src/dagre-wrapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
// Move the nodes to the correct place
let diff = 0;
const { subGraphTitleTotalMargin } = getSubGraphTitleMargins(siteConfig);
sortNodesByHierarchy(graph).forEach(function (v) {
for (const v of sortNodesByHierarchy(graph)) {
const node = graph.node(v);
log.info('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
log.info(
Expand All @@ -141,14 +141,14 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
// A cluster in the non-recursive way
// positionCluster(node);
node.height += subGraphTitleTotalMargin;
insertCluster(clusters, node);
await insertCluster(clusters, node);
clusterDb[node.id].node = node;
} else {
node.y += subGraphTitleTotalMargin / 2;
positionNode(node);
}
}
});
}

// Move the edge labels to the correct place after layout
graph.edges().forEach(function (e) {
Expand Down
25 changes: 15 additions & 10 deletions packages/mermaid/src/dagre-wrapper/nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ function applyNodePropertyBorders(rect, borders, totalWidth, totalHeight) {
rect.attr('stroke-dasharray', strokeDashArray.join(' '));
}

const rectWithTitle = (parent, node) => {
const rectWithTitle = async (parent, node) => {
// const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes);

let classes;
Expand Down Expand Up @@ -586,7 +586,7 @@ const rectWithTitle = (parent, node) => {
}
log.info('Label text abc79', title, text2, typeof text2 === 'object');

const text = label.node().appendChild(createLabel(title, node.labelStyle, true, true));
const text = label.node().appendChild(await createLabel(title, node.labelStyle, true, true));
let bbox = { width: 0, height: 0 };
if (evaluate(getConfig().flowchart.htmlLabels)) {
const div = text.children[0];
Expand All @@ -601,7 +601,12 @@ const rectWithTitle = (parent, node) => {
const descr = label
.node()
.appendChild(
createLabel(textRows.join ? textRows.join('<br/>') : textRows, node.labelStyle, true, true)
await createLabel(
textRows.join ? textRows.join('<br/>') : textRows,
node.labelStyle,
true,
true
)
);

if (evaluate(getConfig().flowchart.htmlLabels)) {
Expand Down Expand Up @@ -876,7 +881,7 @@ const end = (parent, node) => {
return shapeSvg;
};

const class_box = (parent, node) => {
const class_box = async (parent, node) => {
const halfPadding = node.padding / 2;
const rowPadding = 4;
const lineHeight = 8;
Expand Down Expand Up @@ -910,7 +915,7 @@ const class_box = (parent, node) => {
: '';
const interfaceLabel = labelContainer
.node()
.appendChild(createLabel(interfaceLabelText, node.labelStyle, true, true));
.appendChild(await createLabel(interfaceLabelText, node.labelStyle, true, true));
let interfaceBBox = interfaceLabel.getBBox();
if (evaluate(getConfig().flowchart.htmlLabels)) {
const div = interfaceLabel.children[0];
Expand All @@ -935,7 +940,7 @@ const class_box = (parent, node) => {
}
const classTitleLabel = labelContainer
.node()
.appendChild(createLabel(classTitleString, node.labelStyle, true, true));
.appendChild(await createLabel(classTitleString, node.labelStyle, true, true));
select(classTitleLabel).attr('class', 'classTitle');
let classTitleBBox = classTitleLabel.getBBox();
if (evaluate(getConfig().flowchart.htmlLabels)) {
Expand All @@ -950,7 +955,7 @@ const class_box = (parent, node) => {
maxWidth = classTitleBBox.width;
}
const classAttributes = [];
node.classData.members.forEach((member) => {
node.classData.members.forEach(async (member) => {
const parsedInfo = member.getDisplayDetails();
let parsedText = parsedInfo.displayText;
if (getConfig().flowchart.htmlLabels) {
Expand All @@ -959,7 +964,7 @@ const class_box = (parent, node) => {
const lbl = labelContainer
.node()
.appendChild(
createLabel(
await createLabel(
parsedText,
parsedInfo.cssStyle ? parsedInfo.cssStyle : node.labelStyle,
true,
Expand All @@ -984,7 +989,7 @@ const class_box = (parent, node) => {
maxHeight += lineHeight;

const classMethods = [];
node.classData.methods.forEach((member) => {
node.classData.methods.forEach(async (member) => {
const parsedInfo = member.getDisplayDetails();
let displayText = parsedInfo.displayText;
if (getConfig().flowchart.htmlLabels) {
Expand All @@ -993,7 +998,7 @@ const class_box = (parent, node) => {
const lbl = labelContainer
.node()
.appendChild(
createLabel(
await createLabel(
displayText,
parsedInfo.cssStyle ? parsedInfo.cssStyle : node.labelStyle,
true,
Expand Down
7 changes: 6 additions & 1 deletion packages/mermaid/src/dagre-wrapper/shapes/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
);
} else {
text = textNode.appendChild(
createLabel(sanitizeText(decodeEntities(labelText), config), node.labelStyle, false, isNode)
await createLabel(
sanitizeText(decodeEntities(labelText), config),
node.labelStyle,
false,
isNode
)
);
}
// Get the size of the label
Expand Down
14 changes: 14 additions & 0 deletions packages/mermaid/src/diagrams/block/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,20 @@ const getStyles = (options: BlockChartStyleOptions) =>
font-size: 18px;
fill: ${options.textColor};
}
.node label-icon path {
fill: currentColor;
stroke: revert;
stroke-width: revert;
}
/**
* These are copied from font-awesome.css
*/
.label-icon {
display: inline-block;
height: 1em;
overflow: visible;
vertical-align: -0.125em;
}
`;

export default getStyles;
14 changes: 14 additions & 0 deletions packages/mermaid/src/diagrams/class/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ g.classGroup line {
font-size: 18px;
fill: ${options.textColor};
}
.node label-icon path {
fill: currentColor;
stroke: revert;
stroke-width: revert;
}
/**
* These are copied from font-awesome.css
*/
.label-icon {
display: inline-block;
height: 1em;
overflow: visible;
vertical-align: -0.125em;
}
`;

export default getStyles;
Loading
Loading