This commit is contained in:
Ashish Jain
2024-04-26 12:24:02 +02:00
parent a2702e4058
commit dd5bfb3b24
12 changed files with 238 additions and 7 deletions

View File

@@ -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">

View File

@@ -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,
};

View File

@@ -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',
});

View File

@@ -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 {

View File

@@ -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') {

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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;
};

View 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}`;
};

View File

@@ -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;