mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-04 08:06:43 +02:00
Added basic for forms
This commit is contained in:
@@ -63,7 +63,17 @@
|
|||||||
<body>
|
<body>
|
||||||
<pre id="diagram4" class="mermaid">
|
<pre id="diagram4" class="mermaid">
|
||||||
flowchart
|
flowchart
|
||||||
B2@{ icon: "fa:image", label: "test augfuyfavf ydvaubfuac" }@
|
A --> B2@{ icon: "fa:image", form: "square", label: "test augfuyfavf ydvaubfuac" }@ --> C
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram5" class="mermaid">
|
||||||
|
flowchart
|
||||||
|
A --> B2@{ icon: "fa:image", form: "circle", label: "test augfuyfavf ydvaubfuac" }@ --> C
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram6" class="mermaid">
|
||||||
|
flowchart
|
||||||
|
A --> B2@{ icon: "fa:image", label: "test augfuyfavf ydvaubfuac" }@ --> C
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
@@ -93,7 +103,7 @@
|
|||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
// theme: 'base',
|
// theme: 'base',
|
||||||
// handdrawnSeed: 12,
|
// handdrawnSeed: 12,
|
||||||
look: 'handDrawn',
|
// look: 'handDrawn',
|
||||||
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
||||||
// 'elk.nodePlacement.strategy': 'SIMPLE',
|
// 'elk.nodePlacement.strategy': 'SIMPLE',
|
||||||
// 'elk.nodePlacement.strategy': 'LAYERED',
|
// 'elk.nodePlacement.strategy': 'LAYERED',
|
||||||
@@ -102,7 +112,7 @@
|
|||||||
// layout: 'elk',
|
// layout: 'elk',
|
||||||
// layout: 'fixed',
|
// layout: 'fixed',
|
||||||
// htmlLabels: false,
|
// htmlLabels: false,
|
||||||
flowchart: { titleTopMargin: 10, padding: 5 },
|
flowchart: { titleTopMargin: 10, padding: 5, htmlLables: true },
|
||||||
// fontFamily: 'Caveat',
|
// fontFamily: 'Caveat',
|
||||||
fontFamily: 'Kalam',
|
fontFamily: 'Kalam',
|
||||||
// fontFamily: 'courier',
|
// fontFamily: 'courier',
|
||||||
|
@@ -146,6 +146,15 @@ export const addVertex = function (
|
|||||||
}
|
}
|
||||||
if (doc?.icon) {
|
if (doc?.icon) {
|
||||||
vertex.icon = doc?.icon;
|
vertex.icon = doc?.icon;
|
||||||
|
if (vertex.text === id) {
|
||||||
|
vertex.text = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (doc?.form) {
|
||||||
|
vertex.form = doc?.form;
|
||||||
|
}
|
||||||
|
if (doc?.pos) {
|
||||||
|
vertex.pos = doc?.pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -796,7 +805,13 @@ export const lex = {
|
|||||||
|
|
||||||
const getTypeFromVertex = (vertex: FlowVertex) => {
|
const getTypeFromVertex = (vertex: FlowVertex) => {
|
||||||
if (vertex?.icon) {
|
if (vertex?.icon) {
|
||||||
return 'iconCircle';
|
if (vertex.form === 'circle') {
|
||||||
|
return 'iconCircle';
|
||||||
|
}
|
||||||
|
if (vertex.form === 'square') {
|
||||||
|
return 'iconSquare';
|
||||||
|
}
|
||||||
|
return 'icon';
|
||||||
}
|
}
|
||||||
if (vertex.type === 'square') {
|
if (vertex.type === 'square') {
|
||||||
return 'squareRect';
|
return 'squareRect';
|
||||||
@@ -864,6 +879,7 @@ const addNodeFromVertex = (
|
|||||||
linkTarget: vertex.linkTarget,
|
linkTarget: vertex.linkTarget,
|
||||||
tooltip: getTooltip(vertex.id),
|
tooltip: getTooltip(vertex.id),
|
||||||
icon: vertex.icon,
|
icon: vertex.icon,
|
||||||
|
pos: vertex.pos,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -13,7 +13,7 @@ export interface FlowVertex {
|
|||||||
type?: string;
|
type?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
form?: string;
|
form?: string;
|
||||||
pos?: 't';
|
pos?: 't' | 'b';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowText {
|
export interface FlowText {
|
||||||
|
@@ -53,13 +53,13 @@ import { curlyBraceRight } from './shapes/curlyBraceRight.js';
|
|||||||
import { curlyBraces } from './shapes/curlyBraces.js';
|
import { curlyBraces } from './shapes/curlyBraces.js';
|
||||||
import { iconSquare } from './shapes/iconSquare.js';
|
import { iconSquare } from './shapes/iconSquare.js';
|
||||||
import { iconCircle } from './shapes/iconCircle.js';
|
import { iconCircle } from './shapes/iconCircle.js';
|
||||||
|
import { icon } from './shapes/icon.js';
|
||||||
|
|
||||||
//Use these names as the left side to render shapes.
|
//Use these names as the left side to render shapes.
|
||||||
const shapes = {
|
const shapes = {
|
||||||
// States
|
// States
|
||||||
state,
|
state,
|
||||||
stateStart,
|
stateStart,
|
||||||
iconCircle,
|
|
||||||
'small-circle': stateStart,
|
'small-circle': stateStart,
|
||||||
'sm-circ': stateStart,
|
'sm-circ': stateStart,
|
||||||
start: stateStart,
|
start: stateStart,
|
||||||
@@ -228,6 +228,8 @@ const shapes = {
|
|||||||
'brace-r': curlyBraceRight,
|
'brace-r': curlyBraceRight,
|
||||||
curlyBraces,
|
curlyBraces,
|
||||||
iconSquare,
|
iconSquare,
|
||||||
|
iconCircle,
|
||||||
|
icon,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodeElems = new Map();
|
const nodeElems = new Map();
|
||||||
|
@@ -0,0 +1,66 @@
|
|||||||
|
import { log } from '$root/logger.js';
|
||||||
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||||
|
import type { Node } from '$root/rendering-util/types.d.ts';
|
||||||
|
import type { SVG } from '$root/diagram-api/types.js';
|
||||||
|
import { styles2String } from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
|
import { getIconSVG } from '$root/rendering-util/icons.js';
|
||||||
|
|
||||||
|
export const icon = async (parent: SVG, node: Node) => {
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
getNodeClasses(node)
|
||||||
|
);
|
||||||
|
|
||||||
|
const { cssStyles } = node;
|
||||||
|
const topLabel = node.pos === 't';
|
||||||
|
const labelWidth = Math.max(bbox.width + halfPadding * 2, node?.width ?? 0);
|
||||||
|
const labelHeight = Math.max(bbox.height + halfPadding * 2, node?.height ?? 0);
|
||||||
|
|
||||||
|
const iconSize = Math.max(labelHeight - halfPadding, labelWidth - halfPadding, 48);
|
||||||
|
const width = Math.max(labelWidth, iconSize);
|
||||||
|
const height = labelHeight + iconSize + halfPadding * 2;
|
||||||
|
|
||||||
|
const points = [
|
||||||
|
{ x: 0, y: 0 },
|
||||||
|
{ x: 0, y: height },
|
||||||
|
{ x: width, y: height },
|
||||||
|
{ x: width, y: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (node.icon) {
|
||||||
|
const iconElem = shapeSvg.append('g');
|
||||||
|
iconElem.html(
|
||||||
|
`<g>${await getIconSVG(node.icon, { height: iconSize, width: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
|
);
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${0}, ${topLabel ? labelHeight - halfPadding : 0 + halfPadding * 2})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
|
shapeSvg.selectAll('path').attr('style', cssStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeStyles && node.look !== 'handDrawn') {
|
||||||
|
shapeSvg.selectAll('path').attr('style', nodeStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${width / 2 - labelWidth / 2 + halfPadding - (bbox.x - (bbox.left ?? 0))},${(topLabel ? 0 : iconSize) + halfPadding * 2})`
|
||||||
|
);
|
||||||
|
updateNodeBounds(node, shapeSvg);
|
||||||
|
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('iconSquare intersect', node, point);
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
};
|
@@ -1,15 +1,16 @@
|
|||||||
import { log } from '$root/logger.js';
|
import { log } from '$root/logger.js';
|
||||||
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||||
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 type { SVG } from '$root/diagram-api/types.js';
|
||||||
import {
|
import {
|
||||||
styles2String,
|
styles2String,
|
||||||
userNodeOverrides,
|
userNodeOverrides,
|
||||||
} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js';
|
} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
|
import intersect from '../intersect/index.js';
|
||||||
import { getIconSVG } from '$root/rendering-util/icons.js';
|
import { getIconSVG } from '$root/rendering-util/icons.js';
|
||||||
|
|
||||||
export const iconCircle = async (parent: SVGAElement, node: Node): Promise<SVGAElement> => {
|
export const iconCircle = async (parent: SVG, node: Node) => {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
@@ -18,51 +19,58 @@ export const iconCircle = async (parent: SVGAElement, node: Node): Promise<SVGAE
|
|||||||
getNodeClasses(node)
|
getNodeClasses(node)
|
||||||
);
|
);
|
||||||
|
|
||||||
let width = Math.max(bbox.width + (node.padding ?? 0));
|
|
||||||
|
|
||||||
if (node.width) {
|
|
||||||
width = node.width + (node.padding ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const radius = width / 2 + halfPadding;
|
|
||||||
|
|
||||||
let circleElem;
|
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
|
// const topLabel = node.pos === 't';
|
||||||
|
const labelWidth = Math.max(bbox.width + halfPadding * 2, node?.width ?? 0);
|
||||||
|
const labelHeight = Math.max(bbox.height + halfPadding * 2, node?.height ?? 0);
|
||||||
|
|
||||||
if (node.look === 'handDrawn') {
|
const iconSize = Math.max(labelHeight - halfPadding, labelWidth - halfPadding, 48);
|
||||||
// @ts-ignore - rough is not typed
|
const radius = iconSize * 1.5 + halfPadding * 2;
|
||||||
const rc = rough.svg(shapeSvg);
|
|
||||||
const options = userNodeOverrides(node, {});
|
|
||||||
const roughNode = rc.circle(0, 0, radius * 2, options);
|
|
||||||
|
|
||||||
circleElem = shapeSvg.insert(() => roughNode, ':first-child');
|
// @ts-ignore - rough is not typed
|
||||||
circleElem.attr('class', 'basic label-container').attr('style', cssStyles);
|
const rc = rough.svg(shapeSvg);
|
||||||
} else {
|
const options = userNodeOverrides(node, {});
|
||||||
circleElem = shapeSvg
|
|
||||||
.insert('circle', ':first-child')
|
if (node.look !== 'handDrawn') {
|
||||||
.attr('class', 'basic label-container')
|
options.roughness = 0;
|
||||||
.attr('style', nodeStyles)
|
options.fillStyle = 'solid';
|
||||||
.attr('r', radius)
|
|
||||||
.attr('cx', 0)
|
|
||||||
.attr('cy', 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position label at the top
|
const roughNode = rc.circle(0, 0, radius, options);
|
||||||
label.attr('transform', `translate(0, ${-radius + halfPadding})`);
|
const point = rc.circle(0, 0, 2, options);
|
||||||
|
|
||||||
|
const iconShape = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
|
iconShape.insert(() => point);
|
||||||
|
|
||||||
if (node.icon) {
|
if (node.icon) {
|
||||||
const iconElem = shapeSvg.append('g');
|
const iconElem = shapeSvg.append('g');
|
||||||
iconElem.html(
|
iconElem.html(
|
||||||
`<g>${await getIconSVG(node.icon, { height: radius + bbox.height, width: radius + bbox.height, fallbackPrefix: '' })}</g>`
|
`<g>${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
|
);
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-iconElem.node().getBBox().width / 2}, ${-iconElem.node().getBBox().height / 2 - labelHeight / 2 + halfPadding})`
|
||||||
);
|
);
|
||||||
iconElem.attr('transform', `translate(${-radius / 2}, ${-radius / 2})`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNodeBounds(node, circleElem);
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', cssStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeStyles && node.look !== 'handDrawn') {
|
||||||
|
iconShape.selectAll('path').attr('style', nodeStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-label.node().getBBox().width / 2},${iconSize / 2 - label.node().getBBox().height / 2 - halfPadding})`
|
||||||
|
);
|
||||||
|
updateNodeBounds(node, iconShape);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('Circle intersect', node, radius, point);
|
log.info('iconSquare intersect', node, point);
|
||||||
return intersect.circle(node, radius, point);
|
const pos = intersect.circle(node, radius, point);
|
||||||
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
|
@@ -14,28 +14,26 @@ import { getIconSVG } from '$root/rendering-util/icons.js';
|
|||||||
export const iconSquare = async (parent: SVG, node: Node) => {
|
export const iconSquare = async (parent: SVG, node: Node) => {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
getNodeClasses(node)
|
||||||
|
);
|
||||||
|
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
|
const topLabel = node.pos === 't';
|
||||||
|
const labelWidth = Math.max(bbox.width + halfPadding * 2, node?.width ?? 0);
|
||||||
|
const labelHeight = Math.max(bbox.height + halfPadding * 2, node?.height ?? 0);
|
||||||
|
|
||||||
let width = Math.max(bbox.width + (node.padding ?? 0));
|
const iconSize = Math.max(labelHeight - halfPadding, labelWidth - halfPadding, 48);
|
||||||
let height = Math.max(bbox.height + (node.padding ?? 0));
|
const width = Math.max(labelWidth, iconSize);
|
||||||
|
const height = labelHeight + iconSize + halfPadding * 2;
|
||||||
if (node.width) {
|
|
||||||
width = node.width + (node.padding ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.height) {
|
|
||||||
height = node.height + (node.padding ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const iconSize = Math.min(width, height);
|
|
||||||
|
|
||||||
const points = [
|
const points = [
|
||||||
{ x: 0, y: 0 },
|
{ x: 0, y: 0 },
|
||||||
|
{ x: 0, y: height },
|
||||||
|
{ x: width, y: height },
|
||||||
{ x: width, y: 0 },
|
{ x: width, y: 0 },
|
||||||
{ x: width, y: height + iconSize + bbox.height + 5 },
|
|
||||||
{ x: 0, y: height + iconSize + bbox.height + 5 },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
@@ -52,6 +50,17 @@ export const iconSquare = async (parent: SVG, node: Node) => {
|
|||||||
|
|
||||||
const iconShape = shapeSvg.insert(() => lineNode, ':first-child');
|
const iconShape = shapeSvg.insert(() => lineNode, ':first-child');
|
||||||
|
|
||||||
|
if (node.icon) {
|
||||||
|
const iconElem = shapeSvg.append('g');
|
||||||
|
iconElem.html(
|
||||||
|
`<g>${await getIconSVG(node.icon, { height: iconSize, width: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
|
);
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${0}, ${topLabel ? labelHeight - halfPadding : 0 + halfPadding * 2})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (cssStyles && node.look !== 'handDrawn') {
|
if (cssStyles && node.look !== 'handDrawn') {
|
||||||
iconShape.selectAll('path').attr('style', cssStyles);
|
iconShape.selectAll('path').attr('style', cssStyles);
|
||||||
}
|
}
|
||||||
@@ -60,20 +69,12 @@ export const iconSquare = async (parent: SVG, node: Node) => {
|
|||||||
iconShape.selectAll('path').attr('style', nodeStyles);
|
iconShape.selectAll('path').attr('style', nodeStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
iconShape.attr('transform', `translate(${-width / 2},${-height / 2})`);
|
label.attr(
|
||||||
|
'transform',
|
||||||
label.attr('transform', `translate(${-width / 2},${-height / 2})`);
|
`translate(${width / 2 - labelWidth / 2 + halfPadding - (bbox.x - (bbox.left ?? 0))},${(topLabel ? 0 : iconSize) + halfPadding * 2})`
|
||||||
|
);
|
||||||
updateNodeBounds(node, iconShape);
|
updateNodeBounds(node, iconShape);
|
||||||
|
|
||||||
if (node.icon) {
|
|
||||||
const iconElem = shapeSvg.append('g');
|
|
||||||
iconElem.html(
|
|
||||||
`<g>${await getIconSVG(node.icon, { height: height + iconSize, fallbackPrefix: '' })}</g>`
|
|
||||||
);
|
|
||||||
iconElem.attr('transform', `translate(${-iconSize}, ${-iconSize / 2 + bbox.height})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('iconSquare intersect', node, point);
|
log.info('iconSquare intersect', node, point);
|
||||||
const pos = intersect.polygon(node, points, point);
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
@@ -66,6 +66,7 @@ interface Node {
|
|||||||
|
|
||||||
look?: string;
|
look?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
pos?: 't' | 'b';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common properties for any edge in the system
|
// Common properties for any edge in the system
|
||||||
|
@@ -2,6 +2,8 @@ export interface NodeMetaData {
|
|||||||
shape?: string;
|
shape?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
form?: string;
|
||||||
|
pos?: 't' | 'b';
|
||||||
}
|
}
|
||||||
export interface Point {
|
export interface Point {
|
||||||
x: number;
|
x: number;
|
||||||
|
Reference in New Issue
Block a user