mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-11-19 04:04:28 +01:00
#5237 WIP
This commit is contained in:
@@ -59,8 +59,16 @@
|
||||
<body>
|
||||
<pre id="diagram" class="mermaid">
|
||||
stateDiagram-v2
|
||||
Second --> Third
|
||||
Second --> Fourth
|
||||
state fork_state <<fork>>
|
||||
[*] --> fork_state
|
||||
fork_state --> State2
|
||||
fork_state --> State3
|
||||
|
||||
state join_state <<join>>
|
||||
State2 --> join_state
|
||||
State3 --> join_state
|
||||
join_state --> State4
|
||||
State4 --> [*]
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
|
||||
@@ -28,6 +28,7 @@ export const G_EDGE_LABELTYPE = 'text';
|
||||
export const G_EDGE_THICKNESS = 'normal';
|
||||
|
||||
export const CSS_EDGE = 'transition';
|
||||
export const CSS_DIAGRAM = 'statediagram';
|
||||
|
||||
export default {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
@@ -44,4 +45,5 @@ export default {
|
||||
G_EDGE_LABELTYPE,
|
||||
G_EDGE_THICKNESS,
|
||||
CSS_EDGE,
|
||||
CSS_DIAGRAM,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
G_EDGE_THICKNESS,
|
||||
CSS_EDGE,
|
||||
} from './stateCommon.js';
|
||||
import { rect } from 'dagre-d3-es/src/dagre-js/intersect/index.js';
|
||||
|
||||
const START_NODE = '[*]';
|
||||
const START_TYPE = 'start';
|
||||
@@ -555,9 +556,36 @@ const dataFetcher = (parentId, doc, nodes, edges) => {
|
||||
|
||||
stateKeys.forEach((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) {
|
||||
nodes.push({ ...item, labelText: item.id, labelType: 'text', parentId, shape: 'rect' });
|
||||
nodes.push({ ...item, labelText: item.id, labelType: 'text', parentId, shape: itemShape });
|
||||
} else {
|
||||
nodes.push({
|
||||
...item,
|
||||
@@ -565,7 +593,7 @@ const dataFetcher = (parentId, doc, nodes, edges) => {
|
||||
// description: item.id,
|
||||
labelType: 'text',
|
||||
labelStyle: '',
|
||||
shape: 'rect',
|
||||
shape: itemShape,
|
||||
padding: 15,
|
||||
classes: ' statediagram-state',
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import { render } from '../../rendering-util/render.js';
|
||||
import insertElementsForSize, {
|
||||
getDiagramElements,
|
||||
} from '../../rendering-util/inserElementsForSize.js';
|
||||
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
||||
import {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
DEFAULT_NESTED_DOC_DIR,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
STMT_RELATION,
|
||||
DEFAULT_STATE_TYPE,
|
||||
DIVIDER_TYPE,
|
||||
CSS_DIAGRAM,
|
||||
} from './stateCommon.js';
|
||||
|
||||
// Configuration
|
||||
@@ -93,6 +95,8 @@ export const draw = async function (text: string, id: string, _version: string,
|
||||
data4Layout.diagramId = id;
|
||||
console.log('REF1:', data4Layout);
|
||||
await render(data4Layout, svg, element);
|
||||
const padding = 8;
|
||||
setupViewPortForSVG(svg, padding, CSS_DIAGRAM, conf.useMaxWidth);
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
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';
|
||||
|
||||
const formatClass = (str) => {
|
||||
@@ -11,6 +15,10 @@ const formatClass = (str) => {
|
||||
|
||||
const shapes = {
|
||||
rect,
|
||||
stateStart,
|
||||
stateEnd,
|
||||
forkJoin,
|
||||
choice,
|
||||
};
|
||||
|
||||
let nodeElems = {};
|
||||
@@ -19,9 +27,9 @@ export const insertNode = async (elem, node, dir) => {
|
||||
let newEl;
|
||||
let el;
|
||||
|
||||
console.log('insertNode element', elem, elem.node(), rect);
|
||||
// debugger;
|
||||
// Add link when appropriate
|
||||
console.log('node.link', node.link);
|
||||
if (node.link) {
|
||||
let target;
|
||||
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 rough from 'roughjs';
|
||||
import { select } from 'd3';
|
||||
|
||||
/**
|
||||
*
|
||||
* @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;
|
||||
type: string;
|
||||
width?: number;
|
||||
intersect?: (point: any) => any;
|
||||
height?: number;
|
||||
|
||||
// Specific properties for State Diagram nodes TODO remove and use generic properties
|
||||
intersect?: (point: any) => any;
|
||||
style?: string;
|
||||
class?: string;
|
||||
borders?: string;
|
||||
|
||||
Reference in New Issue
Block a user