mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-13 20:39:38 +02:00
Merge from upstream new-shapes branch
This commit is contained in:
1
.github/workflows/release-preview.yml
vendored
1
.github/workflows/release-preview.yml
vendored
@@ -3,6 +3,7 @@ name: Preview release
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [develop]
|
branches: [develop]
|
||||||
|
types: [opened, synchronize, labeled, ready_for_review]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.event.number }}
|
group: ${{ github.workflow }}-${{ github.event.number }}
|
||||||
|
@@ -60,7 +60,7 @@ looks.forEach((look) => {
|
|||||||
it(`without label`, () => {
|
it(`without label`, () => {
|
||||||
let flowchartCode = `flowchart ${direction}\n`;
|
let flowchartCode = `flowchart ${direction}\n`;
|
||||||
newShapesSet.forEach((newShape, index) => {
|
newShapesSet.forEach((newShape, index) => {
|
||||||
flowchartCode += ` n${index} --> n${index}${index}{ shape: ${newShape} }\n`;
|
flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape} }\n`;
|
||||||
});
|
});
|
||||||
imgSnapshotTest(flowchartCode, { look });
|
imgSnapshotTest(flowchartCode, { look });
|
||||||
});
|
});
|
||||||
|
@@ -44,7 +44,7 @@ export const render = async (
|
|||||||
|
|
||||||
// Add the element to the DOM
|
// Add the element to the DOM
|
||||||
if (!node.isGroup) {
|
if (!node.isGroup) {
|
||||||
const childNodeEl = await insertNode(nodeEl, node, { config });
|
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
|
||||||
boundingBox = childNodeEl.node().getBBox();
|
boundingBox = childNodeEl.node().getBBox();
|
||||||
child.domId = childNodeEl;
|
child.domId = childNodeEl;
|
||||||
child.width = boundingBox.width;
|
child.width = boundingBox.width;
|
||||||
|
@@ -87,7 +87,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
// insertCluster(clusters, graph.node(v));
|
// insertCluster(clusters, graph.node(v));
|
||||||
} else {
|
} else {
|
||||||
log.info('Node - the non recursive path', v, node.id, node);
|
log.info('Node - the non recursive path', v, node.id, node);
|
||||||
await insertNode(nodes, graph.node(v), { config: siteConfig });
|
await insertNode(nodes, graph.node(v), { config: siteConfig, dir });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -146,7 +146,7 @@ export const addVertex = function (
|
|||||||
}
|
}
|
||||||
if (doc?.icon) {
|
if (doc?.icon) {
|
||||||
vertex.icon = doc?.icon;
|
vertex.icon = doc?.icon;
|
||||||
if (!doc.label) {
|
if (!doc.label?.trim() && vertex.text === id) {
|
||||||
vertex.text = '';
|
vertex.text = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@ export const addVertex = function (
|
|||||||
}
|
}
|
||||||
if (doc?.img) {
|
if (doc?.img) {
|
||||||
vertex.img = doc?.img;
|
vertex.img = doc?.img;
|
||||||
if (!doc.label) {
|
if (!doc.label?.trim() && vertex.text === id) {
|
||||||
vertex.text = '';
|
vertex.text = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -814,17 +814,17 @@ export const lex = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getTypeFromVertex = (vertex: FlowVertex) => {
|
const getTypeFromVertex = (vertex: FlowVertex) => {
|
||||||
if (vertex?.img) {
|
if (vertex.img) {
|
||||||
return 'imageSquare';
|
return 'imageSquare';
|
||||||
}
|
}
|
||||||
if (vertex?.icon) {
|
if (vertex.icon) {
|
||||||
if (vertex?.form === 'circle') {
|
if (vertex.form === 'circle') {
|
||||||
return 'iconCircle';
|
return 'iconCircle';
|
||||||
}
|
}
|
||||||
if (vertex?.form === 'square') {
|
if (vertex.form === 'square') {
|
||||||
return 'iconSquare';
|
return 'iconSquare';
|
||||||
}
|
}
|
||||||
if (vertex?.form === 'rounded') {
|
if (vertex.form === 'rounded') {
|
||||||
return 'iconRounded';
|
return 'iconRounded';
|
||||||
}
|
}
|
||||||
return 'icon';
|
return 'icon';
|
||||||
|
@@ -125,7 +125,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
|
|||||||
// insertCluster(clusters, graph.node(v));
|
// insertCluster(clusters, graph.node(v));
|
||||||
} else {
|
} else {
|
||||||
log.trace('Node - the non recursive path XAX', v, nodes, graph.node(v), dir);
|
log.trace('Node - the non recursive path XAX', v, nodes, graph.node(v), dir);
|
||||||
await insertNode(nodes, graph.node(v), { config: siteConfig });
|
await insertNode(nodes, graph.node(v), { config: siteConfig, dir });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -1,11 +1,15 @@
|
|||||||
import { getNodeClasses, updateNodeBounds } from './util.js';
|
import { getNodeClasses, updateNodeBounds } from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.js';
|
import type { Node, RenderOptions } from '../../types.js';
|
||||||
import type { SVG } from '../../../diagram-api/types.js';
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
|
||||||
export const forkJoin = (parent: SVG, node: Node, dir: string) => {
|
export const forkJoin = (
|
||||||
|
parent: SVG,
|
||||||
|
node: Node,
|
||||||
|
{ dir, config: { state, themeVariables } }: RenderOptions
|
||||||
|
) => {
|
||||||
const { nodeStyles } = styles2String(node);
|
const { nodeStyles } = styles2String(node);
|
||||||
node.label = '';
|
node.label = '';
|
||||||
const shapeSvg = parent
|
const shapeSvg = parent
|
||||||
@@ -27,7 +31,10 @@ export const forkJoin = (parent: SVG, node: Node, dir: string) => {
|
|||||||
|
|
||||||
// @ts-ignore TODO: Fix rough typings
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, {});
|
const options = userNodeOverrides(node, {
|
||||||
|
stroke: themeVariables.lineColor,
|
||||||
|
fill: themeVariables.lineColor,
|
||||||
|
});
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
@@ -47,7 +54,11 @@ export const forkJoin = (parent: SVG, node: Node, dir: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateNodeBounds(node, shape);
|
updateNodeBounds(node, shape);
|
||||||
|
const padding = state?.padding ?? 0;
|
||||||
|
if (node.width && node.height) {
|
||||||
|
node.width += padding / 2 || 0;
|
||||||
|
node.height += padding / 2 || 0;
|
||||||
|
}
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
return intersect.rect(node, point);
|
return intersect.rect(node, point);
|
||||||
};
|
};
|
||||||
|
@@ -19,18 +19,20 @@ export const icon = async (
|
|||||||
const iconSize = Math.max(assetHeight, assetWidth);
|
const iconSize = Math.max(assetHeight, assetWidth);
|
||||||
const defaultWidth = flowchart?.wrappingWidth;
|
const defaultWidth = flowchart?.wrappingWidth;
|
||||||
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
||||||
const { nodeBorder } = themeVariables;
|
|
||||||
const { stylesMap } = compileStyles(node);
|
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'icon-shape default');
|
const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'icon-shape default');
|
||||||
|
|
||||||
const topLabel = node.pos === 't';
|
const topLabel = node.pos === 't';
|
||||||
|
|
||||||
const height = iconSize;
|
const height = iconSize;
|
||||||
const width = Math.max(iconSize, bbox.width);
|
const width = iconSize;
|
||||||
|
const { nodeBorder } = themeVariables;
|
||||||
|
const { stylesMap } = compileStyles(node);
|
||||||
|
|
||||||
const x = -width / 2;
|
const x = -width / 2;
|
||||||
const y = -height / 2;
|
const y = -height / 2;
|
||||||
|
|
||||||
|
const labelPadding = node.label ? 8 : 0;
|
||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: 'none', fill: 'none' });
|
const options = userNodeOverrides(node, { stroke: 'none', fill: 'none' });
|
||||||
@@ -42,7 +44,17 @@ export const icon = async (
|
|||||||
|
|
||||||
const iconNode = rc.rectangle(x, y, width, height, options);
|
const iconNode = rc.rectangle(x, y, width, height, options);
|
||||||
|
|
||||||
|
const outerWidth = Math.max(width, bbox.width);
|
||||||
|
const outerHeight = height + bbox.height + labelPadding;
|
||||||
|
|
||||||
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
|
...options,
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
const outerShape = shapeSvg.insert(() => outerNode);
|
||||||
|
|
||||||
if (node.icon) {
|
if (node.icon) {
|
||||||
const iconElem = shapeSvg.append('g');
|
const iconElem = shapeSvg.append('g');
|
||||||
@@ -52,21 +64,26 @@ export const icon = async (
|
|||||||
const iconBBox = iconElem.node().getBBox();
|
const iconBBox = iconElem.node().getBBox();
|
||||||
const iconWidth = iconBBox.width;
|
const iconWidth = iconBBox.width;
|
||||||
const iconHeight = iconBBox.height;
|
const iconHeight = iconBBox.height;
|
||||||
|
const iconX = iconBBox.x;
|
||||||
|
const iconY = iconBBox.y;
|
||||||
iconElem.attr(
|
iconElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight + bbox.height / 2 : -height / 2 - bbox.height / 2})`
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - iconY : -outerHeight / 2 - iconY})`
|
||||||
);
|
);
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 - 5 - bbox.height / 2 : height / 2 - bbox.height / 2})`
|
`translate(${-bbox.width / 2},${topLabel ? -height / 2 - bbox.height / 2 - labelPadding / 2 : height / 2 - bbox.height / 2 + labelPadding / 2})`
|
||||||
);
|
);
|
||||||
|
|
||||||
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
iconShape.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${0},${topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
updateNodeBounds(node, shapeSvg);
|
updateNodeBounds(node, outerShape);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('iconSquare intersect', node, point);
|
log.info('iconSquare intersect', node, point);
|
||||||
@@ -75,51 +92,34 @@ export const icon = async (
|
|||||||
}
|
}
|
||||||
const dx = node.x ?? 0;
|
const dx = node.x ?? 0;
|
||||||
const dy = node.y ?? 0;
|
const dy = node.y ?? 0;
|
||||||
const nodeWidth = node.width ?? 0;
|
|
||||||
const nodeHeight = node.height ?? 0;
|
const nodeHeight = node.height ?? 0;
|
||||||
|
let points = [];
|
||||||
if (topLabel) {
|
if (topLabel) {
|
||||||
const points = [
|
points = [
|
||||||
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
{
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
{ x: dx + width / 2, y: dy + nodeHeight / 2 },
|
||||||
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
{ x: dx - width / 2, y: dy + nodeHeight / 2 },
|
||||||
},
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
{
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
|
||||||
y: dy + nodeHeight / 2 - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
|
||||||
y: dy + nodeHeight / 2 - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
|
||||||
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
|
||||||
];
|
];
|
||||||
const pos = intersect.polygon(node, points, point);
|
|
||||||
return pos;
|
|
||||||
} else {
|
} else {
|
||||||
const points = [
|
points = [
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy - nodeHeight / 2 },
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width, y: dy - nodeHeight / 2 },
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 },
|
||||||
{
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width,
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
y: dy - nodeHeight / 2 + height,
|
{ x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 },
|
||||||
},
|
{ x: dx - bbox.width / 2, y: dy + nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + height },
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
{ x: dx + nodeWidth / 2, y: dy + nodeHeight / 2 },
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 },
|
|
||||||
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 - bbox.height },
|
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy + nodeHeight / 2 - bbox.height },
|
|
||||||
];
|
];
|
||||||
const pos = intersect.polygon(node, points, point);
|
|
||||||
return pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
|
@@ -19,15 +19,13 @@ export const iconCircle = async (
|
|||||||
const iconSize = Math.max(assetHeight, assetWidth);
|
const iconSize = Math.max(assetHeight, assetWidth);
|
||||||
const defaultWidth = flowchart?.wrappingWidth;
|
const defaultWidth = flowchart?.wrappingWidth;
|
||||||
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
node.width = Math.max(iconSize, defaultWidth ?? 0);
|
||||||
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'icon-shape default');
|
||||||
parent,
|
|
||||||
node,
|
const padding = 20;
|
||||||
'icon-shape default'
|
const labelPadding = node.label ? 8 : 0;
|
||||||
);
|
|
||||||
|
|
||||||
const topLabel = node.pos === 't';
|
const topLabel = node.pos === 't';
|
||||||
|
|
||||||
const diameter = iconSize + halfPadding * 2;
|
|
||||||
const { nodeBorder, mainBkg } = themeVariables;
|
const { nodeBorder, mainBkg } = themeVariables;
|
||||||
const { stylesMap } = compileStyles(node);
|
const { stylesMap } = compileStyles(node);
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
@@ -39,32 +37,48 @@ export const iconCircle = async (
|
|||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
|
||||||
const iconNode = rc.circle(0, 0, diameter, options);
|
|
||||||
|
|
||||||
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
|
||||||
const iconElem = shapeSvg.append('g');
|
const iconElem = shapeSvg.append('g');
|
||||||
if (node.icon) {
|
if (node.icon) {
|
||||||
iconElem.html(
|
iconElem.html(
|
||||||
`<g>${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}</g>`
|
`<g>${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}</g>`
|
||||||
);
|
);
|
||||||
const iconBBox = iconElem.node().getBBox();
|
|
||||||
const iconWidth = iconBBox.width;
|
|
||||||
const iconHeight = iconBBox.height;
|
|
||||||
iconElem.attr(
|
|
||||||
'transform',
|
|
||||||
`translate(${-iconWidth / 2},${topLabel ? diameter / 2 - iconHeight - halfPadding + bbox.height / 2 : -diameter / 2 + halfPadding - bbox.height / 2})`
|
|
||||||
);
|
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
|
||||||
}
|
}
|
||||||
|
const iconBBox = iconElem.node().getBBox();
|
||||||
|
const iconWidth = iconBBox.width;
|
||||||
|
const iconHeight = iconBBox.height;
|
||||||
|
const iconX = iconBBox.x;
|
||||||
|
const iconY = iconBBox.y;
|
||||||
|
|
||||||
|
const diameter = Math.max(iconWidth, iconHeight) + padding * 2;
|
||||||
|
const iconNode = rc.circle(0, 0, diameter, options);
|
||||||
|
|
||||||
|
const outerWidth = Math.max(diameter, bbox.width);
|
||||||
|
const outerHeight = diameter + bbox.height + labelPadding;
|
||||||
|
|
||||||
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
|
...options,
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
const outerShape = shapeSvg.insert(() => outerNode);
|
||||||
|
iconElem.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? diameter / 2 - iconHeight - padding + bbox.height / 2 - iconY : -diameter / 2 + padding - bbox.height / 2 - labelPadding / 2 - iconY})`
|
||||||
|
);
|
||||||
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-diameter / 2 + diameter / 2 - bbox.width / 2},${topLabel ? -diameter / 2 - bbox.height / 2 : diameter / 2 - bbox.height / 2})`
|
`translate(${-bbox.width / 2},${topLabel ? -diameter / 2 - bbox.height / 2 : diameter / 2 - bbox.height / 2 + labelPadding / 2})`
|
||||||
);
|
);
|
||||||
|
|
||||||
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
iconShape.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2 - labelPadding / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
updateNodeBounds(node, shapeSvg);
|
updateNodeBounds(node, outerShape);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('iconSquare intersect', node, point);
|
log.info('iconSquare intersect', node, point);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { log } from '../../../logger.js';
|
import { log } from '../../../logger.js';
|
||||||
import { labelHelper, updateNodeBounds } from './util.js';
|
import { labelHelper, updateNodeBounds } from './util.js';
|
||||||
import type { Node, RenderOptions } from '../../types.js';
|
import type { Node, RenderOptions } from '../../types.d.ts';
|
||||||
import type { SVG } from '../../../diagram-api/types.js';
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
@@ -36,6 +36,8 @@ export const iconRounded = async (
|
|||||||
const x = -width / 2;
|
const x = -width / 2;
|
||||||
const y = -height / 2;
|
const y = -height / 2;
|
||||||
|
|
||||||
|
const labelPadding = node.label ? 8 : 0;
|
||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
||||||
@@ -47,7 +49,17 @@ export const iconRounded = async (
|
|||||||
|
|
||||||
const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 5), options);
|
const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 5), options);
|
||||||
|
|
||||||
|
const outerWidth = Math.max(width, bbox.width);
|
||||||
|
const outerHeight = height + bbox.height + labelPadding;
|
||||||
|
|
||||||
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
|
...options,
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
const outerShape = shapeSvg.insert(() => outerNode);
|
||||||
|
|
||||||
if (node.icon) {
|
if (node.icon) {
|
||||||
const iconElem = shapeSvg.append('g');
|
const iconElem = shapeSvg.append('g');
|
||||||
@@ -57,21 +69,26 @@ export const iconRounded = async (
|
|||||||
const iconBBox = iconElem.node().getBBox();
|
const iconBBox = iconElem.node().getBBox();
|
||||||
const iconWidth = iconBBox.width;
|
const iconWidth = iconBBox.width;
|
||||||
const iconHeight = iconBBox.height;
|
const iconHeight = iconBBox.height;
|
||||||
|
const iconX = iconBBox.x;
|
||||||
|
const iconY = iconBBox.y;
|
||||||
iconElem.attr(
|
iconElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight - halfPadding + bbox.height / 2 : -height / 2 + halfPadding - bbox.height / 2})`
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - halfPadding - iconY : -outerHeight / 2 + halfPadding - iconY})`
|
||||||
);
|
);
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 - 5 - bbox.height / 2 : height / 2 - bbox.height / 2})`
|
`translate(${-bbox.width / 2},${topLabel ? -height / 2 - bbox.height / 2 - labelPadding / 2 : height / 2 - bbox.height / 2 + labelPadding / 2})`
|
||||||
);
|
);
|
||||||
|
|
||||||
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
iconShape.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${0},${topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
updateNodeBounds(node, shapeSvg);
|
updateNodeBounds(node, outerShape);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('iconSquare intersect', node, point);
|
log.info('iconSquare intersect', node, point);
|
||||||
@@ -80,51 +97,34 @@ export const iconRounded = async (
|
|||||||
}
|
}
|
||||||
const dx = node.x ?? 0;
|
const dx = node.x ?? 0;
|
||||||
const dy = node.y ?? 0;
|
const dy = node.y ?? 0;
|
||||||
const nodeWidth = node.width ?? 0;
|
|
||||||
const nodeHeight = node.height ?? 0;
|
const nodeHeight = node.height ?? 0;
|
||||||
|
let points = [];
|
||||||
if (topLabel) {
|
if (topLabel) {
|
||||||
const points = [
|
points = [
|
||||||
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
{
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
{ x: dx + width / 2, y: dy + nodeHeight / 2 },
|
||||||
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
{ x: dx - width / 2, y: dy + nodeHeight / 2 },
|
||||||
},
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
{
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
|
||||||
y: dy + nodeHeight / 2 - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
|
||||||
y: dy + nodeHeight / 2 - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
|
||||||
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
|
||||||
];
|
];
|
||||||
const pos = intersect.polygon(node, points, point);
|
|
||||||
return pos;
|
|
||||||
} else {
|
} else {
|
||||||
const points = [
|
points = [
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy - nodeHeight / 2 },
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width, y: dy - nodeHeight / 2 },
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 },
|
||||||
{
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width,
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
y: dy - nodeHeight / 2 + height,
|
{ x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 },
|
||||||
},
|
{ x: dx - bbox.width / 2, y: dy + nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + height },
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
{ x: dx + nodeWidth / 2, y: dy + nodeHeight / 2 },
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 },
|
|
||||||
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 - bbox.height },
|
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy + nodeHeight / 2 - bbox.height },
|
|
||||||
];
|
];
|
||||||
const pos = intersect.polygon(node, points, point);
|
|
||||||
return pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
|
@@ -35,6 +35,8 @@ export const iconSquare = async (
|
|||||||
const x = -width / 2;
|
const x = -width / 2;
|
||||||
const y = -height / 2;
|
const y = -height / 2;
|
||||||
|
|
||||||
|
const labelPadding = node.label ? 8 : 0;
|
||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg });
|
||||||
@@ -46,7 +48,17 @@ export const iconSquare = async (
|
|||||||
|
|
||||||
const iconNode = rc.rectangle(x, y, width, height, options);
|
const iconNode = rc.rectangle(x, y, width, height, options);
|
||||||
|
|
||||||
|
const outerWidth = Math.max(width, bbox.width);
|
||||||
|
const outerHeight = height + bbox.height + labelPadding;
|
||||||
|
|
||||||
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
|
...options,
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
const iconShape = shapeSvg.insert(() => iconNode, ':first-child');
|
||||||
|
const outerShape = shapeSvg.insert(() => outerNode);
|
||||||
|
|
||||||
if (node.icon) {
|
if (node.icon) {
|
||||||
const iconElem = shapeSvg.append('g');
|
const iconElem = shapeSvg.append('g');
|
||||||
@@ -56,21 +68,26 @@ export const iconSquare = async (
|
|||||||
const iconBBox = iconElem.node().getBBox();
|
const iconBBox = iconElem.node().getBBox();
|
||||||
const iconWidth = iconBBox.width;
|
const iconWidth = iconBBox.width;
|
||||||
const iconHeight = iconBBox.height;
|
const iconHeight = iconBBox.height;
|
||||||
|
const iconX = iconBBox.x;
|
||||||
|
const iconY = iconBBox.y;
|
||||||
iconElem.attr(
|
iconElem.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight - halfPadding + bbox.height / 2 : -height / 2 + halfPadding - bbox.height / 2})`
|
`translate(${-iconWidth / 2 - iconX},${topLabel ? outerHeight / 2 - iconHeight - halfPadding - iconY : -outerHeight / 2 + halfPadding - iconY})`
|
||||||
);
|
);
|
||||||
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
iconElem.selectAll('path').attr('fill', stylesMap.get('stroke') || nodeBorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 - 5 - bbox.height / 2 : height / 2 - bbox.height / 2})`
|
`translate(${-bbox.width / 2},${topLabel ? -height / 2 - bbox.height / 2 - labelPadding / 2 : height / 2 - bbox.height / 2 + labelPadding / 2})`
|
||||||
);
|
);
|
||||||
|
|
||||||
iconShape.attr('transform', `translate(${0},${topLabel ? bbox.height / 2 : -bbox.height / 2})`);
|
iconShape.attr(
|
||||||
|
'transform',
|
||||||
|
`translate(${0},${topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
updateNodeBounds(node, shapeSvg);
|
updateNodeBounds(node, outerShape);
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('iconSquare intersect', node, point);
|
log.info('iconSquare intersect', node, point);
|
||||||
@@ -79,51 +96,34 @@ export const iconSquare = async (
|
|||||||
}
|
}
|
||||||
const dx = node.x ?? 0;
|
const dx = node.x ?? 0;
|
||||||
const dy = node.y ?? 0;
|
const dy = node.y ?? 0;
|
||||||
const nodeWidth = node.width ?? 0;
|
|
||||||
const nodeHeight = node.height ?? 0;
|
const nodeHeight = node.height ?? 0;
|
||||||
|
let points = [];
|
||||||
if (topLabel) {
|
if (topLabel) {
|
||||||
const points = [
|
points = [
|
||||||
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 - bbox.height / 2 },
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
{
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
{ x: dx + width / 2, y: dy + nodeHeight / 2 },
|
||||||
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
{ x: dx - width / 2, y: dy + nodeHeight / 2 },
|
||||||
},
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
{
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2,
|
|
||||||
y: dy + nodeHeight / 2 - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
|
||||||
y: dy + nodeHeight / 2 - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: dx + nodeWidth / 2 - (bbox.width - width) / 2 - width,
|
|
||||||
y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2,
|
|
||||||
},
|
|
||||||
{ x: dx - nodeWidth / 2, y: dy - nodeHeight / 2 + bbox.height - bbox.height / 2 },
|
|
||||||
];
|
];
|
||||||
const pos = intersect.polygon(node, points, point);
|
|
||||||
return pos;
|
|
||||||
} else {
|
} else {
|
||||||
const points = [
|
points = [
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy - nodeHeight / 2 },
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 },
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width, y: dy - nodeHeight / 2 },
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 },
|
||||||
{
|
{ x: dx + width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
x: dx - nodeWidth / 2 + (bbox.width - width) / 2 + width,
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
y: dy - nodeHeight / 2 + height,
|
{ x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 },
|
||||||
},
|
{ x: dx - bbox.width / 2, y: dy + nodeHeight / 2 },
|
||||||
{ x: dx + nodeWidth / 2, y: dy - nodeHeight / 2 + height },
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
{ x: dx + nodeWidth / 2, y: dy + nodeHeight / 2 },
|
{ x: dx - width / 2, y: dy - nodeHeight / 2 + height },
|
||||||
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 },
|
|
||||||
{ x: dx - nodeWidth / 2, y: dy + nodeHeight / 2 - bbox.height },
|
|
||||||
{ x: dx - nodeWidth / 2 + (bbox.width - width) / 2, y: dy + nodeHeight / 2 - bbox.height },
|
|
||||||
];
|
];
|
||||||
const pos = intersect.polygon(node, points, point);
|
|
||||||
return pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
return shapeSvg;
|
return shapeSvg;
|
||||||
|
@@ -1,15 +1,16 @@
|
|||||||
import { log } from '../../../logger.js';
|
import { log } from '../../../logger.js';
|
||||||
import { labelHelper, updateNodeBounds } from './util.js';
|
import { labelHelper, updateNodeBounds } from './util.js';
|
||||||
import type { Node } from '../../types.js';
|
import type { Node, RenderOptions } from '../../types.d.ts';
|
||||||
import type { SVG } from '../../../diagram-api/types.js';
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import { createPathFromPoints } from './util.js';
|
|
||||||
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
|
||||||
|
|
||||||
export const imageSquare = async (parent: SVG, node: Node) => {
|
export const imageSquare = async (
|
||||||
//image dimensions
|
parent: SVG,
|
||||||
|
node: Node,
|
||||||
|
{ config: { flowchart } }: RenderOptions
|
||||||
|
) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = node?.img ?? '';
|
img.src = node?.img ?? '';
|
||||||
await img.decode();
|
await img.decode();
|
||||||
@@ -17,49 +18,26 @@ export const imageSquare = async (parent: SVG, node: Node) => {
|
|||||||
const imageNaturalWidth = Number(img.naturalWidth.toString().replace('px', ''));
|
const imageNaturalWidth = Number(img.naturalWidth.toString().replace('px', ''));
|
||||||
const imageNaturalHeight = Number(img.naturalHeight.toString().replace('px', ''));
|
const imageNaturalHeight = Number(img.naturalHeight.toString().replace('px', ''));
|
||||||
|
|
||||||
const defaultWidth = getConfig().flowchart?.wrappingWidth;
|
const { labelStyles } = styles2String(node);
|
||||||
|
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
|
||||||
|
const defaultWidth = flowchart?.wrappingWidth;
|
||||||
|
|
||||||
const imageWidth = Math.max(
|
const imageWidth = Math.max(
|
||||||
node.label ? (defaultWidth ?? 0) : 0,
|
node.label ? (defaultWidth ?? 0) : 0,
|
||||||
node?.assetWidth ?? imageNaturalWidth
|
node?.assetWidth ?? imageNaturalWidth
|
||||||
);
|
);
|
||||||
const imageHeight = node?.assetHeight ?? imageNaturalHeight;
|
const imageHeight = node?.assetHeight ?? imageNaturalHeight;
|
||||||
|
node.width = Math.max(imageWidth, defaultWidth ?? 0);
|
||||||
const imagePoints = [
|
|
||||||
{ x: -imageWidth / 2, y: -imageHeight },
|
|
||||||
{ x: imageWidth / 2, y: -imageHeight },
|
|
||||||
{ x: imageWidth / 2, y: 0 },
|
|
||||||
{ x: -imageWidth / 2, y: 0 },
|
|
||||||
];
|
|
||||||
|
|
||||||
//label dimensions
|
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
|
||||||
node.labelStyle = labelStyles;
|
|
||||||
|
|
||||||
// const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
|
||||||
// parent,
|
|
||||||
// node,
|
|
||||||
// 'icon-shape default'
|
|
||||||
// );
|
|
||||||
|
|
||||||
const { cssStyles } = node;
|
|
||||||
// const defaultHeight = bbox.height;
|
|
||||||
// node.height = Math.max(node.height ?? 0, node.label ? (defaultHeight ?? 0) : 0, imageHeight);
|
|
||||||
const labelWidth = Math.max(node.width ?? 0, node.label ? (defaultWidth ?? 0) : 0, imageWidth);
|
|
||||||
node.width = node.label ? labelWidth : 0;
|
|
||||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'image-shape default');
|
const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'image-shape default');
|
||||||
|
|
||||||
// const width = Math.max(bbox.width + (node.padding ?? 0), node?.width ?? 0);
|
const topLabel = node.pos === 't';
|
||||||
const height = Math.max(bbox.height + (node.padding ?? 0), node?.height ?? 0);
|
|
||||||
const labelHeight = node.label ? height : 0;
|
|
||||||
|
|
||||||
const imagePosition = node?.pos ?? 'b';
|
const x = -imageWidth / 2;
|
||||||
|
const y = -imageHeight / 2;
|
||||||
|
|
||||||
const labelPoints = [
|
const labelPadding = node.label ? 8 : 0;
|
||||||
{ x: -labelWidth / 2, y: 0 },
|
|
||||||
{ x: labelWidth / 2, y: 0 },
|
|
||||||
{ x: labelWidth / 2, y: labelHeight },
|
|
||||||
{ x: -labelWidth / 2, y: labelHeight },
|
|
||||||
];
|
|
||||||
|
|
||||||
// @ts-ignore - rough is not typed
|
// @ts-ignore - rough is not typed
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
@@ -70,18 +48,20 @@ export const imageSquare = async (parent: SVG, node: Node) => {
|
|||||||
options.fillStyle = 'solid';
|
options.fillStyle = 'solid';
|
||||||
}
|
}
|
||||||
|
|
||||||
const imagePath = createPathFromPoints(imagePoints);
|
const imageNode = rc.rectangle(x, y, imageWidth, imageHeight, options);
|
||||||
const imagePathNode = rc.path(imagePath, options);
|
|
||||||
|
|
||||||
const linePath = createPathFromPoints(labelPoints);
|
const outerWidth = Math.max(imageWidth, bbox.width);
|
||||||
const lineNode = rc.path(linePath, { ...options });
|
const outerHeight = imageHeight + bbox.height + labelPadding;
|
||||||
|
|
||||||
const imageShape = shapeSvg.insert(() => lineNode, ':first-child').attr('opacity', 0);
|
const outerNode = rc.rectangle(-outerWidth / 2, -outerHeight / 2, outerWidth, outerHeight, {
|
||||||
imageShape.insert(() => imagePathNode, ':first-child');
|
...options,
|
||||||
|
fill: 'none',
|
||||||
|
stroke: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
imageShape.attr('transform', `translate(${0},${(imageHeight - labelHeight) / 2})`);
|
const iconShape = shapeSvg.insert(() => imageNode, ':first-child');
|
||||||
|
const outerShape = shapeSvg.insert(() => outerNode);
|
||||||
|
|
||||||
// Image operations
|
|
||||||
if (node.img) {
|
if (node.img) {
|
||||||
const image = shapeSvg.append('image');
|
const image = shapeSvg.append('image');
|
||||||
|
|
||||||
@@ -91,34 +71,58 @@ export const imageSquare = async (parent: SVG, node: Node) => {
|
|||||||
image.attr('height', imageHeight);
|
image.attr('height', imageHeight);
|
||||||
image.attr('preserveAspectRatio', 'none');
|
image.attr('preserveAspectRatio', 'none');
|
||||||
|
|
||||||
const yPos =
|
image.attr(
|
||||||
imagePosition === 'b' ? -imageHeight / 2 - labelHeight / 2 : (-imageHeight + labelHeight) / 2;
|
'transform',
|
||||||
image.attr('transform', `translate(${-imageWidth / 2}, ${yPos})`);
|
`translate(${-imageWidth / 2},${topLabel ? outerHeight / 2 - imageHeight : -outerHeight / 2})`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cssStyles && node.look !== 'handDrawn') {
|
label.attr(
|
||||||
imageShape.selectAll('path').attr('style', cssStyles);
|
'transform',
|
||||||
}
|
`translate(${-bbox.width / 2},${topLabel ? -imageHeight / 2 - bbox.height / 2 - labelPadding / 2 : imageHeight / 2 - bbox.height / 2 + labelPadding / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
if (nodeStyles && node.look !== 'handDrawn') {
|
iconShape.attr(
|
||||||
imageShape.selectAll('path').attr('style', nodeStyles);
|
'transform',
|
||||||
}
|
`translate(${0},${topLabel ? bbox.height / 2 + labelPadding / 2 : -bbox.height / 2 - labelPadding / 2})`
|
||||||
|
);
|
||||||
|
|
||||||
const yPos =
|
updateNodeBounds(node, outerShape);
|
||||||
imagePosition === 'b'
|
|
||||||
? (imageHeight + labelHeight) / 2 - bbox.height - (bbox.y - (bbox.top ?? 0))
|
|
||||||
: -(imageHeight + labelHeight) / 2 + (node?.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0));
|
|
||||||
|
|
||||||
label.attr('transform', `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))},${yPos})`);
|
|
||||||
|
|
||||||
updateNodeBounds(node, imageShape);
|
|
||||||
|
|
||||||
node.intersect = function (point) {
|
node.intersect = function (point) {
|
||||||
log.info('imageSquare intersect', node, point);
|
log.info('iconSquare intersect', node, point);
|
||||||
|
if (!node.label) {
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
}
|
||||||
|
const dx = node.x ?? 0;
|
||||||
|
const dy = node.y ?? 0;
|
||||||
|
const nodeHeight = node.height ?? 0;
|
||||||
|
let points = [];
|
||||||
|
if (topLabel) {
|
||||||
|
points = [
|
||||||
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 },
|
||||||
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
|
{ x: dx + imageWidth / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
|
{ x: dx + imageWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - imageWidth / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - imageWidth / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + bbox.height + labelPadding },
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
points = [
|
||||||
|
{ x: dx - imageWidth / 2, y: dy - nodeHeight / 2 },
|
||||||
|
{ x: dx + imageWidth / 2, y: dy - nodeHeight / 2 },
|
||||||
|
{ x: dx + imageWidth / 2, y: dy - nodeHeight / 2 + imageHeight },
|
||||||
|
{ x: dx + bbox.width / 2, y: dy - nodeHeight / 2 + imageHeight },
|
||||||
|
{ x: dx + bbox.width / 2 / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - bbox.width / 2, y: dy + nodeHeight / 2 },
|
||||||
|
{ x: dx - bbox.width / 2, y: dy - nodeHeight / 2 + imageHeight },
|
||||||
|
{ x: dx - imageWidth / 2, y: dy - nodeHeight / 2 + imageHeight },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
const combinedPoints = [...imagePoints, ...labelPoints];
|
const pos = intersect.polygon(node, points, point);
|
||||||
|
|
||||||
const pos = intersect.polygon(node, combinedPoints, point);
|
|
||||||
return pos;
|
return pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,11 +1,15 @@
|
|||||||
import { log } from '../../../logger.js';
|
|
||||||
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.js';
|
import type { Node } from '../../types.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
import type { RenderOptions } from '../../types.js';
|
||||||
|
|
||||||
export const note = async (parent: SVGAElement, node: Node) => {
|
export const note = async (
|
||||||
|
parent: SVGAElement,
|
||||||
|
node: Node,
|
||||||
|
{ config: { themeVariables } }: RenderOptions
|
||||||
|
) => {
|
||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
@@ -19,11 +23,13 @@ export const note = async (parent: SVGAElement, node: Node) => {
|
|||||||
node.centerLabel = true;
|
node.centerLabel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('Classes = ', node.cssClasses);
|
|
||||||
// add the rect
|
// add the rect
|
||||||
// @ts-ignore TODO: Fix rough typings
|
// @ts-ignore TODO: Fix rough typings
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, {});
|
const options = userNodeOverrides(node, {
|
||||||
|
fill: themeVariables.noteBkgColor,
|
||||||
|
stroke: themeVariables.noteBorderColor,
|
||||||
|
});
|
||||||
|
|
||||||
if (node.look !== 'handDrawn') {
|
if (node.look !== 'handDrawn') {
|
||||||
options.roughness = 0;
|
options.roughness = 0;
|
||||||
|
@@ -3,7 +3,7 @@ import intersect from '../intersect/index.js';
|
|||||||
import type { Node, RenderOptions } from '../../types.js';
|
import type { Node, RenderOptions } from '../../types.js';
|
||||||
import type { SVG } from '../../../diagram-api/types.js';
|
import type { SVG } from '../../../diagram-api/types.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import { solidStateFill, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
|
|
||||||
export const stateEnd = (
|
export const stateEnd = (
|
||||||
parent: SVG,
|
parent: SVG,
|
||||||
@@ -13,7 +13,7 @@ export const stateEnd = (
|
|||||||
const { labelStyles, nodeStyles } = styles2String(node);
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
node.labelStyle = labelStyles;
|
node.labelStyle = labelStyles;
|
||||||
const { cssStyles } = node;
|
const { cssStyles } = node;
|
||||||
const { lineColor } = themeVariables;
|
const { lineColor, stateBorder, nodeBorder } = themeVariables;
|
||||||
const shapeSvg = parent
|
const shapeSvg = parent
|
||||||
.insert('g')
|
.insert('g')
|
||||||
.attr('class', 'node default')
|
.attr('class', 'node default')
|
||||||
@@ -29,14 +29,17 @@ export const stateEnd = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const roughNode = rc.circle(0, 0, 14, {
|
const roughNode = rc.circle(0, 0, 14, {
|
||||||
...solidStateFill(lineColor),
|
|
||||||
roughness: 0.5,
|
|
||||||
...options,
|
...options,
|
||||||
|
stroke: lineColor,
|
||||||
|
strokeWidth: 2,
|
||||||
});
|
});
|
||||||
|
const innerFill = stateBorder ?? nodeBorder;
|
||||||
const roughInnerNode = rc.circle(0, 0, 5, {
|
const roughInnerNode = rc.circle(0, 0, 5, {
|
||||||
...solidStateFill(lineColor),
|
|
||||||
fillStyle: 'solid',
|
|
||||||
...options,
|
...options,
|
||||||
|
fill: innerFill,
|
||||||
|
stroke: innerFill,
|
||||||
|
strokeWidth: 2,
|
||||||
|
fillStyle: 'solid',
|
||||||
});
|
});
|
||||||
const circle = shapeSvg.insert(() => roughNode, ':first-child');
|
const circle = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
circle.insert(() => roughInnerNode);
|
circle.insert(() => roughInnerNode);
|
||||||
|
@@ -143,4 +143,5 @@ export type LayoutMethod =
|
|||||||
|
|
||||||
export interface RenderOptions {
|
export interface RenderOptions {
|
||||||
config: MermaidConfig;
|
config: MermaidConfig;
|
||||||
|
dir: string;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user