mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
Merge pull request #5844 from mermaid-js/saurabh/image-shape
New Image and Icon shape
This commit is contained in:
commit
67bcd3e0d6
121
cypress/integration/rendering/iconShape.spec.ts
Normal file
121
cypress/integration/rendering/iconShape.spec.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import { imgSnapshotTest } from '../../helpers/util';
|
||||||
|
|
||||||
|
const looks = ['classic', 'handDrawn'] as const;
|
||||||
|
const directions = ['TB', 'BT', 'LR', 'RL'] as const;
|
||||||
|
const forms = [undefined, 'square', 'circle', 'rounded'] as const;
|
||||||
|
const labelPos = [undefined, 't', 'b'] as const;
|
||||||
|
|
||||||
|
looks.forEach((look) => {
|
||||||
|
directions.forEach((direction) => {
|
||||||
|
forms.forEach((form) => {
|
||||||
|
labelPos.forEach((pos) => {
|
||||||
|
describe(`Test iconShape in ${form ? `${form} form,` : ''} ${look} look and dir ${direction} with label position ${pos ? pos : 'not defined'}`, () => {
|
||||||
|
it(`without label`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell'`;
|
||||||
|
if (form) {
|
||||||
|
flowchartCode += `, form: '${form}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with label`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is a label for icon shape'`;
|
||||||
|
if (form) {
|
||||||
|
flowchartCode += `, form: '${form}'`;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with very long label`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is a very very very very very long long long label for icon shape'`;
|
||||||
|
if (form) {
|
||||||
|
flowchartCode += `, form: '${form}'`;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with markdown htmlLabels:true`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold** </br>and <strong>strong</strong> for icon shape'`;
|
||||||
|
if (form) {
|
||||||
|
flowchartCode += `, form: '${form}'`;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with markdown htmlLabels:false`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'This is **bold** </br>and <strong>strong</strong> for icon shape'`;
|
||||||
|
if (form) {
|
||||||
|
flowchartCode += `, form: '${form}'`;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, {
|
||||||
|
look,
|
||||||
|
htmlLabels: false,
|
||||||
|
flowchart: { htmlLabels: false },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with styles`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`;
|
||||||
|
if (form) {
|
||||||
|
flowchartCode += `, form: '${form}'`;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
flowchartCode += ` style nAA fill:#f9f,stroke:#333,stroke-width:4px \n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with classDef`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: 'fa:bell', label: 'new icon shape'`;
|
||||||
|
if (form) {
|
||||||
|
flowchartCode += `, form: '${form}'`;
|
||||||
|
}
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
flowchartCode += ` nAA:::customClazz\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Test iconShape with different h', () => {
|
||||||
|
it('with different h', () => {
|
||||||
|
let flowchartCode = `flowchart TB\n`;
|
||||||
|
const icon = 'fa:bell';
|
||||||
|
const iconHeight = 64;
|
||||||
|
flowchartCode += ` nA --> nAA@{ icon: '${icon}', label: 'icon with different h', h: ${iconHeight} }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode);
|
||||||
|
});
|
||||||
|
});
|
98
cypress/integration/rendering/imageShape.spec.ts
Normal file
98
cypress/integration/rendering/imageShape.spec.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { imgSnapshotTest } from '../../helpers/util';
|
||||||
|
|
||||||
|
const looks = ['classic', 'handDrawn'] as const;
|
||||||
|
const directions = ['TB', 'LR'] as const;
|
||||||
|
const labelPos = [undefined, 't', 'b'] as const;
|
||||||
|
|
||||||
|
looks.forEach((look) => {
|
||||||
|
directions.forEach((direction) => {
|
||||||
|
labelPos.forEach((pos) => {
|
||||||
|
describe(`Test imageShape in ${look} look and dir ${direction} with label position ${pos ? pos : 'not defined'}`, () => {
|
||||||
|
it(`without label`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', w: '100', h: '100' }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with label`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is a label for image shape'`;
|
||||||
|
|
||||||
|
flowchartCode += `, w: '100', h: '200'`;
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with very long label`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is a very very very very very long long long label for image shape'`;
|
||||||
|
|
||||||
|
flowchartCode += `, w: '100', h: '250'`;
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with markdown htmlLabels:true`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is **bold** </br>and <strong>strong</strong> for image shape'`;
|
||||||
|
|
||||||
|
flowchartCode += `, w: '550', h: '200'`;
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look, htmlLabels: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with markdown htmlLabels:false`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'This is **bold** </br>and <strong>strong</strong> for image shape'`;
|
||||||
|
flowchartCode += `, w: '250', h: '200'`;
|
||||||
|
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, {
|
||||||
|
look,
|
||||||
|
htmlLabels: false,
|
||||||
|
flowchart: { htmlLabels: false },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with styles`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'new image shape'`;
|
||||||
|
flowchartCode += `, w: '550', h: '200'`;
|
||||||
|
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
flowchartCode += ` style A fill:#f9f,stroke:#333,stroke-width:4px \n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`with classDef`, () => {
|
||||||
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
|
flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#000000,stroke-dasharray: 5 5\n`;
|
||||||
|
flowchartCode += ` nA --> A@{ img: 'https://cdn.pixabay.com/photo/2020/02/22/18/49/paper-4871356_1280.jpg', label: 'new image shape'`;
|
||||||
|
|
||||||
|
flowchartCode += `, w: '500', h: '550'`;
|
||||||
|
if (pos) {
|
||||||
|
flowchartCode += `, pos: '${pos}'`;
|
||||||
|
}
|
||||||
|
flowchartCode += ` }@\n`;
|
||||||
|
flowchartCode += ` A:::customClazz\n`;
|
||||||
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -60,24 +60,74 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body style="display: flex; gap: 2rem; flex-direction: row">
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
---
|
flowchart TD
|
||||||
config:
|
B2@{ icon: "fa:bell", form: "square", label: "B2 agsyua duadu", pos: "t", h: 80 }@
|
||||||
flowchart:
|
|
||||||
htmlLabels: true
|
|
||||||
---
|
W --> B2
|
||||||
flowchart
|
X --> B2
|
||||||
classDef customClazz fill:#bbf,stroke:#f66,stroke-width:4px,color:#000,stroke-dasharray: 5 5
|
Y --> B2
|
||||||
A1 --> B1@{ shape: taggedWaveEdgedRectangle, label: "test augfuyfavf yafuabffaev ydvaubfuac" }@ --> C1
|
Z --> B2
|
||||||
A2 --> B2@{ shape: taggedRect, label: "test augfuyfavf yafuabffaev ydvaubfuac" }@ --> C2
|
B2 --<strong>sas</strong>--> C
|
||||||
B1:::customClazz
|
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart TB
|
||||||
|
A --test2--> B2@{ icon: "fa:bell", form: "rounded", label: "B2 aiduaid uyawduad uaduabd uyduadb", pos: "b" }@
|
||||||
|
B2 --test--> C
|
||||||
|
D --> B2 --> E
|
||||||
|
style B2 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram43" class="mermaid2">
|
||||||
|
flowchart BT
|
||||||
|
A --test2--> B2@{ icon: "fa:bell", form: "square", label: "B2", pos: "t", h: 40, w: 30 }@
|
||||||
|
B2 --test--> C
|
||||||
|
D --> B2 --> E
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram4" class="mermaid2">
|
||||||
|
flowchart BT
|
||||||
|
A --test2--> B2@{ icon: "fa:bell", label: "B2 awiugdawu uydgayuiwd wuydguy", pos: "b", h: 40, w: 30 }@
|
||||||
|
B2 --test--> C
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram43" class="mermaid2">
|
||||||
|
flowchart BT
|
||||||
|
A --test2--> B2@{ icon: "fa:bell", label: "B2 dawuygd ayuwgd uy", pos: "t", h: 40, w: 30 }@
|
||||||
|
B2 --test--> C
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram6" class="mermaid2">
|
||||||
|
flowchart TB
|
||||||
|
A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "t", w: 200, h: 100 }@ --> C
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram6" class="mermaid2">
|
||||||
|
flowchart TB
|
||||||
|
A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "b", w: 200, h: 100 }@ --> C
|
||||||
|
D --> B2 --> E
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import mermaid from './mermaid.esm.mjs';
|
import mermaid from './mermaid.esm.mjs';
|
||||||
import layouts from './mermaid-layout-elk.esm.mjs';
|
import layouts from './mermaid-layout-elk.esm.mjs';
|
||||||
mermaid.registerLayoutLoaders(layouts);
|
mermaid.registerLayoutLoaders(layouts);
|
||||||
|
mermaid.registerIconPacks([
|
||||||
|
{
|
||||||
|
name: 'logos',
|
||||||
|
loader: () =>
|
||||||
|
fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fa',
|
||||||
|
loader: () =>
|
||||||
|
fetch('https://unpkg.com/@iconify-json/fa6-solid/icons.json').then((res) => res.json()),
|
||||||
|
},
|
||||||
|
]);
|
||||||
mermaid.parseError = function (err, hash) {
|
mermaid.parseError = function (err, hash) {
|
||||||
console.error('Mermaid error: ', err);
|
console.error('Mermaid error: ', err);
|
||||||
};
|
};
|
||||||
@ -87,7 +137,7 @@
|
|||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
// theme: 'base',
|
// theme: 'base',
|
||||||
// handdrawnSeed: 12,
|
// handdrawnSeed: 12,
|
||||||
look: 'handDrawn',
|
look: 'classic',
|
||||||
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
||||||
// 'elk.nodePlacement.strategy': 'SIMPLE',
|
// 'elk.nodePlacement.strategy': 'SIMPLE',
|
||||||
// 'elk.nodePlacement.strategy': 'LAYERED',
|
// 'elk.nodePlacement.strategy': 'LAYERED',
|
||||||
@ -96,7 +146,7 @@
|
|||||||
// layout: 'elk',
|
// layout: 'elk',
|
||||||
// layout: 'fixed',
|
// layout: 'fixed',
|
||||||
// htmlLabels: false,
|
// htmlLabels: false,
|
||||||
flowchart: { titleTopMargin: 10, padding: 5 },
|
flowchart: { titleTopMargin: 10, htmlLabels: true },
|
||||||
// fontFamily: 'Caveat',
|
// fontFamily: 'Caveat',
|
||||||
fontFamily: 'Kalam',
|
fontFamily: 'Kalam',
|
||||||
// fontFamily: 'courier',
|
// fontFamily: 'courier',
|
||||||
|
@ -50,6 +50,23 @@ const contentLoaded = async function () {
|
|||||||
|
|
||||||
mermaid.registerLayoutLoaders(layouts);
|
mermaid.registerLayoutLoaders(layouts);
|
||||||
mermaid.initialize(graphObj.mermaid);
|
mermaid.initialize(graphObj.mermaid);
|
||||||
|
const staticBellIconPack = {
|
||||||
|
prefix: 'fa6-regular',
|
||||||
|
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"/>',
|
||||||
|
width: 448,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
width: 512,
|
||||||
|
height: 512,
|
||||||
|
};
|
||||||
|
mermaid.registerIconPacks([
|
||||||
|
{
|
||||||
|
name: 'fa',
|
||||||
|
loader: () => staticBellIconPack,
|
||||||
|
},
|
||||||
|
]);
|
||||||
await mermaid.run();
|
await mermaid.run();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/rendering-util/types.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L117)
|
[packages/mermaid/src/rendering-util/types.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L122)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/rendering-util/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L116)
|
[packages/mermaid/src/rendering-util/types.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L121)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -40,4 +40,4 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/rendering-util/types.ts:115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L115)
|
[packages/mermaid/src/rendering-util/types.ts:120](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L120)
|
||||||
|
@ -19,4 +19,4 @@ The `parseError` function will not be called.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:49](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L49)
|
[packages/mermaid/src/types.ts:55](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L55)
|
||||||
|
@ -18,7 +18,7 @@ The config passed as YAML frontmatter or directives
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:60](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L60)
|
[packages/mermaid/src/types.ts:66](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L66)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -30,4 +30,4 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:56](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L56)
|
[packages/mermaid/src/types.ts:62](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L62)
|
||||||
|
@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:83](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L83)
|
[packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:73](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L73)
|
[packages/mermaid/src/types.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L79)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -63,4 +63,4 @@ The svg code for the rendered graph.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[packages/mermaid/src/types.ts:69](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L69)
|
[packages/mermaid/src/types.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L75)
|
||||||
|
@ -144,6 +144,30 @@ export const addVertex = function (
|
|||||||
if (doc?.label) {
|
if (doc?.label) {
|
||||||
vertex.text = doc?.label;
|
vertex.text = doc?.label;
|
||||||
}
|
}
|
||||||
|
if (doc?.icon) {
|
||||||
|
vertex.icon = doc?.icon;
|
||||||
|
if (!doc.label) {
|
||||||
|
vertex.text = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doc?.form) {
|
||||||
|
vertex.form = doc?.form;
|
||||||
|
}
|
||||||
|
if (doc?.pos) {
|
||||||
|
vertex.pos = doc?.pos;
|
||||||
|
}
|
||||||
|
if (doc?.img) {
|
||||||
|
vertex.img = doc?.img;
|
||||||
|
if (!doc.label) {
|
||||||
|
vertex.text = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doc.w) {
|
||||||
|
vertex.assetWidth = Number(doc.w);
|
||||||
|
}
|
||||||
|
if (doc.h) {
|
||||||
|
vertex.assetHeight = Number(doc.h);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -792,6 +816,21 @@ export const lex = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getTypeFromVertex = (vertex: FlowVertex) => {
|
const getTypeFromVertex = (vertex: FlowVertex) => {
|
||||||
|
if (vertex?.img) {
|
||||||
|
return 'imageSquare';
|
||||||
|
}
|
||||||
|
if (vertex?.icon) {
|
||||||
|
if (vertex.form === 'circle') {
|
||||||
|
return 'iconCircle';
|
||||||
|
}
|
||||||
|
if (vertex.form === 'square') {
|
||||||
|
return 'iconSquare';
|
||||||
|
}
|
||||||
|
if (vertex.form === 'rounded') {
|
||||||
|
return 'iconRounded';
|
||||||
|
}
|
||||||
|
return 'icon';
|
||||||
|
}
|
||||||
if (vertex.type === 'square') {
|
if (vertex.type === 'square') {
|
||||||
return 'squareRect';
|
return 'squareRect';
|
||||||
}
|
}
|
||||||
@ -857,6 +896,11 @@ const addNodeFromVertex = (
|
|||||||
link: vertex.link,
|
link: vertex.link,
|
||||||
linkTarget: vertex.linkTarget,
|
linkTarget: vertex.linkTarget,
|
||||||
tooltip: getTooltip(vertex.id),
|
tooltip: getTooltip(vertex.id),
|
||||||
|
icon: vertex.icon,
|
||||||
|
pos: vertex.pos,
|
||||||
|
img: vertex.img,
|
||||||
|
assetWidth: vertex.assetWidth,
|
||||||
|
assetHeight: vertex.assetHeight,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -163,6 +163,20 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 0;
|
stroke-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-shape {
|
||||||
|
background-color: ${options.edgeLabelBackground};
|
||||||
|
p {
|
||||||
|
background-color: ${options.edgeLabelBackground};
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
rect {
|
||||||
|
opacity: 0.5;
|
||||||
|
background-color: ${options.edgeLabelBackground};
|
||||||
|
fill: ${options.edgeLabelBackground};
|
||||||
|
}
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default getStyles;
|
export default getStyles;
|
||||||
|
@ -11,6 +11,12 @@ export interface FlowVertex {
|
|||||||
styles: string[];
|
styles: string[];
|
||||||
text?: string;
|
text?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
icon?: string;
|
||||||
|
form?: string;
|
||||||
|
pos?: 't' | 'b';
|
||||||
|
img?: string;
|
||||||
|
assetWidth?: number;
|
||||||
|
assetHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowText {
|
export interface FlowText {
|
||||||
|
@ -51,6 +51,11 @@ import { taggedWaveEdgedRectangle } from './shapes/taggedWaveEdgedRectangle.js';
|
|||||||
import { curlyBraceLeft } from './shapes/curlyBraceLeft.js';
|
import { curlyBraceLeft } from './shapes/curlyBraceLeft.js';
|
||||||
import { curlyBraceRight } from './shapes/curlyBraceRight.js';
|
import { curlyBraceRight } from './shapes/curlyBraceRight.js';
|
||||||
import { curlyBraces } from './shapes/curlyBraces.js';
|
import { curlyBraces } from './shapes/curlyBraces.js';
|
||||||
|
import { iconSquare } from './shapes/iconSquare.js';
|
||||||
|
import { iconCircle } from './shapes/iconCircle.js';
|
||||||
|
import { icon } from './shapes/icon.js';
|
||||||
|
import { imageSquare } from './shapes/imageSquare.js';
|
||||||
|
import { iconRounded } from './shapes/iconRounded.js';
|
||||||
|
|
||||||
//Use these names as the left side to render shapes.
|
//Use these names as the left side to render shapes.
|
||||||
export const shapes = {
|
export const shapes = {
|
||||||
@ -215,6 +220,11 @@ export const shapes = {
|
|||||||
labelRect,
|
labelRect,
|
||||||
'brace-r': curlyBraceRight,
|
'brace-r': curlyBraceRight,
|
||||||
braces: curlyBraces,
|
braces: curlyBraces,
|
||||||
|
iconSquare,
|
||||||
|
iconCircle,
|
||||||
|
icon,
|
||||||
|
iconRounded,
|
||||||
|
imageSquare,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodeElems = new Map();
|
const nodeElems = new Map();
|
||||||
@ -223,6 +233,8 @@ export const insertNode = async (elem, node, dir) => {
|
|||||||
let newEl;
|
let newEl;
|
||||||
let el;
|
let el;
|
||||||
|
|
||||||
|
// console.log("node is ", node.icon, node.shape)
|
||||||
|
|
||||||
//special check for rect shape (with or without rounded corners)
|
//special check for rect shape (with or without rounded corners)
|
||||||
if (node.shape === 'rect') {
|
if (node.shape === 'rect') {
|
||||||
if (node.rx && node.ry) {
|
if (node.rx && node.ry) {
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { labelHelper, updateNodeBounds } from './util.js';
|
||||||
|
import type { Node } from '../../types.d.ts';
|
||||||
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
import rough from 'roughjs';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import { getIconSVG } from '../../icons.js';
|
||||||
|
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
|
export const icon = async (parent: SVG, node: Node) => {
|
||||||
|
const { labelStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
const assetHeight = node.assetHeight ?? 48;
|
||||||
|
const assetWidth = node.assetWidth ?? 48;
|
||||||
|
const iconSize = Math.max(assetHeight, assetWidth);
|
||||||
|
const defaultWidth = getConfig()?.flowchart?.wrappingWidth;
|
||||||
|
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
||||||
|
const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'icon-shape default');
|
||||||
|
|
||||||
|
const topLabel = node.pos === 't';
|
||||||
|
|
||||||
|
const height = iconSize;
|
||||||
|
const width = Math.max(iconSize, bbox.width);
|
||||||
|
|
||||||
|
const x = -width / 2;
|
||||||
|
const y = -height / 2;
|
||||||
|
|
||||||
|
// @ts-ignore - rough is not typed
|
||||||
|
const rc = rough.svg(shapeSvg);
|
||||||
|
const options = userNodeOverrides(node, { stroke: 'none', fill: 'none' });
|
||||||
|
|
||||||
|
if (node.look !== 'handDrawn') {
|
||||||
|
options.roughness = 0;
|
||||||
|
options.fillStyle = 'solid';
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconNode = rc.rectangle(x, y, width, height, options);
|
||||||
|
|
||||||
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
|
||||||
|
if (node.icon) {
|
||||||
|
const iconElem = shapeSvg.append('g');
|
||||||
|
iconElem.html(
|
||||||
|
`<g>${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
|
);
|
||||||
|
const iconBBox = iconElem.node().getBBox();
|
||||||
|
const iconWidth = iconBBox.width;
|
||||||
|
const iconHeight = iconBBox.height;
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight + bbox.height / 2 : -height / 2 - bbox.height / 2})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 - 5 - bbox.height / 2 : height / 2 - bbox.height / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
|
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
||||||
|
|
||||||
|
updateNodeBounds(node, shapeSvg);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('iconSquare intersect', node, point);
|
||||||
|
if (!node.label) {
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
}
|
||||||
|
const dx = node.x ?? 0;
|
||||||
|
const dy = node.y ?? 0;
|
||||||
|
const nodeWidth = node.width ?? 0;
|
||||||
|
const nodeHeight = node.height ?? 0;
|
||||||
|
|
||||||
|
if (topLabel) {
|
||||||
|
const points = [
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
||||||
|
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
||||||
|
y: dy + nodeHeight / 2 - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
||||||
|
y: dy + nodeHeight / 2 - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
||||||
|
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
||||||
|
];
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
|
} else {
|
||||||
|
const points = [
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy - nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width, y: dy - nodeHeight / 2 },
|
||||||
|
{
|
||||||
|
x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width,
|
||||||
|
y: dy - nodeHeight / 2 + height,
|
||||||
|
},
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + height },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 - bbox.height },
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy + nodeHeight / 2 - bbox.height },
|
||||||
|
];
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
@ -0,0 +1,87 @@
|
|||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { labelHelper, updateNodeBounds } from './util.js';
|
||||||
|
import type { Node } from '../../types.d.ts';
|
||||||
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
|
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
import rough from 'roughjs';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import { getIconSVG } from '../../icons.js';
|
||||||
|
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
|
export const iconCircle = async (parent: SVG, node: Node, dir: string) => {
|
||||||
|
const translateHorizontal = dir === 'TB' || dir === 'BT' || dir === 'TD' || dir === 'DT';
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
const assetHeight = node.assetHeight ?? 48;
|
||||||
|
const assetWidth = node.assetWidth ?? 48;
|
||||||
|
const iconSize = Math.max(assetHeight, assetWidth);
|
||||||
|
const defaultWidth = getConfig()?.flowchart?.wrappingWidth;
|
||||||
|
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
||||||
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
'icon-shape default'
|
||||||
|
);
|
||||||
|
const { cssStyles } = node;
|
||||||
|
|
||||||
|
const topLabel = node.pos === 't';
|
||||||
|
|
||||||
|
const diameter = iconSize + halfPadding * 2;
|
||||||
|
const { themeVariables } = getConfig();
|
||||||
|
const { mainBkg } = themeVariables;
|
||||||
|
const { stylesMap } = compileStyles(node);
|
||||||
|
|
||||||
|
// @ts-ignore - rough is not typed
|
||||||
|
const rc = rough.svg(shapeSvg);
|
||||||
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
||||||
|
|
||||||
|
if (node.look !== 'handDrawn') {
|
||||||
|
options.roughness = 0;
|
||||||
|
options.fillStyle = 'solid';
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconNode = rc.circle(0, 0, diameter, options);
|
||||||
|
|
||||||
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
|
||||||
|
if (node.icon) {
|
||||||
|
const iconElem = shapeSvg.append('g');
|
||||||
|
iconElem.html(
|
||||||
|
`<g>${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
|
);
|
||||||
|
const iconBBox = iconElem.node().getBBox();
|
||||||
|
const iconWidth = iconBBox.width;
|
||||||
|
const iconHeight = iconBBox.height;
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-iconWidth / 2},${topLabel ? diameter / 2 - iconHeight - halfPadding + (translateHorizontal ? bbox.height / 2 : 0) : -diameter / 2 + halfPadding - (translateHorizontal ? bbox.height / 2 : 0)})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-diameter / 2 + diameter / 2 - bbox.width / 2},${topLabel ? -diameter / 2 - 2.5 - (translateHorizontal ? bbox.height / 2 : bbox.height) : diameter / 2 + 5 - (translateHorizontal ? bbox.height / 2 : 0)})`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (translateHorizontal) {
|
||||||
|
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', cssStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', nodeStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNodeBounds(node, shapeSvg);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('iconSquare intersect', node, point);
|
||||||
|
const pos = intersect.rect(node, point);
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
@ -0,0 +1,137 @@
|
|||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { labelHelper, updateNodeBounds } from './util.js';
|
||||||
|
import type { Node } from '../../types.js';
|
||||||
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
|
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
import rough from 'roughjs';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import { getIconSVG } from '../../icons.js';
|
||||||
|
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||||
|
import { createRoundedRectPathD } from './roundedRectPath.js';
|
||||||
|
|
||||||
|
export const iconRounded = async (parent: SVG, node: Node) => {
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
const assetHeight = node.assetHeight ?? 48;
|
||||||
|
const assetWidth = node.assetWidth ?? 48;
|
||||||
|
const iconSize = Math.max(assetHeight, assetWidth);
|
||||||
|
const defaultWidth = getConfig()?.flowchart?.wrappingWidth;
|
||||||
|
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
||||||
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
'icon-shape default'
|
||||||
|
);
|
||||||
|
const { cssStyles } = node;
|
||||||
|
|
||||||
|
const topLabel = node.pos === 't';
|
||||||
|
|
||||||
|
const height = iconSize + halfPadding * 2;
|
||||||
|
const width = iconSize + halfPadding * 2;
|
||||||
|
const { themeVariables } = getConfig();
|
||||||
|
const { mainBkg } = themeVariables;
|
||||||
|
const { stylesMap } = compileStyles(node);
|
||||||
|
|
||||||
|
const x = -width / 2;
|
||||||
|
const y = -height / 2;
|
||||||
|
|
||||||
|
// @ts-ignore - rough is not typed
|
||||||
|
const rc = rough.svg(shapeSvg);
|
||||||
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
||||||
|
|
||||||
|
if (node.look !== 'handDrawn') {
|
||||||
|
options.roughness = 0;
|
||||||
|
options.fillStyle = 'solid';
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 5), options);
|
||||||
|
|
||||||
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
|
||||||
|
if (node.icon) {
|
||||||
|
const iconElem = shapeSvg.append('g');
|
||||||
|
iconElem.html(
|
||||||
|
`<g>${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
|
);
|
||||||
|
const iconBBox = iconElem.node().getBBox();
|
||||||
|
const iconWidth = iconBBox.width;
|
||||||
|
const iconHeight = iconBBox.height;
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight + bbox.height / 2 : -height / 2 - bbox.height / 2})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 - 5 - bbox.height / 2 : height / 2 - bbox.height / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
|
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
||||||
|
|
||||||
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', cssStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', nodeStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNodeBounds(node, shapeSvg);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('iconSquare intersect', node, point);
|
||||||
|
if (!node.label) {
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
}
|
||||||
|
const dx = node.x ?? 0;
|
||||||
|
const dy = node.y ?? 0;
|
||||||
|
const nodeWidth = node.width ?? 0;
|
||||||
|
const nodeHeight = node.height ?? 0;
|
||||||
|
|
||||||
|
if (topLabel) {
|
||||||
|
const points = [
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
||||||
|
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
||||||
|
y: dy + nodeHeight / 2 - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
||||||
|
y: dy + nodeHeight / 2 - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
||||||
|
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
||||||
|
];
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
|
} else {
|
||||||
|
const points = [
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy - nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width, y: dy - nodeHeight / 2 },
|
||||||
|
{
|
||||||
|
x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width,
|
||||||
|
y: dy - nodeHeight / 2 + height,
|
||||||
|
},
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + height },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 - bbox.height },
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy + nodeHeight / 2 - bbox.height },
|
||||||
|
];
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
@ -0,0 +1,136 @@
|
|||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { labelHelper, updateNodeBounds } from './util.js';
|
||||||
|
import type { Node } from '../../types.d.ts';
|
||||||
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
|
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
import rough from 'roughjs';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import { getIconSVG } from '../../icons.js';
|
||||||
|
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
|
export const iconSquare = async (parent: SVG, node: Node) => {
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
const assetHeight = node.assetHeight ?? 48;
|
||||||
|
const assetWidth = node.assetWidth ?? 48;
|
||||||
|
const iconSize = Math.max(assetHeight, assetWidth);
|
||||||
|
const defaultWidth = getConfig()?.flowchart?.wrappingWidth;
|
||||||
|
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
||||||
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
'icon-shape default'
|
||||||
|
);
|
||||||
|
const { cssStyles } = node;
|
||||||
|
|
||||||
|
const topLabel = node.pos === 't';
|
||||||
|
|
||||||
|
const height = iconSize + halfPadding * 2;
|
||||||
|
const width = iconSize + halfPadding * 2;
|
||||||
|
const { themeVariables } = getConfig();
|
||||||
|
const { mainBkg } = themeVariables;
|
||||||
|
const { stylesMap } = compileStyles(node);
|
||||||
|
|
||||||
|
const x = -width / 2;
|
||||||
|
const y = -height / 2;
|
||||||
|
|
||||||
|
// @ts-ignore - rough is not typed
|
||||||
|
const rc = rough.svg(shapeSvg);
|
||||||
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
||||||
|
|
||||||
|
if (node.look !== 'handDrawn') {
|
||||||
|
options.roughness = 0;
|
||||||
|
options.fillStyle = 'solid';
|
||||||
|
}
|
||||||
|
|
||||||
|
const iconNode = rc.rectangle(x, y, width, height, options);
|
||||||
|
|
||||||
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
|
||||||
|
if (node.icon) {
|
||||||
|
const iconElem = shapeSvg.append('g');
|
||||||
|
iconElem.html(
|
||||||
|
`<g>${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
|
);
|
||||||
|
const iconBBox = iconElem.node().getBBox();
|
||||||
|
const iconWidth = iconBBox.width;
|
||||||
|
const iconHeight = iconBBox.height;
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight - halfPadding + bbox.height / 2 : -height / 2 + halfPadding - bbox.height / 2})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 - 5 - bbox.height / 2 : height / 2 - bbox.height / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
|
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
||||||
|
|
||||||
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', cssStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', nodeStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNodeBounds(node, shapeSvg);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('iconSquare intersect', node, point);
|
||||||
|
if (!node.label) {
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
}
|
||||||
|
const dx = node.x ?? 0;
|
||||||
|
const dy = node.y ?? 0;
|
||||||
|
const nodeWidth = node.width ?? 0;
|
||||||
|
const nodeHeight = node.height ?? 0;
|
||||||
|
|
||||||
|
if (topLabel) {
|
||||||
|
const points = [
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
||||||
|
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
||||||
|
y: dy + nodeHeight / 2 - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
||||||
|
y: dy + nodeHeight / 2 - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
||||||
|
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
||||||
|
},
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
||||||
|
];
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
|
} else {
|
||||||
|
const points = [
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy - nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width, y: dy - nodeHeight / 2 },
|
||||||
|
{
|
||||||
|
x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width,
|
||||||
|
y: dy - nodeHeight / 2 + height,
|
||||||
|
},
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + height },
|
||||||
|
{ x: dx + nodeWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 - bbox.height },
|
||||||
|
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy + nodeHeight / 2 - bbox.height },
|
||||||
|
];
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
@ -0,0 +1,135 @@
|
|||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||||
|
import type { Node } from '../../types.js';
|
||||||
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
import rough from 'roughjs';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import { createPathFromPoints } from './util.js';
|
||||||
|
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
|
export const imageSquare = async (parent: SVG, node: Node) => {
|
||||||
|
//image dimensions
|
||||||
|
const img = new Image();
|
||||||
|
img.src = node?.img ?? '';
|
||||||
|
await img.decode();
|
||||||
|
|
||||||
|
const imageNaturalWidth = Number(img.naturalWidth.toString().replace('px', ''));
|
||||||
|
const imageNaturalHeight = Number(img.naturalHeight.toString().replace('px', ''));
|
||||||
|
|
||||||
|
const defaultWidth = getConfig().flowchart?.wrappingWidth;
|
||||||
|
const imageWidth = Math.max(
|
||||||
|
node.label ? (defaultWidth ?? 0) : 0,
|
||||||
|
node?.assetWidth ?? imageNaturalWidth
|
||||||
|
);
|
||||||
|
const imageHeight = node?.assetHeight ?? imageNaturalHeight;
|
||||||
|
// const htmlLabels = getConfig().flowchart?.htmlLabels
|
||||||
|
|
||||||
|
const imagePoints = [
|
||||||
|
{ x: -imageWidth / 2, y: -imageHeight },
|
||||||
|
{ x: imageWidth / 2, y: -imageHeight },
|
||||||
|
{ x: imageWidth / 2, y: 0 },
|
||||||
|
{ x: -imageWidth / 2, y: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
//label dimensions
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
|
||||||
|
// const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
// parent,
|
||||||
|
// node,
|
||||||
|
// 'icon-shape default'
|
||||||
|
// );
|
||||||
|
|
||||||
|
const { cssStyles } = node;
|
||||||
|
// const defaultHeight = bbox.height;
|
||||||
|
// node.height = Math.max(node.height ?? 0, node.label ? (defaultHeight ?? 0) : 0, imageHeight);
|
||||||
|
const labelWidth = Math.max(node.width ?? 0, node.label ? (defaultWidth ?? 0) : 0, imageWidth);
|
||||||
|
node.width = node.label ? labelWidth : 0;
|
||||||
|
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
|
|
||||||
|
// const width = Math.max(bbox.width + (node.padding ?? 0), node?.width ?? 0);
|
||||||
|
const height = Math.max(bbox.height + (node.padding ?? 0), node?.height ?? 0);
|
||||||
|
const labelHeight = node.label ? height : 0;
|
||||||
|
|
||||||
|
const imagePosition = node?.pos ?? 'b';
|
||||||
|
|
||||||
|
const labelPoints = [
|
||||||
|
{ x: -labelWidth / 2, y: 0 },
|
||||||
|
{ x: labelWidth / 2, y: 0 },
|
||||||
|
{ x: labelWidth / 2, y: labelHeight },
|
||||||
|
{ x: -labelWidth / 2, y: labelHeight },
|
||||||
|
];
|
||||||
|
|
||||||
|
// @ts-ignore - rough is not typed
|
||||||
|
const rc = rough.svg(shapeSvg);
|
||||||
|
const options = userNodeOverrides(node, {});
|
||||||
|
// const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
||||||
|
|
||||||
|
if (node.look !== 'handDrawn') {
|
||||||
|
options.roughness = 0;
|
||||||
|
options.fillStyle = 'solid';
|
||||||
|
}
|
||||||
|
|
||||||
|
const imagePath = createPathFromPoints(imagePoints);
|
||||||
|
const imagePathNode = rc.path(imagePath, options);
|
||||||
|
|
||||||
|
const linePath = createPathFromPoints(labelPoints);
|
||||||
|
const lineNode = rc.path(linePath, { ...options });
|
||||||
|
|
||||||
|
const imageShape = shapeSvg.insert(() => lineNode, ':first-child').attr('opacity', 0);
|
||||||
|
imageShape.insert(() => imagePathNode, ':first-child');
|
||||||
|
|
||||||
|
imageShape.attr('transform', `translate(${0},${(imageHeight - labelHeight) / 2})`);
|
||||||
|
|
||||||
|
// Image operations
|
||||||
|
if (node.img) {
|
||||||
|
const image = shapeSvg.append('image');
|
||||||
|
|
||||||
|
// Set the image attributes
|
||||||
|
image.attr('href', node.img);
|
||||||
|
image.attr('width', imageWidth);
|
||||||
|
image.attr('height', imageHeight);
|
||||||
|
image.attr('preserveAspectRatio', 'none');
|
||||||
|
|
||||||
|
// const yPos =
|
||||||
|
// imagePosition === 'b'
|
||||||
|
// ? -(imageHeight / 2)
|
||||||
|
// : labelHeight / 2 - height + (bbox.y + (bbox.top ?? 0)) * 2;
|
||||||
|
// image.attr('transform', `translate(${-imageWidth / 2}, ${-imageHeight/2 -labelHeight/2})`);
|
||||||
|
const yPos =
|
||||||
|
imagePosition === 'b' ? -imageHeight / 2 - labelHeight / 2 : (-imageHeight + labelHeight) / 2;
|
||||||
|
image.attr('transform', `translate(${-imageWidth / 2}, ${yPos})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
|
imageShape.selectAll('path').attr('style', cssStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeStyles && node.look !== 'handDrawn') {
|
||||||
|
imageShape.selectAll('path').attr('style', nodeStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
const yPos =
|
||||||
|
imagePosition === 'b'
|
||||||
|
? (imageHeight + labelHeight) / 2 - bbox.height - (bbox.y - (bbox.top ?? 0))
|
||||||
|
: -(imageHeight + labelHeight) / 2 + (node?.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0));
|
||||||
|
|
||||||
|
label.attr('transform', `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))},${yPos})`);
|
||||||
|
|
||||||
|
updateNodeBounds(node, imageShape);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('imageSquare intersect', node, point);
|
||||||
|
|
||||||
|
// Combine image and label points into a single shape
|
||||||
|
const combinedPoints = [...imagePoints, ...labelPoints];
|
||||||
|
|
||||||
|
// Use the polygon intersection with the translated points
|
||||||
|
const pos = intersect.polygon(node, combinedPoints, point);
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
@ -36,6 +36,7 @@ export const labelHelper = async (parent, node, _classes) => {
|
|||||||
width: node.width || getConfig().flowchart.wrappingWidth,
|
width: node.width || getConfig().flowchart.wrappingWidth,
|
||||||
cssClasses: 'markdown-node-label',
|
cssClasses: 'markdown-node-label',
|
||||||
style: node.labelStyle,
|
style: node.labelStyle,
|
||||||
|
addSvgBackground: !!node.icon || !!node.img,
|
||||||
});
|
});
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
|
@ -64,6 +64,11 @@ export interface Node {
|
|||||||
y?: number;
|
y?: number;
|
||||||
|
|
||||||
look?: string;
|
look?: string;
|
||||||
|
icon?: string;
|
||||||
|
pos?: 't' | 'b';
|
||||||
|
img?: string;
|
||||||
|
assetWidth?: number;
|
||||||
|
assetHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common properties for any edge in the system
|
// Common properties for any edge in the system
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
export interface NodeMetaData {
|
export interface NodeMetaData {
|
||||||
shape?: string;
|
shape?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
|
icon?: string;
|
||||||
|
form?: string;
|
||||||
|
pos?: 't' | 'b';
|
||||||
|
img?: string;
|
||||||
|
w?: string;
|
||||||
|
h?: string;
|
||||||
}
|
}
|
||||||
import type { MermaidConfig } from './config.type.js';
|
import type { MermaidConfig } from './config.type.js';
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user