mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-06 20:44:12 +01:00
Rendering, tmp commit before refactoring
This commit is contained in:
@@ -24,6 +24,9 @@
|
|||||||
h1 {
|
h1 {
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
|
.mermaid {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
.mermaid2 {
|
.mermaid2 {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -59,16 +62,17 @@
|
|||||||
<body>
|
<body>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid">
|
||||||
block-beta
|
block-beta
|
||||||
id</pre
|
id1("Wide 1")
|
||||||
>
|
id2("2")
|
||||||
|
id3("3")
|
||||||
|
id4("A final one")
|
||||||
|
|
||||||
|
</pre>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart RL
|
flowchart RL
|
||||||
subgraph "`one`"
|
id
|
||||||
a1 -- l1 --> a2
|
|
||||||
a1 -- l2 --> a2
|
|
||||||
end
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart RL
|
flowchart RL
|
||||||
subgraph "`one`"
|
subgraph "`one`"
|
||||||
a1 -- l1 --> a2
|
a1 -- l1 --> a2
|
||||||
@@ -93,11 +97,11 @@ flowchart LR
|
|||||||
way`"]
|
way`"]
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid2">
|
||||||
classDiagram-v2
|
classDiagram-v2
|
||||||
note "I love this diagram!\nDo you love it?"
|
note "I love this diagram!\nDo you love it?"
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid2">
|
||||||
stateDiagram-v2
|
stateDiagram-v2
|
||||||
State1: The state with a note with minus - and plus + in it
|
State1: The state with a note with minus - and plus + in it
|
||||||
note left of State1
|
note left of State1
|
||||||
@@ -142,7 +146,7 @@ mindmap
|
|||||||
शान्तिः سلام 和平 `"]
|
शान्तिः سلام 和平 `"]
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid2">
|
||||||
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
|
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
|
||||||
flowchart TB
|
flowchart TB
|
||||||
%% I could not figure out how to use double quotes in labels in Mermaid
|
%% I could not figure out how to use double quotes in labels in Mermaid
|
||||||
|
|||||||
@@ -106,6 +106,16 @@ const getBlocks: IGetBlocks = () => {
|
|||||||
log.info('Block in test', blocks, blocks[0].id);
|
log.info('Block in test', blocks, blocks[0].id);
|
||||||
return blocks || [];
|
return blocks || [];
|
||||||
};
|
};
|
||||||
|
type IGetBlock = (id: string) => Block | undefined;
|
||||||
|
const getBlock: IGetBlock = (id: string) => {
|
||||||
|
log.info('Block in test', blocks, blocks[0].id);
|
||||||
|
return blockDatabase[id];
|
||||||
|
};
|
||||||
|
type ISetBlock = (block: Block) => void;
|
||||||
|
const setBlock: ISetBlock = (block: Block) => {
|
||||||
|
log.info('Block in test', blocks, blocks[0].id);
|
||||||
|
blockDatabase[block.id] = block;
|
||||||
|
};
|
||||||
|
|
||||||
type IGetLinks = () => Link[];
|
type IGetLinks = () => Link[];
|
||||||
const getLinks: IGetLinks = () => links;
|
const getLinks: IGetLinks = () => links;
|
||||||
@@ -119,6 +129,8 @@ export interface BlockDB extends DiagramDB {
|
|||||||
addLink: IAddLink;
|
addLink: IAddLink;
|
||||||
getLogger: IGetLogger;
|
getLogger: IGetLogger;
|
||||||
getBlocks: IGetBlocks;
|
getBlocks: IGetBlocks;
|
||||||
|
getBlock: IGetBlock;
|
||||||
|
setBlock: ISetBlock;
|
||||||
getLinks: IGetLinks;
|
getLinks: IGetLinks;
|
||||||
getColumns: IGetColumns;
|
getColumns: IGetColumns;
|
||||||
typeStr2Type: ITypeStr2Type;
|
typeStr2Type: ITypeStr2Type;
|
||||||
@@ -134,6 +146,8 @@ const db: BlockDB = {
|
|||||||
getBlocks,
|
getBlocks,
|
||||||
getLinks,
|
getLinks,
|
||||||
setHierarchy,
|
setHierarchy,
|
||||||
|
getBlock,
|
||||||
|
setBlock,
|
||||||
// getAccTitle,
|
// getAccTitle,
|
||||||
// setAccTitle,
|
// setAccTitle,
|
||||||
// getAccDescription,
|
// getAccDescription,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { DiagramDefinition } from '../../diagram-api/types.js';
|
|||||||
// @ts-ignore: jison doesn't export types
|
// @ts-ignore: jison doesn't export types
|
||||||
import parser from './parser/block.jison';
|
import parser from './parser/block.jison';
|
||||||
import db from './blockDB.js';
|
import db from './blockDB.js';
|
||||||
|
import flowStyles from './styles.js';
|
||||||
import renderer from './blockRenderer.js';
|
import renderer from './blockRenderer.js';
|
||||||
|
|
||||||
// TODO: do we need this?
|
// TODO: do we need this?
|
||||||
@@ -14,4 +15,5 @@ export const diagram: DiagramDefinition = {
|
|||||||
parser,
|
parser,
|
||||||
db,
|
db,
|
||||||
renderer,
|
renderer,
|
||||||
|
styles: flowStyles,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,30 @@
|
|||||||
import { Diagram } from '../../Diagram.js';
|
import { Diagram } from '../../Diagram.js';
|
||||||
import * as configApi from '../../config.js';
|
import * as configApi from '../../config.js';
|
||||||
|
import { calculateBlockSizes } from './renderHelpers.js';
|
||||||
|
import { layout } from './layout.js';
|
||||||
|
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||||
import {
|
import {
|
||||||
select as d3select,
|
select as d3select,
|
||||||
scaleOrdinal as d3scaleOrdinal,
|
scaleOrdinal as d3scaleOrdinal,
|
||||||
schemeTableau10 as d3schemeTableau10,
|
schemeTableau10 as d3schemeTableau10,
|
||||||
|
ContainerElement,
|
||||||
} from 'd3';
|
} from 'd3';
|
||||||
|
|
||||||
import { BlockDB, Block } from './blockDB.js';
|
import { BlockDB } from './blockDB.js';
|
||||||
|
import type { Block } from './blockTypes.js';
|
||||||
|
|
||||||
// import { diagram as BlockDiagram } from './blockDiagram.js';
|
// import { diagram as BlockDiagram } from './blockDiagram.js';
|
||||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||||
import { Uid } from '../../rendering-util/uid.js';
|
import { Uid } from '../../rendering-util/uid.js';
|
||||||
|
|
||||||
export const draw = function (text: string, id: string, _version: string, diagObj: Diagram): void {
|
export const draw = async function (
|
||||||
const { securityLevel } = configApi.getConfig();
|
text: string,
|
||||||
|
id: string,
|
||||||
|
_version: string,
|
||||||
|
diagObj: Diagram
|
||||||
|
): Promise<void> {
|
||||||
|
const { securityLevel, flowchart: conf } = configApi.getConfig();
|
||||||
|
const db = diagObj.db as BlockDB;
|
||||||
let sandboxElement: any;
|
let sandboxElement: any;
|
||||||
if (securityLevel === 'sandbox') {
|
if (securityLevel === 'sandbox') {
|
||||||
sandboxElement = d3select('#i' + id);
|
sandboxElement = d3select('#i' + id);
|
||||||
@@ -27,12 +37,23 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
|||||||
// @ts-ignore TODO root.select is not callable
|
// @ts-ignore TODO root.select is not callable
|
||||||
const svg = securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : d3select(`[id="${id}"]`);
|
const svg = securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : d3select(`[id="${id}"]`);
|
||||||
|
|
||||||
|
const bl = db.getBlocks();
|
||||||
|
|
||||||
|
const nodes = svg.insert('g').attr('class', 'block');
|
||||||
|
await calculateBlockSizes(nodes, bl, db);
|
||||||
|
const bounds = layout(db);
|
||||||
|
|
||||||
|
console.log('Here', bl);
|
||||||
|
|
||||||
// Establish svg dimensions and get width and height
|
// Establish svg dimensions and get width and height
|
||||||
//
|
//
|
||||||
const height = 400;
|
// const bounds = nodes.node().getBoundingClientRect();
|
||||||
const width = 600;
|
const height = bounds.height;
|
||||||
|
const width = bounds.width;
|
||||||
const useMaxWidth = false;
|
const useMaxWidth = false;
|
||||||
configureSvgSize(svg, height, width, useMaxWidth);
|
configureSvgSize(svg, height, width, useMaxWidth);
|
||||||
|
console.log('Here Bounds', bounds);
|
||||||
|
svg.attr('viewBox', `${bounds.x} ${bounds.y} ${bounds.width} ${bounds.height}`);
|
||||||
|
|
||||||
// Prepare data for construction based on diagObj.db
|
// Prepare data for construction based on diagObj.db
|
||||||
// This must be a mutable object with `nodes` and `links` properties:
|
// This must be a mutable object with `nodes` and `links` properties:
|
||||||
@@ -53,107 +74,92 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
|||||||
|
|
||||||
const blocks: LayedBlock[] = [
|
const blocks: LayedBlock[] = [
|
||||||
{
|
{
|
||||||
ID: "ApplicationLayer",
|
ID: 'ApplicationLayer',
|
||||||
label: "Application Layer",
|
label: 'Application Layer',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
ID: "UserInterface",
|
ID: 'UserInterface',
|
||||||
label: "User Interface (WPF, HTML5/CSS3, Swing)",
|
label: 'User Interface (WPF, HTML5/CSS3, Swing)',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 50,
|
y: 50,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "PresentationLayer",
|
ID: 'PresentationLayer',
|
||||||
label: "Presentation Layer",
|
label: 'Presentation Layer',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 50,
|
y: 50,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
ID: "Smack",
|
ID: 'Smack',
|
||||||
label: "J2SE Mobil App (Smack)"
|
label: 'J2SE Mobil App (Smack)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "JsJAC",
|
ID: 'JsJAC',
|
||||||
label: "Java Script Browser App (JsJAC)",
|
label: 'Java Script Browser App (JsJAC)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "babelim",
|
ID: 'babelim',
|
||||||
label: ".NET Windows App (Babel-im)",
|
label: '.NET Windows App (Babel-im)',
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "SessionLayer",
|
ID: 'SessionLayer',
|
||||||
label: "Session Layer",
|
label: 'Session Layer',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 100,
|
y: 100,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
ID: "XMPP",
|
ID: 'XMPP',
|
||||||
label: "XMPP Component"
|
label: 'XMPP Component',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
ID: "Authentication",
|
ID: 'Authentication',
|
||||||
label: "Authentication",
|
label: 'Authentication',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "Authorization",
|
ID: 'Authorization',
|
||||||
label: "Authorization",
|
label: 'Authorization',
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "LDAP",
|
ID: 'LDAP',
|
||||||
label: "LDAP, DB, POP",
|
label: 'LDAP, DB, POP',
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "NetworkLayer",
|
ID: 'NetworkLayer',
|
||||||
label: "Network Layer",
|
label: 'Network Layer',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 150,
|
y: 150,
|
||||||
children: [
|
children: [
|
||||||
{ ID: "HTTP", label: "HTTP" },
|
{ ID: 'HTTP', label: 'HTTP' },
|
||||||
{ ID: "SOCK", label: "SOCK" },
|
{ ID: 'SOCK', label: 'SOCK' },
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "DataLayer",
|
ID: 'DataLayer',
|
||||||
label: "Data Layer",
|
label: 'Data Layer',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 200,
|
y: 200,
|
||||||
children: [
|
children: [
|
||||||
{ ID: "XMPP", label: "XMPP" },
|
{ ID: 'XMPP', label: 'XMPP' },
|
||||||
{ ID: "BDB", label: "Business DB" },
|
{ ID: 'BDB', label: 'Business DB' },
|
||||||
{ ID: "AD", label: "Active Directory" },
|
{ ID: 'AD', label: 'Active Directory' },
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Get color scheme for the graph
|
// Get color scheme for the graph
|
||||||
const colorScheme = d3scaleOrdinal(d3schemeTableau10);
|
const colorScheme = d3scaleOrdinal(d3schemeTableau10);
|
||||||
|
|
||||||
svg
|
|
||||||
.append('g')
|
|
||||||
.attr('class', 'block')
|
|
||||||
.selectAll('.block')
|
|
||||||
.data(blocks)
|
|
||||||
.join('rect')
|
|
||||||
.attr('x', (d: any) => d.x || 0)
|
|
||||||
.attr('y', (d: any) => d.y || 0)
|
|
||||||
.attr('class', 'block')
|
|
||||||
.attr('stroke', 'black')
|
|
||||||
.attr('height', (d: any) => 50)
|
|
||||||
.attr('width', (d: any) => 100)
|
|
||||||
.attr('fill', (d: any) => colorScheme(d.ID));
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ export interface Block {
|
|||||||
parent?: Block;
|
parent?: Block;
|
||||||
type?: BlockType;
|
type?: BlockType;
|
||||||
children: Block[];
|
children: Block[];
|
||||||
|
size?: {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
};
|
||||||
|
node?: any;
|
||||||
columns?: number; // | TBlockColumnsDefaultValue;
|
columns?: number; // | TBlockColumnsDefaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
108
packages/mermaid/src/diagrams/block/layout.ts
Normal file
108
packages/mermaid/src/diagrams/block/layout.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { BlockDB } from './blockDB.js';
|
||||||
|
import type { Block } from './blockTypes.js';
|
||||||
|
|
||||||
|
function layoutBLock(block: Block, db: BlockDB) {
|
||||||
|
if (block.children) {
|
||||||
|
for (const child of block.children) {
|
||||||
|
layoutBLock(child, db);
|
||||||
|
}
|
||||||
|
// find max width of children
|
||||||
|
let maxWidth = 0;
|
||||||
|
let maxHeight = 0;
|
||||||
|
for (const child of block.children) {
|
||||||
|
const { width, height, x, y } = child.size || { width: 0, height: 0, x: 0, y: 0 };
|
||||||
|
if (width > maxWidth) {
|
||||||
|
maxWidth = width;
|
||||||
|
}
|
||||||
|
if (height > maxHeight) {
|
||||||
|
maxHeight = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set width of block to max width of children
|
||||||
|
for (const child of block.children) {
|
||||||
|
if (child.size) {
|
||||||
|
child.size.width = maxWidth;
|
||||||
|
child.size.height = maxHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position items
|
||||||
|
let x = 0;
|
||||||
|
let y = 0;
|
||||||
|
const padding = 10;
|
||||||
|
for (const child of block.children) {
|
||||||
|
if (child.size) {
|
||||||
|
child.size.x = x;
|
||||||
|
child.size.y = y;
|
||||||
|
x += maxWidth + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function positionBlock(block: Block, db: BlockDB) {
|
||||||
|
console.log('Here Positioning', block?.size?.node);
|
||||||
|
// const o = db.getBlock(block.id);
|
||||||
|
// const node;
|
||||||
|
if (block?.size?.node) {
|
||||||
|
const node = block?.size?.node;
|
||||||
|
const size = block?.size;
|
||||||
|
console.log('Here as well', node);
|
||||||
|
if (node) {
|
||||||
|
node.attr(
|
||||||
|
'transform',
|
||||||
|
'translate(' + (size.x - size.width / 2) + ', ' + (size.y - size.height / 2) + ')'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block.children) {
|
||||||
|
for (const child of block.children) {
|
||||||
|
positionBlock(child, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let minX = 0;
|
||||||
|
let minY = 0;
|
||||||
|
let maxX = 0;
|
||||||
|
let maxY = 0;
|
||||||
|
|
||||||
|
function findBounds(block: Block) {
|
||||||
|
if (block.size) {
|
||||||
|
const { x, y, width, height } = block.size;
|
||||||
|
console.log('Here', minX, minY, x, y, width, height);
|
||||||
|
if (x - width < minX) {
|
||||||
|
minX = x - width;
|
||||||
|
}
|
||||||
|
if (y - height < minY) {
|
||||||
|
minY = y - height;
|
||||||
|
}
|
||||||
|
if (x > maxX) {
|
||||||
|
maxX = x;
|
||||||
|
}
|
||||||
|
if (y > maxY) {
|
||||||
|
maxY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block.children) {
|
||||||
|
for (const child of block.children) {
|
||||||
|
findBounds(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function layout(db: BlockDB) {
|
||||||
|
const blocks = db.getBlocks();
|
||||||
|
const root = { id: 'root', type: 'composite', children: blocks } as Block;
|
||||||
|
layoutBLock(root, db);
|
||||||
|
positionBlock(root, db);
|
||||||
|
|
||||||
|
minX = 0;
|
||||||
|
minY = 0;
|
||||||
|
maxX = 0;
|
||||||
|
maxY = 0;
|
||||||
|
findBounds(root);
|
||||||
|
const height = maxY - minY;
|
||||||
|
const width = maxX - minX;
|
||||||
|
return { x: minX, y: minY, width, height };
|
||||||
|
}
|
||||||
270
packages/mermaid/src/diagrams/block/renderHelpers.ts
Normal file
270
packages/mermaid/src/diagrams/block/renderHelpers.ts
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
import { getStylesFromArray } from '../../utils.js';
|
||||||
|
import { insertNode } from '../../dagre-wrapper/nodes.js';
|
||||||
|
import { getConfig } from '../../config.js';
|
||||||
|
import { ContainerElement } from 'd3';
|
||||||
|
import type { Block } from './blockTypes.js';
|
||||||
|
import { BlockDB } from './blockDB.js';
|
||||||
|
|
||||||
|
function getNodeFromBlock(block: Block, db: BlockDB) {
|
||||||
|
const vertex = block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable for storing the classes for the vertex
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let classStr = 'default';
|
||||||
|
if ((vertex?.classes?.length || []) > 0) {
|
||||||
|
classStr = vertex.classes.join(' ');
|
||||||
|
}
|
||||||
|
classStr = classStr + ' flowchart-label';
|
||||||
|
|
||||||
|
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||||
|
let vertexNode;
|
||||||
|
const labelData = { width: 0, height: 0 };
|
||||||
|
|
||||||
|
let radious = 0;
|
||||||
|
let _shape = '';
|
||||||
|
let layoutOptions = {};
|
||||||
|
// Set the shape based parameters
|
||||||
|
switch (vertex.type) {
|
||||||
|
case 'round':
|
||||||
|
radious = 5;
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'square':
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'diamond':
|
||||||
|
_shape = 'question';
|
||||||
|
layoutOptions = {
|
||||||
|
portConstraints: 'FIXED_SIDE',
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'hexagon':
|
||||||
|
_shape = 'hexagon';
|
||||||
|
break;
|
||||||
|
case 'odd':
|
||||||
|
_shape = 'rect_left_inv_arrow';
|
||||||
|
break;
|
||||||
|
case 'lean_right':
|
||||||
|
_shape = 'lean_right';
|
||||||
|
break;
|
||||||
|
case 'lean_left':
|
||||||
|
_shape = 'lean_left';
|
||||||
|
break;
|
||||||
|
case 'trapezoid':
|
||||||
|
_shape = 'trapezoid';
|
||||||
|
break;
|
||||||
|
case 'inv_trapezoid':
|
||||||
|
_shape = 'inv_trapezoid';
|
||||||
|
break;
|
||||||
|
case 'odd_right':
|
||||||
|
_shape = 'rect_left_inv_arrow';
|
||||||
|
break;
|
||||||
|
case 'circle':
|
||||||
|
_shape = 'circle';
|
||||||
|
break;
|
||||||
|
case 'ellipse':
|
||||||
|
_shape = 'ellipse';
|
||||||
|
break;
|
||||||
|
case 'stadium':
|
||||||
|
_shape = 'stadium';
|
||||||
|
break;
|
||||||
|
case 'subroutine':
|
||||||
|
_shape = 'subroutine';
|
||||||
|
break;
|
||||||
|
case 'cylinder':
|
||||||
|
_shape = 'cylinder';
|
||||||
|
break;
|
||||||
|
case 'group':
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'doublecircle':
|
||||||
|
_shape = 'doublecircle';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_shape = 'rect';
|
||||||
|
}
|
||||||
|
|
||||||
|
// const styles = getStylesFromArray(vertex.styles);
|
||||||
|
const styles = getStylesFromArray([]);
|
||||||
|
|
||||||
|
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||||
|
const vertexText = vertex.label;
|
||||||
|
|
||||||
|
// Add the node
|
||||||
|
const node = {
|
||||||
|
labelStyle: styles.labelStyle,
|
||||||
|
shape: _shape,
|
||||||
|
labelText: vertexText,
|
||||||
|
// labelType: vertex.labelType,
|
||||||
|
rx: radious,
|
||||||
|
ry: radious,
|
||||||
|
class: classStr,
|
||||||
|
style: styles.style,
|
||||||
|
id: vertex.id,
|
||||||
|
// link: vertex.link,
|
||||||
|
// linkTarget: vertex.linkTarget,
|
||||||
|
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
|
||||||
|
// domId: diagObj.db.lookUpDomId(vertex.id),
|
||||||
|
// haveCallback: vertex.haveCallback,
|
||||||
|
// width: vertex.type === 'group' ? 500 : undefined,
|
||||||
|
// dir: vertex.dir,
|
||||||
|
type: vertex.type,
|
||||||
|
// props: vertex.props,
|
||||||
|
padding: getConfig()?.flowchart?.padding || 0,
|
||||||
|
};
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function calculateBlockSize(elem: any, block: any, db: any) {
|
||||||
|
console.log('Here befoire 3');
|
||||||
|
const node = getNodeFromBlock(block, db);
|
||||||
|
if (node.type === 'group') return;
|
||||||
|
|
||||||
|
// Add the element to the DOM to size it
|
||||||
|
const nodeEl = await insertNode(elem, node);
|
||||||
|
const boundingBox = nodeEl.node().getBBox();
|
||||||
|
const obj = db.getBlock(node.id);
|
||||||
|
console.log('Here el', nodeEl);
|
||||||
|
obj.size = { width: boundingBox.width, height: boundingBox.height, x: 0, y: 0, node: nodeEl };
|
||||||
|
db.setBlock(obj);
|
||||||
|
// nodeEl.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function calculateBlockSizes(elem: ContainerElement, blocks: Block[], db: BlockDB) {
|
||||||
|
console.log('Here before 2');
|
||||||
|
for (const block of blocks) {
|
||||||
|
await calculateBlockSize(elem, block, db);
|
||||||
|
if (block.children) {
|
||||||
|
await calculateBlockSizes(elem, block.children, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export async function insertBlockPositioned(elem: any, block: any, db: any) {
|
||||||
|
const vertex = block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable for storing the classes for the vertex
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let classStr = 'default';
|
||||||
|
if ((vertex?.classes?.length || []) > 0) {
|
||||||
|
classStr = vertex.classes.join(' ');
|
||||||
|
}
|
||||||
|
classStr = classStr + ' flowchart-label';
|
||||||
|
|
||||||
|
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||||
|
let vertexNode;
|
||||||
|
const labelData = { width: 0, height: 0 };
|
||||||
|
|
||||||
|
let radious = 0;
|
||||||
|
let _shape = '';
|
||||||
|
let layoutOptions = {};
|
||||||
|
// Set the shape based parameters
|
||||||
|
switch (vertex.type) {
|
||||||
|
case 'round':
|
||||||
|
radious = 5;
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'square':
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'diamond':
|
||||||
|
_shape = 'question';
|
||||||
|
layoutOptions = {
|
||||||
|
portConstraints: 'FIXED_SIDE',
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'hexagon':
|
||||||
|
_shape = 'hexagon';
|
||||||
|
break;
|
||||||
|
case 'odd':
|
||||||
|
_shape = 'rect_left_inv_arrow';
|
||||||
|
break;
|
||||||
|
case 'lean_right':
|
||||||
|
_shape = 'lean_right';
|
||||||
|
break;
|
||||||
|
case 'lean_left':
|
||||||
|
_shape = 'lean_left';
|
||||||
|
break;
|
||||||
|
case 'trapezoid':
|
||||||
|
_shape = 'trapezoid';
|
||||||
|
break;
|
||||||
|
case 'inv_trapezoid':
|
||||||
|
_shape = 'inv_trapezoid';
|
||||||
|
break;
|
||||||
|
case 'odd_right':
|
||||||
|
_shape = 'rect_left_inv_arrow';
|
||||||
|
break;
|
||||||
|
case 'circle':
|
||||||
|
_shape = 'circle';
|
||||||
|
break;
|
||||||
|
case 'ellipse':
|
||||||
|
_shape = 'ellipse';
|
||||||
|
break;
|
||||||
|
case 'stadium':
|
||||||
|
_shape = 'stadium';
|
||||||
|
break;
|
||||||
|
case 'subroutine':
|
||||||
|
_shape = 'subroutine';
|
||||||
|
break;
|
||||||
|
case 'cylinder':
|
||||||
|
_shape = 'cylinder';
|
||||||
|
break;
|
||||||
|
case 'group':
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'doublecircle':
|
||||||
|
_shape = 'doublecircle';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_shape = 'rect';
|
||||||
|
}
|
||||||
|
|
||||||
|
// const styles = getStylesFromArray(vertex.styles);
|
||||||
|
const styles = getStylesFromArray([]);
|
||||||
|
|
||||||
|
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||||
|
const vertexText = vertex.label;
|
||||||
|
|
||||||
|
// Add the node
|
||||||
|
const node = {
|
||||||
|
labelStyle: styles.labelStyle,
|
||||||
|
shape: _shape,
|
||||||
|
labelText: vertexText,
|
||||||
|
labelType: vertex.labelType,
|
||||||
|
rx: radious,
|
||||||
|
ry: radious,
|
||||||
|
class: classStr,
|
||||||
|
style: styles.style,
|
||||||
|
id: vertex.id,
|
||||||
|
link: vertex.link,
|
||||||
|
linkTarget: vertex.linkTarget,
|
||||||
|
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
|
||||||
|
// domId: diagObj.db.lookUpDomId(vertex.id),
|
||||||
|
haveCallback: vertex.haveCallback,
|
||||||
|
width: vertex.width,
|
||||||
|
height: vertex.height,
|
||||||
|
dir: vertex.dir,
|
||||||
|
type: vertex.type,
|
||||||
|
props: vertex.props,
|
||||||
|
padding: getConfig()?.flowchart?.padding || 0,
|
||||||
|
};
|
||||||
|
let boundingBox;
|
||||||
|
let nodeEl;
|
||||||
|
|
||||||
|
// Add the element to the DOM
|
||||||
|
if (node.type !== 'group') {
|
||||||
|
nodeEl = await insertNode(elem, node, vertex.dir);
|
||||||
|
// nodeEl.remove();
|
||||||
|
boundingBox = nodeEl.node().getBBox();
|
||||||
|
if (node.id) {
|
||||||
|
const obj = db.getBlock(node.id);
|
||||||
|
obj.size = { width: boundingBox.width, height: boundingBox.height, x: 0, y: 0, node: nodeEl };
|
||||||
|
db.setBlock(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
144
packages/mermaid/src/diagrams/block/styles.ts
Normal file
144
packages/mermaid/src/diagrams/block/styles.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// import khroma from 'khroma';
|
||||||
|
import * as khroma from 'khroma';
|
||||||
|
|
||||||
|
/** Returns the styles given options */
|
||||||
|
export interface FlowChartStyleOptions {
|
||||||
|
arrowheadColor: string;
|
||||||
|
border2: string;
|
||||||
|
clusterBkg: string;
|
||||||
|
clusterBorder: string;
|
||||||
|
edgeLabelBackground: string;
|
||||||
|
fontFamily: string;
|
||||||
|
lineColor: string;
|
||||||
|
mainBkg: string;
|
||||||
|
nodeBorder: string;
|
||||||
|
nodeTextColor: string;
|
||||||
|
tertiaryColor: string;
|
||||||
|
textColor: string;
|
||||||
|
titleColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fade = (color: string, opacity: number) => {
|
||||||
|
// @ts-ignore TODO: incorrect types from khroma
|
||||||
|
const channel = khroma.channel;
|
||||||
|
|
||||||
|
const r = channel(color, 'r');
|
||||||
|
const g = channel(color, 'g');
|
||||||
|
const b = channel(color, 'b');
|
||||||
|
|
||||||
|
// @ts-ignore incorrect types from khroma
|
||||||
|
return khroma.rgba(r, g, b, opacity);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = (options: FlowChartStyleOptions) =>
|
||||||
|
`.label {
|
||||||
|
font-family: ${options.fontFamily};
|
||||||
|
color: ${options.nodeTextColor || options.textColor};
|
||||||
|
}
|
||||||
|
.cluster-label text {
|
||||||
|
fill: ${options.titleColor};
|
||||||
|
}
|
||||||
|
.cluster-label span,p {
|
||||||
|
color: ${options.titleColor};
|
||||||
|
}
|
||||||
|
|
||||||
|
.label text,span,p {
|
||||||
|
fill: ${options.nodeTextColor || options.textColor};
|
||||||
|
color: ${options.nodeTextColor || options.textColor};
|
||||||
|
}
|
||||||
|
|
||||||
|
.node rect,
|
||||||
|
.node circle,
|
||||||
|
.node ellipse,
|
||||||
|
.node polygon,
|
||||||
|
.node path {
|
||||||
|
fill: ${options.mainBkg};
|
||||||
|
stroke: ${options.nodeBorder};
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
.flowchart-label text {
|
||||||
|
text-anchor: middle;
|
||||||
|
}
|
||||||
|
// .flowchart-label .text-outer-tspan {
|
||||||
|
// text-anchor: middle;
|
||||||
|
// }
|
||||||
|
// .flowchart-label .text-inner-tspan {
|
||||||
|
// text-anchor: start;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.node .label {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.node.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrowheadPath {
|
||||||
|
fill: ${options.arrowheadColor};
|
||||||
|
}
|
||||||
|
|
||||||
|
.edgePath .path {
|
||||||
|
stroke: ${options.lineColor};
|
||||||
|
stroke-width: 2.0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowchart-link {
|
||||||
|
stroke: ${options.lineColor};
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edgeLabel {
|
||||||
|
background-color: ${options.edgeLabelBackground};
|
||||||
|
rect {
|
||||||
|
opacity: 0.5;
|
||||||
|
background-color: ${options.edgeLabelBackground};
|
||||||
|
fill: ${options.edgeLabelBackground};
|
||||||
|
}
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For html labels only */
|
||||||
|
.labelBkg {
|
||||||
|
background-color: ${fade(options.edgeLabelBackground, 0.5)};
|
||||||
|
// background-color:
|
||||||
|
}
|
||||||
|
|
||||||
|
.cluster rect {
|
||||||
|
fill: ${options.clusterBkg};
|
||||||
|
stroke: ${options.clusterBorder};
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cluster text {
|
||||||
|
fill: ${options.titleColor};
|
||||||
|
}
|
||||||
|
|
||||||
|
.cluster span,p {
|
||||||
|
color: ${options.titleColor};
|
||||||
|
}
|
||||||
|
/* .cluster div {
|
||||||
|
color: ${options.titleColor};
|
||||||
|
} */
|
||||||
|
|
||||||
|
div.mermaidTooltip {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 200px;
|
||||||
|
padding: 2px;
|
||||||
|
font-family: ${options.fontFamily};
|
||||||
|
font-size: 12px;
|
||||||
|
background: ${options.tertiaryColor};
|
||||||
|
border: 1px solid ${options.border2};
|
||||||
|
border-radius: 2px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flowchartTitleText {
|
||||||
|
text-anchor: middle;
|
||||||
|
font-size: 18px;
|
||||||
|
fill: ${options.textColor};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default getStyles;
|
||||||
@@ -0,0 +1,400 @@
|
|||||||
|
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
||||||
|
import { select, curveLinear, selectAll } from 'd3';
|
||||||
|
import { swimlaneLayout } from './swimlane-layout.js';
|
||||||
|
import { insertNode } from '../../../dagre-wrapper/nodes.js';
|
||||||
|
import flowDb from '../flowDb.js';
|
||||||
|
import { getConfig } from '../../../config.js';
|
||||||
|
import { getStylesFromArray } from '../../../utils.js';
|
||||||
|
import setupGraph, { addEdges, addVertices } from './setup-graph.js';
|
||||||
|
import { render } from '../../../dagre-wrapper/index.js';
|
||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { setupGraphViewbox } from '../../../setupGraphViewbox.js';
|
||||||
|
import common, { evaluate } from '../../common/common.js';
|
||||||
|
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||||
|
import { insertEdge, positionEdgeLabel } from '../../../dagre-wrapper/edges.js';
|
||||||
|
import {
|
||||||
|
clear as clearGraphlib,
|
||||||
|
clusterDb,
|
||||||
|
adjustClustersAndEdges,
|
||||||
|
findNonClusterChild,
|
||||||
|
sortNodesByHierarchy,
|
||||||
|
} from '../../../dagre-wrapper/mermaid-graphlib.js';
|
||||||
|
|
||||||
|
const conf = {};
|
||||||
|
export const setConf = function (cnf) {
|
||||||
|
const keys = Object.keys(cnf);
|
||||||
|
for (const key of keys) {
|
||||||
|
conf[key] = cnf[key];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param element
|
||||||
|
* @param graph
|
||||||
|
* @param layout
|
||||||
|
* @param elem
|
||||||
|
* @param conf
|
||||||
|
*/
|
||||||
|
async function swimlaneRender(layout, vert, elem, g, id, conf) {
|
||||||
|
let max;
|
||||||
|
// draw nodes from layout.graph to element
|
||||||
|
const nodes = layout.graph.nodes();
|
||||||
|
|
||||||
|
// lanes are the swimlanes
|
||||||
|
const lanes = layout.lanes;
|
||||||
|
|
||||||
|
const nodesElements = elem.insert('g').attr('class', 'nodes');
|
||||||
|
// for each node, draw a rect, with a child text inside as label
|
||||||
|
for (const node of nodes) {
|
||||||
|
const nodeFromLayout = layout.graph.node(node);
|
||||||
|
const vertex = vert[node];
|
||||||
|
//Initialise the node
|
||||||
|
/**
|
||||||
|
* Variable for storing the classes for the vertex
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
let classStr = 'default';
|
||||||
|
if (vertex.classes.length > 0) {
|
||||||
|
classStr = vertex.classes.join(' ');
|
||||||
|
}
|
||||||
|
classStr = classStr + ' swimlane-label';
|
||||||
|
const styles = getStylesFromArray(vertex.styles);
|
||||||
|
|
||||||
|
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||||
|
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
|
||||||
|
|
||||||
|
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||||
|
let vertexNode;
|
||||||
|
log.info('vertex', vertex, vertex.labelType);
|
||||||
|
if (vertex.labelType === 'markdown') {
|
||||||
|
log.info('vertex', vertex, vertex.labelType);
|
||||||
|
} else {
|
||||||
|
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||||
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
|
const node = {
|
||||||
|
label: vertexText.replace(
|
||||||
|
/fa[blrs]?:fa-[\w-]+/g,
|
||||||
|
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
vertexNode = addHtmlLabel(elem, node).node();
|
||||||
|
vertexNode.parentNode.removeChild(vertexNode);
|
||||||
|
} else {
|
||||||
|
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
|
||||||
|
|
||||||
|
const rows = vertexText.split(common.lineBreakRegex);
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||||
|
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
||||||
|
tspan.setAttribute('dy', '1em');
|
||||||
|
tspan.setAttribute('x', '1');
|
||||||
|
tspan.textContent = row;
|
||||||
|
svgLabel.appendChild(tspan);
|
||||||
|
}
|
||||||
|
vertexNode = svgLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let radious = 0;
|
||||||
|
let _shape = '';
|
||||||
|
// Set the shape based parameters
|
||||||
|
switch (vertex.type) {
|
||||||
|
case 'round':
|
||||||
|
radious = 5;
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'square':
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'diamond':
|
||||||
|
_shape = 'question';
|
||||||
|
break;
|
||||||
|
case 'hexagon':
|
||||||
|
_shape = 'hexagon';
|
||||||
|
break;
|
||||||
|
case 'odd':
|
||||||
|
_shape = 'rect_left_inv_arrow';
|
||||||
|
break;
|
||||||
|
case 'lean_right':
|
||||||
|
_shape = 'lean_right';
|
||||||
|
break;
|
||||||
|
case 'lean_left':
|
||||||
|
_shape = 'lean_left';
|
||||||
|
break;
|
||||||
|
case 'trapezoid':
|
||||||
|
_shape = 'trapezoid';
|
||||||
|
break;
|
||||||
|
case 'inv_trapezoid':
|
||||||
|
_shape = 'inv_trapezoid';
|
||||||
|
break;
|
||||||
|
case 'odd_right':
|
||||||
|
_shape = 'rect_left_inv_arrow';
|
||||||
|
break;
|
||||||
|
case 'circle':
|
||||||
|
_shape = 'circle';
|
||||||
|
break;
|
||||||
|
case 'ellipse':
|
||||||
|
_shape = 'ellipse';
|
||||||
|
break;
|
||||||
|
case 'stadium':
|
||||||
|
_shape = 'stadium';
|
||||||
|
break;
|
||||||
|
case 'subroutine':
|
||||||
|
_shape = 'subroutine';
|
||||||
|
break;
|
||||||
|
case 'cylinder':
|
||||||
|
_shape = 'cylinder';
|
||||||
|
break;
|
||||||
|
case 'group':
|
||||||
|
_shape = 'rect';
|
||||||
|
break;
|
||||||
|
case 'doublecircle':
|
||||||
|
_shape = 'doublecircle';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_shape = 'rect';
|
||||||
|
}
|
||||||
|
// Add the node
|
||||||
|
let nodeObj = {
|
||||||
|
labelStyle: styles.labelStyle,
|
||||||
|
shape: _shape,
|
||||||
|
labelText: vertexText,
|
||||||
|
labelType: vertex.labelType,
|
||||||
|
rx: radious,
|
||||||
|
ry: radious,
|
||||||
|
class: classStr,
|
||||||
|
style: styles.style,
|
||||||
|
id: vertex.id,
|
||||||
|
link: vertex.link,
|
||||||
|
linkTarget: vertex.linkTarget,
|
||||||
|
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
|
||||||
|
// domId: diagObj.db.lookUpDomId(vertex.id),
|
||||||
|
haveCallback: vertex.haveCallback,
|
||||||
|
width: vertex.type === 'group' ? 500 : undefined,
|
||||||
|
dir: vertex.dir,
|
||||||
|
type: vertex.type,
|
||||||
|
props: vertex.props,
|
||||||
|
padding: getConfig().flowchart.padding,
|
||||||
|
x: nodeFromLayout.x,
|
||||||
|
y: nodeFromLayout.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
let boundingBox;
|
||||||
|
let nodeEl;
|
||||||
|
|
||||||
|
// Add the element to the DOM
|
||||||
|
|
||||||
|
nodeEl = await insertNode(nodesElements, nodeObj, vertex.dir);
|
||||||
|
boundingBox = nodeEl.node().getBBox();
|
||||||
|
nodeEl.attr('transform', `translate(${nodeObj.x}, ${nodeObj.y / 2})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the all the styles from classDef statements in the graph definition.
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* @param diagObj
|
||||||
|
* @returns {object} ClassDef styles
|
||||||
|
*/
|
||||||
|
export const getClasses = function (text, diagObj) {
|
||||||
|
log.info('Extracting classes');
|
||||||
|
diagObj.db.clear();
|
||||||
|
try {
|
||||||
|
// Parse the graph definition
|
||||||
|
diagObj.parse(text);
|
||||||
|
return diagObj.db.getClasses();
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a flowchart in the tag with id: id based on the graph definition in text.
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const draw = async function (text, id, _version, diagObj) {
|
||||||
|
log.info('Drawing flowchart');
|
||||||
|
diagObj.db.clear();
|
||||||
|
flowDb.setGen('gen-2');
|
||||||
|
// Parse the graph definition
|
||||||
|
diagObj.parser.parse(text);
|
||||||
|
|
||||||
|
const { securityLevel, flowchart: conf } = getConfig();
|
||||||
|
|
||||||
|
// Handle root and document for when rendering in sandbox mode
|
||||||
|
let sandboxElement;
|
||||||
|
if (securityLevel === 'sandbox') {
|
||||||
|
sandboxElement = select('#i' + id);
|
||||||
|
}
|
||||||
|
const root =
|
||||||
|
securityLevel === 'sandbox'
|
||||||
|
? select(sandboxElement.nodes()[0].contentDocument.body)
|
||||||
|
: select('body');
|
||||||
|
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
||||||
|
|
||||||
|
// create g as a graphlib graph using setupGraph from setup-graph.js
|
||||||
|
const g = setupGraph(diagObj, id, root, doc);
|
||||||
|
|
||||||
|
let subG;
|
||||||
|
const subGraphs = diagObj.db.getSubGraphs();
|
||||||
|
log.info('Subgraphs - ', subGraphs);
|
||||||
|
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
||||||
|
subG = subGraphs[i];
|
||||||
|
log.info('Subgraph - ', subG);
|
||||||
|
diagObj.db.addVertex(
|
||||||
|
subG.id,
|
||||||
|
{ text: subG.title, type: subG.labelType },
|
||||||
|
'group',
|
||||||
|
undefined,
|
||||||
|
subG.classes,
|
||||||
|
subG.dir
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the vertices/nodes and edges/links from the parsed graph definition
|
||||||
|
const vert = diagObj.db.getVertices();
|
||||||
|
|
||||||
|
const edges = diagObj.db.getEdges();
|
||||||
|
|
||||||
|
log.info('Edges', edges);
|
||||||
|
|
||||||
|
const svg = root.select('#' + id);
|
||||||
|
|
||||||
|
svg.append('g');
|
||||||
|
|
||||||
|
// Run the renderer. This is what draws the final graph.
|
||||||
|
// const element = root.select('#' + id + ' g');
|
||||||
|
console.log('diagObj', diagObj);
|
||||||
|
console.log('subGraphs', diagObj.db.getSubGraphs());
|
||||||
|
const layout = swimlaneLayout(g, diagObj);
|
||||||
|
console.log('custom layout', layout);
|
||||||
|
|
||||||
|
// draw lanes as vertical lines
|
||||||
|
const lanesElements = svg.insert('g').attr('class', 'lanes');
|
||||||
|
|
||||||
|
let laneCount = 0;
|
||||||
|
|
||||||
|
for (const lane of layout.lanes) {
|
||||||
|
laneCount++;
|
||||||
|
|
||||||
|
//draw lane header as rectangle with lane title centered in it
|
||||||
|
const laneHeader = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||||
|
|
||||||
|
// Set attributes for the rectangle
|
||||||
|
laneHeader.setAttribute('x', lane.x); // x-coordinate of the top-left corner
|
||||||
|
laneHeader.setAttribute('y', -50); // y-coordinate of the top-left corner
|
||||||
|
laneHeader.setAttribute('width', lane.width); // width of the rectangle
|
||||||
|
laneHeader.setAttribute('height', '50'); // height of the rectangle
|
||||||
|
if (laneCount % 2 == 0) {
|
||||||
|
//set light blue color for even lanes
|
||||||
|
laneHeader.setAttribute('fill', 'blue'); // fill color of the rectangle
|
||||||
|
} else {
|
||||||
|
//set white color odd lanes
|
||||||
|
laneHeader.setAttribute('fill', 'grey'); // fill color of the rectangle
|
||||||
|
}
|
||||||
|
|
||||||
|
laneHeader.setAttribute('stroke', 'black'); // color of the stroke/border
|
||||||
|
laneHeader.setAttribute('stroke-width', '2'); // width of the stroke/border
|
||||||
|
|
||||||
|
// Append the rectangle to the SVG element
|
||||||
|
lanesElements.node().appendChild(laneHeader);
|
||||||
|
|
||||||
|
//draw lane title
|
||||||
|
const laneTitle = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
|
||||||
|
// Set attributes for the rectangle
|
||||||
|
laneTitle.setAttribute('x', lane.x + lane.width / 2); // x-coordinate of the top-left corner
|
||||||
|
laneTitle.setAttribute('y', -50 + 50 / 2); // y-coordinate of the top-left corner
|
||||||
|
laneTitle.setAttribute('width', lane.width); // width of the rectangle
|
||||||
|
laneTitle.setAttribute('height', '50'); // height of the rectangle
|
||||||
|
laneTitle.setAttribute('fill', 'white'); // fill color of the rectangle
|
||||||
|
laneTitle.setAttribute('stroke-width', '1'); // width of the stroke/border
|
||||||
|
laneTitle.setAttribute('text-anchor', 'middle'); // width of the stroke/border
|
||||||
|
laneTitle.setAttribute('alignment-baseline', 'middle'); // width of the stroke/border
|
||||||
|
laneTitle.setAttribute('font-size', '20'); // width of the stroke/border
|
||||||
|
laneTitle.textContent = lane.title;
|
||||||
|
|
||||||
|
// Append the rectangle to the SVG element
|
||||||
|
lanesElements.node().appendChild(laneTitle);
|
||||||
|
|
||||||
|
//draw lane
|
||||||
|
|
||||||
|
// Create a <rect> element
|
||||||
|
const rectangle = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||||
|
|
||||||
|
// Set attributes for the rectangle
|
||||||
|
rectangle.setAttribute('x', lane.x); // x-coordinate of the top-left corner
|
||||||
|
rectangle.setAttribute('y', 0); // y-coordinate of the top-left corner
|
||||||
|
rectangle.setAttribute('width', lane.width); // width of the rectangle
|
||||||
|
rectangle.setAttribute('height', '500'); // height of the rectangle
|
||||||
|
|
||||||
|
if (laneCount % 2 == 0) {
|
||||||
|
//set light blue color for even lanes
|
||||||
|
rectangle.setAttribute('fill', 'lightblue'); // fill color of the rectangle
|
||||||
|
} else {
|
||||||
|
//set white color odd lanes
|
||||||
|
rectangle.setAttribute('fill', '#ffffff'); // fill color of the rectangle
|
||||||
|
}
|
||||||
|
|
||||||
|
rectangle.setAttribute('stroke', 'black'); // color of the stroke/border
|
||||||
|
rectangle.setAttribute('stroke-width', '2'); // width of the stroke/border
|
||||||
|
|
||||||
|
// Append the rectangle to the SVG element
|
||||||
|
lanesElements.node().appendChild(rectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append lanesElements to elem
|
||||||
|
svg.node().appendChild(lanesElements.node());
|
||||||
|
|
||||||
|
// add lane headers
|
||||||
|
const laneHeaders = svg.insert('g').attr('class', 'laneHeaders');
|
||||||
|
|
||||||
|
addEdges(edges, g, diagObj);
|
||||||
|
|
||||||
|
g.edges().forEach(function (e) {
|
||||||
|
const edge = g.edge(e);
|
||||||
|
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
|
||||||
|
const edgePaths = svg.insert('g').attr('class', 'edgePaths');
|
||||||
|
//create edge points based on start and end node
|
||||||
|
|
||||||
|
//get start node x, y coordinates
|
||||||
|
const sourceNode = layout.graph.node(e.v);
|
||||||
|
//get end node x, y coordinates
|
||||||
|
sourceNode.x = sourceNode.x;
|
||||||
|
sourceNode.y = sourceNode.y;
|
||||||
|
|
||||||
|
const targetNode = layout.graph.node(e.w);
|
||||||
|
targetNode.x = targetNode.x;
|
||||||
|
targetNode.y = targetNode.y;
|
||||||
|
|
||||||
|
edge.points = [];
|
||||||
|
edge.points.push({ x: sourceNode.x, y: sourceNode.y / 2 });
|
||||||
|
edge.points.push({ x: targetNode.x, y: targetNode.y / 2 });
|
||||||
|
|
||||||
|
const paths = insertEdge(edgePaths, e, edge, clusterDb, 'flowchart', g);
|
||||||
|
//positionEdgeLabel(edge, paths);
|
||||||
|
});
|
||||||
|
await swimlaneRender(layout, vert, svg, g, id, conf);
|
||||||
|
|
||||||
|
// utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
|
||||||
|
|
||||||
|
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setConf,
|
||||||
|
addVertices,
|
||||||
|
addEdges,
|
||||||
|
getClasses,
|
||||||
|
draw,
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user