From b3a4e3f40eb88d796f44392fdaa1a565287ee2ae Mon Sep 17 00:00:00 2001 From: saurabhg772244 Date: Wed, 14 Aug 2024 18:48:12 +0530 Subject: [PATCH] Added windowPane --- .../integration/rendering/newShapes.spec.ts | 12 ++- .../rendering-elements/nodes.js | 2 + .../rendering-elements/shapes/windowPane.ts | 83 +++++++++++++++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts diff --git a/cypress/integration/rendering/newShapes.spec.ts b/cypress/integration/rendering/newShapes.spec.ts index 6bbb648ca..526cda3b6 100644 --- a/cypress/integration/rendering/newShapes.spec.ts +++ b/cypress/integration/rendering/newShapes.spec.ts @@ -10,10 +10,16 @@ const newShapesSet1 = [ 'flippedTriangle', 'hourglass', ] as const; -const newShapesSet2 = ['taggedRect', 'multiRect', 'lightningBolt', 'filledCircle'] as const; +const newShapesSet2 = [ + 'taggedRect', + 'multiRect', + 'lightningBolt', + 'filledCircle', + 'windowPane', +] as const; // Aggregate all shape sets into a single array -const newShapesSets = [newShapesSet1, newShapesSet2] as const; +const newShapesSets = [['windowPane']] as const; looks.forEach((look) => { directions.forEach((direction) => { @@ -59,7 +65,7 @@ looks.forEach((look) => { imgSnapshotTest(flowchartCode, { look, htmlLabels: false }); }); - it.skip(`with styles`, () => { + it(`with styles`, () => { let flowchartCode = `flowchart ${direction}\n`; newShapesSet.forEach((newShape, index) => { flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new shape' }@\n`; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js index 6bcd9b67f..ce3c890fe 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js @@ -45,6 +45,7 @@ import { waveEdgedRectangle } from './shapes/waveEdgedRectangle.js'; import { lightningBolt } from './shapes/lightningBolt.js'; import { filledCircle } from './shapes/filledCircle.js'; import { multiWaveEdgedRectangle } from './shapes/multiWaveEdgedRectangle.js'; +import { windowPane } from './shapes/windowPane.js'; const shapes = { state, @@ -93,6 +94,7 @@ const shapes = { lightningBolt, filledCircle, multiWaveEdgedRectangle, + windowPane, }; const nodeElems = new Map(); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts new file mode 100644 index 000000000..6bd9c2c8e --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts @@ -0,0 +1,83 @@ +import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; + +export const windowPane = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const rectOffset = 5; + const x = -w / 2; + const y = -h / 2; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const outerPathPoints = [ + { x: x - rectOffset, y: y - rectOffset }, + { x: x - rectOffset, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y: y - rectOffset }, + ]; + + const innerPathPoints = [ + { x: x - rectOffset, y }, + { x: x + w, y }, + ]; + + const innerSecondPathPoints = [ + { x, y: y - rectOffset }, + { x, y: y + h }, + ]; + + if (node.look !== 'handdrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const outerPath = createPathFromPoints(outerPathPoints); + const outerNode = rc.path(outerPath, options); + const innerPath = createPathFromPoints(innerPathPoints); + const innerNode = rc.path(innerPath, options); + const innerSecondPath = createPathFromPoints(innerSecondPathPoints); + const innerSecondNode = rc.path(innerSecondPath, options); + + const taggedRect = shapeSvg.insert('g', ':first-child'); + taggedRect.insert(() => innerNode, ':first-child'); + taggedRect.insert(() => innerSecondNode, ':first-child'); + taggedRect.insert(() => outerNode, ':first-child'); + taggedRect.attr('transform', `translate(${rectOffset / 2}, ${rectOffset / 2})`); + + taggedRect.attr('class', 'basic label-container'); + + if (cssStyles) { + taggedRect.attr('style', cssStyles); + } + + if (nodeStyles) { + taggedRect.attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) + rectOffset / 2}, ${-(bbox.height / 2) + rectOffset / 2})` + ); + + updateNodeBounds(node, taggedRect); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +};