mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 06:19:24 +02:00
5432 WIP, parsing works
This commit is contained in:
@@ -83,349 +83,16 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="flex">
|
||||
<pre id="diagram" class="mermaid2">
|
||||
---
|
||||
title: hello2
|
||||
config:
|
||||
look: handDrawn
|
||||
layout: elk
|
||||
elk:
|
||||
<!-- nodePlacementStrategy: INTERACTIVE -->
|
||||
<!-- mergeEdges: true -->
|
||||
---
|
||||
stateDiagram-v2
|
||||
direction LR
|
||||
accTitle: An idealized Open Source supply-chain graph
|
||||
|
||||
%%
|
||||
state "🟦 Importer" as author_importer
|
||||
state "🟥 Supplier, Owner" as author_owner
|
||||
state "🟨🟥 Maintainer, Author\n🟨 Custodian" as author
|
||||
state "🟩 Distributor" as repository_distributor
|
||||
state "🟦 Importer" as language_importer
|
||||
state "🟦🟨 Packager" as language_packager
|
||||
state "🟦🟨 OSS Steward" as language_steward
|
||||
state "🟨 Curator" as language_curator
|
||||
state "🟩 Distributor" as language_distributor
|
||||
state "🟦 Contributor" as contributor
|
||||
state "🟦 Importer" as package_importer
|
||||
state "🟨 Patcher" as package_patcher
|
||||
state "🟨🟦 Builder\n🟨🟦 Packager\n🟨🟦 Containerizer" as package_packager
|
||||
state "🟨 Curator" as package_curator
|
||||
state "🟩 Distributor" as package_distributor
|
||||
state "🟦 Importer" as integrator_importer
|
||||
state "🟥 Supplier, Manufacturer, Owner" as integrator_owner
|
||||
state "🟦🟨🟥 Integrator, Developer" as integrator_developer
|
||||
state "🟩🟨 SBOM Redactor\n🟩 Publisher" as integrator_publisher
|
||||
state "🟦🟨 Builder" as integrator_builder
|
||||
state "🟨 Deployer" as deployer
|
||||
state "🟦 Vuln. Checker" as integrator_checker
|
||||
state "🟩🟨 SBOM Redactor" as redactor
|
||||
state "🟦 Consumer\n🟦 User" as consumer
|
||||
state "🟦 Auditor" as auditor_internal
|
||||
state "🟦 Auditor" as auditor_external
|
||||
|
||||
%%
|
||||
classDef createsSBOM stroke:red,stroke-width:3px;
|
||||
classDef updatesSBOM stroke:yellow,stroke-width:3px;
|
||||
classDef assemblesSBOM stroke:yellow,stroke-width:3px;
|
||||
classDef distributesSBOM stroke:green,stroke-width:3px;
|
||||
classDef verifiesSBOM stroke:#07f,stroke-width:3px;
|
||||
|
||||
%%
|
||||
class author_importer verifiesSBOM
|
||||
class author_owner createsSBOM
|
||||
class manufacturer_owner createsSBOM
|
||||
class author assemblesSBOM
|
||||
class package_importer verifiesSBOM
|
||||
class package_patcher updatesSBOM
|
||||
class package_packager assemblesSBOM
|
||||
class package_curator distributesSBOM
|
||||
class package_distributor distributesSBOM
|
||||
class language_importer verifiesSBOM
|
||||
class language_packager assemblesSBOM
|
||||
class language_steward updatesSBOM
|
||||
class language_curator distributesSBOM
|
||||
class language_distributor distributesSBOM
|
||||
class repository_distributor distributesSBOM
|
||||
class integrator_importer verifiesSBOM
|
||||
class integrator_owner createsSBOM
|
||||
class integrator_developer assemblesSBOM
|
||||
class integrator_publisher distributesSBOM
|
||||
class integrator_builder assemblesSBOM
|
||||
class integrator_checker verifiesSBOM
|
||||
class deployer assemblesSBOM
|
||||
class redactor distributesSBOM
|
||||
class auditor_internal verifiesSBOM
|
||||
class auditor_external verifiesSBOM
|
||||
|
||||
state "Maintainer Environment" as environment_maintainer {
|
||||
[*] --> author_importer
|
||||
[*] --> author
|
||||
author_importer --> author
|
||||
author_owner --> author
|
||||
author --> language_packager
|
||||
}
|
||||
|
||||
[*] --> environment_maintainer
|
||||
|
||||
state "Language Ecosystem" as ecosystem_lang {
|
||||
[*] --> language_importer
|
||||
[*] --> language_steward
|
||||
[*] --> language_curator
|
||||
[*] --> language_distributor
|
||||
language_importer --> language_distributor
|
||||
language_importer --> language_curator
|
||||
language_steward --> language_curator
|
||||
language_curator --> language_distributor
|
||||
}
|
||||
|
||||
language_packager --> ecosystem_lang
|
||||
ecosystem_lang --> ecosystem_lang
|
||||
|
||||
state "Public Collaboration Ecosystem" as ecosystem_repo {
|
||||
[*] --> repository_distributor
|
||||
}
|
||||
|
||||
author --> ecosystem_repo
|
||||
ecosystem_repo --> author
|
||||
|
||||
repository_distributor --> contributor
|
||||
contributor --> repository_distributor
|
||||
|
||||
state "Package Ecosystem" as ecosystem_package {
|
||||
[*] --> package_importer
|
||||
[*] --> package_packager
|
||||
[*] --> package_patcher
|
||||
package_importer --> package_patcher
|
||||
package_importer --> package_packager
|
||||
package_patcher --> package_packager
|
||||
package_packager --> package_curator
|
||||
package_packager --> package_distributor
|
||||
package_curator --> package_distributor
|
||||
}
|
||||
|
||||
repository_distributor --> ecosystem_package
|
||||
language_distributor --> ecosystem_package
|
||||
ecosystem_package --> ecosystem_package
|
||||
|
||||
state "Integrator Environment" as environment_integrator {
|
||||
[*] --> integrator_developer
|
||||
[*] --> integrator_importer
|
||||
integrator_importer --> integrator_developer
|
||||
integrator_owner --> integrator_developer
|
||||
integrator_builder --> integrator_publisher
|
||||
integrator_developer --> integrator_checker
|
||||
integrator_checker --> integrator_developer
|
||||
auditor_internal --> integrator_developer
|
||||
integrator_developer --> integrator_builder
|
||||
integrator_developer --> auditor_internal
|
||||
}
|
||||
|
||||
repository_distributor --> environment_integrator
|
||||
language_distributor --> environment_integrator
|
||||
package_distributor --> environment_integrator
|
||||
|
||||
state "Production Environment" as environment_prod {
|
||||
[*] --> deployer
|
||||
deployer --> redactor
|
||||
}
|
||||
|
||||
integrator_publisher --> [*]
|
||||
integrator_developer --> environment_prod
|
||||
integrator_builder --> environment_prod
|
||||
integrator_publisher --> environment_prod
|
||||
|
||||
deployer --> auditor_external
|
||||
deployer --> consumer
|
||||
redactor --> consumer
|
||||
|
||||
|
||||
|
||||
</pre>
|
||||
|
||||
<pre id="diagram" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
look: neo
|
||||
---
|
||||
flowchart RL
|
||||
subgraph " "
|
||||
A5@{ shape: manual-file, label: "a label"}
|
||||
B5@{ shape: manual-input, label: "a label" }
|
||||
C5@{ shape: mul-doc, label: "a label" }
|
||||
D5@{ shape: mul-proc, label: "a label" }
|
||||
E5@{ shape: paper-tape, label: "a label" }
|
||||
B3@{ shape: das, label: "a label" }
|
||||
C3@{ shape: disk, label: "a label" }
|
||||
D4@{ shape: lin-doc, label: "a label" }
|
||||
E4@{ shape: loop-limit, label: "a label" }
|
||||
end
|
||||
subgraph " "
|
||||
B6@{ shape: summary, label: "a label" }
|
||||
C6@{ shape: tag-we-rect, label: "a label" }
|
||||
D6@{ shape: tag-rect, label: "a label" }
|
||||
A2@{ shape: fork}
|
||||
B2@{ shape: hourglass }
|
||||
C2@{ shape: comment, label: "I am a comment" }
|
||||
D2@{ shape: bolt }
|
||||
D3@{ shape: disp, label: "a label" }
|
||||
C4@{ shape: junction, label: "a label" }
|
||||
A4@{ shape: extract, label: "a label"}
|
||||
B52[a fr]@{ shape: fr }
|
||||
end
|
||||
subgraph " "
|
||||
A1@{ shape: text, label: This is a textblock}
|
||||
B1@{ shape: card, label: "a label" }
|
||||
C1@{ shape: lined-proc, label: "a label" }
|
||||
D1@{ shape: start, label: "a label" }
|
||||
E1@{ shape: stop, label: "a label" }
|
||||
E2@{ shape: doc, label: "a label" }
|
||||
A6@{ shape: stored-data, label: "a label"}
|
||||
A3@{ shape: delay, label: "a label" }
|
||||
E3@{ shape: div-proc, label: "a label" }
|
||||
B4[a label]@{ shape: win-pane }
|
||||
end
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
---
|
||||
title: hello2
|
||||
config:
|
||||
look: handDrawn
|
||||
elk:
|
||||
<!-- nodePlacementStrategy: SIMPLE -->
|
||||
---
|
||||
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
|
||||
flowchart TD
|
||||
|
||||
A([Start]) -->|go to booking page| B("select
|
||||
ISBS booking no")
|
||||
A --> QQ{cancel booking}
|
||||
A --> RR{no show}
|
||||
A --> SS{change booking}
|
||||
B -->C(wmpay_request_payment.request_type= 'partial',
|
||||
wmpay_request_payment.status= 'paid',
|
||||
pos_booking.booking_status= ‘partial’ and 'full_deposit')
|
||||
style C text-align:left
|
||||
C -->D{manage booking}
|
||||
|
||||
D -->|cancel|E[ระบบแสดงช่องให้กรอกเหตุผล]
|
||||
E -->F{กดปุ่ม 'cancel' หรือไม่}
|
||||
F -->|Yes|G[ระบบบันทึกค่าใหม่
|
||||
และไม่สามารถแก้ไขข้อมูลได้]
|
||||
F -->|No|H[กดปุ่ม 'close']
|
||||
H -->|ระบบไม่เปลี่ยนแปลงข้อมูล|Z
|
||||
G -->|ระบบส่งข้อมูล|I[(POS_database)]
|
||||
I -->|pos_booking.booking_status='cancel'|Z([End])
|
||||
|
||||
|
||||
D -->|no show|J[ระบบแสดงช่องให้กรอกเหตุผล]
|
||||
J -->K{กดปุ่ม 'noshow' หรือไม่}
|
||||
K -->|Yes|L[ระบบสร้างใบเสร็จอัตโนมัติ
|
||||
Product_id: 439,
|
||||
ItemName: no show]
|
||||
style L text-align:left
|
||||
|
||||
K -->|No|O[กดปุ่ม 'close']
|
||||
O -->|ระบบไม่เปลี่ยนแปลงข้อมูล|Z
|
||||
L -->M[ระบบบันทึกค่าใหม่]
|
||||
M -->|ระบบส่งข้อมูล|N[(POS_database)]
|
||||
N -->|pos_booking.booking_status=‘noshow’|Z
|
||||
|
||||
|
||||
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
---
|
||||
title: hello2
|
||||
config:
|
||||
look: handDrawn
|
||||
layout: dagre
|
||||
elk:
|
||||
nodePlacementStrategy: BRANDES_KOEPF
|
||||
---
|
||||
flowchart
|
||||
A --> A
|
||||
subgraph A
|
||||
B --> B
|
||||
subgraph B
|
||||
C
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
look: handdrawn
|
||||
flowchart:
|
||||
htmlLabels: true
|
||||
---
|
||||
flowchart
|
||||
A[I am a long text, where do I go??? handdrawn - true]
|
||||
</pre
|
||||
>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<pre id="diagram" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
htmlLabels: false
|
||||
---
|
||||
flowchart
|
||||
A[I am a long text, where do I go??? classic - false]
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
flowchart:
|
||||
htmlLabels: true
|
||||
---
|
||||
flowchart
|
||||
A[I am a long text, where do I go??? classic - true]
|
||||
</pre
|
||||
>
|
||||
</div>
|
||||
<pre id="diagram2" class="mermaid2">
|
||||
flowchart LR
|
||||
id1(Start)-->id2(Stop)
|
||||
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
|
||||
|
||||
|
||||
</pre>
|
||||
|
||||
<pre id="diagram3" class="mermaid2">
|
||||
flowchart LR
|
||||
A:::foo & B:::bar --> C:::foobar
|
||||
classDef foo stroke:#f00
|
||||
classDef bar stroke:#0f0
|
||||
classDef ash color:red
|
||||
class C ash
|
||||
style C stroke:#00f, fill:black
|
||||
|
||||
</pre>
|
||||
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
stateDiagram
|
||||
A:::foo
|
||||
B:::bar --> C:::foobar
|
||||
classDef foo stroke:#f00
|
||||
classDef bar stroke:#0f0
|
||||
style C stroke:#00f, fill:black, color:white
|
||||
|
||||
</pre>
|
||||
|
||||
<pre id="diagram4" class="mermaid">
|
||||
flowchart TB
|
||||
A@{
|
||||
label: "aksljhf kasjdh"
|
||||
}
|
||||
kanban
|
||||
id1[Todo]
|
||||
id2[Create JISON]
|
||||
id3[Update DB function]
|
||||
id4[Create parsing tests]
|
||||
id5[define getData]
|
||||
id6[Create renderer]
|
||||
id7[In progress]
|
||||
id8[Design grammar]
|
||||
</pre>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
@@ -456,7 +123,7 @@ flowchart TB
|
||||
messageFontFamily: 'courier',
|
||||
},
|
||||
fontSize: 12,
|
||||
logLevel: 3,
|
||||
logLevel: 0,
|
||||
securityLevel: 'loose',
|
||||
});
|
||||
function callback() {
|
||||
|
@@ -19,6 +19,7 @@ import errorDiagram from '../diagrams/error/errorDiagram.js';
|
||||
import flowchartElk from '../diagrams/flowchart/elk/detector.js';
|
||||
import timeline from '../diagrams/timeline/detector.js';
|
||||
import mindmap from '../diagrams/mindmap/detector.js';
|
||||
import kanban from '../diagrams/kanban/detector.js';
|
||||
import sankey from '../diagrams/sankey/sankeyDetector.js';
|
||||
import { packet } from '../diagrams/packet/detector.js';
|
||||
import block from '../diagrams/block/blockDetector.js';
|
||||
@@ -70,6 +71,7 @@ export const addDiagrams = () => {
|
||||
// Ordering of detectors is important. The first one to return true will be used.
|
||||
registerLazyLoadedDiagrams(
|
||||
c4,
|
||||
kanban,
|
||||
classDiagramV2,
|
||||
classDiagram,
|
||||
er,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore: JISON doesn't support types
|
||||
import parser from './parser/mindmap.jison';
|
||||
import parser from './parser/kanban.jison';
|
||||
import db from './kanbanDb.js';
|
||||
import renderer from './kanbanRenderer.js';
|
||||
import styles from './styles.js';
|
||||
|
@@ -16,8 +16,9 @@ describe('when parsing a kanban ', function () {
|
||||
root`;
|
||||
|
||||
kanban.parse(str);
|
||||
// console.log('Time for checks', kanban.yy.getMindmap().descr);
|
||||
expect(kanban.yy.getMindmap().descr).toEqual('root');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections.length).toEqual(1);
|
||||
expect(sections[0].descr).toEqual('root');
|
||||
});
|
||||
it('KNBN-2 should handle a hierachial kanban definition', function () {
|
||||
const str = `kanban
|
||||
@@ -27,45 +28,59 @@ describe('when parsing a kanban ', function () {
|
||||
`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.descr).toEqual('root');
|
||||
expect(mm.children.length).toEqual(2);
|
||||
expect(mm.children[0].descr).toEqual('child1');
|
||||
expect(mm.children[1].descr).toEqual('child2');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections.length).toEqual(1);
|
||||
expect(sections[0].descr).toEqual('root');
|
||||
expect(sections[0].children.length).toEqual(2);
|
||||
expect(sections[0].children[0].descr).toEqual('child1');
|
||||
expect(sections[0].children[1].descr).toEqual('child2');
|
||||
});
|
||||
|
||||
/** CATCH case when a lower level comes later, should throw
|
||||
* a
|
||||
* b
|
||||
* c
|
||||
*/
|
||||
|
||||
it('3 should handle a simple root definition with a shape and without an id abc123', function () {
|
||||
const str = `kanban
|
||||
(root)`;
|
||||
|
||||
kanban.parse(str);
|
||||
// console.log('Time for checks', kanban.yy.getMindmap().descr);
|
||||
expect(kanban.yy.getMindmap().descr).toEqual('root');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].descr).toEqual('root');
|
||||
});
|
||||
|
||||
it('KNBN-4 should handle a deeper hierachial kanban definition', function () {
|
||||
it('KNBN-4 should not dsitinguis between deeper hierachial levels in thr kanban definition', function () {
|
||||
const str = `kanban
|
||||
root
|
||||
child1
|
||||
leaf1
|
||||
child2`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.descr).toEqual('root');
|
||||
expect(mm.children.length).toEqual(2);
|
||||
expect(mm.children[0].descr).toEqual('child1');
|
||||
expect(mm.children[0].children[0].descr).toEqual('leaf1');
|
||||
expect(mm.children[1].descr).toEqual('child2');
|
||||
});
|
||||
it('5 Multiple roots are illegal', function () {
|
||||
const str = `kanban
|
||||
root
|
||||
fakeRoot`;
|
||||
// less picky is better
|
||||
// expect(() => kanban.parse(str)).toThrow(
|
||||
// 'There can be only one root. No parent could be found for ("fakeRoot")'
|
||||
// );
|
||||
|
||||
expect(() => kanban.parse(str)).toThrow(
|
||||
'There can be only one root. No parent could be found for ("fakeRoot")'
|
||||
);
|
||||
kanban.parse(str);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections.length).toBe(1);
|
||||
expect(sections[0].children.length).toBe(3);
|
||||
});
|
||||
it('5 Multiple sections are ok', function () {
|
||||
const str = `kanban
|
||||
section1
|
||||
section2`;
|
||||
kanban.parse(str);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections.length).toBe(2);
|
||||
expect(sections[0].descr).toBe('section1');
|
||||
expect(sections[1].descr).toBe('section2');
|
||||
|
||||
// expect(() => kanban.parse(str)).toThrow(
|
||||
// 'There can be only one root. No parent could be found for ("fakeRoot")'
|
||||
// );
|
||||
});
|
||||
it('KNBN-6 real root in wrong place', function () {
|
||||
const str = `kanban
|
||||
@@ -73,7 +88,7 @@ describe('when parsing a kanban ', function () {
|
||||
fakeRoot
|
||||
realRootWrongPlace`;
|
||||
expect(() => kanban.parse(str)).toThrow(
|
||||
'There can be only one root. No parent could be found for ("fakeRoot")'
|
||||
'Items without section detected, found section ("fakeRoot")'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -84,10 +99,10 @@ describe('when parsing a kanban ', function () {
|
||||
`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.RECT);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('The root');
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.RECT);
|
||||
});
|
||||
it('KNBN-8 should handle an id and type for a node definition', function () {
|
||||
const str = `kanban
|
||||
@@ -95,10 +110,10 @@ describe('when parsing a kanban ', function () {
|
||||
theId(child1)`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.descr).toEqual('root');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const child = mm.children[0];
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].descr).toEqual('root');
|
||||
expect(sections[0].children.length).toEqual(1);
|
||||
const child = sections[0].children[0];
|
||||
expect(child.descr).toEqual('child1');
|
||||
expect(child.nodeId).toEqual('theId');
|
||||
expect(child.type).toEqual(kanban.yy.nodeType.ROUNDED_RECT);
|
||||
@@ -109,10 +124,10 @@ root
|
||||
theId(child1)`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.descr).toEqual('root');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const child = mm.children[0];
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].descr).toEqual('root');
|
||||
expect(sections[0].children.length).toEqual(1);
|
||||
const child = sections[0].children[0];
|
||||
expect(child.descr).toEqual('child1');
|
||||
expect(child.nodeId).toEqual('theId');
|
||||
expect(child.type).toEqual(kanban.yy.nodeType.ROUNDED_RECT);
|
||||
@@ -123,10 +138,10 @@ root
|
||||
`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.descr).toEqual('the root');
|
||||
expect(mm.children.length).toEqual(0);
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.CIRCLE);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].descr).toEqual('the root');
|
||||
expect(sections[0].children.length).toEqual(0);
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.CIRCLE);
|
||||
});
|
||||
|
||||
it('KNBN-11 multiple types (cloud)', function () {
|
||||
@@ -135,10 +150,10 @@ root
|
||||
`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.descr).toEqual('the root');
|
||||
expect(mm.children.length).toEqual(0);
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.CLOUD);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].descr).toEqual('the root');
|
||||
expect(sections[0].children.length).toEqual(0);
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.CLOUD);
|
||||
});
|
||||
it('KNBN-12 multiple types (bang)', function () {
|
||||
const str = `kanban
|
||||
@@ -146,10 +161,10 @@ root
|
||||
`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.descr).toEqual('the root');
|
||||
expect(mm.children.length).toEqual(0);
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.BANG);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].descr).toEqual('the root');
|
||||
expect(sections[0].children.length).toEqual(0);
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.BANG);
|
||||
});
|
||||
|
||||
it('KNBN-12-a multiple types (hexagon)', function () {
|
||||
@@ -158,10 +173,10 @@ root
|
||||
`;
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.HEXAGON);
|
||||
expect(mm.descr).toEqual('the root');
|
||||
expect(mm.children.length).toEqual(0);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.HEXAGON);
|
||||
expect(sections[0].descr).toEqual('the root');
|
||||
expect(sections[0].children.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
describe('decorations', function () {
|
||||
@@ -173,11 +188,11 @@ root
|
||||
// ::class1 class2
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(mm.icon).toEqual('bomb');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('The root');
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(sections[0].icon).toEqual('bomb');
|
||||
});
|
||||
it('KNBN-14 should be possible to set classes for the node', function () {
|
||||
const str = `kanban
|
||||
@@ -187,11 +202,11 @@ root
|
||||
// ::class1 class2
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(mm.class).toEqual('m-4 p-8');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('The root');
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(sections[0].class).toEqual('m-4 p-8');
|
||||
});
|
||||
it('KNBN-15 should be possible to set both classes and icon for the node', function () {
|
||||
const str = `kanban
|
||||
@@ -202,12 +217,12 @@ root
|
||||
// ::class1 class2
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(mm.class).toEqual('m-4 p-8');
|
||||
expect(mm.icon).toEqual('bomb');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('The root');
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(sections[0].class).toEqual('m-4 p-8');
|
||||
expect(sections[0].icon).toEqual('bomb');
|
||||
});
|
||||
it('KNBN-16 should be possible to set both classes and icon for the node', function () {
|
||||
const str = `kanban
|
||||
@@ -218,12 +233,12 @@ root
|
||||
// ::class1 class2
|
||||
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('The root');
|
||||
expect(mm.type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(mm.class).toEqual('m-4 p-8');
|
||||
expect(mm.icon).toEqual('bomb');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('The root');
|
||||
expect(sections[0].type).toEqual(kanban.yy.nodeType.RECT);
|
||||
expect(sections[0].class).toEqual('m-4 p-8');
|
||||
expect(sections[0].icon).toEqual('bomb');
|
||||
});
|
||||
});
|
||||
describe('descriptions', function () {
|
||||
@@ -232,9 +247,9 @@ root
|
||||
root["String containing []"]
|
||||
`;
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('String containing []');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('String containing []');
|
||||
});
|
||||
it('KNBN-18 should be possible to use node syntax in the descriptions in children', function () {
|
||||
const str = `kanban
|
||||
@@ -242,11 +257,11 @@ root
|
||||
child1["String containing ()"]
|
||||
`;
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('String containing []');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
expect(mm.children[0].descr).toEqual('String containing ()');
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('String containing []');
|
||||
expect(sections[0].children.length).toEqual(1);
|
||||
expect(sections[0].children[0].descr).toEqual('String containing ()');
|
||||
});
|
||||
it('KNBN-19 should be possible to have a child after a class assignment', function () {
|
||||
const str = `kanban
|
||||
@@ -256,16 +271,17 @@ root
|
||||
a(a)
|
||||
b[New Stuff]`;
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('Root');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('Root');
|
||||
expect(sections[0].children.length).toEqual(3);
|
||||
|
||||
const child = mm.children[0];
|
||||
expect(child.nodeId).toEqual('Child');
|
||||
expect(child.children[0].nodeId).toEqual('a');
|
||||
expect(child.children.length).toEqual(2);
|
||||
expect(child.children[1].nodeId).toEqual('b');
|
||||
const item1 = sections[0].children[0];
|
||||
const item2 = sections[0].children[1];
|
||||
const item3 = sections[0].children[2];
|
||||
expect(item1.nodeId).toEqual('Child');
|
||||
expect(item2.nodeId).toEqual('a');
|
||||
expect(item3.nodeId).toEqual('b');
|
||||
});
|
||||
});
|
||||
it('KNBN-20 should be possible to have meaningless empty rows in a kanban abc124', function () {
|
||||
@@ -276,16 +292,17 @@ root
|
||||
|
||||
b[New Stuff]`;
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('Root');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('Root');
|
||||
expect(sections[0].children.length).toEqual(3);
|
||||
|
||||
const child = mm.children[0];
|
||||
expect(child.nodeId).toEqual('Child');
|
||||
expect(child.children[0].nodeId).toEqual('a');
|
||||
expect(child.children.length).toEqual(2);
|
||||
expect(child.children[1].nodeId).toEqual('b');
|
||||
const item1 = sections[0].children[0];
|
||||
const item2 = sections[0].children[1];
|
||||
const item3 = sections[0].children[2];
|
||||
expect(item1.nodeId).toEqual('Child');
|
||||
expect(item2.nodeId).toEqual('a');
|
||||
expect(item3.nodeId).toEqual('b');
|
||||
});
|
||||
it('KNBN-21 should be possible to have comments in a kanban', function () {
|
||||
const str = `kanban
|
||||
@@ -296,16 +313,15 @@ root
|
||||
%% This is a comment
|
||||
b[New Stuff]`;
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('Root');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('Root');
|
||||
|
||||
const child = mm.children[0];
|
||||
const child = sections[0].children[0];
|
||||
expect(child.nodeId).toEqual('Child');
|
||||
expect(child.children[0].nodeId).toEqual('a');
|
||||
expect(child.children.length).toEqual(2);
|
||||
expect(child.children[1].nodeId).toEqual('b');
|
||||
expect(sections[0].children[1].nodeId).toEqual('a');
|
||||
expect(sections[0].children[2].nodeId).toEqual('b');
|
||||
expect(sections[0].children.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('KNBN-22 should be possible to have comments at the end of a line', function () {
|
||||
@@ -315,51 +331,52 @@ root
|
||||
a(a) %% This is a comment
|
||||
b[New Stuff]`;
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.descr).toEqual('Root');
|
||||
expect(mm.children.length).toEqual(1);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].descr).toEqual('Root');
|
||||
expect(sections[0].children.length).toEqual(3);
|
||||
|
||||
const child = mm.children[0];
|
||||
expect(child.nodeId).toEqual('Child');
|
||||
expect(child.children[0].nodeId).toEqual('a');
|
||||
expect(child.children.length).toEqual(2);
|
||||
expect(child.children[1].nodeId).toEqual('b');
|
||||
const child1 = sections[0].children[0];
|
||||
expect(child1.nodeId).toEqual('Child');
|
||||
const child2 = sections[0].children[1];
|
||||
expect(child2.nodeId).toEqual('a');
|
||||
const child3 = sections[0].children[2];
|
||||
expect(child3.nodeId).toEqual('b');
|
||||
});
|
||||
it('KNBN-23 Rows with only spaces should not interfere', function () {
|
||||
const str = 'kanban\nroot\n A\n \n\n B';
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.children.length).toEqual(2);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].children.length).toEqual(2);
|
||||
|
||||
const child = mm.children[0];
|
||||
const child = sections[0].children[0];
|
||||
expect(child.nodeId).toEqual('A');
|
||||
const child2 = mm.children[1];
|
||||
const child2 = sections[0].children[1];
|
||||
expect(child2.nodeId).toEqual('B');
|
||||
});
|
||||
it('KNBN-24 Handle rows above the kanban declarations', function () {
|
||||
const str = '\n \nkanban\nroot\n A\n \n\n B';
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.children.length).toEqual(2);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].children.length).toEqual(2);
|
||||
|
||||
const child = mm.children[0];
|
||||
const child = sections[0].children[0];
|
||||
expect(child.nodeId).toEqual('A');
|
||||
const child2 = mm.children[1];
|
||||
const child2 = sections[0].children[1];
|
||||
expect(child2.nodeId).toEqual('B');
|
||||
});
|
||||
it('KNBN-25 Handle rows above the kanban declarations, no space', function () {
|
||||
const str = '\n\n\nkanban\nroot\n A\n \n\n B';
|
||||
kanban.parse(str);
|
||||
const mm = kanban.yy.getMindmap();
|
||||
expect(mm.nodeId).toEqual('root');
|
||||
expect(mm.children.length).toEqual(2);
|
||||
const sections = kanban.yy.getSections();
|
||||
expect(sections[0].nodeId).toEqual('root');
|
||||
expect(sections[0].children.length).toEqual(2);
|
||||
|
||||
const child = mm.children[0];
|
||||
const child = sections[0].children[0];
|
||||
expect(child.nodeId).toEqual('A');
|
||||
const child2 = mm.children[1];
|
||||
const child2 = sections[0].children[1];
|
||||
expect(child2.nodeId).toEqual('B');
|
||||
});
|
||||
});
|
||||
|
@@ -2,35 +2,75 @@ import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import type { D3Element } from '../../types.js';
|
||||
import { sanitizeText } from '../../diagrams/common/common.js';
|
||||
import { log } from '../../logger.js';
|
||||
import type { MindmapNode } from './kanbanTypes.js';
|
||||
import type { KanbanNode } from './kanbanTypes.js';
|
||||
import type { Node, Edge } from '../../rendering-util/types.js';
|
||||
import defaultConfig from '../../defaultConfig.js';
|
||||
|
||||
let nodes: MindmapNode[] = [];
|
||||
let nodes: KanbanNode[] = [];
|
||||
let sections: KanbanNode[] = [];
|
||||
let cnt = 0;
|
||||
let elements: Record<number, D3Element> = {};
|
||||
|
||||
const clear = () => {
|
||||
nodes = [];
|
||||
sections = [];
|
||||
cnt = 0;
|
||||
elements = {};
|
||||
};
|
||||
|
||||
const getParent = function (level: number) {
|
||||
/*
|
||||
* if your level is the section level return null - then you do not belong to a level
|
||||
* otherwise return the current section
|
||||
*/
|
||||
const getSection = function (level: number) {
|
||||
if (nodes.length === 0) {
|
||||
// console.log('No nodes');
|
||||
return null;
|
||||
}
|
||||
const sectionLevel = nodes[0].level;
|
||||
let lastSection = null;
|
||||
for (let i = nodes.length - 1; i >= 0; i--) {
|
||||
if (nodes[i].level < level) {
|
||||
return nodes[i];
|
||||
if (nodes[i].level === sectionLevel && !lastSection) {
|
||||
lastSection = nodes[i];
|
||||
// console.log('lastSection found', lastSection);
|
||||
}
|
||||
// console.log('HERE', nodes[i].id, level, nodes[i].level, sectionLevel);
|
||||
if (nodes[i].level < sectionLevel) {
|
||||
throw new Error('Items without section detected, found section ("' + nodes[i].descr + '")');
|
||||
}
|
||||
}
|
||||
// No parent found
|
||||
return null;
|
||||
// if (!lastSection) {
|
||||
// // console.log('No last section');
|
||||
// }
|
||||
if (level === lastSection?.level) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// No found
|
||||
return lastSection;
|
||||
};
|
||||
|
||||
const getMindmap = () => {
|
||||
return nodes.length > 0 ? nodes[0] : null;
|
||||
const getSections = function () {
|
||||
console.log('sections', sections);
|
||||
return sections;
|
||||
};
|
||||
|
||||
const getData = function () {
|
||||
const edges = [] as Edge[];
|
||||
const nodes: Node[] = [];
|
||||
|
||||
// const id: string = sanitizeText(id, conf) || 'identifier' + cnt++;
|
||||
|
||||
// const node = {
|
||||
// id,
|
||||
// label: sanitizeText(descr, conf),
|
||||
// isGroup,
|
||||
// } satisfies Node;
|
||||
|
||||
return { nodes, edges, other: {}, config: getConfig() };
|
||||
};
|
||||
|
||||
const addNode = (level: number, id: string, descr: string, type: number) => {
|
||||
log.info('addNode', level, id, descr, type);
|
||||
// log.info('addNode level=', level, 'id=', id, 'descr=', descr, 'type=', type);
|
||||
const conf = getConfig();
|
||||
let padding: number = conf.mindmap?.padding ?? defaultConfig.mindmap.padding;
|
||||
switch (type) {
|
||||
@@ -49,24 +89,17 @@ const addNode = (level: number, id: string, descr: string, type: number) => {
|
||||
children: [],
|
||||
width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth,
|
||||
padding,
|
||||
} satisfies MindmapNode;
|
||||
|
||||
const parent = getParent(level);
|
||||
if (parent) {
|
||||
parent.children.push(node);
|
||||
} satisfies KanbanNode;
|
||||
const section = getSection(level);
|
||||
console.log('Node ', node.descr, ' section', section?.descr);
|
||||
if (section) {
|
||||
section.children.push(node);
|
||||
// Keep all nodes in the list
|
||||
nodes.push(node);
|
||||
} else {
|
||||
if (nodes.length === 0) {
|
||||
// First node, the root
|
||||
nodes.push(node);
|
||||
} else {
|
||||
// Syntax error ... there can only bee one root
|
||||
throw new Error(
|
||||
'There can be only one root. No parent could be found for ("' + node.descr + '")'
|
||||
);
|
||||
}
|
||||
sections.push(node);
|
||||
}
|
||||
nodes.push(node);
|
||||
};
|
||||
|
||||
const nodeType = {
|
||||
@@ -146,7 +179,8 @@ const getElementById = (id: number) => elements[id];
|
||||
const db = {
|
||||
clear,
|
||||
addNode,
|
||||
getMindmap,
|
||||
getSections,
|
||||
getData,
|
||||
nodeType,
|
||||
getType,
|
||||
setElementForId,
|
||||
|
@@ -9,29 +9,13 @@ import { log } from '../../logger.js';
|
||||
import type { D3Element } from '../../types.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||
import type { FilledMindMapNode, MindmapDB, MindmapNode } from './kanbanTypes.js';
|
||||
import type { FilledKanbanNode, KanbanDB, KanbanNode } from './kanbanTypes.js';
|
||||
import { drawNode, positionNode } from './svgDraw.js';
|
||||
import defaultConfig from '../../defaultConfig.js';
|
||||
|
||||
// Inject the layout algorithm into cytoscape
|
||||
cytoscape.use(coseBilkent);
|
||||
|
||||
async function drawNodes(
|
||||
db: MindmapDB,
|
||||
svg: D3Element,
|
||||
mindmap: FilledMindMapNode,
|
||||
section: number,
|
||||
conf: MermaidConfig
|
||||
) {
|
||||
await drawNode(db, svg, mindmap, section, conf);
|
||||
if (mindmap.children) {
|
||||
await Promise.all(
|
||||
mindmap.children.map((child, index) =>
|
||||
drawNodes(db, svg, child, section < 0 ? index : section, conf)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
async function drawSection(section: FilledKanbanNode, svg: D3Element, conf: MermaidConfig) {}
|
||||
|
||||
declare module 'cytoscape' {
|
||||
interface EdgeSingular {
|
||||
@@ -66,7 +50,7 @@ function drawEdges(edgesEl: D3Element, cy: cytoscape.Core) {
|
||||
});
|
||||
}
|
||||
|
||||
function addNodes(mindmap: MindmapNode, cy: cytoscape.Core, conf: MermaidConfig, level: number) {
|
||||
function addNodes(mindmap: KanbanNode, cy: cytoscape.Core, conf: MermaidConfig, level: number) {
|
||||
cy.add({
|
||||
group: 'nodes',
|
||||
data: {
|
||||
@@ -101,7 +85,7 @@ function addNodes(mindmap: MindmapNode, cy: cytoscape.Core, conf: MermaidConfig,
|
||||
}
|
||||
}
|
||||
|
||||
function layoutMindmap(node: MindmapNode, conf: MermaidConfig): Promise<cytoscape.Core> {
|
||||
function layoutMindmap(node: KanbanNode, conf: MermaidConfig): Promise<cytoscape.Core> {
|
||||
return new Promise((resolve) => {
|
||||
// Add temporary render element
|
||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||
@@ -142,7 +126,7 @@ function layoutMindmap(node: MindmapNode, conf: MermaidConfig): Promise<cytoscap
|
||||
});
|
||||
}
|
||||
|
||||
function positionNodes(db: MindmapDB, cy: cytoscape.Core) {
|
||||
function positionNodes(db: KanbanDB, cy: cytoscape.Core) {
|
||||
cy.nodes().map((node, id) => {
|
||||
const data = node.data();
|
||||
data.x = node.position().x;
|
||||
@@ -161,9 +145,10 @@ function positionNodes(db: MindmapDB, cy: cytoscape.Core) {
|
||||
export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
||||
log.debug('Rendering mindmap diagram\n' + text);
|
||||
|
||||
const db = diagObj.db as MindmapDB;
|
||||
const mm = db.getMindmap();
|
||||
if (!mm) {
|
||||
const db = diagObj.db as KanbanDB;
|
||||
const sections = db.getSections();
|
||||
// const sections = db.getData();
|
||||
if (!sections) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -176,14 +161,14 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
|
||||
// this gives us the size of the nodes and we can set the positions later
|
||||
|
||||
const edgesElem = svg.append('g');
|
||||
edgesElem.attr('class', 'mindmap-edges');
|
||||
edgesElem.attr('class', 'sections');
|
||||
const nodesElem = svg.append('g');
|
||||
nodesElem.attr('class', 'mindmap-nodes');
|
||||
await drawNodes(db, nodesElem, mm as FilledMindMapNode, -1, conf);
|
||||
nodesElem.attr('class', 'items');
|
||||
await drawSections(db, nodesElem, sections as FilledKanbanNode, -1, conf);
|
||||
|
||||
// Next step is to layout the mindmap, giving each node a position
|
||||
|
||||
const cy = await layoutMindmap(mm, conf);
|
||||
const cy = await layoutMindmap(sections, conf);
|
||||
|
||||
// After this we can draw, first the edges and the then nodes with the correct position
|
||||
drawEdges(edgesElem, cy);
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import type { RequiredDeep } from 'type-fest';
|
||||
import type mindmapDb from './kanbanDb.js';
|
||||
import type kanbanDb from './kanbanDb.js';
|
||||
|
||||
export interface MindmapNode {
|
||||
export interface KanbanNode {
|
||||
id: number;
|
||||
nodeId: string;
|
||||
level: number;
|
||||
descr: string;
|
||||
type: number;
|
||||
children: MindmapNode[];
|
||||
children: KanbanNode[];
|
||||
width: number;
|
||||
padding: number;
|
||||
section?: number;
|
||||
@@ -18,5 +18,5 @@ export interface MindmapNode {
|
||||
y?: number;
|
||||
}
|
||||
|
||||
export type FilledMindMapNode = RequiredDeep<MindmapNode>;
|
||||
export type MindmapDB = typeof mindmapDb;
|
||||
export type FilledKanbanNode = RequiredDeep<KanbanNode>;
|
||||
export type KanbanDB = typeof kanbanDb;
|
||||
|
@@ -20,7 +20,7 @@
|
||||
|
||||
\s*\%\%.* {yy.getLogger().trace('Found comment',yytext); return 'SPACELINE';}
|
||||
// \%\%[^\n]*\n /* skip comments */
|
||||
"kanban" return 'MINDMAP';
|
||||
"kanban" return 'KANBAN';
|
||||
":::" { this.begin('CLASS'); }
|
||||
<CLASS>.+ { this.popState();return 'CLASS'; }
|
||||
<CLASS>\n { this.popState();}
|
||||
@@ -80,8 +80,8 @@ spaceLines
|
||||
;
|
||||
|
||||
mindMap
|
||||
: MINDMAP document { return yy; }
|
||||
| MINDMAP NL document { return yy; }
|
||||
: KANBAN document { return yy; }
|
||||
| KANBAN NL document { return yy; }
|
||||
;
|
||||
|
||||
stop
|
||||
|
105
packages/mermaid/src/diagrams/kanban/samples.md
Normal file
105
packages/mermaid/src/diagrams/kanban/samples.md
Normal file
@@ -0,0 +1,105 @@
|
||||
```mermaid
|
||||
kanban
|
||||
New
|
||||
Sometimes wrong Shape type is highlighted
|
||||
In progress
|
||||
|
||||
|
||||
````
|
||||
|
||||
|
||||
|
||||
```mermaid
|
||||
kanban
|
||||
Todo
|
||||
Create JISON
|
||||
Update DB function
|
||||
Create parsing tests
|
||||
define getData
|
||||
Create renderer
|
||||
In progress
|
||||
Design grammar
|
||||
|
||||
````
|
||||
|
||||
Adding ID
|
||||
```mermaid
|
||||
kanban
|
||||
id1[Todo]
|
||||
id2[Create JISON]
|
||||
id3[Update DB function]
|
||||
id4[Create parsing tests]
|
||||
id5[define getData]
|
||||
id6[Create renderer]
|
||||
id7[In progress]
|
||||
id8[Design grammar]
|
||||
|
||||
````
|
||||
|
||||
Background color for section
|
||||
```mermaid
|
||||
kanban
|
||||
id1[Todo]
|
||||
id2[Create JISON]
|
||||
id3[Update DB function]
|
||||
id4[Create parsing tests]
|
||||
id5[define getData]
|
||||
id6[Create renderer]
|
||||
id7[In progress]
|
||||
id8[Design grammar]
|
||||
|
||||
style n2 stroke:#AA00FF,fill:#E1BEE7
|
||||
````
|
||||
|
||||
Background color for section
|
||||
```mermaid
|
||||
kanban
|
||||
id1[Todo]
|
||||
id2[Create JISON]
|
||||
id3[Update DB function]
|
||||
id4[Create parsing tests]
|
||||
id5[define getData]
|
||||
id6[Create renderer]
|
||||
id7[In progress]
|
||||
id8[Design grammar]
|
||||
|
||||
id2@{
|
||||
assigned: knsv
|
||||
icon: heart
|
||||
priority: high
|
||||
descr: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||
}
|
||||
style n1 stroke:#AA00FF,fill:#E1BEE7
|
||||
````
|
||||
|
||||
|
||||
Background color for section
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
kanban:
|
||||
showIds: true
|
||||
fields: [[title],[description][id, assigned]]
|
||||
---
|
||||
kanban
|
||||
id1[Todo]
|
||||
id2[Create JISON]
|
||||
id3[Update DB function]
|
||||
id4[Create parsing tests]
|
||||
id5[define getData]
|
||||
id6[Create renderer]
|
||||
id7[In progress]
|
||||
id8[Design grammar]
|
||||
|
||||
id2@{
|
||||
assigned: knsv
|
||||
icon: heart
|
||||
priority: high
|
||||
descr: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||
}
|
||||
style n1 stroke:#AA00FF,fill:#E1BEE7
|
||||
````
|
||||
|
||||
|
||||
priority - dedicated
|
||||
link - dedicated
|
@@ -1,5 +1,5 @@
|
||||
import { createText } from '../../rendering-util/createText.js';
|
||||
import type { FilledMindMapNode, MindmapDB } from './kanbanTypes.js';
|
||||
import type { FilledKanbanNode, KanbanDB } from './kanbanTypes.js';
|
||||
import type { Point, D3Element } from '../../types.js';
|
||||
import { parseFontSize } from '../../utils.js';
|
||||
import type { MermaidConfig } from '../../config.type.js';
|
||||
@@ -7,9 +7,9 @@ import type { MermaidConfig } from '../../config.type.js';
|
||||
const MAX_SECTIONS = 12;
|
||||
|
||||
type ShapeFunction = (
|
||||
db: MindmapDB,
|
||||
db: KanbanDB,
|
||||
elem: D3Element,
|
||||
node: FilledMindMapNode,
|
||||
node: FilledKanbanNode,
|
||||
section?: number
|
||||
) => void;
|
||||
|
||||
@@ -120,7 +120,7 @@ function insertPolygonShape(
|
||||
w: number,
|
||||
h: number,
|
||||
points: Point[],
|
||||
node: FilledMindMapNode
|
||||
node: FilledKanbanNode
|
||||
) {
|
||||
return parent
|
||||
.insert('polygon', ':first-child')
|
||||
@@ -136,9 +136,9 @@ function insertPolygonShape(
|
||||
}
|
||||
|
||||
const hexagonBkg: ShapeFunction = function (
|
||||
_db: MindmapDB,
|
||||
_db: KanbanDB,
|
||||
elem: D3Element,
|
||||
node: FilledMindMapNode
|
||||
node: FilledKanbanNode
|
||||
) {
|
||||
const h = node.height;
|
||||
const f = 4;
|
||||
@@ -175,9 +175,9 @@ const roundedRectBkg: ShapeFunction = function (db, elem, node) {
|
||||
* @returns The height nodes dom element
|
||||
*/
|
||||
export const drawNode = async function (
|
||||
db: MindmapDB,
|
||||
db: KanbanDB,
|
||||
elem: D3Element,
|
||||
node: FilledMindMapNode,
|
||||
node: FilledKanbanNode,
|
||||
fullSection: number,
|
||||
conf: MermaidConfig
|
||||
): Promise<number> {
|
||||
@@ -298,7 +298,7 @@ export const drawNode = async function (
|
||||
return node.height;
|
||||
};
|
||||
|
||||
export const positionNode = function (db: MindmapDB, node: FilledMindMapNode) {
|
||||
export const positionNode = function (db: KanbanDB, node: FilledKanbanNode) {
|
||||
const nodeElem = db.getElementById(node.id);
|
||||
|
||||
const x = node.x || 0;
|
||||
|
@@ -280,6 +280,127 @@ const roundedWithTitle = async (parent, node) => {
|
||||
|
||||
return { cluster: shapeSvg, labelBBox: bbox };
|
||||
};
|
||||
const kanbanSection = async (parent, node) => {
|
||||
const siteConfig = getConfig();
|
||||
|
||||
const { themeVariables, handDrawnSeed } = siteConfig;
|
||||
const { altBackground, compositeBackground, compositeTitleBackground, nodeBorder } =
|
||||
themeVariables;
|
||||
|
||||
// Add outer g element
|
||||
const shapeSvg = parent
|
||||
.insert('g')
|
||||
.attr('class', node.cssClasses)
|
||||
.attr('id', node.id)
|
||||
.attr('data-id', node.id)
|
||||
.attr('data-look', node.look);
|
||||
|
||||
// add the rect
|
||||
const outerRectG = shapeSvg.insert('g', ':first-child');
|
||||
|
||||
// Create the label and insert it after the rect
|
||||
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
||||
let innerRect = shapeSvg.append('rect');
|
||||
|
||||
const text = label
|
||||
.node()
|
||||
.appendChild(await createLabel(node.label, node.labelStyle, undefined, true));
|
||||
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
|
||||
if (evaluate(siteConfig.flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
bbox = div.getBoundingClientRect();
|
||||
dv.attr('width', bbox.width);
|
||||
dv.attr('height', bbox.height);
|
||||
}
|
||||
|
||||
// Rounded With Title
|
||||
const padding = 0 * node.padding;
|
||||
const halfPadding = padding / 2;
|
||||
|
||||
const width =
|
||||
(node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width) + padding;
|
||||
if (node.width <= bbox.width + node.padding) {
|
||||
node.diff = (width - node.width) / 2 - node.padding;
|
||||
} else {
|
||||
node.diff = -node.padding;
|
||||
}
|
||||
|
||||
const height = node.height + padding;
|
||||
// const height = node.height + padding;
|
||||
const innerHeight = node.height + padding - bbox.height - 6;
|
||||
const x = node.x - width / 2;
|
||||
const y = node.y - height / 2;
|
||||
node.width = width;
|
||||
const innerY = node.y - node.height / 2 - halfPadding + bbox.height + 2;
|
||||
|
||||
// add the rect
|
||||
let rect;
|
||||
if (node.look === 'handDrawn') {
|
||||
const isAlt = node.cssClasses.includes('statediagram-cluster-alt');
|
||||
const rc = rough.svg(shapeSvg);
|
||||
const roughOuterNode =
|
||||
node.rx || node.ry
|
||||
? rc.path(createRoundedRectPathD(x, y, width, height, 10), {
|
||||
roughness: 0.7,
|
||||
fill: compositeTitleBackground,
|
||||
fillStyle: 'solid',
|
||||
stroke: nodeBorder,
|
||||
seed: handDrawnSeed,
|
||||
})
|
||||
: rc.rectangle(x, y, width, height, { seed: handDrawnSeed });
|
||||
|
||||
rect = shapeSvg.insert(() => roughOuterNode, ':first-child');
|
||||
const roughInnerNode = rc.rectangle(x, innerY, width, innerHeight, {
|
||||
fill: isAlt ? altBackground : compositeBackground,
|
||||
fillStyle: isAlt ? 'hachure' : 'solid',
|
||||
stroke: nodeBorder,
|
||||
seed: handDrawnSeed,
|
||||
});
|
||||
|
||||
rect = shapeSvg.insert(() => roughOuterNode, ':first-child');
|
||||
innerRect = shapeSvg.insert(() => roughInnerNode);
|
||||
} else {
|
||||
rect = outerRectG.insert('rect', ':first-child');
|
||||
const outerRectClass = 'outer';
|
||||
|
||||
// center the rect around its coordinate
|
||||
rect
|
||||
.attr('class', outerRectClass)
|
||||
.attr('x', x)
|
||||
.attr('y', y)
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.attr('data-look', node.look);
|
||||
innerRect
|
||||
.attr('class', 'inner')
|
||||
.attr('x', x)
|
||||
.attr('y', innerY)
|
||||
.attr('width', width)
|
||||
.attr('height', innerHeight);
|
||||
}
|
||||
|
||||
label.attr(
|
||||
'transform',
|
||||
`translate(${node.x - bbox.width / 2}, ${y + 1 - (evaluate(siteConfig.flowchart.htmlLabels) ? 0 : 3)})`
|
||||
);
|
||||
|
||||
const rectBox = rect.node().getBBox();
|
||||
node.height = rectBox.height;
|
||||
node.offsetX = 0;
|
||||
// Used by layout engine to position subgraph in parent
|
||||
node.offsetY = bbox.height - node.padding / 2;
|
||||
node.labelBBox = bbox;
|
||||
|
||||
node.intersect = function (point) {
|
||||
return intersectRect(node, point);
|
||||
};
|
||||
|
||||
return { cluster: shapeSvg, labelBBox: bbox };
|
||||
};
|
||||
const divider = (parent, node) => {
|
||||
const siteConfig = getConfig();
|
||||
|
||||
@@ -355,6 +476,7 @@ const shapes = {
|
||||
roundedWithTitle,
|
||||
noteGroup,
|
||||
divider,
|
||||
kanbanSection,
|
||||
};
|
||||
|
||||
let clusterElems = new Map();
|
||||
|
Reference in New Issue
Block a user