At last something is working
This commit is contained in:
parent
1782f69c8f
commit
afaf87e414
|
@ -17,22 +17,20 @@
|
|||
<h2>Simple flow</h2>
|
||||
<pre class="mermaid">
|
||||
sankey
|
||||
a--10->b
|
||||
c--20->b
|
||||
b--15->d
|
||||
a-- 7->d
|
||||
node_a->10->node_b;
|
||||
node_c->20->node_b;
|
||||
node_b->15->node_d;
|
||||
node_a-> 7->node_d;
|
||||
</pre>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
theme: 'default',
|
||||
// themeCSS: '.node rect { fill: red; }',
|
||||
logLevel: 3,
|
||||
securityLevel: 'loose',
|
||||
flowchart: { curve: 'basis' },
|
||||
gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorMargin: 50 },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -13,8 +13,10 @@ services:
|
|||
- ./:/mermaid
|
||||
- root_cache:/root/.cache
|
||||
- root_local:/root/.local
|
||||
- root_local:/root/.npm
|
||||
ports:
|
||||
- 9000:9000
|
||||
volumes:
|
||||
root_cache:
|
||||
root_local:
|
||||
root_npm:
|
||||
|
|
|
@ -85,7 +85,12 @@
|
|||
"coveralls": "^3.1.1",
|
||||
"cypress": "^12.10.0",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
<<<<<<< HEAD
|
||||
"esbuild": "^0.18.0",
|
||||
=======
|
||||
"d3-sankey": "^0.12.3",
|
||||
"esbuild": "^0.17.18",
|
||||
>>>>>>> 84c278d0 (At last something is working)
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-cypress": "^2.13.2",
|
||||
|
|
|
@ -32,6 +32,7 @@ export interface MermaidConfig {
|
|||
mindmap?: MindmapDiagramConfig;
|
||||
gitGraph?: GitGraphDiagramConfig;
|
||||
c4?: C4DiagramConfig;
|
||||
sankey?: SankeyDiagramConfig;
|
||||
dompurifyConfig?: DOMPurify.Config;
|
||||
wrap?: boolean;
|
||||
fontSize?: number;
|
||||
|
@ -411,6 +412,8 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
|||
wrappingWidth?: number;
|
||||
}
|
||||
|
||||
export interface SankeyDiagramConfig extends BaseDiagramConfig {}
|
||||
|
||||
export interface FontConfig {
|
||||
fontSize?: string | number;
|
||||
fontFamily?: string;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
/lex
|
||||
|
||||
%start start
|
||||
%left ARROW
|
||||
|
||||
%% // language grammar
|
||||
|
||||
|
@ -66,10 +67,19 @@ attribute: ATTRIBUTE EQUAL value | ATTRIBUTE;
|
|||
|
||||
value: VALUE | OPEN_STRING STRING CLOSE_STRING;
|
||||
|
||||
stream: node ARROW AMOUNT ARROW tail { yy.addNode($1); yy.addLink(); };
|
||||
tail: stream | node;
|
||||
stream: node[source] ARROW amount ARROW tail[target] {
|
||||
$$=$source;
|
||||
yy.addLink($source, $target, $amount);
|
||||
};
|
||||
|
||||
node: NODE { yy.addNode($1) };
|
||||
amount: AMOUNT { $$=parseFloat($AMOUNT); };
|
||||
|
||||
tail
|
||||
: stream { $$ = $stream }
|
||||
| node { $$ = $node; }
|
||||
;
|
||||
|
||||
node: NODE { $$ = yy.addNode($NODE); };
|
||||
|
||||
// : NODE exhaust intake exhaust_chain optional_attributes EOS
|
||||
// exhaust_chain: ARROW AMOUNT intake_chain | ;
|
||||
|
|
|
@ -21,7 +21,7 @@ describe('Sankey diagram', function () {
|
|||
it('recognizes one flow', () => {
|
||||
const str = `
|
||||
sankey
|
||||
a -> 30 -> b -> 20 -> c
|
||||
node_a -> 30 -> node_b -> 20 -> node_c
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
|
@ -30,9 +30,9 @@ describe('Sankey diagram', function () {
|
|||
it('recognizes multiple flows', () => {
|
||||
const str = `
|
||||
sankey
|
||||
a -> 30 -> b -> 12 -> e
|
||||
c -> 30 -> d -> 12 -> e
|
||||
c -> 40 -> e -> 12 -> q
|
||||
node_a -> 30 -> node_b -> 12 -> node_e
|
||||
node_c -> 30 -> node_d -> 12 -> node_e
|
||||
node_c -> 40 -> node_e -> 12 -> node_q
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
|
@ -44,7 +44,7 @@ describe('Sankey diagram', function () {
|
|||
sankey
|
||||
node[]
|
||||
node[attr=1]
|
||||
a -> 30 -> b
|
||||
node_a -> 30 -> node_b
|
||||
node[attrWithoutValue]
|
||||
node[attr = 3]
|
||||
node[attr1 = 23413 attr2=1234]
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { log } from '../../logger.js';
|
||||
import mermaidAPI from '../../mermaidAPI.js';
|
||||
// import { log } from '../../logger.js';
|
||||
// import mermaidAPI from '../../mermaidAPI.js';
|
||||
import * as configApi from '../../config.js';
|
||||
import common from '../common/common.js';
|
||||
import {
|
||||
// setAccTitle,
|
||||
// getAccTitle,
|
||||
// getAccDescription,
|
||||
// setAccDescription,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
clear as commonClear,
|
||||
|
@ -20,46 +20,54 @@ import {
|
|||
// return text.trimStart().replace(/^\s*%%(?!{)[^\n]+\n?/gm, '');
|
||||
// };
|
||||
let links: Array<Link> = [];
|
||||
let nodes: { [id: string]: Node } = {};
|
||||
let nodes: Array<Node> = [];
|
||||
let nodesHash: Record<string, Node> = {};
|
||||
|
||||
const clear = function () {
|
||||
const clear = () => {
|
||||
links = [];
|
||||
nodes = {};
|
||||
nodes = [];
|
||||
nodesHash = {};
|
||||
commonClear();
|
||||
};
|
||||
|
||||
type Nullable<T> = T | null;
|
||||
|
||||
interface ILink {
|
||||
source?: Node;
|
||||
target?: Node;
|
||||
amount?: number;
|
||||
}
|
||||
|
||||
class Link {
|
||||
sourceNode: Nullable<Node>;
|
||||
targetNode: Nullable<Node>;
|
||||
source: Nullable<Node>;
|
||||
target: Nullable<Node>;
|
||||
amount: Nullable<number>;
|
||||
constructor() {
|
||||
this.sourceNode = null;
|
||||
this.targetNode = null;
|
||||
this.source = null;
|
||||
this.target = null;
|
||||
this.amount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a stream between two elements on the diagram
|
||||
* Adds a link between two elements on the diagram
|
||||
*
|
||||
* @param sourceNodeID - The id Node where the link starts
|
||||
* @param targetNodeID - The id Node where the link ends
|
||||
* @param source - Node where the link starts
|
||||
* @param target - Node where the link ends
|
||||
* @param amount - number, float or integer, describes the amount to be passed
|
||||
*/
|
||||
|
||||
interface IAddLink {
|
||||
sourceNodeID?: string;
|
||||
targetNodeID?: string;
|
||||
// amount?: number;
|
||||
}
|
||||
|
||||
const addLink = ({ sourceNodeID, targetNodeID }: IAddLink = {}): Link => {
|
||||
// const addLink = ({ source, target, amount }: ILink = {}): Link => {
|
||||
const addLink = (source?: Node, target?: Node, amount?: number): Link => {
|
||||
const link: Link = new Link();
|
||||
|
||||
if (sourceNodeID !== undefined) {
|
||||
link.sourceNode = addNode(sourceNodeID);
|
||||
if (source !== undefined) {
|
||||
link.source = source;
|
||||
}
|
||||
if (targetNodeID !== undefined) {
|
||||
link.targetNode = addNode(targetNodeID);
|
||||
if (target !== undefined) {
|
||||
link.target = target;
|
||||
}
|
||||
if (amount !== undefined) {
|
||||
link.amount = amount;
|
||||
}
|
||||
|
||||
links.push(link);
|
||||
|
@ -68,11 +76,11 @@ const addLink = ({ sourceNodeID, targetNodeID }: IAddLink = {}): Link => {
|
|||
};
|
||||
|
||||
class Node {
|
||||
id: string;
|
||||
ID: string;
|
||||
title: string;
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
this.title = id;
|
||||
constructor(ID: string) {
|
||||
this.ID = ID;
|
||||
this.title = ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,25 +89,28 @@ class Node {
|
|||
*
|
||||
* @param id - The id Node
|
||||
*/
|
||||
const addNode = (id: string): Node => {
|
||||
id = common.sanitizeText(id, configApi.getConfig());
|
||||
if (nodes[id] === undefined) {
|
||||
nodes[id] = new Node(id);
|
||||
const addNode = (ID: string): Node => {
|
||||
ID = common.sanitizeText(ID, configApi.getConfig());
|
||||
if (nodesHash[ID] === undefined) {
|
||||
nodesHash[ID] = new Node(ID);
|
||||
}
|
||||
const node = nodes[id];
|
||||
const node = nodesHash[ID];
|
||||
nodes.push(node);
|
||||
// debugger;
|
||||
return node;
|
||||
};
|
||||
|
||||
export default {
|
||||
// sankey interface
|
||||
nodesHash,
|
||||
nodes,
|
||||
links,
|
||||
addLink,
|
||||
addNode,
|
||||
// common DB interface
|
||||
// TODO: If this is a must this probably should be an interface
|
||||
// getAccTitle,
|
||||
// setAccTitle,
|
||||
// getAccDescription,
|
||||
// setAccDescription,
|
||||
getAccTitle,
|
||||
setAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
getDiagramTitle,
|
||||
setDiagramTitle,
|
||||
clear,
|
||||
|
|
|
@ -1,22 +1,202 @@
|
|||
// @ts-nocheck TODO: fix file
|
||||
import { Diagram } from '../../Diagram.js';
|
||||
import { log } from '../../logger.js';
|
||||
import * as configApi from '../../config.js';
|
||||
import {
|
||||
select as d3select,
|
||||
scaleOrdinal as d3scaleOrdinal,
|
||||
schemeTableau10 as d3schemeTableau10,
|
||||
// rgb as d3rgb,
|
||||
map as d3map,
|
||||
} from 'd3';
|
||||
import {
|
||||
sankey as d3sankey,
|
||||
sankeyLinkHorizontal
|
||||
} from 'd3-sankey';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import sankeyDB from './sankeyDB.js';
|
||||
|
||||
/**
|
||||
* Draws a sequenceDiagram in the tag with id: id based on the graph definition in text.
|
||||
*
|
||||
* @param _text - The text of the diagram
|
||||
* @param text - The text of the diagram
|
||||
* @param id - The id of the diagram which will be used as a DOM element id¨
|
||||
* @param _version - Mermaid version from package.json
|
||||
* @param diagObj - A standard diagram containing the db and the text and type etc of the diagram
|
||||
*/
|
||||
export const draw = function (text: string, id: string, _version: string, diagObj: Diagram) {
|
||||
// debugger;
|
||||
// diagObj.db.clear();
|
||||
export const draw = function (text: string, id: string, _version: string, diagObj: Diagram): void {
|
||||
|
||||
// First of all parse sankey language
|
||||
// Everything that is parsed will be stored in diagObj.DB
|
||||
// That is why we need to clear DB first
|
||||
//
|
||||
if (typeof (diagObj?.db?.clear) !== 'undefined') { // why do we need to check for undefined? typescript complains
|
||||
diagObj?.db?.clear();
|
||||
}
|
||||
// Launch parsing
|
||||
diagObj.parser.parse(text);
|
||||
log.debug('Parsed sankey diagram');
|
||||
|
||||
// const elem = doc.getElementById(id);
|
||||
// Figure out what is happening there
|
||||
// The main thing is svg object that is a wrapper from d3 for svg operations
|
||||
//
|
||||
const { securityLevel, sequence: conf } = configApi.getConfig();
|
||||
let sandboxElement;
|
||||
if (securityLevel === 'sandbox') {
|
||||
sandboxElement = select('#i' + id);
|
||||
}
|
||||
const root =
|
||||
securityLevel === 'sandbox'
|
||||
? d3select(sandboxElement.nodes()[0].contentDocument.body)
|
||||
: d3select('body');
|
||||
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
||||
const svg = securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : d3select(`[id="${id}"]`);
|
||||
|
||||
// Establish svg dimensions
|
||||
//
|
||||
const elem = doc.getElementById(id);
|
||||
const width = elem.parentElement.offsetWidth;
|
||||
|
||||
const height = 600;
|
||||
configureSvgSize(svg, height, width, true);
|
||||
|
||||
// Prepare data for construction
|
||||
// This must be a mutable object with 2 properties:
|
||||
// `nodes` and `links`
|
||||
//
|
||||
// let graph = {
|
||||
// "nodes": [
|
||||
// { "id": "Alice" },
|
||||
// { "id": "Bob" },
|
||||
// { "id": "Carol" }
|
||||
// ],
|
||||
// "links": [
|
||||
// { "source": "Alice", "target": "Bob", "value": 23 },
|
||||
// { "source": "Bob", "target": "Carol", "value": 43 }
|
||||
// ]
|
||||
// };
|
||||
//
|
||||
let graph = {
|
||||
"nodes": [
|
||||
{ "id": "Alice" },
|
||||
{ "id": "Bob" },
|
||||
{ "id": "Carol" }
|
||||
],
|
||||
"links": [
|
||||
{ "source": "Alice", "target": "Bob", "value": 23 },
|
||||
{ "source": "Bob", "target": "Carol", "value": 43 }
|
||||
]
|
||||
};
|
||||
|
||||
// Construct and configure a Sankey generator
|
||||
// That will be a functino that calculates nodes and links dimentions
|
||||
//
|
||||
const sankey = d3sankey()
|
||||
.nodeId((d) => d.id) // we use 'id' property to identify node
|
||||
.nodeWidth(36)
|
||||
.nodePadding(290)
|
||||
.size([width, height]);
|
||||
|
||||
// .nodeAlign(d3Sankey.sankeyLeft) // d3.sankeyLeft, etc.
|
||||
// .nodeWidth(15)
|
||||
// .nodePadding(10)
|
||||
// .extent([[1, 5], [width - 1, height - 5]]);
|
||||
// .nodeId(d => d['id'])
|
||||
//
|
||||
|
||||
// Compute the Sankey layout
|
||||
// Namely calalculate nodes and links positions
|
||||
// Our `graph` object will be mutated by this
|
||||
//
|
||||
sankey(graph);
|
||||
|
||||
// debugger;
|
||||
return 'TEST';
|
||||
|
||||
// const node = svg.append("g")
|
||||
// .selectAll("rect")
|
||||
// .data(graph.nodes)
|
||||
// .join("rect")
|
||||
// .attr("x", d => d.x0)
|
||||
// .attr("y", d => d.y0)
|
||||
// .attr("height", d => d.y1 - d.y0)
|
||||
// .attr("width", d => d.x1 - d.x0);
|
||||
// // .attr("stroke", nodeStroke)
|
||||
// // .attr("stroke-width", nodeStrokeWidth)
|
||||
// // .attr("stroke-opacity", nodeStrokeOpacity)
|
||||
// // .attr("stroke-linejoin", nodeStrokeLinejoin)
|
||||
|
||||
var color = d3scaleOrdinal(d3schemeTableau10);
|
||||
// Creates the rects that represent the nodes.
|
||||
const rect = svg.append("g")
|
||||
.attr("stroke", "#000")
|
||||
.selectAll("rect")
|
||||
.data(graph.nodes)
|
||||
.join("rect")
|
||||
.attr("x", d => d.x0)
|
||||
.attr("y", d => d.y0)
|
||||
.attr("height", d => d.y1 - d.y0)
|
||||
.attr("width", d => d.x1 - d.x0)
|
||||
.attr("fill", d => color(d.node));
|
||||
|
||||
|
||||
// // add in the links
|
||||
// var link = svg.append("g")
|
||||
// .selectAll(".link")
|
||||
// .data(graph.links)
|
||||
// .enter()
|
||||
// .append("path")
|
||||
// .attr("class", "link")
|
||||
// .attr("d", sankeyLinkHorizontal())
|
||||
// .style("stroke-width", function (d) { return Math.max(1, d.dy); })
|
||||
// .sort(function (a, b) { return b.dy - a.dy; });
|
||||
|
||||
// // add in the nodes
|
||||
// var node = svg.append("g")
|
||||
// .selectAll(".node")
|
||||
// .data(graph.nodes)
|
||||
// .enter().append("g")
|
||||
// .attr("class", "node")
|
||||
// .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; })
|
||||
// // .call(d3.drag()
|
||||
// // .subject(function(d) { return d; })
|
||||
// // .on("start", function() { this.parentNode.appendChild(this); })
|
||||
// // .on("drag", dragmove))
|
||||
// ;
|
||||
|
||||
|
||||
// // add the rectangles for the nodes
|
||||
// node
|
||||
// .append("rect")
|
||||
// .attr("height", function (d) { return d.dy; })
|
||||
// .attr("width", generator.nodeWidth())
|
||||
// .style("fill", function (d) { return d.color = color(d.name.replace(/ .*/, "")); })
|
||||
// .style("stroke", function (d) { return d3rgb(d.color).darker(2); })
|
||||
// // Add hover text
|
||||
// .append("title")
|
||||
// .text(function (d) { return d.name + "\n" + "There is " + d.value + " stuff in this node"; });
|
||||
|
||||
|
||||
// // add in the title for the nodes
|
||||
// node
|
||||
// .append("text")
|
||||
// .attr("x", -6)
|
||||
// .attr("y", function (d) { return d.dy / 2; })
|
||||
// .attr("dy", ".35em")
|
||||
// .attr("text-anchor", "end")
|
||||
// .attr("transform", null)
|
||||
// .text(function (d) { return d.name; })
|
||||
// .filter(function (d) { return d.x < width / 2; })
|
||||
// .attr("x", 6 + generator.nodeWidth())
|
||||
// .attr("text-anchor", "start");
|
||||
|
||||
// console.log();
|
||||
// debugger;
|
||||
// .layout(1);
|
||||
|
||||
// const { nodes, links } = generator({
|
||||
// nodes: graph.nodes,
|
||||
// links: graph.links,
|
||||
// });
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
15
run
15
run
|
@ -10,23 +10,19 @@ args=${@:2}
|
|||
case $command in
|
||||
|
||||
sh)
|
||||
$RUN mermaid sh $args
|
||||
$RUN --service-ports mermaid sh $args
|
||||
;;
|
||||
|
||||
i | install)
|
||||
$RUN mermaid sh -c "npx pnpm install $args"
|
||||
;;
|
||||
|
||||
test)
|
||||
$RUN mermaid sh -c "npx pnpm test $args"
|
||||
add)
|
||||
$RUN mermaid sh -c "npx pnpm -w add $args"
|
||||
;;
|
||||
|
||||
vitest)
|
||||
$RUN mermaid sh -c "npx pnpm vitest $args"
|
||||
;;
|
||||
|
||||
e2e)
|
||||
$RUN mermaid sh -c "npx pnpm e2e $args"
|
||||
test | vitest | e2e )
|
||||
$RUN mermaid sh -c "npx pnpm $command $args"
|
||||
;;
|
||||
|
||||
lint)
|
||||
|
@ -46,6 +42,7 @@ Run commonly used commands within docker containers
|
|||
\033[1m$name install\033[0m # Equvalent of pnpm install
|
||||
\033[1m$name dev\033[0m # Run dev server with examples, open http://localhost:9000
|
||||
|
||||
$name add # Add package, 'run add d3-sankey'
|
||||
$name lint # Equvalent of pnpm -w run lint:fix
|
||||
$name test # Run unit tests
|
||||
$name vitest # Run watcher for unit tests
|
||||
|
|
Loading…
Reference in New Issue