feat(arch): added 4 default icons, added config field for icons

This commit is contained in:
NicolasNewman 2024-03-27 09:30:44 -05:00
parent a493e2fbb3
commit 0ab7a3d8ec
9 changed files with 156 additions and 21 deletions

View File

@ -135,6 +135,12 @@ export interface MermaidConfig {
*
*/
legacyMathML?: boolean;
/**
* This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code.
* For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram.
*
*/
iconLibraries?: Array<import('./rendering-util/svgRegister.js').IconLibrary>;
/**
* This option controls if the generated ids of nodes in the SVG are
* generated randomly or based on a seed.

View File

@ -31,6 +31,8 @@ import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility.
import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js';
import { preprocessDiagram } from './preprocess.js';
import { decodeEntities } from './utils.js';
import { registerIcons } from './rendering-util/svgRegister.js';
import defaultIconLibrary from './rendering-util/svg/index.js';
const MAX_TEXTLENGTH = 50_000;
const MAX_TEXTLENGTH_EXCEEDED_MSG =
@ -502,6 +504,13 @@ function initialize(options: MermaidConfig = {}) {
// Set default options
configApi.saveConfigFromInitialize(options);
registerIcons(defaultIconLibrary)
if (options?.iconLibraries) {
options.iconLibraries.forEach((library) => {
registerIcons(library);
});
}
if (options?.theme && options.theme in theme) {
// Todo merge with user options
options.themeVariables = theme[options.theme as keyof typeof theme].getThemeVariables(

View File

@ -0,0 +1,16 @@
/**
* @author Nicolas Newman
* @see https://github.com/NicolasNewman/IconLibrary
*/
import { createIcon } from "../svgRegister.js";
export default createIcon(`<g>
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
<path id="b" data-name="4" d="m20,57.86c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<path id="c" data-name="3" d="m20,45.95c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<path id="d" data-name="2" d="m20,34.05c0,3.94,8.95,7.14,20,7.14s20-3.2,20-7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<ellipse id="e" data-name="1" cx="40" cy="22.14" rx="20" ry="7.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="20" y1="57.86" x2="20" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="60" y1="57.86" x2="60" y2="22.14" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
</g>`, 80)

View File

@ -0,0 +1,18 @@
/**
* @author Nicolas Newman
* @see https://github.com/NicolasNewman/IconLibrary
*/
import { createIcon } from "../svgRegister.js";
export default createIcon(`<g>
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
<rect x="20" y="15" width="40" height="50" rx="1" ry="1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<ellipse cx="24" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<ellipse cx="56" cy="19.17" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<ellipse cx="24" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<ellipse cx="56" cy="60.83" rx=".8" ry=".83" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<ellipse cx="40" cy="33.75" rx="14" ry="14.58" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<ellipse cx="40" cy="33.75" rx="4" ry="4.17" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<path d="m37.51,42.52l-4.83,13.22c-.26.71-1.1,1.02-1.76.64l-4.18-2.42c-.66-.38-.81-1.26-.33-1.84l9.01-10.8c.88-1.05,2.56-.08,2.09,1.2Z" style="fill: #fff; stroke-width: 0px;"/>
</g>`, 80)

View File

@ -0,0 +1,14 @@
import { IconLibrary } from "../svgRegister.js";
import database from "./database.js";
import server from "./server.js";
import disk from "./disk.js";
import internet from "./internet.js";
const defaultIconLibrary: IconLibrary = {
database: database,
server: server,
disk: disk,
internet: internet,
}
export default defaultIconLibrary

View File

@ -0,0 +1,17 @@
/**
* @author Nicolas Newman
* @see https://github.com/NicolasNewman/IconLibrary
*/
import { createIcon } from "../svgRegister.js";
export default createIcon(`<g>
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
<circle cx="40" cy="40" r="22.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="40" y1="17.5" x2="40" y2="62.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="17.5" y1="40" x2="62.5" y2="40" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<path d="m39.99,17.51c-15.28,11.1-15.28,33.88,0,44.98" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<path d="m40.01,17.51c15.28,11.1,15.28,33.88,0,44.98" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="19.75" y1="30.1" x2="60.25" y2="30.1" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="19.75" y1="49.9" x2="60.25" y2="49.9" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
</g>`, 80)

View File

@ -0,0 +1,40 @@
/**
* @author Nicolas Newman
* @see https://github.com/NicolasNewman/IconLibrary
*/
import { createIcon } from "../svgRegister.js";
export default createIcon(`<g>
<rect width="80" height="80" style="fill: #087ebf; stroke-width: 0px;"/>
<rect x="17.5" y="17.5" width="45" height="45" rx="2" ry="2" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="17.5" y1="32.5" x2="62.5" y2="32.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<line x1="17.5" y1="47.5" x2="62.5" y2="47.5" style="fill: none; stroke: #fff; stroke-miterlimit: 10; stroke-width: 2px;"/>
<g>
<path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/>
<path d="m56.25,25c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/>
</g>
<g>
<path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/>
<path d="m56.25,40c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/>
</g>
<g>
<path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: #fff; stroke-width: 0px;"/>
<path d="m56.25,55c0,.27-.45.5-1,.5h-10.5c-.55,0-1-.23-1-.5s.45-.5,1-.5h10.5c.55,0,1,.23,1,.5Z" style="fill: none; stroke: #fff; stroke-miterlimit: 10;"/>
</g>
<g>
<circle cx="32.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
<circle cx="27.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
<circle cx="22.5" cy="25" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
</g>
<g>
<circle cx="32.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
<circle cx="27.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
<circle cx="22.5" cy="40" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
</g>
<g>
<circle cx="32.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
<circle cx="27.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
<circle cx="22.5" cy="55" r=".75" style="fill: #fff; stroke: #fff; stroke-miterlimit: 10;"/>
</g>
</g>`, 80)

View File

@ -1,33 +1,42 @@
import { Selection } from "d3-selection";
import { Selection } from 'd3-selection';
type IconResolver = (parent: Selection<SVGGElement, unknown, Element | null, unknown>) => Selection<SVGGElement, unknown, Element | null, unknown>
type IconLibrary = Record<string, IconResolver>
type IconResolver = (
parent: Selection<SVGGElement, unknown, Element | null, unknown>, width?: number
) => Selection<SVGGElement, unknown, Element | null, unknown>;
type IconLibrary = Record<string, IconResolver>;
const icons: IconLibrary = {}
const icons: IconLibrary = {};
const isIconNameInUse = (name: string): boolean => {
return icons[name] !== undefined;
}
return icons[name] !== undefined;
};
const registerIcon = (name: string, resolver: IconResolver) => {
if(!isIconNameInUse(name)) {
icons[name] = resolver;
}
}
if (!isIconNameInUse(name)) {
icons[name] = resolver;
}
};
const registerIcons = (library: IconLibrary) => {
Object.entries(library).forEach(([name, resolver]) => {
if (!isIconNameInUse(name)) {
icons[name] = resolver;
}
})
}
Object.entries(library).forEach(([name, resolver]) => {
if (!isIconNameInUse(name)) {
icons[name] = resolver;
}
});
};
const getIcon = (name: string): IconResolver | null => {
if (isIconNameInUse(name)) {
return icons[name];
}
return null; // TODO: return default
if (isIconNameInUse(name)) {
return icons[name];
}
return null; // TODO: return default
};
const createIcon = (icon: string, originalSize: number): IconResolver => {
return (parent: Selection<SVGGElement, unknown, Element | null, unknown>, size: number = originalSize) => {
parent.html(`<g style="transform: scale(${size / originalSize})">${icon}</g>`)
return parent
}
}
export { registerIcon, registerIcons, getIcon, isIconNameInUse, IconLibrary }
export { registerIcon, registerIcons, getIcon, isIconNameInUse, createIcon, IconLibrary };

View File

@ -177,6 +177,12 @@ properties:
fall back to legacy rendering for KaTeX.
type: boolean
default: false
iconLibraries:
description: |
This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code.
For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram.
tsType: Array<import('./rendering-util/svgRegister.js').IconLibrary>
# tsType: Record<string, IconResolver>
deterministicIds:
description: |
This option controls if the generated ids of nodes in the SVG are