Skip to content

Commit

Permalink
Merge pull request #5430 from jakobskrym/feature/support-fa-kit-custo…
Browse files Browse the repository at this point in the history
…m-icons

Feature/support fa kit custom icons
  • Loading branch information
sidharthv96 committed Apr 3, 2024
2 parents e852596 + 38beca1 commit 3ccfea8
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 22 deletions.
30 changes: 30 additions & 0 deletions docs/syntax/flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,36 @@ Adding this snippet in the `<head>` would add support for Font Awesome v6.5.1
/>
```

### Custom icons

It is possible to use custom icons served from Font Awesome as long as the website imports the corresponding kit.

Note that this is currently a paid feature from Font Awesome.

For custom icons, you need to use the `fak` prefix.

**Example**

```
flowchart TD
B[fa:fa-twitter] %% standard icon
B-->E(fak:fa-custom-icon-name) %% custom icon
```

And trying to render it

```mermaid-example
flowchart TD
B["fa:fa-twitter for peace"]
B-->C["fab:fa-truck-bold a custom icon"]
```

```mermaid
flowchart TD
B["fa:fa-twitter for peace"]
B-->C["fab:fa-truck-bold a custom icon"]
```

## Graph declarations with spaces between vertices and link and without semicolon

- In graph declarations, the statements also can now end without a semicolon. After release 0.2.16, ending a graph statement with semicolon is just optional. So the below graph declaration is also valid along with the old declarations of the graph.
Expand Down
6 changes: 2 additions & 4 deletions packages/mermaid/src/dagre-wrapper/createLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { log } from '../logger.js';
import { getConfig } from '../diagram-api/diagramAPI.js';
import { evaluate } from '../diagrams/common/common.js';
import { decodeEntities } from '../utils.js';
import { replaceIconSubstring } from '../rendering-util/createText.js';

/**
* @param dom
Expand Down Expand Up @@ -59,10 +60,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
log.debug('vertexText' + vertexText);
const node = {
isNode,
label: decodeEntities(vertexText).replace(
/fa[blrs]?:fa-[\w-]+/g, // cspell: disable-line
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
label: replaceIconSubstring(decodeEntities(vertexText)),
labelStyle: style.replace('fill:', 'color:'),
};
let vertexNode = addHtmlLabel(node);
Expand Down
18 changes: 4 additions & 14 deletions packages/mermaid/src/diagrams/flowchart/flowRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import common, { evaluate, renderKatex } from '../common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import flowChartShapes from './flowChartShapes.js';
import { replaceIconSubstring } from '../../rendering-util/createText.js';

const conf = {};
export const setConf = function (cnf) {
Expand Down Expand Up @@ -56,14 +57,9 @@ export const addVertices = async function (vert, g, svgId, root, _doc, diagObj)
let vertexNode;
if (evaluate(getConfig().flowchart.htmlLabels)) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
const replacedVertexText = replaceIconSubstring(vertexText);
const node = {
label: await renderKatex(
vertexText.replace(
/fa[blrs]?:fa-[\w-]+/g, // cspell:disable-line
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
getConfig()
),
label: await renderKatex(replacedVertexText, getConfig()),
};
vertexNode = addHtmlLabel(svg, node).node();
vertexNode.parentNode.removeChild(vertexNode);
Expand Down Expand Up @@ -242,13 +238,7 @@ export const addEdges = async function (edges, g, diagObj) {
edgeData.labelType = 'html';
edgeData.label = `<span id="L-${linkId}" class="edgeLabel L-${linkNameStart}' L-${linkNameEnd}" style="${
edgeData.labelStyle
}">${await renderKatex(
edge.text.replace(
/fa[blrs]?:fa-[\w-]+/g, // cspell:disable-line
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
getConfig()
)}</span>`;
}">${await renderKatex(replaceIconSubstring(edge.text), getConfig())}</span>`;
} else {
edgeData.labelType = 'text';
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
Expand Down
24 changes: 24 additions & 0 deletions packages/mermaid/src/docs/syntax/flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,30 @@ Adding this snippet in the `<head>` would add support for Font Awesome v6.5.1
/>
```

### Custom icons

It is possible to use custom icons served from Font Awesome as long as the website imports the corresponding kit.

Note that this is currently a paid feature from Font Awesome.

For custom icons, you need to use the `fak` prefix.

**Example**

```
flowchart TD
B[fa:fa-twitter] %% standard icon
B-->E(fak:fa-custom-icon-name) %% custom icon
```

And trying to render it

```mermaid-example
flowchart TD
B["fa:fa-twitter for peace"]
B-->C["fab:fa-truck-bold a custom icon"]
```

## Graph declarations with spaces between vertices and link and without semicolon

- In graph declarations, the statements also can now end without a semicolon. After release 0.2.16, ending a graph statement with semicolon is just optional. So the below graph declaration is also valid along with the old declarations of the graph.
Expand Down
34 changes: 34 additions & 0 deletions packages/mermaid/src/rendering-util/createText.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, it, expect } from 'vitest';
import { replaceIconSubstring } from './createText.js';

describe('replaceIconSubstring', () => {
it('converts FontAwesome icon notations to HTML tags', () => {
const input = 'This is an icon: fa:fa-user and fab:fa-github';
const output = replaceIconSubstring(input);
const expected =
"This is an icon: <i class='fa fa-user'></i> and <i class='fab fa-github'></i>";
expect(output).toEqual(expected);
});

it('handles strings without FontAwesome icon notations', () => {
const input = 'This string has no icons';
const output = replaceIconSubstring(input);
expect(output).toEqual(input); // No change expected
});

it('correctly processes multiple FontAwesome icon notations in one string', () => {
const input = 'Icons galore: fa:fa-arrow-right, fak:fa-truck, fas:fa-home';
const output = replaceIconSubstring(input);
const expected =
"Icons galore: <i class='fa fa-arrow-right'></i>, <i class='fak fa-truck'></i>, <i class='fas fa-home'></i>";
expect(output).toEqual(expected);
});

it('correctly replaces a very long icon name with the fak prefix', () => {
const input = 'Here is a long icon: fak:fa-truck-driving-long-winding-road in use';
const output = replaceIconSubstring(input);
const expected =
"Here is a long icon: <i class='fak fa-truck-driving-long-winding-road'></i> in use";
expect(output).toEqual(expected);
});
});
19 changes: 15 additions & 4 deletions packages/mermaid/src/rendering-util/createText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,19 @@ function updateTextContentAndStyles(tspan: any, wrappedLine: MarkdownWord[]) {
});
}

/**
* Convert fontawesome labels into fontawesome icons by using a regex pattern
* @param text - The raw string to convert
* @returns string with fontawesome icons as i tags
*/
export function replaceIconSubstring(text: string) {
// The letters 'bklrs' stand for possible endings of the fontawesome prefix (e.g. 'fab' for brands, 'fak' for fa-kit) // cspell: disable-line
return text.replace(
/fa[bklrs]?:fa-[\w-]+/g, // cspell: disable-line
(s) => `<i class='${s.replace(':', ' ')}'></i>`
);
}

// Note when using from flowcharts converting the API isNode means classes should be set accordingly. When using htmlLabels => to sett classes to'nodeLabel' when isNode=true otherwise 'edgeLabel'
// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row'
export const createText = (
Expand All @@ -189,12 +202,10 @@ export const createText = (
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?

const htmlText = markdownToHTML(text, config);
const decodedReplacedText = replaceIconSubstring(decodeEntities(htmlText));
const node = {
isNode,
label: decodeEntities(htmlText).replace(
/fa[blrs]?:fa-[\w-]+/g, // cspell: disable-line
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
label: decodedReplacedText,
labelStyle: style.replace('fill:', 'color:'),
};
const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
Expand Down

0 comments on commit 3ccfea8

Please sign in to comment.