mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-19 20:24:16 +01:00
#5237 WIP
This commit is contained in:
@@ -59,8 +59,16 @@
|
|||||||
<body>
|
<body>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid">
|
||||||
stateDiagram-v2
|
stateDiagram-v2
|
||||||
Second --> Third
|
state fork_state <<fork>>
|
||||||
Second --> Fourth
|
[*] --> fork_state
|
||||||
|
fork_state --> State2
|
||||||
|
fork_state --> State3
|
||||||
|
|
||||||
|
state join_state <<join>>
|
||||||
|
State2 --> join_state
|
||||||
|
State3 --> join_state
|
||||||
|
join_state --> State4
|
||||||
|
State4 --> [*]
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const G_EDGE_LABELTYPE = 'text';
|
|||||||
export const G_EDGE_THICKNESS = 'normal';
|
export const G_EDGE_THICKNESS = 'normal';
|
||||||
|
|
||||||
export const CSS_EDGE = 'transition';
|
export const CSS_EDGE = 'transition';
|
||||||
|
export const CSS_DIAGRAM = 'statediagram';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
DEFAULT_DIAGRAM_DIRECTION,
|
DEFAULT_DIAGRAM_DIRECTION,
|
||||||
@@ -44,4 +45,5 @@ export default {
|
|||||||
G_EDGE_LABELTYPE,
|
G_EDGE_LABELTYPE,
|
||||||
G_EDGE_THICKNESS,
|
G_EDGE_THICKNESS,
|
||||||
CSS_EDGE,
|
CSS_EDGE,
|
||||||
|
CSS_DIAGRAM,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
G_EDGE_THICKNESS,
|
G_EDGE_THICKNESS,
|
||||||
CSS_EDGE,
|
CSS_EDGE,
|
||||||
} from './stateCommon.js';
|
} from './stateCommon.js';
|
||||||
|
import { rect } from 'dagre-d3-es/src/dagre-js/intersect/index.js';
|
||||||
|
|
||||||
const START_NODE = '[*]';
|
const START_NODE = '[*]';
|
||||||
const START_TYPE = 'start';
|
const START_TYPE = 'start';
|
||||||
@@ -555,9 +556,36 @@ const dataFetcher = (parentId, doc, nodes, edges) => {
|
|||||||
|
|
||||||
stateKeys.forEach((key) => {
|
stateKeys.forEach((key) => {
|
||||||
const item = currentDocument.states[key];
|
const item = currentDocument.states[key];
|
||||||
|
console.log('Item:', item);
|
||||||
|
|
||||||
|
let itemShape = 'rect';
|
||||||
|
if (item.type === 'default' && item.id === 'root_start') {
|
||||||
|
itemShape = 'stateStart';
|
||||||
|
}
|
||||||
|
if (item.type === 'default' && item.id === 'root_end') {
|
||||||
|
itemShape = 'stateEnd';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === 'fork' || item.type === 'join') {
|
||||||
|
itemShape = 'forkJoin';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === 'choice') {
|
||||||
|
itemShape = 'choice';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.id === '</choice>' && item.type === 'default') {
|
||||||
|
//ignore this item
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.id === '</join></fork>' && item.type === 'default') {
|
||||||
|
//ignore this item
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
nodes.push({ ...item, labelText: item.id, labelType: 'text', parentId, shape: 'rect' });
|
nodes.push({ ...item, labelText: item.id, labelType: 'text', parentId, shape: itemShape });
|
||||||
} else {
|
} else {
|
||||||
nodes.push({
|
nodes.push({
|
||||||
...item,
|
...item,
|
||||||
@@ -565,7 +593,7 @@ const dataFetcher = (parentId, doc, nodes, edges) => {
|
|||||||
// description: item.id,
|
// description: item.id,
|
||||||
labelType: 'text',
|
labelType: 'text',
|
||||||
labelStyle: '',
|
labelStyle: '',
|
||||||
shape: 'rect',
|
shape: itemShape,
|
||||||
padding: 15,
|
padding: 15,
|
||||||
classes: ' statediagram-state',
|
classes: ' statediagram-state',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { render } from '../../rendering-util/render.js';
|
|||||||
import insertElementsForSize, {
|
import insertElementsForSize, {
|
||||||
getDiagramElements,
|
getDiagramElements,
|
||||||
} from '../../rendering-util/inserElementsForSize.js';
|
} from '../../rendering-util/inserElementsForSize.js';
|
||||||
|
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
||||||
import {
|
import {
|
||||||
DEFAULT_DIAGRAM_DIRECTION,
|
DEFAULT_DIAGRAM_DIRECTION,
|
||||||
DEFAULT_NESTED_DOC_DIR,
|
DEFAULT_NESTED_DOC_DIR,
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
STMT_RELATION,
|
STMT_RELATION,
|
||||||
DEFAULT_STATE_TYPE,
|
DEFAULT_STATE_TYPE,
|
||||||
DIVIDER_TYPE,
|
DIVIDER_TYPE,
|
||||||
|
CSS_DIAGRAM,
|
||||||
} from './stateCommon.js';
|
} from './stateCommon.js';
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
@@ -93,6 +95,8 @@ export const draw = async function (text: string, id: string, _version: string,
|
|||||||
data4Layout.diagramId = id;
|
data4Layout.diagramId = id;
|
||||||
console.log('REF1:', data4Layout);
|
console.log('REF1:', data4Layout);
|
||||||
await render(data4Layout, svg, element);
|
await render(data4Layout, svg, element);
|
||||||
|
const padding = 8;
|
||||||
|
setupViewPortForSVG(svg, padding, CSS_DIAGRAM, conf.useMaxWidth);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { log } from '$root/logger.js';
|
import { log } from '$root/logger.js';
|
||||||
import { rect } from './shapes/rect.js';
|
import { rect } from './shapes/rect.ts';
|
||||||
|
import { stateStart } from './shapes/stateStart.ts';
|
||||||
|
import { stateEnd } from './shapes/stateEnd.ts';
|
||||||
|
import { forkJoin } from './shapes/forkJoin.ts';
|
||||||
|
import { choice } from './shapes/choice.ts';
|
||||||
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
const formatClass = (str) => {
|
const formatClass = (str) => {
|
||||||
@@ -11,6 +15,10 @@ const formatClass = (str) => {
|
|||||||
|
|
||||||
const shapes = {
|
const shapes = {
|
||||||
rect,
|
rect,
|
||||||
|
stateStart,
|
||||||
|
stateEnd,
|
||||||
|
forkJoin,
|
||||||
|
choice,
|
||||||
};
|
};
|
||||||
|
|
||||||
let nodeElems = {};
|
let nodeElems = {};
|
||||||
@@ -19,9 +27,9 @@ export const insertNode = async (elem, node, dir) => {
|
|||||||
let newEl;
|
let newEl;
|
||||||
let el;
|
let el;
|
||||||
|
|
||||||
console.log('insertNode element', elem, elem.node(), rect);
|
|
||||||
// debugger;
|
// debugger;
|
||||||
// Add link when appropriate
|
// Add link when appropriate
|
||||||
|
console.log('node.link', node.link);
|
||||||
if (node.link) {
|
if (node.link) {
|
||||||
let target;
|
let target;
|
||||||
if (getConfig().securityLevel === 'sandbox') {
|
if (getConfig().securityLevel === 'sandbox') {
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import type { Node } from '$root/rendering-util/types.d.ts';
|
||||||
|
import type { SVG } from '$root/diagram-api/types.js';
|
||||||
|
|
||||||
|
export const choice = (parent: SVG, node: Node) => {
|
||||||
|
const shapeSvg = parent
|
||||||
|
.insert('g')
|
||||||
|
.attr('class', 'node default')
|
||||||
|
.attr('id', node.domId || node.id);
|
||||||
|
|
||||||
|
const s = 28;
|
||||||
|
const points = [
|
||||||
|
{ x: 0, y: s / 2 },
|
||||||
|
{ x: s / 2, y: 0 },
|
||||||
|
{ x: 0, y: -s / 2 },
|
||||||
|
{ x: -s / 2, y: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const choice = shapeSvg.insert('polygon', ':first-child').attr(
|
||||||
|
'points',
|
||||||
|
points
|
||||||
|
.map(function (d) {
|
||||||
|
return d.x + ',' + d.y;
|
||||||
|
})
|
||||||
|
.join(' ')
|
||||||
|
);
|
||||||
|
// center the circle around its coordinate
|
||||||
|
choice.attr('class', 'state-start').attr('r', 7).attr('width', 28).attr('height', 28);
|
||||||
|
node.width = 28;
|
||||||
|
node.height = 28;
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
return intersect.circle(node, 14, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { log } from '$root/logger.js';
|
||||||
|
import { updateNodeBounds } from './util.js';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import type { Node } from '$root/rendering-util/types.d.ts';
|
||||||
|
import type { SVG } from '$root/diagram-api/types.js';
|
||||||
|
|
||||||
|
export const forkJoin = (parent: SVG, node: Node, dir: string) => {
|
||||||
|
const shapeSvg = parent
|
||||||
|
.insert('g')
|
||||||
|
.attr('class', 'node default')
|
||||||
|
.attr('id', node.domId || node.id);
|
||||||
|
|
||||||
|
let width = 70;
|
||||||
|
let height = 10;
|
||||||
|
|
||||||
|
if (dir === 'LR') {
|
||||||
|
width = 10;
|
||||||
|
height = 70;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shape = shapeSvg
|
||||||
|
.append('rect')
|
||||||
|
.attr('x', (-1 * width) / 2)
|
||||||
|
.attr('y', (-1 * height) / 2)
|
||||||
|
.attr('width', width)
|
||||||
|
.attr('height', height)
|
||||||
|
.attr('class', 'fork-join');
|
||||||
|
|
||||||
|
updateNodeBounds(node, shape);
|
||||||
|
let nodeHeight = 0;
|
||||||
|
let nodeWidth = 0;
|
||||||
|
let nodePadding = 10;
|
||||||
|
if (node.height) {
|
||||||
|
nodeHeight = node.height;
|
||||||
|
}
|
||||||
|
if (node.width) {
|
||||||
|
nodeWidth = node.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.padding) {
|
||||||
|
nodePadding = node.padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.height = nodeHeight + nodePadding / 2;
|
||||||
|
node.width = nodeWidth + nodePadding / 2;
|
||||||
|
node.intersect = function (point) {
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import intersect from '../intersect/index.js';
|
|||||||
import type { Node } from '$root/rendering-util/types.d.ts';
|
import type { Node } from '$root/rendering-util/types.d.ts';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param rect
|
* @param rect
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { log } from '$root/logger.js';
|
||||||
|
import { updateNodeBounds } from './util.js';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import type { Node } from '$root/rendering-util/types.d.ts';
|
||||||
|
import type { SVG } from '$root/diagram-api/types.js';
|
||||||
|
|
||||||
|
export const stateEnd = (parent: SVG, node: Node) => {
|
||||||
|
const shapeSvg = parent
|
||||||
|
.insert('g')
|
||||||
|
.attr('class', 'node default')
|
||||||
|
.attr('id', node.domId || node.id);
|
||||||
|
const innerCircle = shapeSvg.insert('circle', ':first-child');
|
||||||
|
const circle = shapeSvg.insert('circle', ':first-child');
|
||||||
|
|
||||||
|
circle.attr('class', 'state-start').attr('r', 7).attr('width', 14).attr('height', 14);
|
||||||
|
|
||||||
|
innerCircle.attr('class', 'state-end').attr('r', 5).attr('width', 10).attr('height', 10);
|
||||||
|
|
||||||
|
updateNodeBounds(node, circle);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
return intersect.circle(node, 7, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { log } from '$root/logger.js';
|
||||||
|
import { updateNodeBounds } from './util.js';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import type { Node } from '$root/rendering-util/types.d.ts';
|
||||||
|
import type { SVG } from '$root/diagram-api/types.js';
|
||||||
|
|
||||||
|
export const stateStart = (parent: SVG, node: Node) => {
|
||||||
|
const shapeSvg = parent
|
||||||
|
.insert('g')
|
||||||
|
.attr('class', 'node default')
|
||||||
|
.attr('id', node.domId || node.id);
|
||||||
|
const circle = shapeSvg.insert('circle', ':first-child');
|
||||||
|
|
||||||
|
// center the circle around its coordinate
|
||||||
|
circle.attr('class', 'state-start').attr('r', 7).attr('width', 14).attr('height', 14);
|
||||||
|
|
||||||
|
updateNodeBounds(node, circle);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
return intersect.circle(node, 7, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
||||||
40
packages/mermaid/src/rendering-util/setupViewPortForSVG.ts
Normal file
40
packages/mermaid/src/rendering-util/setupViewPortForSVG.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { configureSvgSize } from '$root/setupGraphViewbox.js';
|
||||||
|
import type { SVG } from '$root/diagram-api/types.js';
|
||||||
|
import { log } from '$root/logger.js';
|
||||||
|
|
||||||
|
export const setupViewPortForSVG = (
|
||||||
|
svg: SVG,
|
||||||
|
padding: number,
|
||||||
|
cssDiagram: string,
|
||||||
|
useMaxWidth: boolean
|
||||||
|
) => {
|
||||||
|
// Initialize the SVG element and set the diagram class
|
||||||
|
svg.attr('class', cssDiagram);
|
||||||
|
|
||||||
|
// Calculate the dimensions and position with padding
|
||||||
|
const { width, height, x, y } = calculateDimensionsWithPadding(svg, padding);
|
||||||
|
|
||||||
|
// Configure the size and aspect ratio of the SVG
|
||||||
|
configureSvgSize(svg, height, width, useMaxWidth);
|
||||||
|
|
||||||
|
// Update the viewBox to ensure all content is visible with padding
|
||||||
|
const viewBox = createViewBox(x, y, width, height, padding);
|
||||||
|
svg.attr('viewBox', viewBox);
|
||||||
|
|
||||||
|
// Log the viewBox configuration for debugging
|
||||||
|
log.debug(`viewBox configured: ${viewBox}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateDimensionsWithPadding = (svg: SVG, padding: number) => {
|
||||||
|
const bounds = svg.node()?.getBBox() || { width: 0, height: 0, x: 0, y: 0 };
|
||||||
|
return {
|
||||||
|
width: bounds.width + padding * 2,
|
||||||
|
height: bounds.height + padding * 2,
|
||||||
|
x: bounds.x,
|
||||||
|
y: bounds.y,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createViewBox = (x: number, y: number, width: number, height: number, padding: number) => {
|
||||||
|
return `${x - padding} ${y - padding} ${width} ${height}`;
|
||||||
|
};
|
||||||
@@ -33,8 +33,10 @@ interface Node {
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
type: string;
|
type: string;
|
||||||
width?: number;
|
width?: number;
|
||||||
intersect?: (point: any) => any;
|
height?: number;
|
||||||
|
|
||||||
// Specific properties for State Diagram nodes TODO remove and use generic properties
|
// Specific properties for State Diagram nodes TODO remove and use generic properties
|
||||||
|
intersect?: (point: any) => any;
|
||||||
style?: string;
|
style?: string;
|
||||||
class?: string;
|
class?: string;
|
||||||
borders?: string;
|
borders?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user