mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-25 21:56:25 +01:00
Compare commits
99 Commits
6097-elk-g
...
sidv/testC
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98b7ac8b19 | ||
|
|
5747009c8f | ||
|
|
a179d4a3e2 | ||
|
|
dc513ec211 | ||
|
|
5f58d4f7b0 | ||
|
|
3536ceb5d3 | ||
|
|
7ed33a91c0 | ||
|
|
f8bf03d365 | ||
|
|
4202ee521a | ||
|
|
0e25fd42f9 | ||
|
|
feee5d1c8c | ||
|
|
ab6d92c544 | ||
|
|
d27d4abab7 | ||
|
|
9681a27b31 | ||
|
|
ba534985ec | ||
|
|
edc1ae1e0c | ||
|
|
c00f3fb090 | ||
|
|
74a56fc58a | ||
|
|
08ffbb61e9 | ||
|
|
232e60c8cb | ||
|
|
cd9ca76e39 | ||
|
|
c13d988392 | ||
|
|
15ffe2021a | ||
|
|
4fbc19e0d7 | ||
|
|
2b8998fdd1 | ||
|
|
202ef31071 | ||
|
|
b09f2e836a | ||
|
|
6a814a0d91 | ||
|
|
b07bb9b3ff | ||
|
|
5120ed09ab | ||
|
|
61f3fc5ede | ||
|
|
90bbf90a83 | ||
|
|
92efc24283 | ||
|
|
1c45df4567 | ||
|
|
1575a93136 | ||
|
|
bde653b1c2 | ||
|
|
e9e663ffa3 | ||
|
|
963efa64c7 | ||
|
|
e2e5101005 | ||
|
|
6ea13cded3 | ||
|
|
c063b92cc9 | ||
|
|
304f133227 | ||
|
|
54a0dd0af6 | ||
|
|
ab5b7694c6 | ||
|
|
eb76dfb1ca | ||
|
|
28d7ec092c | ||
|
|
84b03f3a08 | ||
|
|
f9ac9867c1 | ||
|
|
ee6fb83265 | ||
|
|
46c16c963b | ||
|
|
32a68d489e | ||
|
|
7bbebff583 | ||
|
|
c432aec2f6 | ||
|
|
64237fbaa7 | ||
|
|
7ba415dad1 | ||
|
|
7f47678862 | ||
|
|
127bac1147 | ||
|
|
9a90d795ca | ||
|
|
f7fe8f2f59 | ||
|
|
1d9c2aab8d | ||
|
|
8fba9c1236 | ||
|
|
04800ff677 | ||
|
|
0f08c3bc9c | ||
|
|
7809b5a93f | ||
|
|
ef9bb53e67 | ||
|
|
3e32332814 | ||
|
|
e1aab25144 | ||
|
|
c8697301ee | ||
|
|
fcb1de915b | ||
|
|
e345294658 | ||
|
|
9936099e25 | ||
|
|
6cc31b7453 | ||
|
|
29ca1504dd | ||
|
|
16d2251e43 | ||
|
|
bc2cc61240 | ||
|
|
6836e9c7a8 | ||
|
|
8dc016e7f9 | ||
|
|
3cad7984a3 | ||
|
|
f5bae98098 | ||
|
|
0695893e30 | ||
|
|
9ef6090c8c | ||
|
|
ec0d9c389a | ||
|
|
323b07a2e4 | ||
|
|
c153d0455f | ||
|
|
9b00f1f2fb | ||
|
|
a055ff7db3 | ||
|
|
d032723a47 | ||
|
|
daf83f596d | ||
|
|
df636c6d0a | ||
|
|
64554a6c60 | ||
|
|
becadf0a7d | ||
|
|
54d485f173 | ||
|
|
b4f5b8ddaf | ||
|
|
cb5c1ae367 | ||
|
|
b29081d4e8 | ||
|
|
654097c438 | ||
|
|
1e672868c4 | ||
|
|
8cb1c68166 | ||
|
|
d752240efc |
5
.changeset/bright-ads-exist.md
Normal file
5
.changeset/bright-ads-exist.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
Fixes for consistent edge id creation & handling edge cases for animate edge feature
|
||||
5
.changeset/chatty-elephants-warn.md
Normal file
5
.changeset/chatty-elephants-warn.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
Fix for issue #6195 - allowing @ signs inside node labels
|
||||
5
.changeset/chilly-years-cheat.md
Normal file
5
.changeset/chilly-years-cheat.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each class diagram
|
||||
5
.changeset/dull-tips-cough.md
Normal file
5
.changeset/dull-tips-cough.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: revert state db to resolve getData returning empty nodes and edges
|
||||
8
.changeset/great-ghosts-rule.md
Normal file
8
.changeset/great-ghosts-rule.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
Flowchart new syntax for node metadata bugs
|
||||
|
||||
- Incorrect label mapping for nodes when using `&`
|
||||
- Syntax error when `}` with trailing spaces before new line
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@mermaid-js/layout-elk': patch
|
||||
---
|
||||
|
||||
fix: Updated offset calculations for diamond shape when handling intersections
|
||||
5
.changeset/many-brooms-promise.md
Normal file
5
.changeset/many-brooms-promise.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
Adding support for animation of flowchart edges
|
||||
5
.changeset/new-kiwis-listen.md
Normal file
5
.changeset/new-kiwis-listen.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each flowchart
|
||||
5
.changeset/stupid-dots-do.md
Normal file
5
.changeset/stupid-dots-do.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Gantt, Sankey and User Journey diagram are now able to pick font-family from mermaid config.
|
||||
5
.changeset/witty-crews-smell.md
Normal file
5
.changeset/witty-crews-smell.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
`mermaidAPI.getDiagramFromText()` now returns a new different db for each state diagram
|
||||
2
.github/workflows/autofix.yml
vendored
2
.github/workflows/autofix.yml
vendored
@@ -42,4 +42,4 @@ jobs:
|
||||
working-directory: ./packages/mermaid
|
||||
run: pnpm run docs:build
|
||||
|
||||
- uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c # main
|
||||
- uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef # main
|
||||
|
||||
20
.github/workflows/e2e.yml
vendored
20
.github/workflows/e2e.yml
vendored
@@ -7,6 +7,9 @@ on:
|
||||
- master
|
||||
- release/**
|
||||
pull_request:
|
||||
issue_comment:
|
||||
types:
|
||||
- created
|
||||
merge_group:
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -28,8 +31,12 @@ env:
|
||||
) ||
|
||||
github.event.before
|
||||
}}
|
||||
# Check if this is a new comment with '/visual-test'
|
||||
RUN_VISUAL_TEST: >-
|
||||
${{ github.event_name == 'issue_comment' && github.event.action == 'created' && contains(github.event.comment.body, '/visual-test') && github.event.issue.pull_request != null }}
|
||||
jobs:
|
||||
cache:
|
||||
if: ${{ github.event_name != 'issue_comment' || contains(github.event.comment.body, '/visual-test') }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
|
||||
@@ -127,16 +134,17 @@ jobs:
|
||||
# e.g. if this action was run from a fork
|
||||
record: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
VITEST_COVERAGE: true
|
||||
# Only set Argos environment variables if the visual test comment trigger is present
|
||||
ARGOS_TOKEN: ${{ env.RUN_VISUAL_TEST == 'true' && secrets.ARGOS_TOKEN || '' }}
|
||||
ARGOS_PARALLEL: ${{ env.RUN_VISUAL_TEST == 'true' }}
|
||||
ARGOS_PARALLEL_TOTAL: ${{ env.RUN_VISUAL_TEST == 'true' && strategy.job-total || 1 }}
|
||||
ARGOS_PARALLEL_INDEX: ${{ env.RUN_VISUAL_TEST == 'true' && matrix.containers || 1 }}
|
||||
CYPRESS_COMMIT: ${{ github.sha }}
|
||||
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
|
||||
ARGOS_PARALLEL: true
|
||||
ARGOS_PARALLEL_TOTAL: ${{ strategy.job-total }}
|
||||
ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}
|
||||
CYPRESS_RECORD_KEY: ${{ env.RUN_VISUAL_TEST == 'true' && secrets.CYPRESS_RECORD_KEY || ''}}
|
||||
SPLIT: ${{ strategy.job-total }}
|
||||
SPLIT_INDEX: ${{ strategy.job-index }}
|
||||
SPLIT_FILE: 'cypress/timings.json'
|
||||
VITEST_COVERAGE: true
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
|
||||
|
||||
2
.github/workflows/scorecard.yml
vendored
2
.github/workflows/scorecard.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
results_format: sarif
|
||||
publish_results: true
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
|
||||
uses: actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3.pre.node20
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
@@ -95,6 +95,10 @@ In our release process we rely heavily on visual regression tests using [applito
|
||||
|
||||
<!-- </Main description> -->
|
||||
|
||||
## Mermaid AI Bot
|
||||
|
||||
[Mermaid](https://codeparrot.ai/oracle?owner=mermaid-js&repo=mermaid) Bot will help you understand this repository better. You can ask for code examples, installation guide, debugging help and much more.
|
||||
|
||||
## Examples
|
||||
|
||||
**The following are some examples of the diagrams, charts and graphs that can be made using Mermaid. Click here to jump into the [text syntax](https://mermaid.js.org/intro/syntax-reference.html).**
|
||||
|
||||
@@ -23,7 +23,7 @@ export default eyesPlugin(
|
||||
});
|
||||
// copy any needed variables from process.env to config.env
|
||||
config.env.useAppli = process.env.USE_APPLI ? true : false;
|
||||
config.env.useArgos = !!process.env.CI;
|
||||
config.env.useArgos = !!process.env.CI && !!process.env.ARGOS_TOKEN;
|
||||
|
||||
if (config.env.useArgos) {
|
||||
registerArgosTask(on, config, {
|
||||
|
||||
@@ -132,3 +132,10 @@ export const verifyScreenshot = (name: string): void => {
|
||||
cy.matchImageSnapshot(name);
|
||||
}
|
||||
};
|
||||
|
||||
export const verifyNumber = (value: number, expected: number, deltaPercent = 10): void => {
|
||||
expect(value).to.be.within(
|
||||
expected * (1 - deltaPercent / 100),
|
||||
expected * (1 + deltaPercent / 100)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||
import { imgSnapshotTest, renderGraph, verifyNumber } from '../../helpers/util.ts';
|
||||
|
||||
describe('Flowchart ELK', () => {
|
||||
it('1-elk: should render a simple flowchart', () => {
|
||||
@@ -109,7 +109,7 @@ describe('Flowchart ELK', () => {
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.be.within(230 * 0.95, 230 * 1.05);
|
||||
verifyNumber(maxWidthValue, 380);
|
||||
});
|
||||
});
|
||||
it('8-elk: should render a flowchart when useMaxWidth is false', () => {
|
||||
@@ -128,7 +128,7 @@ describe('Flowchart ELK', () => {
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
|
||||
expect(width).to.be.within(230 * 0.95, 230 * 1.05);
|
||||
verifyNumber(width, 380);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
@@ -692,7 +692,7 @@ A --> B
|
||||
{}
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
const edges = svg.querySelectorAll('.edges > path');
|
||||
const edges = svg[0].querySelectorAll('.edges > path');
|
||||
edges.forEach((edge) => {
|
||||
expect(edge).to.have.class('flowchart-link');
|
||||
});
|
||||
@@ -739,7 +739,7 @@ NL\`") --"\`1o **bold**\`"--> c
|
||||
{ flowchart: { titleTopMargin: 0 } }
|
||||
);
|
||||
});
|
||||
it('Wrapping long text with a new line', () => {
|
||||
it.skip('Wrapping long text with a new line', () => {
|
||||
imgSnapshotTest(
|
||||
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||
flowchart-elk LR
|
||||
|
||||
@@ -1076,4 +1076,41 @@ end
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('New @ sytax for node metadata edge cases', () => {
|
||||
it('should be possible to use @ syntax to add labels on multi nodes', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should be possible to use @ syntax to add labels with trail spaces and &', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should be possible to use @ syntax to add labels with trail spaces', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
n2["label for n2"]
|
||||
n4@{ label: "labe for n4"}
|
||||
n5@{ label: "labe for n5"}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should be possible to use @ syntax to add labels with trail spaces and edge/link', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
A["A"] --> B["for B"] & C@{ label: "for c"} & E@{label : "for E"}
|
||||
D@{label: "for D"}
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ describe('Graph', () => {
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Two| E[iPad]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
{ flowchart: { htmlLabels: false }, fontFamily: 'courier' }
|
||||
|
||||
@@ -78,58 +78,78 @@
|
||||
font-family: monospace;
|
||||
font-size: 72px;
|
||||
}
|
||||
|
||||
pre {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* tspan {
|
||||
font-size: 6px !important;
|
||||
} */
|
||||
/* .flowchart-link {
|
||||
stroke-dasharray: 4, 4 !important;
|
||||
animation: flow 1s linear infinite;
|
||||
animation: dashdraw 4.93282s linear infinite;
|
||||
stroke-width: 2px !important;
|
||||
} */
|
||||
|
||||
@keyframes dashdraw {
|
||||
from {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*stroke-width:2;stroke-dasharray:10.000000,9.865639;stroke-dashoffset:-198.656393;animation: 4.932820s linear infinite;*/
|
||||
/* stroke-width:2;stroke-dasharray:10.000000,9.865639;stroke-dashoffset:-198.656393;animation: dashdraw 4.932820s linear infinite;*/
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
%% subgraph s1["Untitled subgraph"]
|
||||
C{"Evaluate"}
|
||||
%% end
|
||||
|
||||
B --> C
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
%% A ==> B
|
||||
%% A2 --> B2
|
||||
D --> I((I the Circle))
|
||||
D --> I
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
subgraph S2
|
||||
subgraph s1["APA"]
|
||||
D{"Use the editor"}
|
||||
end
|
||||
|
||||
|
||||
D -- Mermaid js --> I(("fa:fa-code Text"))
|
||||
D --> I
|
||||
D --> E --> I
|
||||
|
||||
end
|
||||
AB["apa@apa@"] --> B(("`apa@apa`"))
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
flowchart
|
||||
D(("for D"))
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
flowchart LR
|
||||
A e1@==> B
|
||||
e1@{ animate: true}
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
flowchart LR
|
||||
A e1@--> B
|
||||
classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round
|
||||
class e1 animate
|
||||
</pre>
|
||||
<h2>infinite</h2>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
A e1@--> B
|
||||
classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
|
||||
class e1 animate
|
||||
</pre>
|
||||
<h2>Mermaid - edge-animation-slow</h2>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
A e1@--> B
|
||||
e1@{ animation: fast}
|
||||
</pre>
|
||||
<h2>Mermaid - edge-animation-fast</h2>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
A e1@--> B
|
||||
classDef animate stroke-dasharray: 1000,stroke-dashoffset: 1000,animation: dash 10s linear;
|
||||
class e1 edge-animation-fast
|
||||
</pre>
|
||||
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
|
||||
info </pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
@@ -154,7 +174,7 @@ config:
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
@@ -167,7 +187,7 @@ config:
|
||||
D-->I
|
||||
D-->I
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
@@ -206,7 +226,7 @@ flowchart LR
|
||||
n8@{ shape: rect}
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
@@ -222,7 +242,7 @@ flowchart LR
|
||||
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
@@ -231,7 +251,7 @@ flowchart LR
|
||||
A{A} --> B & C
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
@@ -243,7 +263,7 @@ flowchart LR
|
||||
end
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
@@ -261,7 +281,7 @@ flowchart LR
|
||||
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
kanban:
|
||||
@@ -280,81 +300,81 @@ kanban
|
||||
task3[💻 Develop login feature]@{ ticket: 103 }
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||
A:::AClass
|
||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||
A:::AClass
|
||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||
A:::AClass
|
||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||
A:::AClass
|
||||
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
kanban
|
||||
id2[In progress]
|
||||
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
kanban:
|
||||
@@ -414,7 +434,10 @@ kanban
|
||||
window.callback = function () {
|
||||
alert('A callback was triggered');
|
||||
};
|
||||
mermaid.initialize({
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
await mermaid.initialize({
|
||||
// theme: 'base',
|
||||
// theme: 'default',
|
||||
// theme: 'forest',
|
||||
@@ -426,9 +449,11 @@ kanban
|
||||
// layout: 'fixed',
|
||||
// htmlLabels: false,
|
||||
flowchart: { titleTopMargin: 10 },
|
||||
|
||||
// fontFamily: 'Caveat',
|
||||
// fontFamily: 'Kalam',
|
||||
// fontFamily: 'courier',
|
||||
fontFamily: 'arial',
|
||||
sequence: {
|
||||
actorFontFamily: 'courier',
|
||||
noteFontFamily: 'courier',
|
||||
@@ -440,10 +465,9 @@ kanban
|
||||
fontSize: 12,
|
||||
logLevel: 0,
|
||||
securityLevel: 'loose',
|
||||
callback,
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
|
||||
mermaid.parseError = function (err, hash) {
|
||||
console.error('In parse error:');
|
||||
console.error(err);
|
||||
|
||||
@@ -62,56 +62,23 @@
|
||||
|
||||
<body style="display: flex; gap: 2rem; flex-direction: row">
|
||||
<pre id="diagram4" class="mermaid">
|
||||
flowchart LR
|
||||
A@{ icon: "fa:window-minimize", form: circle }
|
||||
E@{ icon: "fa:window-minimize", form: circle }
|
||||
B@{ icon: "fa:bell", form: circle }
|
||||
B2@{ icon: "fa:bell", form: circle }
|
||||
C@{ icon: "fa:address-book", form: square }
|
||||
D@{ icon: "fa:star-half", form: square }
|
||||
A --> E
|
||||
B --> B2
|
||||
|
||||
flowchart
|
||||
A --> A
|
||||
subgraph B
|
||||
B1 --> B1
|
||||
end
|
||||
subgraph C
|
||||
subgraph C1
|
||||
C2 --> C2
|
||||
subgraph D
|
||||
D1 --> D1
|
||||
end
|
||||
D --> D
|
||||
end
|
||||
C1 --> C1
|
||||
end
|
||||
|
||||
</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
|
||||
>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
import layouts from './mermaid-layout-elk.esm.mjs';
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
|
||||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Arial';
|
||||
background-color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="diagram"></div>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
import layouts from './mermaid-layout-elk.esm.mjs';
|
||||
mermaid.registerLayoutLoaders(layouts);
|
||||
mermaid.parseError = function (err, hash) {
|
||||
console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
//look: 'handdrawn',
|
||||
// layout: 'fixed',
|
||||
theme: 'dark',
|
||||
//layout: 'elk',
|
||||
fontFamily: 'Kalam',
|
||||
logLevel: 1,
|
||||
});
|
||||
|
||||
let shape = 'card';
|
||||
// let simplified = true;
|
||||
let simplified = false;
|
||||
let algorithm = 'elk';
|
||||
// let algorithm = 'dagre';
|
||||
let code = `---
|
||||
config:
|
||||
layout: ${algorithm}
|
||||
---
|
||||
flowchart TD
|
||||
A["Abrakadabra"] --> C["C"] & D["I am a circle"] & n4["Untitled Node"]
|
||||
D@{ shape: diamond}
|
||||
B["Bombrakadombra"] --> D & C & D
|
||||
C --> E["E"] & B
|
||||
D --> E & A
|
||||
n4 --> C
|
||||
A@{ shape: ${shape}}
|
||||
B@{ shape: ${shape}}
|
||||
C@{ shape: ${shape}}
|
||||
D@{ shape: ${shape}}
|
||||
E@{ shape: ${shape}}
|
||||
n4@{ shape: ${shape}}
|
||||
|
||||
`;
|
||||
if (simplified) {
|
||||
code = `---
|
||||
config:
|
||||
layout: ${algorithm}
|
||||
---
|
||||
flowchart LR
|
||||
A["Abrakadabra"] --> C["C"] & C & C & C & C
|
||||
%% A["Abrakadabra"] --> C
|
||||
A@{ shape: ${shape}}
|
||||
C@{ shape: ${shape}}
|
||||
|
||||
`;
|
||||
}
|
||||
console.log(code);
|
||||
const { svg } = await mermaid.render('the-id-of-the-svg', code, undefined, undefined);
|
||||
const elem = document.querySelector('#diagram');
|
||||
elem.innerHTML = svg;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/rendering-util/types.ts:144](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L144)
|
||||
[packages/mermaid/src/rendering-util/types.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L148)
|
||||
|
||||
---
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/rendering-util/types.ts:143](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L143)
|
||||
[packages/mermaid/src/rendering-util/types.ts:147](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L147)
|
||||
|
||||
---
|
||||
|
||||
@@ -40,4 +40,4 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/rendering-util/types.ts:142](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L142)
|
||||
[packages/mermaid/src/rendering-util/types.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L146)
|
||||
|
||||
@@ -19,4 +19,4 @@ The `parseError` function will not be called.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/types.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L59)
|
||||
[packages/mermaid/src/types.ts:64](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L64)
|
||||
|
||||
@@ -18,7 +18,7 @@ The config passed as YAML frontmatter or directives
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/types.ts:70](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L70)
|
||||
[packages/mermaid/src/types.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L75)
|
||||
|
||||
---
|
||||
|
||||
@@ -30,4 +30,4 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/types.ts:66](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L66)
|
||||
[packages/mermaid/src/types.ts:71](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L71)
|
||||
|
||||
@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
|
||||
[packages/mermaid/src/types.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L103)
|
||||
|
||||
---
|
||||
|
||||
@@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
|
||||
[packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93)
|
||||
|
||||
---
|
||||
|
||||
@@ -63,4 +63,4 @@ The svg code for the rendered graph.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
|
||||
[packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
|
||||
|
||||
@@ -99,8 +99,7 @@ Blogging frameworks and platforms
|
||||
- [Nextra](https://nextra.site/)
|
||||
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||
- [WordPress](https://wordpress.org)
|
||||
- [WordPress Markdown Editor](https://wordpress.org/plugins/wp-githuber-md)
|
||||
- [WP-ReliableMD](https://wordpress.org/plugins/wp-reliablemd/)
|
||||
- [MerPRess](https://wordpress.org/plugins/merpress/)
|
||||
|
||||
### CMS/ECM
|
||||
|
||||
|
||||
@@ -1183,6 +1183,91 @@ flowchart TB
|
||||
B --> D
|
||||
```
|
||||
|
||||
### Attaching an ID to Edges
|
||||
|
||||
Mermaid now supports assigning IDs to edges, similar to how IDs and metadata can be attached to nodes. This feature lays the groundwork for more advanced styling, classes, and animation capabilities on edges.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
To give an edge an ID, prepend the edge syntax with the ID followed by an `@` character. For example:
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
```
|
||||
|
||||
In this example, `e1` is the ID of the edge connecting `A` to `B`. You can then use this ID in later definitions or style statements, just like with nodes.
|
||||
|
||||
### Turning an Animation On
|
||||
|
||||
Once you have assigned an ID to an edge, you can turn on animations for that edge by defining the edge’s properties:
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A e1@==> B
|
||||
e1@{ animate: true }
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@==> B
|
||||
e1@{ animate: true }
|
||||
```
|
||||
|
||||
This tells Mermaid that the edge `e1` should be animated.
|
||||
|
||||
### Selecting Type of Animation
|
||||
|
||||
In the initial version, two animation speeds are supported: `fast` and `slow`. Selecting a specific animation type is a shorthand for enabling animation and setting the animation speed in one go.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
e1@{ animation: fast }
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
e1@{ animation: fast }
|
||||
```
|
||||
|
||||
This is equivalent to `{ animate: true, animation: fast }`.
|
||||
|
||||
### Using classDef Statements for Animations
|
||||
|
||||
You can also animate edges by assigning a class to them and then defining animation properties in a `classDef` statement. For example:
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
|
||||
class e1 animate
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
|
||||
class e1 animate
|
||||
```
|
||||
|
||||
In this snippet:
|
||||
|
||||
- `e1@-->` creates an edge with ID `e1`.
|
||||
- `classDef animate` defines a class named `animate` with styling and animation properties.
|
||||
- `class e1 animate` applies the `animate` class to the edge `e1`.
|
||||
|
||||
**Note on Escaping Commas:**
|
||||
When setting the `stroke-dasharray` property, remember to escape commas as `\,` since commas are used as delimiters in Mermaid’s style definitions.
|
||||
|
||||
## New arrow types
|
||||
|
||||
There are new types of arrows supported:
|
||||
|
||||
@@ -500,7 +500,7 @@ mermaid.ganttConfig = {
|
||||
sectionFontSize: 24, // Font size for sections
|
||||
numberSectionStyles: 1, // The number of alternating section styles
|
||||
axisFormat: '%d/%m', // Date/time format of the axis
|
||||
tickInterval: '1 week', // Axis ticks
|
||||
tickInterval: '1week', // Axis ticks
|
||||
topAxis: true, // When this flag is set, date labels will be added to the top of the chart
|
||||
displayMode: 'compact', // Turns compact mode on
|
||||
weekday: 'sunday', // On which day a week-based interval should start
|
||||
|
||||
@@ -18,7 +18,7 @@ timeline
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook
|
||||
: Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@ timeline
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook
|
||||
: Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
```
|
||||
|
||||
@@ -67,7 +67,7 @@ timeline
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
```
|
||||
|
||||
@@ -76,7 +76,7 @@ timeline
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
```
|
||||
|
||||
@@ -198,7 +198,7 @@ However, if there is no section defined, then we have two possibilities:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
|
||||
```
|
||||
@@ -208,7 +208,7 @@ However, if there is no section defined, then we have two possibilities:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
|
||||
```
|
||||
@@ -239,7 +239,7 @@ let us look at same example, where we have disabled the multiColor option.
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
|
||||
```
|
||||
@@ -250,7 +250,7 @@ let us look at same example, where we have disabled the multiColor option.
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
|
||||
```
|
||||
@@ -278,7 +278,7 @@ Now let's override the default values for the `cScale0` to `cScale2` variables:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -296,7 +296,7 @@ Now let's override the default values for the `cScale0` to `cScale2` variables:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -329,7 +329,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -342,7 +342,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -357,7 +357,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -370,7 +370,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -385,7 +385,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -398,7 +398,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -413,7 +413,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -426,7 +426,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -441,7 +441,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -454,7 +454,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @mermaid-js/layout-elk
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6090](https://github.com/mermaid-js/mermaid/pull/6090) [`654097c`](https://github.com/mermaid-js/mermaid/commit/654097c43801b2d606bc3d2bef8c6fbc3301e9e4) Thanks [@knsv](https://github.com/knsv)! - fix: Updated offset calculations for diamond shape when handling intersections
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mermaid-js/layout-elk",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"description": "ELK layout engine for mermaid",
|
||||
"module": "dist/mermaid-layout-elk.core.mjs",
|
||||
"types": "dist/layouts.d.ts",
|
||||
|
||||
@@ -4,8 +4,7 @@ import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from '
|
||||
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
|
||||
|
||||
type Node = LayoutData['nodes'][number];
|
||||
// Used to calculate distances in order to avoid floating number rounding issues when comparing floating numbers
|
||||
const epsilon = 0.0001;
|
||||
|
||||
interface LabelData {
|
||||
width: number;
|
||||
height: number;
|
||||
@@ -18,16 +17,7 @@ interface NodeWithVertex extends Omit<Node, 'domId'> {
|
||||
labelData?: LabelData;
|
||||
domId?: Node['domId'] | SVGGroup | d3.Selection<SVGAElement, unknown, Element | null, unknown>;
|
||||
}
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
function distance(p1?: Point, p2?: Point): number {
|
||||
if (!p1 || !p2) {
|
||||
return 0;
|
||||
}
|
||||
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
||||
}
|
||||
|
||||
export const render = async (
|
||||
data4Layout: LayoutData,
|
||||
svg: SVG,
|
||||
@@ -70,7 +60,6 @@ export const render = async (
|
||||
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
|
||||
const boundingBox = childNodeEl.node()!.getBBox();
|
||||
child.domId = childNodeEl;
|
||||
child.calcIntersect = node.calcIntersect;
|
||||
child.width = boundingBox.width;
|
||||
child.height = boundingBox.height;
|
||||
} else {
|
||||
@@ -470,6 +459,302 @@ export const render = async (
|
||||
}
|
||||
}
|
||||
|
||||
function intersectLine(
|
||||
p1: { y: number; x: number },
|
||||
p2: { y: number; x: number },
|
||||
q1: { x: any; y: any },
|
||||
q2: { x: any; y: any }
|
||||
) {
|
||||
log.debug('UIO intersectLine', p1, p2, q1, q2);
|
||||
// Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
|
||||
// p7 and p473.
|
||||
|
||||
// let a1, a2, b1, b2, c1, c2;
|
||||
// let r1, r2, r3, r4;
|
||||
// let denom, offset, num;
|
||||
// let x, y;
|
||||
|
||||
// Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
|
||||
// b1 y + c1 = 0.
|
||||
const a1 = p2.y - p1.y;
|
||||
const b1 = p1.x - p2.x;
|
||||
const c1 = p2.x * p1.y - p1.x * p2.y;
|
||||
|
||||
// Compute r3 and r4.
|
||||
const r3 = a1 * q1.x + b1 * q1.y + c1;
|
||||
const r4 = a1 * q2.x + b1 * q2.y + c1;
|
||||
|
||||
const epsilon = 1e-6;
|
||||
|
||||
// Check signs of r3 and r4. If both point 3 and point 4 lie on
|
||||
// same side of line 1, the line segments do not intersect.
|
||||
if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
|
||||
const a2 = q2.y - q1.y;
|
||||
const b2 = q1.x - q2.x;
|
||||
const c2 = q2.x * q1.y - q1.x * q2.y;
|
||||
|
||||
// Compute r1 and r2
|
||||
const r1 = a2 * p1.x + b2 * p1.y + c2;
|
||||
const r2 = a2 * p2.x + b2 * p2.y + c2;
|
||||
|
||||
// Check signs of r1 and r2. If both point 1 and point 2 lie
|
||||
// on same side of second line segment, the line segments do
|
||||
// not intersect.
|
||||
if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Line segments intersect: compute intersection point.
|
||||
const denom = a1 * b2 - a2 * b1;
|
||||
if (denom === 0) {
|
||||
return /*COLLINEAR*/;
|
||||
}
|
||||
|
||||
const offset = Math.abs(denom / 2);
|
||||
|
||||
// The denom/2 is to get rounding instead of truncating. It
|
||||
// is added or subtracted to the numerator, depending upon the
|
||||
// sign of the numerator.
|
||||
let num = b1 * c2 - b2 * c1;
|
||||
const x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
|
||||
num = a2 * c1 - a1 * c2;
|
||||
const y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
|
||||
return { x: x, y: y };
|
||||
}
|
||||
|
||||
function sameSign(r1: number, r2: number) {
|
||||
return r1 * r2 > 0;
|
||||
}
|
||||
const diamondIntersection = (
|
||||
bounds: { x: any; y: any; width: any; height: any },
|
||||
outsidePoint: { x: number; y: number },
|
||||
insidePoint: any
|
||||
) => {
|
||||
const x1 = bounds.x;
|
||||
const y1 = bounds.y;
|
||||
|
||||
const w = bounds.width; //+ bounds.padding;
|
||||
const h = bounds.height; // + bounds.padding;
|
||||
|
||||
const polyPoints = [
|
||||
{ x: x1, y: y1 - h / 2 },
|
||||
{ x: x1 + w / 2, y: y1 },
|
||||
{ x: x1, y: y1 + h / 2 },
|
||||
{ x: x1 - w / 2, y: y1 },
|
||||
];
|
||||
log.debug(
|
||||
`APA16 diamondIntersection calc abc89:
|
||||
outsidePoint: ${JSON.stringify(outsidePoint)}
|
||||
insidePoint : ${JSON.stringify(insidePoint)}
|
||||
node-bounds : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`,
|
||||
JSON.stringify(polyPoints)
|
||||
);
|
||||
|
||||
const intersections = [];
|
||||
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let minY = Number.POSITIVE_INFINITY;
|
||||
|
||||
polyPoints.forEach(function (entry) {
|
||||
minX = Math.min(minX, entry.x);
|
||||
minY = Math.min(minY, entry.y);
|
||||
});
|
||||
|
||||
const left = x1 - w / 2 - minX;
|
||||
const top = y1 - h / 2 - minY;
|
||||
|
||||
for (let i = 0; i < polyPoints.length; i++) {
|
||||
const p1 = polyPoints[i];
|
||||
const p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];
|
||||
const intersect = intersectLine(
|
||||
bounds,
|
||||
outsidePoint,
|
||||
{ x: left + p1.x, y: top + p1.y },
|
||||
{ x: left + p2.x, y: top + p2.y }
|
||||
);
|
||||
|
||||
if (intersect) {
|
||||
intersections.push(intersect);
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersections.length) {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
log.debug('UIO intersections', intersections);
|
||||
|
||||
if (intersections.length > 1) {
|
||||
// More intersections, find the one nearest to edge end point
|
||||
intersections.sort(function (p, q) {
|
||||
const pdx = p.x - outsidePoint.x;
|
||||
const pdy = p.y - outsidePoint.y;
|
||||
const distp = Math.sqrt(pdx * pdx + pdy * pdy);
|
||||
|
||||
const qdx = q.x - outsidePoint.x;
|
||||
const qdy = q.y - outsidePoint.y;
|
||||
const distq = Math.sqrt(qdx * qdx + qdy * qdy);
|
||||
|
||||
return distp < distq ? -1 : distp === distq ? 0 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
return intersections[0];
|
||||
};
|
||||
|
||||
const intersection = (
|
||||
node: { x: any; y: any; width: number; height: number },
|
||||
outsidePoint: { x: number; y: number },
|
||||
insidePoint: { x: number; y: number }
|
||||
) => {
|
||||
log.debug(`intersection calc abc89:
|
||||
outsidePoint: ${JSON.stringify(outsidePoint)}
|
||||
insidePoint : ${JSON.stringify(insidePoint)}
|
||||
node : x:${node.x} y:${node.y} w:${node.width} h:${node.height}`);
|
||||
const x = node.x;
|
||||
const y = node.y;
|
||||
|
||||
const dx = Math.abs(x - insidePoint.x);
|
||||
// const dy = Math.abs(y - insidePoint.y);
|
||||
const w = node.width / 2;
|
||||
let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx;
|
||||
const h = node.height / 2;
|
||||
|
||||
const Q = Math.abs(outsidePoint.y - insidePoint.y);
|
||||
const R = Math.abs(outsidePoint.x - insidePoint.x);
|
||||
|
||||
if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) {
|
||||
// Intersection is top or bottom of rect.
|
||||
const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y;
|
||||
r = (R * q) / Q;
|
||||
const res = {
|
||||
x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r,
|
||||
y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q,
|
||||
};
|
||||
|
||||
if (r === 0) {
|
||||
res.x = outsidePoint.x;
|
||||
res.y = outsidePoint.y;
|
||||
}
|
||||
if (R === 0) {
|
||||
res.x = outsidePoint.x;
|
||||
}
|
||||
if (Q === 0) {
|
||||
res.y = outsidePoint.y;
|
||||
}
|
||||
|
||||
log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line
|
||||
|
||||
return res;
|
||||
} else {
|
||||
// Intersection onn sides of rect
|
||||
if (insidePoint.x < outsidePoint.x) {
|
||||
r = outsidePoint.x - w - x;
|
||||
} else {
|
||||
// r = outsidePoint.x - w - x;
|
||||
r = x - w - outsidePoint.x;
|
||||
}
|
||||
const q = (Q * r) / R;
|
||||
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w;
|
||||
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
|
||||
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
|
||||
// let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
|
||||
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
|
||||
log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y });
|
||||
if (r === 0) {
|
||||
_x = outsidePoint.x;
|
||||
_y = outsidePoint.y;
|
||||
}
|
||||
if (R === 0) {
|
||||
_x = outsidePoint.x;
|
||||
}
|
||||
if (Q === 0) {
|
||||
_y = outsidePoint.y;
|
||||
}
|
||||
|
||||
return { x: _x, y: _y };
|
||||
}
|
||||
};
|
||||
const outsideNode = (
|
||||
node: { x: any; y: any; width: number; height: number },
|
||||
point: { x: number; y: number }
|
||||
) => {
|
||||
const x = node.x;
|
||||
const y = node.y;
|
||||
const dx = Math.abs(point.x - x);
|
||||
const dy = Math.abs(point.y - y);
|
||||
const w = node.width / 2;
|
||||
const h = node.height / 2;
|
||||
if (dx >= w || dy >= h) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* This function will page a path and node where the last point(s) in the path is inside the node
|
||||
* and return an update path ending by the border of the node.
|
||||
*/
|
||||
const cutPathAtIntersect = (
|
||||
_points: any[],
|
||||
bounds: { x: any; y: any; width: any; height: any; padding: any },
|
||||
isDiamond: boolean
|
||||
) => {
|
||||
log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond);
|
||||
const points: any[] = [];
|
||||
let lastPointOutside = _points[0];
|
||||
let isInside = false;
|
||||
_points.forEach((point: any) => {
|
||||
// check if point is inside the boundary rect
|
||||
if (!outsideNode(bounds, point) && !isInside) {
|
||||
// First point inside the rect found
|
||||
// Calc the intersection coord between the point anf the last point outside the rect
|
||||
let inter;
|
||||
|
||||
if (isDiamond) {
|
||||
const inter2 = diamondIntersection(bounds, lastPointOutside, point);
|
||||
const distance = Math.sqrt(
|
||||
(lastPointOutside.x - inter2.x) ** 2 + (lastPointOutside.y - inter2.y) ** 2
|
||||
);
|
||||
if (distance > 1) {
|
||||
inter = inter2;
|
||||
}
|
||||
}
|
||||
if (!inter) {
|
||||
inter = intersection(bounds, lastPointOutside, point);
|
||||
}
|
||||
|
||||
// Check case where the intersection is the same as the last point
|
||||
let pointPresent = false;
|
||||
points.forEach((p) => {
|
||||
pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
|
||||
});
|
||||
// if (!pointPresent) {
|
||||
if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
|
||||
points.push(inter);
|
||||
} else {
|
||||
log.debug('abc88 no intersect', inter, points);
|
||||
}
|
||||
// points.push(inter);
|
||||
isInside = true;
|
||||
} else {
|
||||
// Outside
|
||||
log.debug('abc88 outside', point, lastPointOutside, points);
|
||||
lastPointOutside = point;
|
||||
// points.push(point);
|
||||
if (!isInside) {
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
});
|
||||
return points;
|
||||
};
|
||||
|
||||
// @ts-ignore - ELK is not typed
|
||||
const elk = new ELK();
|
||||
const element = svg.select('g');
|
||||
@@ -592,10 +877,9 @@ export const render = async (
|
||||
setIncludeChildrenPolicy(target, ancestorId);
|
||||
}
|
||||
});
|
||||
// const copy = JSON.parse(JSON.stringify({ ...elkGraph }));
|
||||
// console.log('APA13 layout before', copy);
|
||||
|
||||
const g = await elk.layout(elkGraph);
|
||||
// console.log('APA13 layout', JSON.parse(JSON.stringify(g)));
|
||||
|
||||
// debugger;
|
||||
await drawNodes(0, 0, g.children, svg, subGraphsEl, 0);
|
||||
g.edges?.map(
|
||||
@@ -682,59 +966,43 @@ export const render = async (
|
||||
startNode.innerHTML
|
||||
);
|
||||
}
|
||||
|
||||
if (startNode.calcIntersect) {
|
||||
// console.log(
|
||||
// 'APA13 calculating start intersection start node',
|
||||
// startNode.id,
|
||||
// startNode.x,
|
||||
// startNode.y,
|
||||
// 'w:',
|
||||
// startNode.width,
|
||||
// 'h:',
|
||||
// startNode.height,
|
||||
// '\nPos',
|
||||
// edge.points[0]
|
||||
// );
|
||||
const intersection = startNode.calcIntersect(
|
||||
{
|
||||
x: startNode.offset.posX + startNode.width / 2,
|
||||
y: startNode.offset.posY + startNode.height / 2,
|
||||
width: startNode.width,
|
||||
height: startNode.height,
|
||||
},
|
||||
edge.points[0]
|
||||
);
|
||||
|
||||
if (distance(intersection, edge.points[0]) > epsilon) {
|
||||
edge.points.unshift(intersection);
|
||||
}
|
||||
if (startNode.shape === 'diamond' || startNode.shape === 'diam') {
|
||||
edge.points.unshift({
|
||||
x: startNode.offset.posX + startNode.width / 2,
|
||||
y: startNode.offset.posY + startNode.height / 2,
|
||||
});
|
||||
}
|
||||
if (endNode.calcIntersect) {
|
||||
const intersection = endNode.calcIntersect(
|
||||
{
|
||||
x: endNode.offset.posX + endNode.width / 2,
|
||||
y: endNode.offset.posY + endNode.height / 2,
|
||||
width: endNode.width,
|
||||
height: endNode.height,
|
||||
},
|
||||
edge.points[edge.points.length - 1]
|
||||
);
|
||||
// if (edge.id === 'L_n4_C_10_0') {
|
||||
// console.log('APA14 lineData', edge.points, 'intersection:', intersection);
|
||||
// console.log(
|
||||
// 'APA14! calculating end intersection\ndistance:',
|
||||
// distance(intersection, edge.points[edge.points.length - 1])
|
||||
// );
|
||||
// }
|
||||
|
||||
if (distance(intersection, edge.points[edge.points.length - 1]) > epsilon) {
|
||||
// console.log('APA13! distance ok\nintersection:', intersection);
|
||||
edge.points.push(intersection);
|
||||
// console.log('APA13! distance ok\npoints:', edge.points);
|
||||
}
|
||||
if (endNode.shape === 'diamond' || endNode.shape === 'diam') {
|
||||
edge.points.push({
|
||||
x: endNode.offset.posX + endNode.width / 2,
|
||||
y: endNode.offset.posY + endNode.height / 2,
|
||||
});
|
||||
}
|
||||
|
||||
edge.points = cutPathAtIntersect(
|
||||
edge.points.reverse(),
|
||||
{
|
||||
x: startNode.offset.posX + startNode.width / 2,
|
||||
y: startNode.offset.posY + startNode.height / 2,
|
||||
width: sw,
|
||||
height: startNode.height,
|
||||
padding: startNode.padding,
|
||||
},
|
||||
startNode.shape === 'diamond' || startNode.shape === 'diam'
|
||||
).reverse();
|
||||
|
||||
edge.points = cutPathAtIntersect(
|
||||
edge.points,
|
||||
{
|
||||
x: endNode.offset.posX + endNode.width / 2,
|
||||
y: endNode.offset.posY + endNode.height / 2,
|
||||
width: ew,
|
||||
height: endNode.height,
|
||||
padding: endNode.padding,
|
||||
},
|
||||
endNode.shape === 'diamond' || endNode.shape === 'diam'
|
||||
);
|
||||
|
||||
const paths = insertEdge(
|
||||
edgesEl,
|
||||
edge,
|
||||
@@ -744,6 +1012,7 @@ export const render = async (
|
||||
endNode,
|
||||
data4Layout.diagramId
|
||||
);
|
||||
log.info('APA12 edge points after insert', JSON.stringify(edge.points));
|
||||
|
||||
edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
|
||||
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
||||
import { parser } from './parser/classDiagram.jison';
|
||||
import classDb from './classDb.js';
|
||||
import { ClassDB } from './classDb.js';
|
||||
|
||||
describe('class diagram, ', function () {
|
||||
describe('when parsing data from a classDiagram it', function () {
|
||||
let classDb;
|
||||
beforeEach(function () {
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
parser.yy.clear();
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/classDiagram.jison';
|
||||
import db from './classDb.js';
|
||||
import { ClassDB } from './classDb.js';
|
||||
import styles from './styles.js';
|
||||
import renderer from './classRenderer-v3-unified.js';
|
||||
|
||||
export const diagram: DiagramDefinition = {
|
||||
parser,
|
||||
db,
|
||||
get db() {
|
||||
return new ClassDB();
|
||||
},
|
||||
renderer,
|
||||
styles,
|
||||
init: (cnf) => {
|
||||
@@ -15,6 +17,5 @@ export const diagram: DiagramDefinition = {
|
||||
cnf.class = {};
|
||||
}
|
||||
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
|
||||
db.clear();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/unbound-method -- Broken for Vitest mocks, see https://github.com/vitest-dev/eslint-plugin-vitest/pull/286 */
|
||||
// @ts-expect-error Jison doesn't export types
|
||||
import { parser } from './parser/classDiagram.jison';
|
||||
import classDb from './classDb.js';
|
||||
import { ClassDB } from './classDb.js';
|
||||
import { vi, describe, it, expect } from 'vitest';
|
||||
import type { ClassMap, NamespaceNode } from './classTypes.js';
|
||||
const spyOn = vi.spyOn;
|
||||
@@ -10,8 +11,9 @@ const abstractCssStyle = 'font-style:italic;';
|
||||
|
||||
describe('given a basic class diagram, ', function () {
|
||||
describe('when parsing class definition', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
it('should handle classes within namespaces', () => {
|
||||
@@ -564,8 +566,9 @@ class C13["With Città foreign language"]
|
||||
});
|
||||
|
||||
describe('when parsing class defined in brackets', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
|
||||
@@ -656,8 +659,9 @@ class C13["With Città foreign language"]
|
||||
});
|
||||
|
||||
describe('when parsing comments', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
|
||||
@@ -746,8 +750,9 @@ foo()
|
||||
});
|
||||
|
||||
describe('when parsing click statements', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
it('should handle href link', function () {
|
||||
@@ -857,8 +862,9 @@ foo()
|
||||
});
|
||||
|
||||
describe('when parsing annotations', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
|
||||
@@ -921,8 +927,9 @@ foo()
|
||||
|
||||
describe('given a class diagram with members and methods ', function () {
|
||||
describe('when parsing members', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
|
||||
@@ -980,8 +987,9 @@ describe('given a class diagram with members and methods ', function () {
|
||||
});
|
||||
|
||||
describe('when parsing method definition', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
|
||||
@@ -1067,8 +1075,9 @@ describe('given a class diagram with members and methods ', function () {
|
||||
|
||||
describe('given a class diagram with generics, ', function () {
|
||||
describe('when parsing valid generic classes', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
|
||||
@@ -1180,8 +1189,9 @@ namespace space {
|
||||
|
||||
describe('given a class diagram with relationships, ', function () {
|
||||
describe('when parsing basic relationships', function () {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb.clear();
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
});
|
||||
|
||||
@@ -1714,7 +1724,9 @@ class Class2
|
||||
});
|
||||
|
||||
describe('when parsing classDiagram with text labels', () => {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(function () {
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
parser.yy.clear();
|
||||
});
|
||||
@@ -1897,3 +1909,40 @@ class C13["With Città foreign language"]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('class db class', () => {
|
||||
let classDb: ClassDB;
|
||||
beforeEach(() => {
|
||||
classDb = new ClassDB();
|
||||
});
|
||||
// This is to ensure that functions used in class JISON are exposed as function from ClassDB
|
||||
it('should have functions used in class JISON as own property', () => {
|
||||
const functionsUsedInParser = [
|
||||
'addRelation',
|
||||
'cleanupLabel',
|
||||
'setAccTitle',
|
||||
'setAccDescription',
|
||||
'addClassesToNamespace',
|
||||
'addNamespace',
|
||||
'setCssClass',
|
||||
'addMembers',
|
||||
'addClass',
|
||||
'setClassLabel',
|
||||
'addAnnotation',
|
||||
'addMember',
|
||||
'addNote',
|
||||
'defineClass',
|
||||
'setDirection',
|
||||
'relationType',
|
||||
'lineType',
|
||||
'setClickEvent',
|
||||
'setTooltip',
|
||||
'setLink',
|
||||
'setCssStyle',
|
||||
] as const satisfies (keyof ClassDB)[];
|
||||
|
||||
for (const fun of functionsUsedInParser) {
|
||||
expect(Object.hasOwn(classDb, fun)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/classDiagram.jison';
|
||||
import db from './classDb.js';
|
||||
import { ClassDB } from './classDb.js';
|
||||
import styles from './styles.js';
|
||||
import renderer from './classRenderer-v3-unified.js';
|
||||
|
||||
export const diagram: DiagramDefinition = {
|
||||
parser,
|
||||
db,
|
||||
get db() {
|
||||
return new ClassDB();
|
||||
},
|
||||
renderer,
|
||||
styles,
|
||||
init: (cnf) => {
|
||||
@@ -15,6 +17,5 @@ export const diagram: DiagramDefinition = {
|
||||
cnf.class = {};
|
||||
}
|
||||
cnf.class.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
|
||||
db.clear();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { parser } from './classDiagram.jison';
|
||||
import classDb from '../classDb.js';
|
||||
import { ClassDB } from '../classDb.js';
|
||||
|
||||
describe('class diagram', function () {
|
||||
let classDb;
|
||||
beforeEach(function () {
|
||||
classDb = new ClassDB();
|
||||
parser.yy = classDb;
|
||||
parser.yy.clear();
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import flowDb from './flowDb.js';
|
||||
import { FlowDB } from './flowDb.js';
|
||||
import type { FlowSubGraph } from './types.js';
|
||||
|
||||
describe('flow db subgraphs', () => {
|
||||
let flowDb: FlowDB;
|
||||
let subgraphs: FlowSubGraph[];
|
||||
beforeEach(() => {
|
||||
flowDb = new FlowDB();
|
||||
subgraphs = [
|
||||
{ nodes: ['a', 'b', 'c', 'e'] },
|
||||
{ nodes: ['f', 'g', 'h'] },
|
||||
@@ -44,8 +46,9 @@ describe('flow db subgraphs', () => {
|
||||
});
|
||||
|
||||
describe('flow db addClass', () => {
|
||||
let flowDb: FlowDB;
|
||||
beforeEach(() => {
|
||||
flowDb.clear();
|
||||
flowDb = new FlowDB();
|
||||
});
|
||||
it('should detect many classes', () => {
|
||||
flowDb.addClass('a,b', ['stroke-width: 8px']);
|
||||
@@ -65,3 +68,33 @@ describe('flow db addClass', () => {
|
||||
expect(classes.get('a')?.styles).toEqual(['stroke-width: 8px']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('flow db class', () => {
|
||||
let flowDb: FlowDB;
|
||||
beforeEach(() => {
|
||||
flowDb = new FlowDB();
|
||||
});
|
||||
// This is to ensure that functions used in flow JISON are exposed as function from FlowDB
|
||||
it('should have functions used in flow JISON as own property', () => {
|
||||
const functionsUsedInParser = [
|
||||
'setDirection',
|
||||
'addSubGraph',
|
||||
'setAccTitle',
|
||||
'setAccDescription',
|
||||
'addVertex',
|
||||
'addLink',
|
||||
'setClass',
|
||||
'destructLink',
|
||||
'addClass',
|
||||
'setClickEvent',
|
||||
'setTooltip',
|
||||
'setLink',
|
||||
'updateLink',
|
||||
'updateLinkInterpolate',
|
||||
] as const satisfies (keyof FlowDB)[];
|
||||
|
||||
for (const fun of functionsUsedInParser) {
|
||||
expect(Object.hasOwn(flowDb, fun)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,17 @@
|
||||
import type { MermaidConfig } from '../../config.type.js';
|
||||
import { setConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import flowDb from './flowDb.js';
|
||||
import { FlowDB } from './flowDb.js';
|
||||
import renderer from './flowRenderer-v3-unified.js';
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import flowParser from './parser/flow.jison';
|
||||
//import flowParser from './parser/flow.jison';
|
||||
import flowParser from './parser/flowParser.ts';
|
||||
import flowStyles from './styles.js';
|
||||
|
||||
export const diagram = {
|
||||
parser: flowParser,
|
||||
db: flowDb,
|
||||
get db() {
|
||||
return new FlowDB();
|
||||
},
|
||||
renderer,
|
||||
styles: flowStyles,
|
||||
init: (cnf: MermaidConfig) => {
|
||||
@@ -20,7 +23,5 @@ export const diagram = {
|
||||
}
|
||||
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
|
||||
setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
|
||||
flowDb.clear();
|
||||
flowDb.setGen('gen-2');
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/rende
|
||||
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
||||
import type { LayoutData } from '../../rendering-util/types.js';
|
||||
import utils from '../../utils.js';
|
||||
import { getDirection } from './flowDb.js';
|
||||
|
||||
export const getClasses = function (
|
||||
text: string,
|
||||
@@ -37,7 +36,7 @@ export const draw = async function (text: string, id: string, _version: string,
|
||||
log.debug('Data: ', data4Layout);
|
||||
// Create the root SVG
|
||||
const svg = getDiagramElement(id, securityLevel);
|
||||
const direction = getDirection();
|
||||
const direction = diag.db.getDirection();
|
||||
|
||||
data4Layout.type = diag.type;
|
||||
data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('[Arrows] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
import { cleanupComments } from '../../../diagram-api/comments.js';
|
||||
|
||||
@@ -9,7 +9,7 @@ setConfig({
|
||||
|
||||
describe('[Comments] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('when parsing directions', function () {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -39,10 +39,31 @@ const doubleEndedEdges = [
|
||||
{ edgeStart: '<==', edgeEnd: '==>', stroke: 'thick', type: 'double_arrow_point' },
|
||||
{ edgeStart: '<-.', edgeEnd: '.->', stroke: 'dotted', type: 'double_arrow_point' },
|
||||
];
|
||||
const regularEdges = [
|
||||
{ edgeStart: '--', edgeEnd: '--x', stroke: 'normal', type: 'arrow_cross' },
|
||||
{ edgeStart: '==', edgeEnd: '==x', stroke: 'thick', type: 'arrow_cross' },
|
||||
{ edgeStart: '-.', edgeEnd: '.-x', stroke: 'dotted', type: 'arrow_cross' },
|
||||
{ edgeStart: '--', edgeEnd: '--o', stroke: 'normal', type: 'arrow_circle' },
|
||||
{ edgeStart: '==', edgeEnd: '==o', stroke: 'thick', type: 'arrow_circle' },
|
||||
{ edgeStart: '-.', edgeEnd: '.-o', stroke: 'dotted', type: 'arrow_circle' },
|
||||
{ edgeStart: '--', edgeEnd: '-->', stroke: 'normal', type: 'arrow_point' },
|
||||
{ edgeStart: '==', edgeEnd: '==>', stroke: 'thick', type: 'arrow_point' },
|
||||
{ edgeStart: '-.', edgeEnd: '.->', stroke: 'dotted', type: 'arrow_point' },
|
||||
|
||||
{ edgeStart: '--', edgeEnd: '----x', stroke: 'normal', type: 'arrow_cross' },
|
||||
{ edgeStart: '==', edgeEnd: '====x', stroke: 'thick', type: 'arrow_cross' },
|
||||
{ edgeStart: '-.', edgeEnd: '...-x', stroke: 'dotted', type: 'arrow_cross' },
|
||||
{ edgeStart: '--', edgeEnd: '----o', stroke: 'normal', type: 'arrow_circle' },
|
||||
{ edgeStart: '==', edgeEnd: '====o', stroke: 'thick', type: 'arrow_circle' },
|
||||
{ edgeStart: '-.', edgeEnd: '...-o', stroke: 'dotted', type: 'arrow_circle' },
|
||||
{ edgeStart: '--', edgeEnd: '---->', stroke: 'normal', type: 'arrow_point' },
|
||||
{ edgeStart: '==', edgeEnd: '====>', stroke: 'thick', type: 'arrow_point' },
|
||||
{ edgeStart: '-.', edgeEnd: '...->', stroke: 'dotted', type: 'arrow_point' },
|
||||
];
|
||||
|
||||
describe('[Edges] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
@@ -67,6 +88,74 @@ describe('[Edges] when parsing', () => {
|
||||
expect(edges[0].type).toBe('arrow_circle');
|
||||
});
|
||||
|
||||
describe('edges with ids', function () {
|
||||
describe('open ended edges with ids and labels', function () {
|
||||
regularEdges.forEach((edgeType) => {
|
||||
it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
|
||||
const res = flow.parser.parse(
|
||||
`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`
|
||||
);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
expect(vert.get('A').id).toBe('A');
|
||||
expect(vert.get('B').id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].id).toBe('e1');
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe(`${edgeType.type}`);
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
|
||||
});
|
||||
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
|
||||
const res = flow.parser.parse(
|
||||
`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`
|
||||
);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
expect(vert.get('A').id).toBe('A');
|
||||
expect(vert.get('B').id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].id).toBe('e1');
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe(`${edgeType.type}`);
|
||||
expect(edges[0].text).toBe('');
|
||||
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
|
||||
});
|
||||
});
|
||||
it('should handle normal edges where you also have a node with metadata', function () {
|
||||
const res = flow.parser.parse(`flowchart LR
|
||||
A id1@-->B
|
||||
A@{ shape: 'rect' }
|
||||
`);
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
|
||||
expect(edges[0].id).toBe('id1');
|
||||
});
|
||||
});
|
||||
describe('double ended edges with ids and labels', function () {
|
||||
doubleEndedEdges.forEach((edgeType) => {
|
||||
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
|
||||
const res = flow.parser.parse(
|
||||
`flowchart TD;\nA e1@${edgeType.edgeStart} label ${edgeType.edgeEnd} B;`
|
||||
);
|
||||
const vert = flow.parser.yy.getVertices();
|
||||
const edges = flow.parser.yy.getEdges();
|
||||
expect(vert.get('A').id).toBe('A');
|
||||
expect(vert.get('B').id).toBe('B');
|
||||
expect(edges.length).toBe(1);
|
||||
expect(edges[0].id).toBe('e1');
|
||||
expect(edges[0].start).toBe('A');
|
||||
expect(edges[0].end).toBe('B');
|
||||
expect(edges[0].type).toBe(`${edgeType.type}`);
|
||||
expect(edges[0].text).toBe('label');
|
||||
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('edges', function () {
|
||||
doubleEndedEdges.forEach((edgeType) => {
|
||||
it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('[Text] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
import { vi } from 'vitest';
|
||||
const spyOn = vi.spyOn;
|
||||
@@ -9,7 +9,9 @@ setConfig({
|
||||
});
|
||||
|
||||
describe('[Interactions] when parsing', () => {
|
||||
let flowDb;
|
||||
beforeEach(function () {
|
||||
flowDb = new FlowDB();
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('[Lines] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('parsing a flow chart with markdown strings', function () {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('when parsing directions', function () {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
@@ -251,7 +251,7 @@ describe('when parsing directions', function () {
|
||||
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
|
||||
expect(data4Layout.nodes[0].label).toEqual('This is a<br/>multiline string');
|
||||
});
|
||||
it(' should be possible to use } in strings', function () {
|
||||
it('should be possible to use } in strings', function () {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
A@{
|
||||
label: "This is a string with }"
|
||||
@@ -264,7 +264,7 @@ describe('when parsing directions', function () {
|
||||
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
|
||||
expect(data4Layout.nodes[0].label).toEqual('This is a string with }');
|
||||
});
|
||||
it(' should be possible to use @ in strings', function () {
|
||||
it('should be possible to use @ in strings', function () {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
A@{
|
||||
label: "This is a string with @"
|
||||
@@ -277,7 +277,7 @@ describe('when parsing directions', function () {
|
||||
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
|
||||
expect(data4Layout.nodes[0].label).toEqual('This is a string with @');
|
||||
});
|
||||
it(' should be possible to use @ in strings', function () {
|
||||
it('should be possible to use @ in strings', function () {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
A@{
|
||||
label: "This is a string with}"
|
||||
@@ -290,4 +290,126 @@ describe('when parsing directions', function () {
|
||||
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
|
||||
expect(data4Layout.nodes[0].label).toEqual('This is a string with}');
|
||||
});
|
||||
|
||||
it('should be possible to use @ syntax to add labels on multi nodes', function () {
|
||||
const res = flow.parser.parse(`flowchart TB
|
||||
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"}
|
||||
`);
|
||||
|
||||
const data4Layout = flow.parser.yy.getData();
|
||||
expect(data4Layout.nodes.length).toBe(3);
|
||||
expect(data4Layout.nodes[0].label).toEqual('label for n2');
|
||||
expect(data4Layout.nodes[1].label).toEqual('labe for n4');
|
||||
expect(data4Layout.nodes[2].label).toEqual('labe for n5');
|
||||
});
|
||||
|
||||
it('should be possible to use @ syntax to add labels on multi nodes with edge/link', function () {
|
||||
const res = flow.parser.parse(`flowchart TD
|
||||
A["A"] --> B["for B"] & C@{ label: "for c"} & E@{label : "for E"}
|
||||
D@{label: "for D"}
|
||||
`);
|
||||
|
||||
const data4Layout = flow.parser.yy.getData();
|
||||
expect(data4Layout.nodes.length).toBe(5);
|
||||
expect(data4Layout.nodes[0].label).toEqual('A');
|
||||
expect(data4Layout.nodes[1].label).toEqual('for B');
|
||||
expect(data4Layout.nodes[2].label).toEqual('for c');
|
||||
expect(data4Layout.nodes[3].label).toEqual('for E');
|
||||
expect(data4Layout.nodes[4].label).toEqual('for D');
|
||||
});
|
||||
|
||||
it('should be possible to use @ syntax in labels', function () {
|
||||
const res = flow.parser.parse(`flowchart TD
|
||||
A["@A@"] --> B["@for@ B@"] & C@{ label: "@for@ c@"} & E{"\`@for@ E@\`"} & D(("@for@ D@"))
|
||||
H1{{"@for@ H@"}}
|
||||
H2{{"\`@for@ H@\`"}}
|
||||
Q1{"@for@ Q@"}
|
||||
Q2{"\`@for@ Q@\`"}
|
||||
AS1>"@for@ AS@"]
|
||||
AS2>"\`@for@ AS@\`"]
|
||||
`);
|
||||
|
||||
const data4Layout = flow.parser.yy.getData();
|
||||
expect(data4Layout.nodes.length).toBe(11);
|
||||
expect(data4Layout.nodes[0].label).toEqual('@A@');
|
||||
expect(data4Layout.nodes[1].label).toEqual('@for@ B@');
|
||||
expect(data4Layout.nodes[2].label).toEqual('@for@ c@');
|
||||
expect(data4Layout.nodes[3].label).toEqual('@for@ E@');
|
||||
expect(data4Layout.nodes[4].label).toEqual('@for@ D@');
|
||||
expect(data4Layout.nodes[5].label).toEqual('@for@ H@');
|
||||
expect(data4Layout.nodes[6].label).toEqual('@for@ H@');
|
||||
expect(data4Layout.nodes[7].label).toEqual('@for@ Q@');
|
||||
expect(data4Layout.nodes[8].label).toEqual('@for@ Q@');
|
||||
expect(data4Layout.nodes[9].label).toEqual('@for@ AS@');
|
||||
expect(data4Layout.nodes[10].label).toEqual('@for@ AS@');
|
||||
});
|
||||
|
||||
it('should handle unique edge creation with using @ and &', function () {
|
||||
const res = flow.parser.parse(`flowchart TD
|
||||
A & B e1@--> C & D
|
||||
A1 e2@--> C1 & D1
|
||||
`);
|
||||
|
||||
const data4Layout = flow.parser.yy.getData();
|
||||
expect(data4Layout.nodes.length).toBe(7);
|
||||
expect(data4Layout.edges.length).toBe(6);
|
||||
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
|
||||
expect(data4Layout.edges[1].id).toEqual('L_A_D_0');
|
||||
expect(data4Layout.edges[2].id).toEqual('e1');
|
||||
expect(data4Layout.edges[3].id).toEqual('L_B_D_0');
|
||||
expect(data4Layout.edges[4].id).toEqual('e2');
|
||||
expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0');
|
||||
});
|
||||
|
||||
it('should handle redefine same edge ids again', function () {
|
||||
const res = flow.parser.parse(`flowchart TD
|
||||
A & B e1@--> C & D
|
||||
A1 e1@--> C1 & D1
|
||||
`);
|
||||
|
||||
const data4Layout = flow.parser.yy.getData();
|
||||
expect(data4Layout.nodes.length).toBe(7);
|
||||
expect(data4Layout.edges.length).toBe(6);
|
||||
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
|
||||
expect(data4Layout.edges[1].id).toEqual('L_A_D_0');
|
||||
expect(data4Layout.edges[2].id).toEqual('e1');
|
||||
expect(data4Layout.edges[3].id).toEqual('L_B_D_0');
|
||||
expect(data4Layout.edges[4].id).toEqual('L_A1_C1_0');
|
||||
expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0');
|
||||
});
|
||||
|
||||
it('should handle overriding edge animate again', function () {
|
||||
const res = flow.parser.parse(`flowchart TD
|
||||
A e1@--> B
|
||||
C e2@--> D
|
||||
E e3@--> F
|
||||
e1@{ animate: true }
|
||||
e2@{ animate: false }
|
||||
e3@{ animate: true }
|
||||
e3@{ animate: false }
|
||||
`);
|
||||
|
||||
const data4Layout = flow.parser.yy.getData();
|
||||
expect(data4Layout.nodes.length).toBe(6);
|
||||
expect(data4Layout.edges.length).toBe(3);
|
||||
expect(data4Layout.edges[0].id).toEqual('e1');
|
||||
expect(data4Layout.edges[0].animate).toEqual(true);
|
||||
expect(data4Layout.edges[1].id).toEqual('e2');
|
||||
expect(data4Layout.edges[1].animate).toEqual(false);
|
||||
expect(data4Layout.edges[2].id).toEqual('e3');
|
||||
expect(data4Layout.edges[2].animate).toEqual(false);
|
||||
});
|
||||
|
||||
it.skip('should be possible to use @ syntax to add labels with trail spaces', function () {
|
||||
const res = flow.parser.parse(
|
||||
`flowchart TB
|
||||
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"} `
|
||||
);
|
||||
|
||||
const data4Layout = flow.parser.yy.getData();
|
||||
expect(data4Layout.nodes.length).toBe(3);
|
||||
expect(data4Layout.nodes[0].label).toEqual('label for n2');
|
||||
expect(data4Layout.nodes[1].label).toEqual('labe for n4');
|
||||
expect(data4Layout.nodes[2].label).toEqual('labe for n5');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -31,7 +31,7 @@ const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_
|
||||
|
||||
describe('[Singlenodes] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('[Style] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('[Text] when parsing', () => {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('when parsing flowcharts', function () {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
|
||||
@@ -141,6 +141,7 @@ that id.
|
||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||
.*direction\s+LR[^\n]* return 'direction_lr';
|
||||
|
||||
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
|
||||
[0-9]+ return 'NUM';
|
||||
\# return 'BRKT';
|
||||
":::" return 'STYLE_SEPARATOR';
|
||||
@@ -201,7 +202,9 @@ that id.
|
||||
"*" return 'MULT';
|
||||
"#" return 'BRKT';
|
||||
"&" return 'AMP';
|
||||
([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ return 'NODE_STRING';
|
||||
([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ {
|
||||
return 'NODE_STRING';
|
||||
}
|
||||
"-" return 'MINUS'
|
||||
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
|
||||
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
|
||||
@@ -361,7 +364,7 @@ spaceList
|
||||
|
||||
statement
|
||||
: vertexStatement separator
|
||||
{ /* console.warn('finat vs', $vertexStatement.nodes); */ $$=$vertexStatement.nodes}
|
||||
{ $$=$vertexStatement.nodes}
|
||||
| styleStatement separator
|
||||
{$$=[];}
|
||||
| linkStyleStatement separator
|
||||
@@ -396,7 +399,7 @@ shapeData:
|
||||
;
|
||||
|
||||
vertexStatement: vertexStatement link node shapeData
|
||||
{ /* console.warn('vs shapeData',$vertexStatement.stmt,$node, $shapeData);*/ yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } }
|
||||
{ /* console.warn('vs shapeData',$vertexStatement.stmt,$node, $shapeData);*/ yy.addVertex($node[$node.length-1],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } }
|
||||
| vertexStatement link node
|
||||
{ /*console.warn('vs',$vertexStatement.stmt,$node);*/ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } }
|
||||
| vertexStatement link node spaceList
|
||||
@@ -404,7 +407,7 @@ vertexStatement: vertexStatement link node shapeData
|
||||
|node spaceList { /*console.warn('vertexStatement: node spaceList', $node);*/ $$ = {stmt: $node, nodes:$node }}
|
||||
|node shapeData {
|
||||
/*console.warn('vertexStatement: node shapeData', $node[0], $shapeData);*/
|
||||
yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData);
|
||||
yy.addVertex($node[$node.length-1],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData);
|
||||
$$ = {stmt: $node, nodes:$node, shapeData: $shapeData}
|
||||
}
|
||||
|node { /* console.warn('vertexStatement: single node', $node); */ $$ = {stmt: $node, nodes:$node }}
|
||||
@@ -413,7 +416,7 @@ vertexStatement: vertexStatement link node shapeData
|
||||
node: styledVertex
|
||||
{ /*console.warn('nod', $styledVertex);*/ $$ = [$styledVertex];}
|
||||
| node shapeData spaceList AMP spaceList styledVertex
|
||||
{ yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); $$ = $node.concat($styledVertex); /*console.warn('pip2', $node[0], $styledVertex, $$);*/ }
|
||||
{ yy.addVertex($node[$node.length-1],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); $$ = $node.concat($styledVertex); /*console.warn('pip2', $node[0], $styledVertex, $$);*/ }
|
||||
| node spaceList AMP spaceList styledVertex
|
||||
{ $$ = $node.concat($styledVertex); /*console.warn('pip', $node[0], $styledVertex, $$);*/ }
|
||||
;
|
||||
@@ -472,6 +475,8 @@ link: linkStatement arrowText
|
||||
{$$ = $linkStatement;}
|
||||
| START_LINK edgeText LINK
|
||||
{var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText};}
|
||||
| LINK_ID START_LINK edgeText LINK
|
||||
{var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText, "id": $LINK_ID};}
|
||||
;
|
||||
|
||||
edgeText: edgeTextToken
|
||||
@@ -487,6 +492,8 @@ edgeText: edgeTextToken
|
||||
|
||||
linkStatement: LINK
|
||||
{var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};}
|
||||
| LINK_ID LINK
|
||||
{var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length, "id": $LINK_ID};}
|
||||
;
|
||||
|
||||
arrowText:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { cleanupComments } from '../../../diagram-api/comments.js';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
@@ -9,7 +9,7 @@ setConfig({
|
||||
|
||||
describe('parsing a flow chart', function () {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
});
|
||||
|
||||
|
||||
12
packages/mermaid/src/diagrams/flowchart/parser/flowParser.ts
Normal file
12
packages/mermaid/src/diagrams/flowchart/parser/flowParser.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import flowJisonParser from './flow.jison';
|
||||
|
||||
const newParser = Object.assign({}, flowJisonParser);
|
||||
|
||||
newParser.parse = (src: string): unknown => {
|
||||
// remove the trailing whitespace after closing curly braces when ending a line break
|
||||
const newSrc = src.replace(/}\s*\n/g, '}\n');
|
||||
return flowJisonParser.parse(newSrc);
|
||||
};
|
||||
|
||||
export default newParser;
|
||||
@@ -1,5 +1,5 @@
|
||||
import flowDb from '../flowDb.js';
|
||||
import flow from './flow.jison';
|
||||
import { FlowDB } from '../flowDb.js';
|
||||
import flow from './flowParser.ts';
|
||||
import { setConfig } from '../../../config.js';
|
||||
|
||||
setConfig({
|
||||
@@ -8,7 +8,7 @@ setConfig({
|
||||
|
||||
describe('when parsing subgraphs', function () {
|
||||
beforeEach(function () {
|
||||
flow.parser.yy = flowDb;
|
||||
flow.parser.yy = new FlowDB();
|
||||
flow.parser.yy.clear();
|
||||
flow.parser.yy.setGen('gen-2');
|
||||
});
|
||||
|
||||
@@ -53,6 +53,7 @@ export interface FlowText {
|
||||
}
|
||||
|
||||
export interface FlowEdge {
|
||||
isUserDefinedId: boolean;
|
||||
start: string;
|
||||
end: string;
|
||||
interpolate?: string;
|
||||
@@ -62,6 +63,10 @@ export interface FlowEdge {
|
||||
length?: number;
|
||||
text: string;
|
||||
labelType: 'text';
|
||||
classes: string[];
|
||||
id?: string;
|
||||
animation?: 'fast' | 'slow';
|
||||
animate?: boolean;
|
||||
}
|
||||
|
||||
export interface FlowClass {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const getStyles = (options) =>
|
||||
`
|
||||
.mermaid-main-font {
|
||||
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
|
||||
font-family: ${options.fontFamily};
|
||||
}
|
||||
|
||||
.exclude-range {
|
||||
@@ -45,7 +45,7 @@ const getStyles = (options) =>
|
||||
|
||||
.sectionTitle {
|
||||
text-anchor: start;
|
||||
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
|
||||
font-family: ${options.fontFamily};
|
||||
}
|
||||
|
||||
|
||||
@@ -86,13 +86,13 @@ const getStyles = (options) =>
|
||||
|
||||
.taskText {
|
||||
text-anchor: middle;
|
||||
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
|
||||
font-family: ${options.fontFamily};
|
||||
}
|
||||
|
||||
.taskTextOutsideRight {
|
||||
fill: ${options.taskTextDarkColor};
|
||||
text-anchor: start;
|
||||
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
|
||||
font-family: ${options.fontFamily};
|
||||
}
|
||||
|
||||
.taskTextOutsideLeft {
|
||||
@@ -248,7 +248,7 @@ const getStyles = (options) =>
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: ${options.titleColor || options.textColor};
|
||||
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
|
||||
font-family: ${options.fontFamily};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@ import parser from './parser/sankey.jison';
|
||||
import db from './sankeyDB.js';
|
||||
import renderer from './sankeyRenderer.js';
|
||||
import { prepareTextForParsing } from './sankeyUtils.js';
|
||||
import sankeyStyles from './styles.js';
|
||||
|
||||
const originalParse = parser.parse.bind(parser);
|
||||
parser.parse = (text: string) => originalParse(prepareTextForParsing(text));
|
||||
|
||||
export const diagram: DiagramDefinition = {
|
||||
styles: sankeyStyles,
|
||||
parser,
|
||||
db,
|
||||
renderer,
|
||||
|
||||
@@ -136,7 +136,6 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
||||
svg
|
||||
.append('g')
|
||||
.attr('class', 'node-labels')
|
||||
.attr('font-family', 'sans-serif')
|
||||
.attr('font-size', 14)
|
||||
.selectAll('text')
|
||||
.data(graph.nodes)
|
||||
|
||||
6
packages/mermaid/src/diagrams/sankey/styles.js
Normal file
6
packages/mermaid/src/diagrams/sankey/styles.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const getStyles = (options) =>
|
||||
`.label {
|
||||
font-family: ${options.fontFamily};
|
||||
}`;
|
||||
|
||||
export default getStyles;
|
||||
@@ -15,6 +15,5 @@ export const diagram: DiagramDefinition = {
|
||||
cnf.state = {};
|
||||
}
|
||||
cnf.state.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
|
||||
db.clear();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,6 +15,5 @@ export const diagram: DiagramDefinition = {
|
||||
cnf.state = {};
|
||||
}
|
||||
cnf.state.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
|
||||
db.clear();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const getStyles = (options) =>
|
||||
`.label {
|
||||
font-family: 'trebuchet ms', verdana, arial, sans-serif;
|
||||
font-family: var(--mermaid-font-family);
|
||||
font-family: ${options.fontFamily};
|
||||
color: ${options.textColor};
|
||||
}
|
||||
.mouth {
|
||||
@@ -79,8 +78,7 @@ const getStyles = (options) =>
|
||||
text-align: center;
|
||||
max-width: 200px;
|
||||
padding: 2px;
|
||||
font-family: 'trebuchet ms', verdana, arial, sans-serif;
|
||||
font-family: var(--mermaid-font-family);
|
||||
font-family: ${options.fontFamily};
|
||||
font-size: 12px;
|
||||
background: ${options.tertiaryColor};
|
||||
border: 1px solid ${options.border2};
|
||||
|
||||
@@ -94,8 +94,7 @@ Blogging frameworks and platforms
|
||||
- [Nextra](https://nextra.site/)
|
||||
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||
- [WordPress](https://wordpress.org)
|
||||
- [WordPress Markdown Editor](https://wordpress.org/plugins/wp-githuber-md)
|
||||
- [WP-ReliableMD](https://wordpress.org/plugins/wp-reliablemd/)
|
||||
- [MerPRess](https://wordpress.org/plugins/merpress/)
|
||||
|
||||
### CMS/ECM
|
||||
|
||||
|
||||
@@ -711,6 +711,67 @@ flowchart TB
|
||||
B --> D
|
||||
```
|
||||
|
||||
### Attaching an ID to Edges
|
||||
|
||||
Mermaid now supports assigning IDs to edges, similar to how IDs and metadata can be attached to nodes. This feature lays the groundwork for more advanced styling, classes, and animation capabilities on edges.
|
||||
|
||||
**Syntax:**
|
||||
|
||||
To give an edge an ID, prepend the edge syntax with the ID followed by an `@` character. For example:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
```
|
||||
|
||||
In this example, `e1` is the ID of the edge connecting `A` to `B`. You can then use this ID in later definitions or style statements, just like with nodes.
|
||||
|
||||
### Turning an Animation On
|
||||
|
||||
Once you have assigned an ID to an edge, you can turn on animations for that edge by defining the edge’s properties:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@==> B
|
||||
e1@{ animate: true }
|
||||
```
|
||||
|
||||
This tells Mermaid that the edge `e1` should be animated.
|
||||
|
||||
### Selecting Type of Animation
|
||||
|
||||
In the initial version, two animation speeds are supported: `fast` and `slow`. Selecting a specific animation type is a shorthand for enabling animation and setting the animation speed in one go.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
e1@{ animation: fast }
|
||||
```
|
||||
|
||||
This is equivalent to `{ animate: true, animation: fast }`.
|
||||
|
||||
### Using classDef Statements for Animations
|
||||
|
||||
You can also animate edges by assigning a class to them and then defining animation properties in a `classDef` statement. For example:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A e1@–> B
|
||||
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
|
||||
class e1 animate
|
||||
```
|
||||
|
||||
In this snippet:
|
||||
|
||||
- `e1@-->` creates an edge with ID `e1`.
|
||||
- `classDef animate` defines a class named `animate` with styling and animation properties.
|
||||
- `class e1 animate` applies the `animate` class to the edge `e1`.
|
||||
|
||||
**Note on Escaping Commas:**
|
||||
When setting the `stroke-dasharray` property, remember to escape commas as `\,` since commas are used as delimiters in Mermaid’s style definitions.
|
||||
|
||||
## New arrow types
|
||||
|
||||
There are new types of arrows supported:
|
||||
|
||||
@@ -390,7 +390,7 @@ mermaid.ganttConfig = {
|
||||
sectionFontSize: 24, // Font size for sections
|
||||
numberSectionStyles: 1, // The number of alternating section styles
|
||||
axisFormat: '%d/%m', // Date/time format of the axis
|
||||
tickInterval: '1 week', // Axis ticks
|
||||
tickInterval: '1week', // Axis ticks
|
||||
topAxis: true, // When this flag is set, date labels will be added to the top of the chart
|
||||
displayMode: 'compact', // Turns compact mode on
|
||||
weekday: 'sunday', // On which day a week-based interval should start
|
||||
|
||||
@@ -12,7 +12,7 @@ timeline
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook
|
||||
: Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ timeline
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
```
|
||||
|
||||
@@ -134,7 +134,7 @@ However, if there is no section defined, then we have two possibilities:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
|
||||
```
|
||||
@@ -165,7 +165,7 @@ let us look at same example, where we have disabled the multiColor option.
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
|
||||
```
|
||||
@@ -193,7 +193,7 @@ Now let's override the default values for the `cScale0` to `cScale2` variables:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -226,7 +226,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -241,7 +241,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -256,7 +256,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -271,7 +271,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
@@ -286,7 +286,7 @@ Let's put them to use, and see how our sample diagram looks in different themes:
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook : Google
|
||||
2005 : Youtube
|
||||
2005 : YouTube
|
||||
2006 : Twitter
|
||||
2007 : Tumblr
|
||||
2008 : Instagram
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { assert, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// -------------------------------------
|
||||
// Mocks and mocking
|
||||
@@ -69,6 +69,8 @@ import { compile, serialize } from 'stylis';
|
||||
import { Diagram } from './Diagram.js';
|
||||
import { decodeEntities, encodeEntities } from './utils.js';
|
||||
import { toBase64 } from './utils/base64.js';
|
||||
import { ClassDB } from './diagrams/class/classDb.js';
|
||||
import { FlowDB } from './diagrams/flowchart/flowDb.js';
|
||||
|
||||
/**
|
||||
* @see https://vitest.dev/guide/mocking.html Mock part of a module
|
||||
@@ -832,5 +834,93 @@ graph TD;A--x|text including URL space|B;`)
|
||||
expect(diagram).toBeInstanceOf(Diagram);
|
||||
expect(diagram.type).toBe('flowchart-v2');
|
||||
});
|
||||
|
||||
it('should not modify db when rendering different diagrams', async () => {
|
||||
const flowDiagram1 = await mermaidAPI.getDiagramFromText(
|
||||
`flowchart LR
|
||||
A -- text --> B -- text2 --> C`
|
||||
);
|
||||
const flowDiagram2 = await mermaidAPI.getDiagramFromText(
|
||||
`flowchart TD
|
||||
A -- text --> B -- text2 --> C`
|
||||
);
|
||||
// Since flowDiagram will return new Db object each time, we can compare the db to be different.
|
||||
expect(flowDiagram1.db).not.toBe(flowDiagram2.db);
|
||||
assert(flowDiagram1.db instanceof FlowDB);
|
||||
assert(flowDiagram2.db instanceof FlowDB);
|
||||
expect(flowDiagram1.db.getDirection()).not.toEqual(flowDiagram2.db.getDirection());
|
||||
|
||||
const classDiagram1 = await mermaidAPI.getDiagramFromText(
|
||||
`classDiagram
|
||||
direction TB
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides`
|
||||
);
|
||||
const classDiagram2 = await mermaidAPI.getDiagramFromText(
|
||||
`classDiagram
|
||||
direction LR
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides`
|
||||
);
|
||||
// Since classDiagram will return new Db object each time, we can compare the db to be different.
|
||||
expect(classDiagram1.db).not.toBe(classDiagram2.db);
|
||||
assert(classDiagram1.db instanceof ClassDB);
|
||||
assert(classDiagram2.db instanceof ClassDB);
|
||||
expect(classDiagram1.db.getDirection()).not.toEqual(classDiagram2.db.getDirection());
|
||||
|
||||
const sequenceDiagram1 = await mermaidAPI.getDiagramFromText(
|
||||
`sequenceDiagram
|
||||
Alice->>+John: Hello John, how are you?
|
||||
Alice->>+John: John, can you hear me?
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
John-->>-Alice: I feel great!`
|
||||
);
|
||||
const sequenceDiagram2 = await mermaidAPI.getDiagramFromText(
|
||||
`sequenceDiagram
|
||||
Alice->>+John: Hello John, how are you?
|
||||
Alice->>+John: John, can you hear me?
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
John-->>-Alice: I feel great!`
|
||||
);
|
||||
// Since sequenceDiagram will return same Db object each time, we can compare the db to be same.
|
||||
expect(sequenceDiagram1.db).toBe(sequenceDiagram2.db);
|
||||
});
|
||||
});
|
||||
|
||||
// Sequence Diagram currently uses a singleton DB, so this test will fail
|
||||
it.fails('should not modify db when rendering different sequence diagrams', async () => {
|
||||
const sequenceDiagram1 = await mermaidAPI.getDiagramFromText(
|
||||
`sequenceDiagram
|
||||
Alice->>Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?`
|
||||
);
|
||||
const sequenceDiagram2 = await mermaidAPI.getDiagramFromText(
|
||||
`sequenceDiagram
|
||||
Alice->>Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?`
|
||||
);
|
||||
expect(sequenceDiagram1.db).not.toBe(sequenceDiagram2.db);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,11 +5,11 @@ import { createText } from '../createText.js';
|
||||
import utils from '../../utils.js';
|
||||
import { getLineFunctionsWithOffset } from '../../utils/lineWithOffset.js';
|
||||
import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { curveBasis, curveLinear, line, select } from 'd3';
|
||||
import { curveBasis, line, select } from 'd3';
|
||||
import rough from 'roughjs';
|
||||
import createLabel from './createLabel.js';
|
||||
import { addEdgeMarkers } from './edgeMarker.ts';
|
||||
import { isLabelStyle } from './shapes/handDrawnShapeStyles.js';
|
||||
|
||||
const edgeLabels = new Map();
|
||||
const terminalLabels = new Map();
|
||||
@@ -336,40 +336,6 @@ const cutPathAtIntersect = (_points, boundaryNode) => {
|
||||
return points;
|
||||
};
|
||||
|
||||
const adjustForArrowHeads = function (lineData, size = 5, shouldLog = false) {
|
||||
const newLineData = [...lineData];
|
||||
const lastPoint = lineData[lineData.length - 1];
|
||||
const secondLastPoint = lineData[lineData.length - 2];
|
||||
|
||||
const distanceBetweenLastPoints = Math.sqrt(
|
||||
(lastPoint.x - secondLastPoint.x) ** 2 + (lastPoint.y - secondLastPoint.y) ** 2
|
||||
);
|
||||
|
||||
if (shouldLog) {
|
||||
log.debug('APA14 distanceBetweenLastPoints', distanceBetweenLastPoints);
|
||||
}
|
||||
if (distanceBetweenLastPoints < size) {
|
||||
// Calculate the direction vector from the last point to the second last point
|
||||
const directionX = secondLastPoint.x - lastPoint.x;
|
||||
const directionY = secondLastPoint.y - lastPoint.y;
|
||||
|
||||
// Normalize the direction vector
|
||||
const magnitude = Math.sqrt(directionX ** 2 + directionY ** 2);
|
||||
const normalizedX = directionX / magnitude;
|
||||
const normalizedY = directionY / magnitude;
|
||||
|
||||
// Calculate the new position for the second last point
|
||||
const adjustedSecondLastPoint = {
|
||||
x: lastPoint.x + normalizedX * size,
|
||||
y: lastPoint.y + normalizedY * size,
|
||||
};
|
||||
|
||||
// Replace the second last point in the new line data
|
||||
newLineData[newLineData.length - 2] = adjustedSecondLastPoint;
|
||||
}
|
||||
|
||||
return newLineData;
|
||||
};
|
||||
function extractCornerPoints(points) {
|
||||
const cornerPoints = [];
|
||||
const cornerPointPositions = [];
|
||||
@@ -457,115 +423,19 @@ const fixCorners = function (lineData) {
|
||||
return newLineData;
|
||||
};
|
||||
|
||||
export const generateRoundedPath = (points, radius, endPosition) => {
|
||||
if (points.length < 2) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// console.trace('here', points);
|
||||
const path = [];
|
||||
const startPoint = points[0];
|
||||
|
||||
path.push(`M ${startPoint.x},${startPoint.y}`);
|
||||
|
||||
for (let i = 1; i < points.length - 1; i++) {
|
||||
const currPoint = points[i];
|
||||
const nextPoint = points[i + 1];
|
||||
const prevPoint = points[i - 1];
|
||||
|
||||
// Calculate vectors
|
||||
const v1 = { x: currPoint.x - prevPoint.x, y: currPoint.y - prevPoint.y };
|
||||
const v2 = { x: nextPoint.x - currPoint.x, y: nextPoint.y - currPoint.y };
|
||||
|
||||
// Normalize vectors
|
||||
const v1Length = Math.hypot(v1.x, v1.y);
|
||||
const v2Length = Math.hypot(v2.x, v2.y);
|
||||
const v1Normalized = { x: v1.x / v1Length, y: v1.y / v1Length };
|
||||
const v2Normalized = { x: v2.x / v2Length, y: v2.y / v2Length };
|
||||
|
||||
// Calculate tangent points
|
||||
const tangentLength = Math.min(radius, v1Length / 2, v2Length / 2);
|
||||
const tangent1 = {
|
||||
x: currPoint.x - v1Normalized.x * tangentLength,
|
||||
y: currPoint.y - v1Normalized.y * tangentLength,
|
||||
};
|
||||
const tangent2 = {
|
||||
x: currPoint.x + v2Normalized.x * tangentLength,
|
||||
y: currPoint.y + v2Normalized.y * tangentLength,
|
||||
};
|
||||
|
||||
if (endPosition) {
|
||||
const { bottomY, leftX, rightX, topY } = endPosition;
|
||||
if (startPoint.pos === 'b' && tangent1.y > topY) {
|
||||
tangent1.y = topY;
|
||||
tangent2.y = topY;
|
||||
currPoint.y = topY;
|
||||
}
|
||||
if (startPoint.pos === 't' && tangent1.y < bottomY) {
|
||||
tangent1.y = bottomY;
|
||||
tangent2.y = bottomY;
|
||||
currPoint.y = bottomY;
|
||||
}
|
||||
if (startPoint.pos === 'l' && tangent1.x < rightX) {
|
||||
tangent1.x = rightX;
|
||||
tangent2.x = rightX;
|
||||
currPoint.x = rightX;
|
||||
}
|
||||
if (startPoint.pos === 'r' && tangent1.x > leftX) {
|
||||
tangent1.x = leftX;
|
||||
tangent2.x = leftX;
|
||||
currPoint.x = leftX;
|
||||
}
|
||||
if (tangent2.x && tangent2.y && tangent1.x && tangent1.y) {
|
||||
path.push(
|
||||
`L ${tangent1.x},${tangent1.y}`,
|
||||
`Q ${currPoint.x},${currPoint.y} ${tangent2.x},${tangent2.y}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (tangent2.x && tangent2.y && tangent1.x && tangent1.y) {
|
||||
path.push(
|
||||
`L ${tangent1.x},${tangent1.y}`,
|
||||
`Q ${currPoint.x},${currPoint.y} ${tangent2.x},${tangent2.y}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Last point
|
||||
const lastPoint = points[points.length - 1];
|
||||
if (endPosition) {
|
||||
if (startPoint.pos === 'b') {
|
||||
if (endPosition?.topY && points[1].y > endPosition?.topY && points[2].y > endPosition?.topY) {
|
||||
points[1].y = endPosition?.topY;
|
||||
points[2].y = endPosition?.topY;
|
||||
}
|
||||
path.push(`L ${lastPoint.x},${endPosition.topY}`);
|
||||
}
|
||||
if (startPoint.pos === 't') {
|
||||
if (points[1].y < endPosition.bottomY) {
|
||||
points[1].y = endPosition.bottomY;
|
||||
points[2].y = endPosition.bottomY;
|
||||
}
|
||||
path.push(`L ${lastPoint.x},${endPosition.bottomY}`);
|
||||
}
|
||||
if (startPoint.pos === 'l') {
|
||||
path.push(`L ${endPosition.rightX},${lastPoint.y}`);
|
||||
}
|
||||
if (startPoint.pos === 'r') {
|
||||
path.push(`L ${endPosition.leftX},${lastPoint.y}`);
|
||||
}
|
||||
} else {
|
||||
path.push(`L ${lastPoint.x},${lastPoint.y}`);
|
||||
}
|
||||
return path.join(' ');
|
||||
};
|
||||
|
||||
export const insertEdge = function (elem, edge, clusterDb, diagramType, startNode, endNode, id) {
|
||||
const { handDrawnSeed } = getConfig();
|
||||
let points = edge.points;
|
||||
let pointsHasChanged = false;
|
||||
const tail = startNode;
|
||||
var head = endNode;
|
||||
const edgeClassStyles = [];
|
||||
for (const key in edge.cssCompiledStyles) {
|
||||
if (isLabelStyle(key)) {
|
||||
continue;
|
||||
}
|
||||
edgeClassStyles.push(edge.cssCompiledStyles[key]);
|
||||
}
|
||||
|
||||
if (head.intersect && tail.intersect) {
|
||||
points = points.slice(1, edge.points.length - 1);
|
||||
@@ -600,21 +470,14 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
||||
}
|
||||
|
||||
let lineData = points.filter((p) => !Number.isNaN(p.y));
|
||||
lineData = adjustForArrowHeads(lineData, 4, edge.id === 'L_n4_C_10_0');
|
||||
lineData = fixCorners(lineData);
|
||||
// if (edge.id === 'L_n4_C_10_0') {
|
||||
// console.log('APA14 lineData', lineData);
|
||||
// }
|
||||
// lineData = adjustForArrowHeads(lineData);
|
||||
let curve = curveBasis;
|
||||
// let curve = curveLinear;
|
||||
if (edge.curve) {
|
||||
curve = edge.curve;
|
||||
}
|
||||
|
||||
const { x, y } = getLineFunctionsWithOffset(edge);
|
||||
const lineFunction = line().x(x).y(y).curve(curve);
|
||||
// const lineFunction = line().curve(curve);
|
||||
|
||||
let strokeClasses;
|
||||
switch (edge.thickness) {
|
||||
@@ -645,8 +508,8 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
||||
}
|
||||
let svgPath;
|
||||
let linePath = lineFunction(lineData);
|
||||
// let linePath = generateRoundedPath(lineData, 5);
|
||||
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
|
||||
|
||||
if (edge.look === 'handDrawn') {
|
||||
const rc = rough.svg(elem);
|
||||
Object.assign([], lineData);
|
||||
@@ -667,22 +530,46 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
|
||||
svgPath.attr('d', d);
|
||||
elem.node().appendChild(svgPath.node());
|
||||
} else {
|
||||
const stylesFromClasses = edgeClassStyles.join(';');
|
||||
const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : '';
|
||||
let animationClass = '';
|
||||
if (edge.animate) {
|
||||
animationClass = ' edge-animation-fast';
|
||||
}
|
||||
if (edge.animation) {
|
||||
animationClass = ' edge-animation-' + edge.animation;
|
||||
}
|
||||
svgPath = elem
|
||||
.append('path')
|
||||
.attr('d', linePath)
|
||||
.attr('id', edge.id)
|
||||
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
|
||||
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
|
||||
.attr(
|
||||
'class',
|
||||
' ' +
|
||||
strokeClasses +
|
||||
(edge.classes ? ' ' + edge.classes : '') +
|
||||
(animationClass ? animationClass : '')
|
||||
)
|
||||
.attr('style', stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles);
|
||||
}
|
||||
|
||||
// DEBUG code, DO NOT REMOVE
|
||||
// adds a red circle at each edge coordinate
|
||||
// points.forEach((point) => {
|
||||
// cornerPoints.forEach((point) => {
|
||||
// elem
|
||||
// .append('circle')
|
||||
// .style('stroke', 'red')
|
||||
// .style('fill', 'red')
|
||||
// .attr('r', 1)
|
||||
// .style('stroke', 'blue')
|
||||
// .style('fill', 'blue')
|
||||
// .attr('r', 3)
|
||||
// .attr('cx', point.x)
|
||||
// .attr('cy', point.y);
|
||||
// });
|
||||
// lineData.forEach((point) => {
|
||||
// elem
|
||||
// .append('circle')
|
||||
// .style('stroke', 'blue')
|
||||
// .style('fill', 'blue')
|
||||
// .attr('r', 3)
|
||||
// .attr('cx', point.x)
|
||||
// .attr('cy', point.y);
|
||||
// });
|
||||
|
||||
@@ -2,87 +2,64 @@
|
||||
* Returns the point at which two lines, p and q, intersect or returns undefined if they do not intersect.
|
||||
*/
|
||||
function intersectLine(p1, p2, q1, q2) {
|
||||
{
|
||||
// Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
|
||||
// p7 and p473.
|
||||
// Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,
|
||||
// p7 and p473.
|
||||
|
||||
// Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
|
||||
// b1 y + c1 = 0.
|
||||
const a1 = p2.y - p1.y;
|
||||
const b1 = p1.x - p2.x;
|
||||
const c1 = p2.x * p1.y - p1.x * p2.y;
|
||||
var a1, a2, b1, b2, c1, c2;
|
||||
var r1, r2, r3, r4;
|
||||
var denom, offset, num;
|
||||
var x, y;
|
||||
|
||||
// Compute r3 and r4.
|
||||
const r3 = a1 * q1.x + b1 * q1.y + c1;
|
||||
const r4 = a1 * q2.x + b1 * q2.y + c1;
|
||||
// Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +
|
||||
// b1 y + c1 = 0.
|
||||
a1 = p2.y - p1.y;
|
||||
b1 = p1.x - p2.x;
|
||||
c1 = p2.x * p1.y - p1.x * p2.y;
|
||||
|
||||
const epsilon = 1e-6;
|
||||
// Compute r3 and r4.
|
||||
r3 = a1 * q1.x + b1 * q1.y + c1;
|
||||
r4 = a1 * q2.x + b1 * q2.y + c1;
|
||||
|
||||
// Check signs of r3 and r4. If both point 3 and point 4 lie on
|
||||
// same side of line 1, the line segments do not intersect.
|
||||
if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
|
||||
const a2 = q2.y - q1.y;
|
||||
const b2 = q1.x - q2.x;
|
||||
const c2 = q2.x * q1.y - q1.x * q2.y;
|
||||
|
||||
// Compute r1 and r2
|
||||
const r1 = a2 * p1.x + b2 * p1.y + c2;
|
||||
const r2 = a2 * p2.x + b2 * p2.y + c2;
|
||||
|
||||
// Check signs of r1 and r2. If both point 1 and point 2 lie
|
||||
// on same side of second line segment, the line segments do
|
||||
// not intersect.
|
||||
if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Line segments intersect: compute intersection point.
|
||||
const denom = a1 * b2 - a2 * b1;
|
||||
if (denom === 0) {
|
||||
return /*COLLINEAR*/;
|
||||
}
|
||||
|
||||
const offset = Math.abs(denom / 2);
|
||||
|
||||
// The denom/2 is to get rounding instead of truncating. It
|
||||
// is added or subtracted to the numerator, depending upon the
|
||||
// sign of the numerator.
|
||||
let num = b1 * c2 - b2 * c1;
|
||||
const x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
|
||||
num = a2 * c1 - a1 * c2;
|
||||
const y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
// console.log(
|
||||
// 'APA13 intersectLine intersection',
|
||||
// '\np1: (',
|
||||
// p1.x,
|
||||
// p1.y,
|
||||
// ')',
|
||||
// '\np2: (',
|
||||
// p2.x,
|
||||
// p2.y,
|
||||
// ')',
|
||||
// '\nq1: (',
|
||||
// q1.x,
|
||||
// q1.y,
|
||||
// ')',
|
||||
// '\np1: (',
|
||||
// q2.x,
|
||||
// q2.y,
|
||||
// ')',
|
||||
// 'offset:',
|
||||
// offset,
|
||||
// '\nintersection: (',
|
||||
// x,
|
||||
// y,
|
||||
// ')'
|
||||
// );
|
||||
return { x: x, y: y };
|
||||
// Check signs of r3 and r4. If both point 3 and point 4 lie on
|
||||
// same side of line 1, the line segments do not intersect.
|
||||
if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
|
||||
a2 = q2.y - q1.y;
|
||||
b2 = q1.x - q2.x;
|
||||
c2 = q2.x * q1.y - q1.x * q2.y;
|
||||
|
||||
// Compute r1 and r2
|
||||
r1 = a2 * p1.x + b2 * p1.y + c2;
|
||||
r2 = a2 * p2.x + b2 * p2.y + c2;
|
||||
|
||||
// Check signs of r1 and r2. If both point 1 and point 2 lie
|
||||
// on same side of second line segment, the line segments do
|
||||
// not intersect.
|
||||
if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) {
|
||||
return /*DON'T_INTERSECT*/;
|
||||
}
|
||||
|
||||
// Line segments intersect: compute intersection point.
|
||||
denom = a1 * b2 - a2 * b1;
|
||||
if (denom === 0) {
|
||||
return /*COLLINEAR*/;
|
||||
}
|
||||
|
||||
offset = Math.abs(denom / 2);
|
||||
|
||||
// The denom/2 is to get rounding instead of truncating. It
|
||||
// is added or subtracted to the numerator, depending upon the
|
||||
// sign of the numerator.
|
||||
num = b1 * c2 - b2 * c1;
|
||||
x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
|
||||
num = a2 * c1 - a1 * c2;
|
||||
y = num < 0 ? (num - offset) / denom : (num + offset) / denom;
|
||||
|
||||
return { x: x, y: y };
|
||||
}
|
||||
|
||||
function sameSign(r1, r2) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import intersectLine from './intersect-line.js';
|
||||
function intersectPolygon(node, polyPoints, point) {
|
||||
let x1 = node.x;
|
||||
let y1 = node.y;
|
||||
// console.trace('APA14 intersectPolygon', x1, y1, polyPoints, point);
|
||||
|
||||
let intersections = [];
|
||||
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
@@ -24,7 +24,7 @@ function intersectPolygon(node, polyPoints, point) {
|
||||
|
||||
let left = x1 - node.width / 2 - minX;
|
||||
let top = y1 - node.height / 2 - minY;
|
||||
// console.log('APA13 intersectPolygon2 ', left, y1);
|
||||
|
||||
for (let i = 0; i < polyPoints.length; i++) {
|
||||
let p1 = polyPoints[i];
|
||||
let p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];
|
||||
@@ -34,9 +34,7 @@ function intersectPolygon(node, polyPoints, point) {
|
||||
{ x: left + p1.x, y: top + p1.y },
|
||||
{ x: left + p2.x, y: top + p2.y }
|
||||
);
|
||||
// console.log('APA13 intersectPolygon3 ', intersect);
|
||||
if (intersect) {
|
||||
// console.log('APA13 intersectPolygon4 ', intersect);
|
||||
intersections.push(intersect);
|
||||
}
|
||||
}
|
||||
@@ -44,7 +42,6 @@ function intersectPolygon(node, polyPoints, point) {
|
||||
if (!intersections.length) {
|
||||
return node;
|
||||
}
|
||||
// console.log('APA12 intersectPolygon5 ');
|
||||
|
||||
if (intersections.length > 1) {
|
||||
// More intersections, find the one nearest to edge end point
|
||||
@@ -57,8 +54,6 @@ function intersectPolygon(node, polyPoints, point) {
|
||||
let qdy = q.y - point.y;
|
||||
let distq = Math.sqrt(qdx * qdx + qdy * qdy);
|
||||
|
||||
// console.log('APA12 intersectPolygon6 ');
|
||||
|
||||
return distp < distq ? -1 : distp === distq ? 0 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import { handleUndefinedAttr } from '../../../utils.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export function anchor<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||
const { labelStyles } = styles2String(node);
|
||||
@@ -38,11 +37,6 @@ export function anchor<T extends SVGGraphicsElement>(parent: D3Selection<T>, nod
|
||||
|
||||
updateNodeBounds(node, circleElem);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const radius = bounds.width / 2;
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('Circle intersect', node, radius, point);
|
||||
return intersect.circle(node, radius, point);
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
function generateArcPoints(
|
||||
x1: number,
|
||||
@@ -71,15 +70,7 @@ function generateArcPoints(
|
||||
|
||||
return points;
|
||||
}
|
||||
function getPoints(w: number, h: number, rx: number, ry: number) {
|
||||
return [
|
||||
{ x: w / 2, y: -h / 2 },
|
||||
{ x: -w / 2, y: -h / 2 },
|
||||
...generateArcPoints(-w / 2, -h / 2, -w / 2, h / 2, rx, ry, false),
|
||||
{ x: w / 2, y: h / 2 },
|
||||
...generateArcPoints(w / 2, h / 2, w / 2, -h / 2, rx, ry, true),
|
||||
];
|
||||
}
|
||||
|
||||
export async function bowTieRect<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||
const { labelStyles, nodeStyles } = styles2String(node);
|
||||
node.labelStyle = labelStyles;
|
||||
@@ -93,7 +84,13 @@ export async function bowTieRect<T extends SVGGraphicsElement>(parent: D3Selecti
|
||||
// let shape: d3.Selection<SVGPathElement | SVGGElement, unknown, null, undefined>;
|
||||
const { cssStyles } = node;
|
||||
|
||||
const points = getPoints(w, h, rx, ry);
|
||||
const points = [
|
||||
{ x: w / 2, y: -h / 2 },
|
||||
{ x: -w / 2, y: -h / 2 },
|
||||
...generateArcPoints(-w / 2, -h / 2, -w / 2, h / 2, rx, ry, false),
|
||||
{ x: w / 2, y: h / 2 },
|
||||
...generateArcPoints(w / 2, h / 2, w / 2, -h / 2, rx, ry, true),
|
||||
];
|
||||
|
||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||
const rc = rough.svg(shapeSvg);
|
||||
@@ -121,16 +118,6 @@ export async function bowTieRect<T extends SVGGraphicsElement>(parent: D3Selecti
|
||||
|
||||
updateNodeBounds(node, bowTieRectShape);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const w = bounds.width;
|
||||
const h = bounds.height;
|
||||
|
||||
const ry = h / 2;
|
||||
const rx = ry / (2.5 + h / 50);
|
||||
|
||||
const points = getPoints(w, h, rx, ry);
|
||||
return intersect.polygon(bounds, points, point);
|
||||
};
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.polygon(node, points, point);
|
||||
return pos;
|
||||
|
||||
@@ -3,25 +3,17 @@ import intersect from '../intersect/index.js';
|
||||
import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
|
||||
import { insertPolygonShape } from './insertPolygonShape.js';
|
||||
import { createPathFromPoints } from './util.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
function getPoints(w: number, h: number, padding: number) {
|
||||
const left = 0;
|
||||
const right = w;
|
||||
const top = -h;
|
||||
const bottom = 0;
|
||||
return [
|
||||
{ x: left + padding, y: top },
|
||||
{ x: right, y: top },
|
||||
{ x: right, y: bottom },
|
||||
{ x: left, y: bottom },
|
||||
{ x: left, y: top + padding },
|
||||
{ x: left + padding, y: top },
|
||||
];
|
||||
}
|
||||
// const createPathFromPoints = (points: { x: number; y: number }[]): string => {
|
||||
// const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`);
|
||||
// pointStrings.push('Z');
|
||||
// return pointStrings.join(' ');
|
||||
// };
|
||||
|
||||
export async function card<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||
const { labelStyles, nodeStyles } = styles2String(node);
|
||||
node.labelStyle = labelStyles;
|
||||
@@ -30,8 +22,18 @@ export async function card<T extends SVGGraphicsElement>(parent: D3Selection<T>,
|
||||
const h = bbox.height + node.padding;
|
||||
const padding = 12;
|
||||
const w = bbox.width + node.padding + padding;
|
||||
|
||||
const points = getPoints(w, h, padding);
|
||||
const left = 0;
|
||||
const right = w;
|
||||
const top = -h;
|
||||
const bottom = 0;
|
||||
const points = [
|
||||
{ x: left + padding, y: top },
|
||||
{ x: right, y: top },
|
||||
{ x: right, y: bottom },
|
||||
{ x: left, y: bottom },
|
||||
{ x: left, y: top + padding },
|
||||
{ x: left + padding, y: top },
|
||||
];
|
||||
|
||||
let polygon: D3Selection<SVGGElement> | Awaited<ReturnType<typeof insertPolygonShape>>;
|
||||
const { cssStyles } = node;
|
||||
@@ -60,17 +62,6 @@ export async function card<T extends SVGGraphicsElement>(parent: D3Selection<T>,
|
||||
|
||||
updateNodeBounds(node, polygon);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const h = bounds.height;
|
||||
const padding = 12;
|
||||
const w = bounds.width;
|
||||
|
||||
const points = getPoints(w, h, padding);
|
||||
|
||||
const res = intersect.polygon(bounds, points, point);
|
||||
return { x: res.x - 0.5, y: res.y - 0.5 };
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
return intersect.polygon(node, points, point);
|
||||
};
|
||||
|
||||
@@ -4,15 +4,7 @@ import rough from 'roughjs';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import { createPathFromPoints, getNodeClasses } from './util.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
function getPoints(s: number) {
|
||||
return [
|
||||
{ x: 0, y: s / 2 },
|
||||
{ x: s / 2, y: 0 },
|
||||
{ x: 0, y: -s / 2 },
|
||||
{ x: -s / 2, y: 0 },
|
||||
];
|
||||
}
|
||||
|
||||
export function choice<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||
const { nodeStyles } = styles2String(node);
|
||||
node.label = '';
|
||||
@@ -24,7 +16,12 @@ export function choice<T extends SVGGraphicsElement>(parent: D3Selection<T>, nod
|
||||
|
||||
const s = Math.max(28, node.width ?? 0);
|
||||
|
||||
const points = getPoints(s);
|
||||
const points = [
|
||||
{ x: 0, y: s / 2 },
|
||||
{ x: s / 2, y: 0 },
|
||||
{ x: 0, y: -s / 2 },
|
||||
{ x: -s / 2, y: 0 },
|
||||
];
|
||||
|
||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||
const rc = rough.svg(shapeSvg);
|
||||
@@ -50,13 +47,6 @@ export function choice<T extends SVGGraphicsElement>(parent: D3Selection<T>, nod
|
||||
node.width = 28;
|
||||
node.height = 28;
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const s = Math.max(28, bounds.width ?? 0);
|
||||
|
||||
const points = getPoints(s);
|
||||
return intersect.circle(bounds, points, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
return intersect.polygon(node, points, point);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import { handleUndefinedAttr } from '../../../utils.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function circle<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||
const { labelStyles, nodeStyles } = styles2String(node);
|
||||
@@ -36,10 +35,7 @@ export async function circle<T extends SVGGraphicsElement>(parent: D3Selection<T
|
||||
}
|
||||
|
||||
updateNodeBounds(node, circleElem);
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const radius = bounds.width / 2;
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('Circle intersect', node, radius, point);
|
||||
return intersect.circle(node, radius, point);
|
||||
|
||||
@@ -9,7 +9,6 @@ import intersect from '../intersect/index.js';
|
||||
import { textHelper } from '../../../diagrams/class/shapeUtil.js';
|
||||
import { evaluate } from '../../../diagrams/common/common.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||
const config = getConfig();
|
||||
@@ -200,9 +199,6 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
}
|
||||
|
||||
updateNodeBounds(node, rect);
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
return intersect.rect(bounds, point);
|
||||
};
|
||||
node.intersect = function (point) {
|
||||
return intersect.rect(node, point);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import intersect from '../intersect/index.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
function createLine(r: number) {
|
||||
const xAxis45 = Math.cos(Math.PI / 4); // cosine of 45 degrees
|
||||
@@ -58,11 +57,6 @@ export function crossedCircle<T extends SVGGraphicsElement>(parent: D3Selection<
|
||||
|
||||
updateNodeBounds(node, crossedCircle);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const radius = Math.max(30, bounds?.width ?? 0);
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('crossedCircle intersect', node, { radius, point });
|
||||
const pos = intersect.circle(node, radius, point);
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
function generateCirclePoints(
|
||||
centerX: number,
|
||||
@@ -36,21 +35,6 @@ function generateCirclePoints(
|
||||
return points;
|
||||
}
|
||||
|
||||
function getRectPoints(w: number, h: number, radius: number) {
|
||||
return [
|
||||
{ x: w / 2, y: -h / 2 - radius },
|
||||
{ x: -w / 2, y: -h / 2 - radius },
|
||||
...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0),
|
||||
{ x: -w / 2 - radius, y: -radius },
|
||||
...generateCirclePoints(w / 2 + w * 0.1, -radius, radius, 20, -180, -270),
|
||||
...generateCirclePoints(w / 2 + w * 0.1, radius, radius, 20, -90, -180),
|
||||
{ x: -w / 2 - radius, y: h / 2 },
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
{ x: -w / 2, y: h / 2 + radius },
|
||||
{ x: w / 2, y: h / 2 + radius },
|
||||
];
|
||||
}
|
||||
|
||||
export async function curlyBraceLeft<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
node: Node
|
||||
@@ -73,7 +57,18 @@ export async function curlyBraceLeft<T extends SVGGraphicsElement>(
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
];
|
||||
|
||||
const rectPoints = getRectPoints(w, h, radius);
|
||||
const rectPoints = [
|
||||
{ x: w / 2, y: -h / 2 - radius },
|
||||
{ x: -w / 2, y: -h / 2 - radius },
|
||||
...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0),
|
||||
{ x: -w / 2 - radius, y: -radius },
|
||||
...generateCirclePoints(w / 2 + w * 0.1, -radius, radius, 20, -180, -270),
|
||||
...generateCirclePoints(w / 2 + w * 0.1, radius, radius, 20, -90, -180),
|
||||
{ x: -w / 2 - radius, y: h / 2 },
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
{ x: -w / 2, y: h / 2 + radius },
|
||||
{ x: w / 2, y: h / 2 + radius },
|
||||
];
|
||||
|
||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||
const rc = rough.svg(shapeSvg);
|
||||
@@ -110,15 +105,6 @@ export async function curlyBraceLeft<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, curlyBraceLeftShape);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const w = bounds.width;
|
||||
const h = bounds.height;
|
||||
const radius = Math.max(5, h * 0.1);
|
||||
|
||||
const rectPoints = getRectPoints(w, h, radius);
|
||||
return intersect.polygon(bounds, rectPoints, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.polygon(node, rectPoints, point);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
function generateCirclePoints(
|
||||
centerX: number,
|
||||
@@ -36,21 +35,6 @@ function generateCirclePoints(
|
||||
return points;
|
||||
}
|
||||
|
||||
function getRectPoints(w: number, h: number, radius: number) {
|
||||
return [
|
||||
{ x: -w / 2, y: -h / 2 - radius },
|
||||
{ x: w / 2, y: -h / 2 - radius },
|
||||
...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0),
|
||||
{ x: w / 2 + radius, y: -radius },
|
||||
...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270),
|
||||
...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180),
|
||||
{ x: w / 2 + radius, y: h / 2 },
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
{ x: w / 2, y: h / 2 + radius },
|
||||
{ x: -w / 2, y: h / 2 + radius },
|
||||
];
|
||||
}
|
||||
|
||||
export async function curlyBraceRight<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
node: Node
|
||||
@@ -73,7 +57,18 @@ export async function curlyBraceRight<T extends SVGGraphicsElement>(
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
];
|
||||
|
||||
const rectPoints = getRectPoints(w, h, radius);
|
||||
const rectPoints = [
|
||||
{ x: -w / 2, y: -h / 2 - radius },
|
||||
{ x: w / 2, y: -h / 2 - radius },
|
||||
...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0),
|
||||
{ x: w / 2 + radius, y: -radius },
|
||||
...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270),
|
||||
...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180),
|
||||
{ x: w / 2 + radius, y: h / 2 },
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
{ x: w / 2, y: h / 2 + radius },
|
||||
{ x: -w / 2, y: h / 2 + radius },
|
||||
];
|
||||
|
||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||
const rc = rough.svg(shapeSvg);
|
||||
@@ -110,15 +105,6 @@ export async function curlyBraceRight<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, curlyBraceRightShape);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const w = bounds.width;
|
||||
const h = bounds.height;
|
||||
const radius = Math.max(5, h * 0.1);
|
||||
|
||||
const rectPoints = getRectPoints(w, h, radius);
|
||||
return intersect.polygon(bounds, rectPoints, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.polygon(node, rectPoints, point);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
function generateCirclePoints(
|
||||
centerX: number,
|
||||
@@ -36,25 +35,6 @@ function generateCirclePoints(
|
||||
return points;
|
||||
}
|
||||
|
||||
const getRectPoints = (w: number, h: number, radius: number) => [
|
||||
{ x: w / 2, y: -h / 2 - radius },
|
||||
{ x: -w / 2, y: -h / 2 - radius },
|
||||
...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0),
|
||||
{ x: -w / 2 - radius, y: -radius },
|
||||
...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270),
|
||||
...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180),
|
||||
{ x: -w / 2 - radius, y: h / 2 },
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
{ x: -w / 2, y: h / 2 + radius },
|
||||
{ x: w / 2 - radius - radius / 2, y: h / 2 + radius },
|
||||
...generateCirclePoints(-w / 2 + radius + radius / 2, -h / 2, radius, 20, -90, -180),
|
||||
{ x: w / 2 - radius / 2, y: radius },
|
||||
...generateCirclePoints(-w / 2 - radius / 2, -radius, radius, 20, 0, 90),
|
||||
...generateCirclePoints(-w / 2 - radius / 2, radius, radius, 20, -90, 0),
|
||||
{ x: w / 2 - radius / 2, y: -radius },
|
||||
...generateCirclePoints(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270),
|
||||
];
|
||||
|
||||
export async function curlyBraces<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
node: Node
|
||||
@@ -86,7 +66,24 @@ export async function curlyBraces<T extends SVGGraphicsElement>(
|
||||
...generateCirclePoints(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270),
|
||||
];
|
||||
|
||||
const rectPoints = getRectPoints(w, h, radius);
|
||||
const rectPoints = [
|
||||
{ x: w / 2, y: -h / 2 - radius },
|
||||
{ x: -w / 2, y: -h / 2 - radius },
|
||||
...generateCirclePoints(w / 2, -h / 2, radius, 20, -90, 0),
|
||||
{ x: -w / 2 - radius, y: -radius },
|
||||
...generateCirclePoints(w / 2 + radius * 2, -radius, radius, 20, -180, -270),
|
||||
...generateCirclePoints(w / 2 + radius * 2, radius, radius, 20, -90, -180),
|
||||
{ x: -w / 2 - radius, y: h / 2 },
|
||||
...generateCirclePoints(w / 2, h / 2, radius, 20, 0, 90),
|
||||
{ x: -w / 2, y: h / 2 + radius },
|
||||
{ x: w / 2 - radius - radius / 2, y: h / 2 + radius },
|
||||
...generateCirclePoints(-w / 2 + radius + radius / 2, -h / 2, radius, 20, -90, -180),
|
||||
{ x: w / 2 - radius / 2, y: radius },
|
||||
...generateCirclePoints(-w / 2 - radius / 2, -radius, radius, 20, 0, 90),
|
||||
...generateCirclePoints(-w / 2 - radius / 2, radius, radius, 20, -90, 0),
|
||||
{ x: w / 2 - radius / 2, y: -radius },
|
||||
...generateCirclePoints(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270),
|
||||
];
|
||||
|
||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||
const rc = rough.svg(shapeSvg);
|
||||
@@ -127,15 +124,6 @@ export async function curlyBraces<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, curlyBracesShape);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const w = bounds.width;
|
||||
const h = bounds.height;
|
||||
const radius = Math.max(5, h * 0.1);
|
||||
|
||||
const rectPoints = getRectPoints(w, h, radius);
|
||||
return intersect.polygon(bounds, rectPoints, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.polygon(node, rectPoints, point);
|
||||
|
||||
|
||||
@@ -10,16 +10,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
const getTrapezoidPoints = (rw: number, tw: number, totalHeight: number, radius: number) => [
|
||||
{ x: rw, y: 0 },
|
||||
{ x: tw, y: 0 },
|
||||
{ x: 0, y: totalHeight / 2 },
|
||||
{ x: tw, y: totalHeight },
|
||||
{ x: rw, y: totalHeight },
|
||||
...generateCirclePoints(-rw, -totalHeight / 2, radius, 50, 270, 90),
|
||||
];
|
||||
|
||||
export async function curvedTrapezoid<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -49,7 +39,14 @@ export async function curvedTrapezoid<T extends SVGGraphicsElement>(
|
||||
const rw = totalWidth - radius;
|
||||
const tw = totalHeight / 4;
|
||||
|
||||
const points = getTrapezoidPoints(rw, tw, totalHeight, radius);
|
||||
const points = [
|
||||
{ x: rw, y: 0 },
|
||||
{ x: tw, y: 0 },
|
||||
{ x: 0, y: totalHeight / 2 },
|
||||
{ x: tw, y: totalHeight },
|
||||
{ x: rw, y: totalHeight },
|
||||
...generateCirclePoints(-rw, -totalHeight / 2, radius, 50, 270, 90),
|
||||
];
|
||||
|
||||
const pathData = createPathFromPoints(points);
|
||||
const shapeNode = rc.path(pathData, options);
|
||||
@@ -69,20 +66,6 @@ export async function curvedTrapezoid<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, polygon);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const w = bounds.width;
|
||||
const h = bounds.height;
|
||||
const radius = h / 2;
|
||||
|
||||
const totalWidth = w,
|
||||
totalHeight = h;
|
||||
const rw = totalWidth - radius;
|
||||
const tw = totalHeight / 4;
|
||||
const points = getTrapezoidPoints(rw, tw, totalHeight, radius);
|
||||
|
||||
return intersect.polygon(bounds, points, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.polygon(node, points, point);
|
||||
return pos;
|
||||
|
||||
@@ -5,7 +5,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import { handleUndefinedAttr } from '../../../utils.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export const createCylinderPathD = (
|
||||
x: number,
|
||||
@@ -97,24 +96,22 @@ export async function cylinder<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
`translate(${-(bbox.width / 2) - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + (node.padding ?? 0) / 1.5 - (bbox.y - (bbox.top ?? 0))})`
|
||||
);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const w = bounds.width;
|
||||
const rx = w / 2;
|
||||
const ry = rx / (2.5 + w / 50);
|
||||
const h = bounds.height;
|
||||
const pos = intersect.rect(bounds, point);
|
||||
const x = pos.x - (bounds.x ?? 0);
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.rect(node, point);
|
||||
const x = pos.x - (node.x ?? 0);
|
||||
|
||||
if (
|
||||
rx != 0 &&
|
||||
(Math.abs(x) < (w ?? 0) / 2 ||
|
||||
(Math.abs(x) == (w ?? 0) / 2 && Math.abs(pos.y - (bounds.y ?? 0)) > (h ?? 0) / 2 - ry))
|
||||
(Math.abs(x) < (node.width ?? 0) / 2 ||
|
||||
(Math.abs(x) == (node.width ?? 0) / 2 &&
|
||||
Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry))
|
||||
) {
|
||||
let y = ry * ry * (1 - (x * x) / (rx * rx));
|
||||
if (y > 0) {
|
||||
y = Math.sqrt(y);
|
||||
}
|
||||
y = ry - y;
|
||||
if (point.y - (bounds.y ?? 0) > 0) {
|
||||
if (point.y - (node.y ?? 0) > 0) {
|
||||
y = -y;
|
||||
}
|
||||
|
||||
@@ -124,14 +121,5 @@ export async function cylinder<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
return pos;
|
||||
};
|
||||
|
||||
node.intersect = function (point: Point) {
|
||||
return this.calcIntersect
|
||||
? this.calcIntersect(
|
||||
{ x: node.x ?? 0, y: node.y ?? 0, width: node.width ?? 0, height: node.height ?? 0 },
|
||||
point
|
||||
)
|
||||
: { x: 0, y: 0 };
|
||||
};
|
||||
|
||||
return shapeSvg;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function dividedRectangle<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -63,10 +62,6 @@ export async function dividedRectangle<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, polygon);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
return intersect.rect(bounds, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.rect(node, point);
|
||||
return pos;
|
||||
|
||||
@@ -5,7 +5,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import { handleUndefinedAttr } from '../../../utils.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export const createCylinderPathD = (
|
||||
x: number,
|
||||
@@ -92,36 +91,29 @@ export async function cylinder<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
|
||||
updateNodeBounds(node, cylinder);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const pos = intersect.rect(bounds, point);
|
||||
const x = pos.x - (bounds.x ?? 0);
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.rect(node, point);
|
||||
const x = pos.x - (node.x ?? 0);
|
||||
|
||||
if (
|
||||
rx != 0 &&
|
||||
(Math.abs(x) < (bounds.width ?? 0) / 2 ||
|
||||
(Math.abs(x) == (bounds.width ?? 0) / 2 &&
|
||||
Math.abs(pos.y - (bounds.y ?? 0)) > (bounds.height ?? 0) / 2 - ry))
|
||||
(Math.abs(x) < (node.width ?? 0) / 2 ||
|
||||
(Math.abs(x) == (node.width ?? 0) / 2 &&
|
||||
Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry))
|
||||
) {
|
||||
let y = ry * ry * (1 - (x * x) / (rx * rx));
|
||||
if (y != 0) {
|
||||
y = Math.sqrt(y);
|
||||
}
|
||||
y = ry - y;
|
||||
if (point.y - (bounds.y ?? 0) > 0) {
|
||||
if (point.y - (node.y ?? 0) > 0) {
|
||||
y = -y;
|
||||
}
|
||||
|
||||
pos.y += y;
|
||||
}
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
return this.calcIntersect
|
||||
? this.calcIntersect(
|
||||
{ x: node.x ?? 0, y: node.y ?? 0, width: node.width ?? 0, height: node.height ?? 0 },
|
||||
point
|
||||
)
|
||||
: { x: 0, y: 0 };
|
||||
return pos;
|
||||
};
|
||||
|
||||
return shapeSvg;
|
||||
|
||||
@@ -6,7 +6,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import { handleUndefinedAttr } from '../../../utils.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function doublecircle<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -63,11 +62,6 @@ export async function doublecircle<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, circleGroup);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const radius = bounds.width / 2;
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('DoubleCircle intersect', node, outerRadius, point);
|
||||
return intersect.circle(node, outerRadius, point);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import { handleUndefinedAttr } from '../../../utils.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function drawRect<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -63,10 +62,6 @@ export async function drawRect<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, rect);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
return intersect.rect(bounds, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
return intersect.rect(node, point);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ import intersect from '../intersect/index.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import { getNodeClasses, updateNodeBounds } from './util.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export function filledCircle<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -47,11 +46,6 @@ export function filledCircle<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, filledCircle);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const radius = bounds.width / 2;
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('filledCircle intersect', node, { radius, point });
|
||||
const pos = intersect.circle(node, radius, point);
|
||||
|
||||
@@ -6,15 +6,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import { createPathFromPoints } from './util.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
function getPoints(tw: number, h: number) {
|
||||
return [
|
||||
{ x: 0, y: -h },
|
||||
{ x: tw, y: -h },
|
||||
{ x: tw / 2, y: 0 },
|
||||
];
|
||||
}
|
||||
|
||||
export async function flippedTriangle<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -28,7 +19,11 @@ export async function flippedTriangle<T extends SVGGraphicsElement>(
|
||||
const h = w + bbox.height;
|
||||
|
||||
const tw = w + bbox.height;
|
||||
const points = getPoints(tw, h);
|
||||
const points = [
|
||||
{ x: 0, y: -h },
|
||||
{ x: tw, y: -h },
|
||||
{ x: tw / 2, y: 0 },
|
||||
];
|
||||
|
||||
const { cssStyles } = node;
|
||||
|
||||
@@ -64,16 +59,6 @@ export async function flippedTriangle<T extends SVGGraphicsElement>(
|
||||
`translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${-h / 2 + (node.padding ?? 0) / 2 + (bbox.y - (bbox.top ?? 0))})`
|
||||
);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
// TODO: Implement intersect for this shape
|
||||
const w = bounds.width;
|
||||
const h = bounds.height;
|
||||
|
||||
const tw = w + bounds.height;
|
||||
const points = getPoints(tw, h);
|
||||
return intersect.polygon(node, points, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('Triangle intersect', node, points, point);
|
||||
return intersect.polygon(node, points, point);
|
||||
|
||||
@@ -4,7 +4,6 @@ import intersect from '../intersect/index.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import { getNodeClasses, updateNodeBounds } from './util.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export function forkJoin<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -60,10 +59,6 @@ export function forkJoin<T extends SVGGraphicsElement>(
|
||||
node.width += padding / 2 || 0;
|
||||
node.height += padding / 2 || 0;
|
||||
}
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
return intersect.rect(bounds, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
return intersect.rect(node, point);
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function halfRoundedRectangle<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -64,12 +63,6 @@ export async function halfRoundedRectangle<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, polygon);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
// TODO: Implement intersect for this shape
|
||||
const radius = bounds.width / 2;
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('Pill intersect', node, { radius, point });
|
||||
const pos = intersect.polygon(node, points, point);
|
||||
|
||||
@@ -32,7 +32,28 @@ export const styles2Map = (styles: string[]) => {
|
||||
});
|
||||
return styleMap;
|
||||
};
|
||||
|
||||
export const isLabelStyle = (key: string) => {
|
||||
return (
|
||||
key === 'color' ||
|
||||
key === 'font-size' ||
|
||||
key === 'font-family' ||
|
||||
key === 'font-weight' ||
|
||||
key === 'font-style' ||
|
||||
key === 'text-decoration' ||
|
||||
key === 'text-align' ||
|
||||
key === 'text-transform' ||
|
||||
key === 'line-height' ||
|
||||
key === 'letter-spacing' ||
|
||||
key === 'word-spacing' ||
|
||||
key === 'text-shadow' ||
|
||||
key === 'text-overflow' ||
|
||||
key === 'white-space' ||
|
||||
key === 'word-wrap' ||
|
||||
key === 'word-break' ||
|
||||
key === 'overflow-wrap' ||
|
||||
key === 'hyphens'
|
||||
);
|
||||
};
|
||||
export const styles2String = (node: Node) => {
|
||||
const { stylesArray } = compileStyles(node);
|
||||
const labelStyles: string[] = [];
|
||||
@@ -42,26 +63,7 @@ export const styles2String = (node: Node) => {
|
||||
|
||||
stylesArray.forEach((style) => {
|
||||
const key = style[0];
|
||||
if (
|
||||
key === 'color' ||
|
||||
key === 'font-size' ||
|
||||
key === 'font-family' ||
|
||||
key === 'font-weight' ||
|
||||
key === 'font-style' ||
|
||||
key === 'text-decoration' ||
|
||||
key === 'text-align' ||
|
||||
key === 'text-transform' ||
|
||||
key === 'line-height' ||
|
||||
key === 'letter-spacing' ||
|
||||
key === 'word-spacing' ||
|
||||
key === 'text-shadow' ||
|
||||
key === 'text-overflow' ||
|
||||
key === 'white-space' ||
|
||||
key === 'word-wrap' ||
|
||||
key === 'word-break' ||
|
||||
key === 'overflow-wrap' ||
|
||||
key === 'hyphens'
|
||||
) {
|
||||
if (isLabelStyle(key)) {
|
||||
labelStyles.push(style.join(':') + ' !important');
|
||||
} else {
|
||||
nodeStyles.push(style.join(':') + ' !important');
|
||||
|
||||
@@ -5,7 +5,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import { insertPolygonShape } from './insertPolygonShape.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export const createHexagonPathD = (
|
||||
x: number,
|
||||
@@ -73,12 +72,6 @@ export async function hexagon<T extends SVGGraphicsElement>(parent: D3Selection<
|
||||
|
||||
updateNodeBounds(node, polygon);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
// TODO: Implement intersect for this shape
|
||||
const radius = bounds.width / 2;
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
return intersect.polygon(node, points, point);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { Node } from '../../types.js';
|
||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import rough from 'roughjs';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function hourglass<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||
const { labelStyles, nodeStyles } = styles2String(node);
|
||||
@@ -53,18 +52,6 @@ export async function hourglass<T extends SVGGraphicsElement>(parent: D3Selectio
|
||||
|
||||
// label.attr('transform', `translate(${-bbox.width / 2}, ${(h/2)})`); // To transform text below hourglass shape
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
const { width: w, height: h } = bounds;
|
||||
const points = [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: w, y: 0 },
|
||||
{ x: 0, y: h },
|
||||
{ x: w, y: h },
|
||||
];
|
||||
const res = intersect.polygon(bounds, points, point);
|
||||
return { x: res.x - 0.5, y: res.y - 0.5 };
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('Pill intersect', node, { points });
|
||||
const pos = intersect.polygon(node, points, point);
|
||||
|
||||
@@ -6,7 +6,6 @@ import intersect from '../intersect/index.js';
|
||||
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import { labelHelper, updateNodeBounds } from './util.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import type { Bounds, Point } from '../../../types.js';
|
||||
|
||||
export async function icon<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -98,12 +97,6 @@ export async function icon<T extends SVGGraphicsElement>(
|
||||
|
||||
updateNodeBounds(node, outerShape);
|
||||
|
||||
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||
// TODO: Implement intersect for this shape
|
||||
const radius = bounds.width / 2;
|
||||
return intersect.circle(bounds, radius, point);
|
||||
};
|
||||
|
||||
node.intersect = function (point) {
|
||||
log.info('iconSquare intersect', node, point);
|
||||
if (!node.label) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user