diff --git a/docs/config/setup/interfaces/mermaid.RenderResult.md b/docs/config/setup/interfaces/mermaid.RenderResult.md index f4960e6d0..6fd67a2ac 100644 --- a/docs/config/setup/interfaces/mermaid.RenderResult.md +++ b/docs/config/setup/interfaces/mermaid.RenderResult.md @@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present. #### Defined in -[packages/mermaid/src/types.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L100) +[packages/mermaid/src/types.ts:105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L105) --- @@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc. #### Defined in -[packages/mermaid/src/types.ts:90](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L90) +[packages/mermaid/src/types.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L95) --- @@ -63,4 +63,4 @@ The svg code for the rendered graph. #### Defined in -[packages/mermaid/src/types.ts:86](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L86) +[packages/mermaid/src/types.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L91) diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts index 39ad5dcda..9ddd81780 100644 --- a/packages/mermaid-layout-elk/src/render.ts +++ b/packages/mermaid-layout-elk/src/render.ts @@ -10,6 +10,21 @@ import { curveLinear } from 'd3'; import ELK from 'elkjs/lib/elk.bundled.js'; import { type TreeData, findCommonAncestor } from './find-common-ancestor.js'; +type Node = LayoutData['nodes'][number]; + +interface LabelData { + width: number; + height: number; + wrappingWidth?: number; + labelNode?: SVGGElement | null; +} + +interface NodeWithVertex extends Omit { + children?: unknown[]; + labelData?: LabelData; + domId?: Node['domId'] | SVGGroup | d3.Selection; +} + export const render = async ( data4Layout: LayoutData, svg: SVG, @@ -31,27 +46,37 @@ export const render = async ( const nodeDb: Record = {}; const clusterDb: Record = {}; - const addVertex = async (nodeEl: any, graph: { children: any[] }, nodeArr: any, node: any) => { - const labelData: any = { width: 0, height: 0 }; + const addVertex = async ( + nodeEl: SVGGroup, + graph: { children: NodeWithVertex[] }, + nodeArr: Node[], + node: Node + ) => { + const labelData: LabelData = { width: 0, height: 0 }; - let boundingBox; - const child = { - ...node, - }; - graph.children.push(child); - nodeDb[node.id] = child; const config = getConfig(); // Add the element to the DOM if (!node.isGroup) { + const child: NodeWithVertex = { + ...node, + }; + graph.children.push(child); + nodeDb[node.id] = child; + const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir }); - boundingBox = childNodeEl.node().getBBox(); + const boundingBox = childNodeEl.node()!.getBBox(); child.domId = childNodeEl; child.width = boundingBox.width; child.height = boundingBox.height; } else { // A subgraph - child.children = []; + const child: NodeWithVertex & { children: NodeWithVertex[] } = { + ...node, + children: [], + }; + graph.children.push(child); + nodeDb[node.id] = child; await addVertices(nodeEl, nodeArr, child, node.id); if (node.label) { @@ -75,28 +100,16 @@ export const render = async ( }; const addVertices = async function ( - nodeEl: any, - nodeArr: any[], - graph: { - id: string; - layoutOptions: { - 'elk.hierarchyHandling': string; - 'elk.algorithm': any; - 'nodePlacement.strategy': any; - 'elk.layered.mergeEdges': any; - 'elk.direction': string; - 'spacing.baseValue': number; - }; - children: never[]; - edges: never[]; - }, - parentId?: undefined + nodeEl: SVGGroup, + nodeArr: Node[], + graph: { children: NodeWithVertex[] }, + parentId?: string ) { - const siblings = nodeArr.filter((node: { parentId: any }) => node.parentId === parentId); + const siblings = nodeArr.filter((node) => node?.parentId === parentId); log.info('addVertices APA12', siblings, parentId); // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition await Promise.all( - siblings.map(async (node: any) => { + siblings.map(async (node) => { await addVertex(nodeEl, graph, nodeArr, node); }) ); diff --git a/packages/mermaid/src/rendering-util/createText.ts b/packages/mermaid/src/rendering-util/createText.ts index 4eb8a275b..9d84600c4 100644 --- a/packages/mermaid/src/rendering-util/createText.ts +++ b/packages/mermaid/src/rendering-util/createText.ts @@ -207,7 +207,7 @@ export const createText = async ( width = 200, addSvgBackground = false, } = {}, - config: MermaidConfig + config?: MermaidConfig ) => { log.debug( 'XYZ createText', diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.spec.ts b/packages/mermaid/src/rendering-util/rendering-elements/nodes.spec.ts index a7a7691e5..c1f0e1437 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/nodes.spec.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.spec.ts @@ -84,7 +84,7 @@ describe('Test Alias for shapes', function () { }); it('should support alias for shadedProcess shape ', function () { - const aliases = ['lined-process', 'lined-rectangle', 'lin-proc', 'lin-rect']; + const aliases = ['lined-process', 'lined-rectangle', 'lin-proc', 'lin-rect'] as const; for (const alias of aliases) { expect(shapes[alias]).toBe(shapes['shaded-process']); } diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js b/packages/mermaid/src/rendering-util/rendering-elements/nodes.ts similarity index 60% rename from packages/mermaid/src/rendering-util/rendering-elements/nodes.js rename to packages/mermaid/src/rendering-util/rendering-elements/nodes.ts index 4a9cfac57..2deb9ec64 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.ts @@ -1,10 +1,18 @@ import { log } from '../../logger.js'; import { shapes } from './shapes.js'; +import type { Node, ShapeRenderOptions } from '../types.js'; +import type { SVGGroup } from '../../mermaid.js'; +import type { D3Selection } from '../../types.js'; +import { handleUndefinedAttr } from '../../utils.js'; +import type { graphlib } from 'dagre-d3-es'; -const nodeElems = new Map(); +type ShapeHandler = (typeof shapes)[keyof typeof shapes]; +type NodeElement = D3Selection | Awaited>; -export const insertNode = async (elem, node, renderOptions) => { - let newEl; +const nodeElems = new Map(); + +export async function insertNode(elem: SVGGroup, node: Node, renderOptions: ShapeRenderOptions) { + let newEl: NodeElement | undefined; let el; //special check for rect shape (with or without rounded corners) @@ -16,7 +24,7 @@ export const insertNode = async (elem, node, renderOptions) => { } } - const shapeHandler = shapes[node.shape]; + const shapeHandler = shapes[(node.shape ?? 'undefined') as keyof typeof shapes]; if (!shapeHandler) { throw new Error(`No such shape: ${node.shape}. Please check your syntax.`); @@ -30,7 +38,10 @@ export const insertNode = async (elem, node, renderOptions) => { } else if (node.linkTarget) { target = node.linkTarget || '_blank'; } - newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target); + newEl = elem + .insert('svg:a') + .attr('xlink:href', node.link) + .attr('target', target ?? null); el = await shapeHandler(newEl, node, renderOptions); } else { el = await shapeHandler(elem, node, renderOptions); @@ -40,7 +51,7 @@ export const insertNode = async (elem, node, renderOptions) => { newEl.attr('data-id', node.id); newEl.attr('data-node', true); newEl.attr('data-et', 'node'); - newEl.attr('data-look', node.look); + newEl.attr('data-look', handleUndefinedAttr(node.look)); if (node.tooltip) { el.attr('title', node.tooltip); @@ -49,12 +60,12 @@ export const insertNode = async (elem, node, renderOptions) => { nodeElems.set(node.id, newEl); if (node.haveCallback) { - nodeElems.get(node.id).attr('class', nodeElems.get(node.id).attr('class') + ' clickable'); + newEl.attr('class', newEl.attr('class') + ' clickable'); } return newEl; -}; +} -export const setNodeElem = (elem, node) => { +export const setNodeElem = (elem: NodeElement, node: Pick) => { nodeElems.set(node.id, elem); }; @@ -62,8 +73,8 @@ export const clear = () => { nodeElems.clear(); }; -export const positionNode = (node) => { - const el = nodeElems.get(node.id); +export const positionNode = (node: ReturnType) => { + const el = nodeElems.get(node.id)!; log.trace( 'Transforming node', node.diff, diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts index b336fc823..89beb85e0 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts @@ -1,3 +1,5 @@ +import type { Entries } from 'type-fest'; +import type { D3Selection, MaybePromise } from '../../types.js'; import type { Node, ShapeRenderOptions } from '../types.js'; import { anchor } from './shapes/anchor.js'; import { bowTieRect } from './shapes/bowTieRect.js'; @@ -56,8 +58,11 @@ import { waveEdgedRectangle } from './shapes/waveEdgedRectangle.js'; import { waveRectangle } from './shapes/waveRectangle.js'; import { windowPane } from './shapes/windowPane.js'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type ShapeHandler = (parent: any, node: Node, options: ShapeRenderOptions) => unknown; +type ShapeHandler = ( + parent: D3Selection, + node: Node, + options: ShapeRenderOptions +) => MaybePromise>; export interface ShapeDefinition { semanticName: string; @@ -75,7 +80,7 @@ export interface ShapeDefinition { handler: ShapeHandler; } -export const shapesDefs: ShapeDefinition[] = [ +export const shapesDefs = [ { semanticName: 'Process', name: 'Rectangle', @@ -442,11 +447,11 @@ export const shapesDefs: ShapeDefinition[] = [ aliases: ['lined-document'], handler: linedWaveEdgedRect, }, -]; +] as const satisfies ShapeDefinition[]; const generateShapeMap = () => { // These are the shapes that didn't have documentation present - const shapeMap: Record = { + const undocumentedShapes = { // States state, choice, @@ -464,18 +469,25 @@ const generateShapeMap = () => { imageSquare, anchor, - }; + } as const; - for (const shape of shapesDefs) { - for (const alias of [ - shape.shortName, - ...(shape.aliases ?? []), - ...(shape.internalAliases ?? []), - ]) { - shapeMap[alias] = shape.handler; - } - } - return shapeMap; + const entries = [ + ...(Object.entries(undocumentedShapes) as Entries), + ...shapesDefs.flatMap((shape) => { + const aliases = [ + shape.shortName, + ...('aliases' in shape ? shape.aliases : []), + ...('internalAliases' in shape ? shape.internalAliases : []), + ]; + return aliases.map((alias) => [alias, shape.handler] as const); + }), + ]; + return Object.fromEntries(entries) as Record< + (typeof entries)[number][0], + (typeof entries)[number][1] + > satisfies Record; }; export const shapes = generateShapeMap(); + +export type ShapeID = keyof typeof shapes; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts index af2f1cd63..11821a11b 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts @@ -1,11 +1,13 @@ import { log } from '../../../logger.js'; import { updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import { handleUndefinedAttr } from '../../../utils.js'; +import type { D3Selection } from '../../../types.js'; -export const anchor = (parent: SVGAElement, node: Node): Promise => { +export function anchor(parent: D3Selection, node: Node) { const { labelStyles } = styles2String(node); node.labelStyle = labelStyles; const classes = getNodeClasses(node); @@ -14,7 +16,6 @@ export const anchor = (parent: SVGAElement, node: Node): Promise => cssClasses = 'anchor'; } const shapeSvg = parent - // @ts-ignore - SVGElement is not typed .insert('g') .attr('class', cssClasses) .attr('id', node.domId || node.id); @@ -23,6 +24,7 @@ export const anchor = (parent: SVGAElement, node: Node): Promise => const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { fill: 'black', stroke: 'none', fillStyle: 'solid' }); @@ -31,7 +33,7 @@ export const anchor = (parent: SVGAElement, node: Node): Promise => } const roughNode = rc.circle(0, 0, radius * 2, options); const circleElem = shapeSvg.insert(() => roughNode, ':first-child'); - circleElem.attr('class', 'anchor').attr('style', cssStyles); + circleElem.attr('class', 'anchor').attr('style', handleUndefinedAttr(cssStyles)); updateNodeBounds(node, circleElem); @@ -41,4 +43,4 @@ export const anchor = (parent: SVGAElement, node: Node): Promise => }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts index 2690f8579..164473645 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts @@ -1,8 +1,9 @@ import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; function generateArcPoints( x1: number, @@ -84,7 +85,7 @@ function calculateArcSagitta(chord: number, radiusX: number, radiusY: number) { return semiMinorAxis * (1 - Math.sqrt(1 - (chord / semiMajorAxis / 2) ** 2)); } -export const bowTieRect = async (parent: SVGAElement, node: Node) => { +export async function bowTieRect(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -132,6 +133,7 @@ export const bowTieRect = async (parent: SVGAElement, node: Node) => { ...generateArcPoints(w / 2, h / 2, w / 2, -h / 2, rx, ry, true), ]; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -163,4 +165,4 @@ export const bowTieRect = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts index edc7c538f..6a8597402 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts @@ -1,11 +1,12 @@ import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { insertPolygonShape } from './insertPolygonShape.js'; import { createPathFromPoints } from './util.js'; +import type { D3Selection } from '../../../types.js'; // const createPathFromPoints = (points: { x: number; y: number }[]): string => { // const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`); @@ -16,7 +17,7 @@ import { createPathFromPoints } from './util.js'; /// Size of the notch on the top left corner const NOTCH_SIZE = 12; -export async function card(parent: SVGAElement, node: Node): Promise { +export async function card(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -49,10 +50,11 @@ export async function card(parent: SVGAElement, node: Node): Promise; + let polygon: D3Selection | Awaited>; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createPathFromPoints(points); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/choice.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/choice.ts index 6cb747898..4edd68587 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/choice.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/choice.ts @@ -1,12 +1,11 @@ import intersect from '../intersect/index.js'; import type { Node } from '../../types.js'; -import type { SVG } from '../../../diagram-api/types.js'; -// @ts-ignore TODO: Fix rough typings import rough from 'roughjs'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { createPathFromPoints, getNodeClasses } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const choice = (parent: SVG, node: Node) => { +export function choice(parent: D3Selection, node: Node) { const { nodeStyles } = styles2String(node); node.label = ''; const shapeSvg = parent @@ -24,7 +23,7 @@ export const choice = (parent: SVG, node: Node) => { { x: -s / 2, y: 0 }, ]; - // @ts-ignore TODO: Fix rough typings + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -53,4 +52,4 @@ export const choice = (parent: SVG, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts index 2d237f158..c2790d61a 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts @@ -4,8 +4,10 @@ import intersect from '../intersect/index.js'; import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; -export const circle = async (parent: SVGAElement, node: Node): Promise => { +export async function circle(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; // If incoming height & width are present, subtract the padding from them @@ -24,12 +26,13 @@ export const circle = async (parent: SVGAElement, node: Node): Promise roughNode, ':first-child'); - circleElem.attr('class', 'basic label-container').attr('style', cssStyles); + circleElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); } else { circleElem = shapeSvg .insert('circle', ':first-child') @@ -48,4 +51,4 @@ export const circle = async (parent: SVGAElement, node: Node): Promise { +export function crossedCircle(parent: D3Selection, node: Node) { node.label = ''; const shapeSvg = parent .insert('g') @@ -28,7 +28,7 @@ export const crossedCircle = (parent: SVG, node: Node) => { const radius = node?.width ? node?.width / 2 : node?.height ? node?.height / 2 : 25; const { cssStyles } = node; - // @ts-expect-error shapeSvg d3 class is incorrect? + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -58,4 +58,4 @@ export const crossedCircle = (parent: SVG, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts index fe8f6008c..060451a5e 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceLeft.ts @@ -1,8 +1,9 @@ import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; function generateCirclePoints( centerX: number, @@ -34,7 +35,10 @@ function generateCirclePoints( return points; } -export const curlyBraceLeft = async (parent: SVGAElement, node: Node) => { +export async function curlyBraceLeft( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); @@ -84,6 +88,7 @@ export const curlyBraceLeft = async (parent: SVGAElement, node: Node) => { { x: w / 2, y: h / 2 + radius }, ]; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { fill: 'none' }); @@ -126,4 +131,4 @@ export const curlyBraceLeft = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts index d406eee47..7f7ab4200 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraceRight.ts @@ -1,8 +1,9 @@ import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; function generateCirclePoints( centerX: number, @@ -34,7 +35,10 @@ function generateCirclePoints( return points; } -export const curlyBraceRight = async (parent: SVGAElement, node: Node) => { +export async function curlyBraceRight( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); @@ -87,6 +91,7 @@ export const curlyBraceRight = async (parent: SVGAElement, node: Node) => { { x: -w / 2, y: h / 2 + radius }, ]; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { fill: 'none' }); @@ -130,4 +135,4 @@ export const curlyBraceRight = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraces.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraces.ts index ad30d6eb9..97f5953f1 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraces.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curlyBraces.ts @@ -1,8 +1,9 @@ import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; function generateCirclePoints( centerX: number, @@ -34,7 +35,10 @@ function generateCirclePoints( return points; } -export const curlyBraces = async (parent: SVGAElement, node: Node) => { +export async function curlyBraces( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); @@ -101,6 +105,7 @@ export const curlyBraces = async (parent: SVGAElement, node: Node) => { ...generateCirclePoints(-w / 2 + radius + radius / 2, h / 2, radius, 30, -180, -270), ]; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { fill: 'transparent' }); @@ -142,4 +147,4 @@ export const curlyBraces = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts index 1068a5304..c70c66bf8 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts @@ -6,11 +6,15 @@ import { generateCirclePoints, } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; -export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => { +export async function curvedTrapezoid( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -39,6 +43,7 @@ export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => { const radius = h / 2; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -85,4 +90,4 @@ export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts index aa08b8111..76fa7b388 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts @@ -4,6 +4,8 @@ import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { getConfig } from '../../../config.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; export const createCylinderPathD = ( x: number, @@ -52,7 +54,7 @@ export const createInnerCylinderPathD = ( const MIN_HEIGHT = 8; const MIN_WIDTH = 8; -export const cylinder = async (parent: SVGAElement, node: Node) => { +export async function cylinder(parent: D3Selection, node: Node) { const { themeVariables } = getConfig(); const { useGradient } = themeVariables; const { labelStyles, nodeStyles } = styles2String(node); @@ -85,10 +87,11 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { const ry = rx / (2.5 + w / 50); const h = (node.height ? node.height : bbox.height) + labelPaddingX + ry; - let cylinder: d3.Selection; + let cylinder: D3Selection | D3Selection; const { cssStyles } = node; if (node.look === 'handDrawn' || (node.look === 'neo' && !useGradient)) { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); @@ -118,7 +121,7 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { .insert('path', ':first-child') .attr('d', pathData) .attr('class', 'basic label-container') - .attr('style', cssStyles) + .attr('style', handleUndefinedAttr(cssStyles)) .attr('style', nodeStyles); } @@ -159,4 +162,4 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts index 68269f5c7..571466fab 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts @@ -1,10 +1,14 @@ import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; -export const dividedRectangle = async (parent: SVGAElement, node: Node) => { +export async function dividedRectangle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -34,6 +38,7 @@ export const dividedRectangle = async (parent: SVGAElement, node: Node) => { const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); if (node.look !== 'handDrawn') { @@ -81,4 +86,4 @@ export const dividedRectangle = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts index 9a76047f4..d5ea3aa76 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts @@ -1,8 +1,10 @@ import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; export const createCylinderPathD = ( x: number, @@ -48,7 +50,7 @@ export const createInnerCylinderPathD = ( ): string => { return [`M${x - width / 2},${-height / 2}`, `a${rx},${ry} 0,0,0 ${width},0`].join(' '); }; -export const cylinder = async (parent: SVGAElement, node: Node) => { +export async function cylinder(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); @@ -57,10 +59,11 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { const ry = rx / (2.5 + w / 50); const h = bbox.height + ry + node.padding; - let cylinder: d3.Selection; + let cylinder: D3Selection | D3Selection; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); @@ -79,7 +82,7 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { .insert('path', ':first-child') .attr('d', pathData) .attr('class', 'basic label-container') - .attr('style', cssStyles) + .attr('style', handleUndefinedAttr(cssStyles)) .attr('style', nodeStyles); } @@ -114,4 +117,4 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts index fa85ffa49..426d76471 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts @@ -4,8 +4,13 @@ import intersect from '../intersect/index.js'; import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; -export const doublecircle = async (parent: SVGAElement, node: Node): Promise => { +export async function doublecircle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); const gap = 5; node.labelStyle = labelStyles; @@ -30,6 +35,7 @@ export const doublecircle = async (parent: SVGAElement, node: Node): Promise outerRoughNode, ':first-child'); - circleGroup.attr('class', node.cssClasses).attr('style', cssStyles); + circleGroup + .attr('class', handleUndefinedAttr(node.cssClasses)) + .attr('style', handleUndefinedAttr(cssStyles)); circleGroup.node()?.appendChild(outerRoughNode); circleGroup.node()?.appendChild(innerRoughNode); @@ -73,4 +81,4 @@ export const doublecircle = async (parent: SVGAElement, node: Node): Promise { +export async function drawRect( + parent: D3Selection, + node: Node, + options: RectOptions +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; // If incoming height & width are present, subtract the padding from them @@ -45,7 +51,7 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt : rc.rectangle(x, y, totalWidth, totalHeight, options); rect = shapeSvg.insert(() => roughNode, ':first-child'); - rect.attr('class', 'basic label-container').attr('style', cssStyles); + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); } else { rect = shapeSvg.insert('rect', ':first-child'); @@ -54,9 +60,9 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt rect .attr('class', rectClass) .attr('style', nodeStyles) - .attr('rx', rx) + .attr('rx', handleUndefinedAttr(rx)) .attr('data-id', node.id) - .attr('ry', ry) + .attr('ry', handleUndefinedAttr(ry)) .attr('x', x) .attr('y', y) .attr('width', totalWidth) @@ -71,4 +77,4 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts index e9989d4ae..e3215e2df 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts @@ -1,16 +1,16 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import { log } from '../../../logger.js'; -import type { Node, ShapeRenderOptions } from '../../types.ts'; +import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { userNodeOverrides } from './handDrawnShapeStyles.js'; import { getNodeClasses, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const filledCircle = ( - parent: SVG, +export function filledCircle( + parent: D3Selection, node: Node, { config: { themeVariables } }: ShapeRenderOptions -) => { +) { node.label = ''; // If incoming height & width are present, subtract the padding from them @@ -42,7 +42,7 @@ export const filledCircle = ( const radius = (node.width ?? 0) / 2; const { cssStyles } = node; - // @ts-expect-error shapeSvg d3 class is incorrect? + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const { nodeBorder } = themeVariables; const options = userNodeOverrides(node, { fillStyle: 'solid' }); @@ -70,4 +70,4 @@ export const filledCircle = ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts index d3e0924b4..f1abf0e33 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts @@ -1,14 +1,18 @@ import { log } from '../../../logger.js'; import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { createPathFromPoints } from './util.js'; +import type { D3Selection } from '../../../types.js'; const MIN_HEIGHT = 10; const MIN_WIDTH = 10; -export const flippedTriangle = async (parent: SVGAElement, node: Node): Promise => { +export async function flippedTriangle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -41,6 +45,7 @@ export const flippedTriangle = async (parent: SVGAElement, node: Node): Promise< const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); if (node.look !== 'handDrawn') { @@ -78,4 +83,4 @@ export const flippedTriangle = async (parent: SVGAElement, node: Node): Promise< }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/forkJoin.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/forkJoin.ts index f68a0c93e..4f3701944 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/forkJoin.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/forkJoin.ts @@ -1,15 +1,15 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { getNodeClasses, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const forkJoin = ( - parent: SVG, +export function forkJoin( + parent: D3Selection, node: Node, { dir, config: { state, themeVariables } }: ShapeRenderOptions -) => { +) { const { nodeStyles } = styles2String(node); node.label = ''; const shapeSvg = parent @@ -29,7 +29,7 @@ export const forkJoin = ( const x = (-1 * width) / 2; const y = (-1 * height) / 2; - // @ts-ignore TODO: Fix rough typings + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { stroke: themeVariables.lineColor, @@ -63,4 +63,4 @@ export const forkJoin = ( return intersect.rect(node, point); }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts index 16c54b123..83ff8580b 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts @@ -7,11 +7,15 @@ import { generateCirclePoints, } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; -export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => { +export async function halfRoundedRectangle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const minWidth = 15, @@ -39,6 +43,7 @@ export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => { const radius = h / 2; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -81,4 +86,4 @@ export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => { return pos; }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts index cab5dc5ee..f752e5000 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts @@ -3,8 +3,8 @@ import intersect from '../intersect/index.js'; import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; - import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; export const createHexagonPathD = ( x: number, @@ -24,7 +24,7 @@ export const createHexagonPathD = ( ].join(' '); }; -export const hexagon = async (parent: SVGAElement, node: Node): Promise => { +export async function hexagon(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); const f = 4; node.labelStyle = labelStyles; @@ -53,10 +53,11 @@ export const hexagon = async (parent: SVGAElement, node: Node): Promise; + let polygon: D3Selection | Awaited>; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createHexagonPathD(0, 0, w, h, m); @@ -87,4 +88,4 @@ export const hexagon = async (parent: SVGAElement, node: Node): Promise { +export async function hourglass(parent: D3Selection, node: Node) { node.label = ''; const { shapeSvg } = await labelHelper(parent, node, getNodeClasses(node)); @@ -14,6 +15,7 @@ export const hourglass = async (parent: SVGAElement, node: Node) => { const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -49,4 +51,4 @@ export const hourglass = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts index 05aa8eeda..789dfe430 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts @@ -1,17 +1,17 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import { log } from '../../../logger.js'; import { getIconSVG } from '../../icons.js'; -import type { Node, ShapeRenderOptions } from '../../types.ts'; +import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const icon = async ( - parent: SVG, +export async function icon( + parent: D3Selection, node: Node, { config: { themeVariables, flowchart } }: ShapeRenderOptions -) => { +) { const { labelStyles } = styles2String(node); node.labelStyle = labelStyles; const assetHeight = node.assetHeight ?? 48; @@ -33,6 +33,7 @@ export const icon = async ( const labelPadding = node.label ? 8 : 0; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { stroke: 'none', fill: 'none' }); @@ -64,7 +65,7 @@ export const icon = async ( fallbackPrefix: '', })}` ); - const iconBBox = iconElem.node().getBBox(); + const iconBBox = iconElem.node()!.getBBox(); const iconWidth = iconBBox.width; const iconHeight = iconBBox.height; const iconX = iconBBox.x; @@ -79,9 +80,9 @@ export const icon = async ( ); iconElem.attr('style', `color : ${stylesMap.get('stroke') ?? nodeBorder};`); iconElem - .selectAll('path') + .selectAll('path') .nodes() - .forEach((path: SVGPathElement) => { + .forEach((path) => { if (path.getAttribute('fill') === 'currentColor') { path.setAttribute('class', 'icon'); } @@ -150,4 +151,4 @@ export const icon = async ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts index 87ef798c5..598e352a1 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts @@ -1,17 +1,17 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import { log } from '../../../logger.js'; import { getIconSVG } from '../../icons.js'; -import type { Node, ShapeRenderOptions } from '../../types.ts'; +import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const iconCircle = async ( - parent: SVG, +export async function iconCircle( + parent: D3Selection, node: Node, { config: { themeVariables, flowchart } }: ShapeRenderOptions -) => { +) { const { labelStyles } = styles2String(node); node.labelStyle = labelStyles; const defaultWidth = flowchart?.wrappingWidth; @@ -48,6 +48,7 @@ export const iconCircle = async ( const { nodeBorder } = themeVariables; const { stylesMap } = compileStyles(node); + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { stroke: 'transparent' }); @@ -66,7 +67,7 @@ export const iconCircle = async ( })}` ); } - const iconBBox = iconElem.node().getBBox(); + const iconBBox = iconElem.node()!.getBBox(); const iconWidth = iconBBox.width; // const iconHeight = iconBBox.height; const iconX = iconBBox.x; @@ -96,9 +97,9 @@ export const iconCircle = async ( ); iconElem.attr('style', `color : ${stylesMap.get('stroke') ?? nodeBorder};`); iconElem - .selectAll('path') + .selectAll('path') .nodes() - .forEach((path: SVGPathElement) => { + .forEach((path) => { if (path.getAttribute('fill') === 'currentColor') { path.setAttribute('class', 'icon'); } @@ -136,4 +137,4 @@ export const iconCircle = async ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts index a89ffe40e..338ad6e16 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts @@ -1,18 +1,18 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import { log } from '../../../logger.js'; import { getIconSVG } from '../../icons.js'; -import type { Node, ShapeRenderOptions } from '../../types.ts'; +import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { createRoundedRectPathD } from './roundedRectPath.js'; import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const iconRounded = async ( - parent: SVG, +export async function iconRounded( + parent: D3Selection, node: Node, { config: { themeVariables, flowchart } }: ShapeRenderOptions -) => { +) { const { labelStyles } = styles2String(node); node.labelStyle = labelStyles; const defaultWidth = flowchart?.wrappingWidth; @@ -51,6 +51,7 @@ export const iconRounded = async ( const x = -width / 2; const y = -height / 2; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { stroke: 'transparent' }); @@ -78,7 +79,7 @@ export const iconRounded = async ( iconElem.html( `${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}` ); - const iconBBox = iconElem.node().getBBox(); + const iconBBox = iconElem.node()!.getBBox(); const iconWidth = iconBBox.width; // const iconHeight = iconBBox.height; const iconX = iconBBox.x; @@ -89,9 +90,9 @@ export const iconRounded = async ( ); iconElem.attr('style', `color : ${stylesMap.get('stroke') ?? nodeBorder};`); iconElem - .selectAll('path') + .selectAll('path') .nodes() - .forEach((path: SVGPathElement) => { + .forEach((path) => { if (path.getAttribute('fill') === 'currentColor') { path.setAttribute('class', 'icon'); } @@ -157,4 +158,4 @@ export const iconRounded = async ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts index c4c9f9a55..ea50cf730 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts @@ -1,17 +1,17 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import { log } from '../../../logger.js'; import { getIconSVG } from '../../icons.js'; -import type { Node, ShapeRenderOptions } from '../../types.ts'; +import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const iconSquare = async ( - parent: SVG, +export async function iconSquare( + parent: D3Selection, node: Node, { config: { themeVariables, flowchart } }: ShapeRenderOptions -) => { +) { const { labelStyles } = styles2String(node); node.labelStyle = labelStyles; const defaultWidth = flowchart?.wrappingWidth; @@ -54,6 +54,7 @@ export const iconSquare = async ( const x = -width / 2; const y = -height / 2; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, { stroke: 'transparent' }); @@ -80,7 +81,7 @@ export const iconSquare = async ( iconElem.html( `${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}` ); - const iconBBox = iconElem.node().getBBox(); + const iconBBox = iconElem.node()!.getBBox(); const iconWidth = iconBBox.width; // const iconHeight = iconBBox.height; const iconX = iconBBox.x; @@ -91,9 +92,9 @@ export const iconSquare = async ( ); iconElem.attr('style', `color : ${stylesMap.get('stroke') ?? nodeBorder};`); iconElem - .selectAll('path') + .selectAll('path') .nodes() - .forEach((path: SVGPathElement) => { + .forEach((path) => { if (path.getAttribute('fill') === 'currentColor') { path.setAttribute('class', 'icon'); } @@ -169,4 +170,4 @@ export const iconSquare = async ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/imageSquare.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/imageSquare.ts index edc1f1371..d6e7f561b 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/imageSquare.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/imageSquare.ts @@ -1,15 +1,15 @@ -import type { SVG } from '../../../diagram-api/types.js'; import { log } from '../../../logger.js'; -import type { Node, ShapeRenderOptions } from '../../types.ts'; +import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const imageSquare = async ( - parent: SVG, +export async function imageSquare( + parent: D3Selection, node: Node, { config: { flowchart } }: ShapeRenderOptions -) => { +) { const img = new Image(); img.src = node?.img ?? ''; await img.decode(); @@ -161,4 +161,4 @@ export const imageSquare = async ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts index 6c00b9523..874996444 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts @@ -1,5 +1,7 @@ -export function insertPolygonShape( - parent: any, +import type { D3Selection } from '../../../types.js'; + +export function insertPolygonShape( + parent: D3Selection, w: number, h: number, points: { x: number; y: number }[] diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts index 48fa8e529..1ca28e824 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts @@ -4,6 +4,7 @@ import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; // export const createInvertedTrapezoidPathD = ( // x: number, @@ -20,7 +21,10 @@ import { insertPolygonShape } from './insertPolygonShape.js'; // ].join(' '); // }; -export const inv_trapezoid = async (parent: SVGAElement, node: Node): Promise => { +export async function inv_trapezoid( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -51,10 +55,11 @@ export const inv_trapezoid = async (parent: SVGAElement, node: Node): Promise; + let polygon: typeof shapeSvg | ReturnType; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createPathFromPoints(points); @@ -86,4 +91,4 @@ export const inv_trapezoid = async (parent: SVGAElement, node: Node): Promise { +export async function roundedRect( + parent: D3Selection, + node: Node +) { const options = { rx: 5, ry: 5, @@ -13,9 +17,9 @@ export const roundedRect = async (parent: SVGAElement, node: Node) => { } as RectOptions; return drawRect(parent, node, options); -}; +} -export const labelRect = async (parent: SVGElement, node: Node) => { +export async function labelRect(parent: D3Selection, node: Node) { const { shapeSvg, bbox, label } = await labelHelper(parent, node, 'label'); // log.trace('Classes = ', node.class); @@ -52,4 +56,4 @@ export const labelRect = async (parent: SVGElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts index 4d03d06e8..03c8b8d3c 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts @@ -4,8 +4,9 @@ import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; -export const lean_left = async (parent: SVGAElement, node: Node): Promise => { +export async function lean_left(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -36,10 +37,11 @@ export const lean_left = async (parent: SVGAElement, node: Node): Promise; + let polygon: typeof shapeSvg | ReturnType; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createPathFromPoints(points); @@ -71,4 +73,4 @@ export const lean_left = async (parent: SVGAElement, node: Node): Promise => { +export async function lean_right(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -38,10 +39,11 @@ export const lean_right = async (parent: SVGAElement, node: Node): Promise; + let polygon: typeof shapeSvg | ReturnType; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createPathFromPoints(points); @@ -72,4 +74,4 @@ export const lean_right = async (parent: SVGAElement, node: Node): Promise { +export function lightningBolt(parent: D3Selection, node: Node) { node.label = ''; const shapeSvg = parent .insert('g') @@ -58,4 +59,4 @@ export const lightningBolt = (parent: SVG, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts index 1aa5b3a97..c305ab67a 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts @@ -3,6 +3,8 @@ import intersect from '../intersect/index.js'; import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; export const createCylinderPathD = ( x: number, @@ -58,7 +60,10 @@ export const createInnerCylinderPathD = ( const MIN_HEIGHT = 10; const MIN_WIDTH = 10; -export const linedCylinder = async (parent: SVGAElement, node: Node) => { +export async function linedCylinder( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -89,10 +94,11 @@ export const linedCylinder = async (parent: SVGAElement, node: Node) => { const h = (node?.height ? node?.height : bbox.height) + ry + labelPaddingY; const outerOffset = h * 0.1; // 10% of height - let cylinder: d3.Selection; + let cylinder: typeof shapeSvg | D3Selection; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry, outerOffset); const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); @@ -114,7 +120,7 @@ export const linedCylinder = async (parent: SVGAElement, node: Node) => { .insert('path', ':first-child') .attr('d', pathData) .attr('class', 'basic label-container') - .attr('style', cssStyles) + .attr('style', handleUndefinedAttr(cssStyles)) .attr('style', nodeStyles); } @@ -155,4 +161,4 @@ export const linedCylinder = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts index ef2f3c534..e55ffbcac 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts @@ -5,11 +5,15 @@ import { generateFullSineWavePoints, } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import rough from 'roughjs'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; -export const linedWaveEdgedRect = async (parent: SVGAElement, node: Node) => { +export async function linedWaveEdgedRect( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -37,6 +41,7 @@ export const linedWaveEdgedRect = async (parent: SVGAElement, node: Node) => { const finalH = h + waveAmplitude; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -93,4 +98,4 @@ export const linedWaveEdgedRect = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts index 036b980e8..58ce7b49e 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts @@ -1,10 +1,11 @@ import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; -export const multiRect = async (parent: SVGAElement, node: Node) => { +export async function multiRect(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -34,6 +35,7 @@ export const multiRect = async (parent: SVGAElement, node: Node) => { const y = -h / 2; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -98,4 +100,4 @@ export const multiRect = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts index 5dc56667d..04908ddcf 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts @@ -6,11 +6,15 @@ import { generateFullSineWavePoints, } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import rough from 'roughjs'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; -export const multiWaveEdgedRectangle = async (parent: SVGAElement, node: Node) => { +export async function multiWaveEdgedRectangle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); @@ -84,6 +88,7 @@ export const multiWaveEdgedRectangle = async (parent: SVGAElement, node: Node) = { x, y }, ]; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -126,4 +131,4 @@ export const multiWaveEdgedRectangle = async (parent: SVGAElement, node: Node) = }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/note.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/note.ts index 926a76749..403294783 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/note.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/note.ts @@ -3,12 +3,13 @@ import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const note = async ( - parent: SVGAElement, +export async function note( + parent: D3Selection, node: Node, { config: { themeVariables } }: ShapeRenderOptions -) => { +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); @@ -55,4 +56,4 @@ export const note = async ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts index 50e946f95..11d166936 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts @@ -5,6 +5,7 @@ import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; export const createDecisionBoxPathD = (x: number, y: number, size: number): string => { return [ @@ -16,7 +17,7 @@ export const createDecisionBoxPathD = (x: number, y: number, size: number): stri ].join(' '); }; -export const question = async (parent: SVGAElement, node: Node): Promise => { +export async function question(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const padding = (node.padding ?? 0) * 4; @@ -44,10 +45,11 @@ export const question = async (parent: SVGAElement, node: Node): Promise; + let polygon: typeof shapeSvg | ReturnType; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createDecisionBoxPathD(0, 0, s); @@ -83,4 +85,4 @@ export const question = async (parent: SVGAElement, node: Node): Promise( + parent: D3Selection, node: Node -): Promise => { +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -36,6 +37,7 @@ export const rect_left_inv_arrow = async ( ]; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -71,4 +73,4 @@ export const rect_left_inv_arrow = async ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts index a71ebabd7..bcaf2787a 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts @@ -9,8 +9,12 @@ import rough from 'roughjs'; import { getConfig } from '../../../diagram-api/diagramAPI.js'; import { createRoundedRectPathD } from './roundedRectPath.js'; import { log } from '../../../logger.js'; +import type { D3Selection } from '../../../types.js'; -export const rectWithTitle = async (parent: SVGElement, node: Node) => { +export async function rectWithTitle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; let classes; @@ -36,7 +40,7 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => { const title = node.label; - const text = label.node().appendChild(await createLabel(title, node.labelStyle, true, true)); + const text = label.node()!.appendChild(await createLabel(title, node.labelStyle, true, true)); let bbox = { width: 0, height: 0 }; if (evaluate(getConfig()?.flowchart?.htmlLabels)) { const div = text.children[0]; @@ -49,7 +53,7 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => { const textRows = description || []; const titleBox = text.getBBox(); const descr = label - .node() + .node()! .appendChild( await createLabel( textRows.join ? textRows.join('
') : textRows, @@ -87,7 +91,7 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => { // Get the size of the label // Bounding box for title and text - bbox = label.node().getBBox(); + bbox = label.node()!.getBBox(); // Center the label label.attr( @@ -151,4 +155,4 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts index e43c00dc0..e2ad53fc5 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts @@ -1,7 +1,11 @@ import type { Node, RectOptions } from '../../types.js'; +import type { D3Selection } from '../../../types.js'; import { drawRect } from './drawRect.js'; -export const roundedRect = async (parent: SVGAElement, node: Node) => { +export async function roundedRect( + parent: D3Selection, + node: Node +) { const nodePadding = node.padding ?? 0; const options = { rx: node.look === 'neo' ? 3 : 5, @@ -13,4 +17,4 @@ export const roundedRect = async (parent: SVGAElement, node: Node) => { } as RectOptions; return drawRect(parent, node, options); -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts index 202447225..43979700f 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts @@ -1,13 +1,18 @@ import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; /// Width of the frame on the left of the shape const FRAME_WIDTH = 8; -export const shadedProcess = async (parent: SVGAElement, node: Node) => { +export async function shadedProcess( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -33,6 +38,7 @@ export const shadedProcess = async (parent: SVGAElement, node: Node) => { const y = -totalHeight / 2; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -58,7 +64,7 @@ export const shadedProcess = async (parent: SVGAElement, node: Node) => { const rect = shapeSvg.insert(() => roughNode, ':first-child'); - rect.attr('class', 'basic label-container').attr('style', cssStyles); + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); if (nodeStyles && node.look !== 'handDrawn') { rect.selectAll('path').attr('style', nodeStyles); @@ -75,4 +81,4 @@ export const shadedProcess = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts index ecdd745a6..eb0dd1ce5 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts @@ -1,10 +1,11 @@ import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; -export const slopedRect = async (parent: SVGAElement, node: Node) => { +export async function slopedRect(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -32,6 +33,7 @@ export const slopedRect = async (parent: SVGAElement, node: Node) => { const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -75,4 +77,4 @@ export const slopedRect = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts index 5ab8e2833..af72a798f 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts @@ -1,7 +1,8 @@ import type { Node, RectOptions } from '../../types.js'; +import type { D3Selection } from '../../../types.js'; import { drawRect } from './drawRect.js'; -export const squareRect = async (parent: SVGAElement, node: Node) => { +export async function squareRect(parent: D3Selection, node: Node) { const options = { rx: 0, ry: 0, @@ -10,4 +11,4 @@ export const squareRect = async (parent: SVGAElement, node: Node) => { labelPaddingY: (node?.padding || 0) * 1, } as RectOptions; return drawRect(parent, node, options); -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts index 13e0f33c8..84546c737 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts @@ -4,6 +4,8 @@ import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { createRoundedRectPathD } from './roundedRectPath.js'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; export const createStadiumPathD = ( x: number, @@ -50,7 +52,7 @@ export const createStadiumPathD = ( ].join(' '); }; -export const stadium = async (parent: SVGAElement, node: Node) => { +export async function stadium(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -82,6 +84,7 @@ export const stadium = async (parent: SVGAElement, node: Node) => { let rect; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -89,7 +92,7 @@ export const stadium = async (parent: SVGAElement, node: Node) => { const roughNode = rc.path(pathData, options); rect = shapeSvg.insert(() => roughNode, ':first-child'); - rect.attr('class', 'basic label-container').attr('style', cssStyles); + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); } else { rect = shapeSvg.insert('rect', ':first-child'); @@ -111,4 +114,4 @@ export const stadium = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts index 566a8a2b4..5ab6aaa61 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts @@ -1,11 +1,12 @@ import type { Node, RectOptions } from '../../types.js'; +import type { D3Selection } from '../../../types.js'; import { drawRect } from './drawRect.js'; -export const state = async (parent: SVGAElement, node: Node) => { +export async function state(parent: D3Selection, node: Node) { const options = { rx: node.look === 'neo' ? 3 : 5, ry: node.look === 'neo' ? 3 : 5, classes: 'flowchart-node', } as RectOptions; return drawRect(parent, node, options); -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateEnd.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateEnd.ts index 6b2c772df..7accda7c1 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateEnd.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateEnd.ts @@ -1,15 +1,15 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const stateEnd = ( - parent: SVG, +export function stateEnd( + parent: D3Selection, node: Node, { config: { themeVariables } }: ShapeRenderOptions -) => { +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { cssStyles } = node; @@ -83,4 +83,4 @@ export const stateEnd = ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateStart.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateStart.ts index 8f15e3bc7..6d433d10b 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateStart.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stateStart.ts @@ -1,15 +1,15 @@ import rough from 'roughjs'; -import type { SVG } from '../../../diagram-api/types.js'; import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; import { solidStateFill } from './handDrawnShapeStyles.js'; import { updateNodeBounds } from './util.js'; +import type { D3Selection } from '../../../types.js'; -export const stateStart = ( - parent: SVG, +export function stateStart( + parent: D3Selection, node: Node, { config: { themeVariables } }: ShapeRenderOptions -) => { +) { const { lineColor } = themeVariables; // If incoming height & width are present, subtract the padding from them @@ -39,25 +39,29 @@ export const stateStart = ( .attr('class', 'node default') .attr('id', node.domId || node.id); - let circle: d3.Selection; + let circle: D3Selection | D3Selection; if (node.look === 'handDrawn') { // @ts-ignore TODO: Fix rough typings const rc = rough.svg(shapeSvg); const roughNode = rc.circle(0, 0, node.width, solidStateFill(lineColor)); // @ts-ignore TODO: Fix typings circle = shapeSvg.insert(() => roughNode); + // center the circle around its coordinate + circle + .attr('class', 'state-start') + .attr('r', (node.width ?? 7) / 2) + .attr('width', node.width ?? 14) + .attr('height', node.height ?? 14); } else { circle = shapeSvg.insert('circle', ':first-child'); + // center the circle around its coordinate + circle + .attr('class', 'state-start') + .attr('r', (node.width ?? 7) / 2) + .attr('width', node.width ?? 14) + .attr('height', node.height ?? 14); } - // center the circle around its coordinate - // @ts-ignore TODO: Fix typings - circle - .attr('class', 'state-start') - .attr('r', (node.width ?? 7) / 2) - .attr('width', node.width ?? 14) - .attr('height', node.height ?? 14); - updateNodeBounds(node, circle); node.intersect = function (point) { @@ -65,4 +69,4 @@ export const stateStart = ( }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts index 2f125d5da..1c123c5c4 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts @@ -5,6 +5,8 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { insertPolygonShape } from './insertPolygonShape.js'; import { getConfig } from '../../../config.js'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; export const createSubroutinePathD = ( x: number, @@ -35,7 +37,7 @@ export const createSubroutinePathD = ( // width of the frame on the left and right side of the shape const FRAME_WIDTH = 8; -export const subroutine = async (parent: SVGAElement, node: Node) => { +export async function subroutine(parent: D3Selection, node: Node) { const { themeVariables } = getConfig(); const { useGradient } = themeVariables; const { labelStyles, nodeStyles } = styles2String(node); @@ -78,6 +80,7 @@ export const subroutine = async (parent: SVGAElement, node: Node) => { ]; if (node.look === 'handDrawn' || (node.look === 'neo' && !useGradient)) { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -96,7 +99,7 @@ export const subroutine = async (parent: SVGAElement, node: Node) => { l2El.attr('class', 'neo-line'); const rect = shapeSvg.insert(() => roughNode, ':first-child'); const { cssStyles } = node; - rect.attr('class', 'basic label-container').attr('style', cssStyles); + rect.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); updateNodeBounds(node, rect); } else { const el = insertPolygonShape(shapeSvg, w, h, points); @@ -111,4 +114,4 @@ export const subroutine = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts index 03fc9ee6c..86cf87de0 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts @@ -1,13 +1,14 @@ import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; /// The width/height of the tag in comparison to the height of the node const TAG_RATIO = 0.2; -export const taggedRect = async (parent: SVGAElement, node: Node) => { +export async function taggedRect(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -41,6 +42,7 @@ export const taggedRect = async (parent: SVGAElement, node: Node) => { const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -90,4 +92,4 @@ export const taggedRect = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts index 1947adab9..2dfab77a9 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts @@ -6,11 +6,15 @@ import { createPathFromPoints, } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import rough from 'roughjs'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; -export const taggedWaveEdgedRectangle = async (parent: SVGAElement, node: Node) => { +export async function taggedWaveEdgedRectangle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -46,6 +50,7 @@ export const taggedWaveEdgedRectangle = async (parent: SVGAElement, node: Node) const widthDif = minWidth - w; const extraW = widthDif > 0 ? widthDif / 2 : 0; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -120,4 +125,4 @@ export const taggedWaveEdgedRectangle = async (parent: SVGAElement, node: Node) }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts index 0a0bc4026..397c99b8b 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts @@ -1,9 +1,10 @@ import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; -export async function text(parent: SVGAElement, node: Node): Promise { +export async function text(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts index 64da61bbf..364a1dc0d 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts @@ -1,8 +1,10 @@ import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; export const createCylinderPathD = ( x: number, @@ -52,7 +54,10 @@ export const createInnerCylinderPathD = ( const MIN_HEIGHT = 5; const MIN_WIDTH = 10; -export const tiltedCylinder = async (parent: SVGAElement, node: Node) => { +export async function tiltedCylinder( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -80,9 +85,10 @@ export const tiltedCylinder = async (parent: SVGAElement, node: Node) => { const w = (node.width ? node.width : bbox.width) + rx + labelPadding; const { cssStyles } = node; - let cylinder: d3.Selection; + let cylinder: D3Selection | D3Selection; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); const innerPathData = createInnerCylinderPathD(0, 0, w, h, rx, ry); @@ -100,19 +106,19 @@ export const tiltedCylinder = async (parent: SVGAElement, node: Node) => { .insert('path', ':first-child') .attr('d', pathData) .attr('class', 'basic label-container') - .attr('style', cssStyles) + .attr('style', handleUndefinedAttr(cssStyles)) .attr('style', nodeStyles); + cylinder.attr('class', 'basic label-container'); + + if (cssStyles) { + cylinder.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles) { + cylinder.selectAll('path').attr('style', nodeStyles); + } } - cylinder.attr('class', 'basic label-container'); - - if (cssStyles && node.look !== 'handDrawn') { - cylinder.selectAll('path').attr('style', cssStyles); - } - - if (nodeStyles && node.look !== 'handDrawn') { - cylinder.selectAll('path').attr('style', nodeStyles); - } cylinder.attr('label-offset-x', rx); cylinder.attr('transform', `translate(${-w / 2}, ${h / 2} )`); @@ -149,4 +155,4 @@ export const tiltedCylinder = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts index a41258e14..eee46096c 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts @@ -4,6 +4,7 @@ import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { insertPolygonShape } from './insertPolygonShape.js'; +import type { D3Selection } from '../../../types.js'; // export const createTrapezoidPathD = ( // x: number, @@ -20,7 +21,7 @@ import { insertPolygonShape } from './insertPolygonShape.js'; // ].join(' '); // }; -export const trapezoid = async (parent: SVGAElement, node: Node): Promise => { +export async function trapezoid(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -51,10 +52,11 @@ export const trapezoid = async (parent: SVGAElement, node: Node): Promise; + let polygon: typeof shapeSvg | ReturnType; const { cssStyles } = node; if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); const pathData = createPathFromPoints(points); @@ -85,4 +87,4 @@ export const trapezoid = async (parent: SVGAElement, node: Node): Promise { +export async function trapezoidalPentagon( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -31,6 +35,7 @@ export const trapezoidalPentagon = async (parent: SVGAElement, node: Node) => { (node?.height ? node?.height : Math.max(minHeight, bbox.height)) + (labelPaddingY ?? 0) * 2; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -70,4 +75,4 @@ export const trapezoidalPentagon = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts index 2ef8db1e4..09603744c 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts @@ -1,17 +1,18 @@ import { log } from '../../../logger.js'; import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { createPathFromPoints } from './util.js'; import { evaluate } from '../../../diagrams/common/common.js'; import { getConfig } from '../../../diagram-api/diagramAPI.js'; +import type { D3Selection } from '../../../types.js'; const MIN_HEIGHT = 10; const MIN_WIDTH = 10; -export const triangle = async (parent: SVGAElement, node: Node): Promise => { +export async function triangle(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -40,6 +41,7 @@ export const triangle = async (parent: SVGAElement, node: Node): Promise { +export const labelHelper = async ( + parent: D3Selection, + node: Node, + _classes?: string +) => { let cssClasses; - const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels); + const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels); if (!_classes) { cssClasses = 'node default'; } else { @@ -21,7 +28,10 @@ export const labelHelper = async (parent, node, _classes) => { .attr('id', node.domId || node.id); // Create the label and insert it after the rect - const labelEl = shapeSvg.insert('g').attr('class', 'label').attr('style', node.labelStyle); + const labelEl = shapeSvg + .insert('g') + .attr('class', 'label') + .attr('style', handleUndefinedAttr(node.labelStyle)); // Replace label with default value if undefined let label; @@ -31,19 +41,19 @@ export const labelHelper = async (parent, node, _classes) => { label = typeof node.label === 'string' ? node.label : node.label[0]; } - let text; - text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), { + const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), { useHtmlLabels, - width: node.width < 0 ? 0 : node.width || getConfig().flowchart.wrappingWidth, + width: (node.width ?? 0) < 0 ? 0 : node.width || getConfig().flowchart?.wrappingWidth, + // @ts-expect-error -- This is currently not used. Should this be `classes` instead? cssClasses: 'markdown-node-label', style: node.labelStyle, addSvgBackground: !!node.icon || !!node.img, }); // Get the size of the label let bbox = text.getBBox(); - const halfPadding = node.padding / 2; + const halfPadding = (node?.padding ?? 0) / 2; - if (evaluate(getConfig().flowchart.htmlLabels)) { + if (evaluate(getConfig().flowchart?.htmlLabels)) { const div = text.children[0]; const dv = select(text); @@ -69,7 +79,8 @@ export const labelHelper = async (parent, node, _classes) => { ? getConfig().fontSize : window.getComputedStyle(document.body).fontSize; const enlargingFactor = 5; - const width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px'; + const [parsedBodyFontSize = defaultConfig.fontSize] = parseFontSize(bodyFontSize); + const width = parsedBodyFontSize * enlargingFactor + 'px'; img.style.minWidth = width; img.style.maxWidth = width; } else { @@ -109,8 +120,12 @@ export const labelHelper = async (parent, node, _classes) => { return { shapeSvg, bbox, halfPadding, label: labelEl }; }; -export const updateNodeBounds = (node, element) => { - const bbox = element.node().getBBox(); +export const updateNodeBounds = ( + node: Node, + // D3Selection is for the roughjs case, D3Selection is for the non-roughjs case + element: D3Selection | D3Selection +) => { + const bbox = element.node()!.getBBox(); node.width = bbox.width; node.height = bbox.height; log.debug('updateNodeBounds: #####################################'); @@ -119,12 +134,17 @@ export const updateNodeBounds = (node, element) => { }; /** - * @param parent - * @param w - * @param h - * @param points + * @param parent - Parent element to append the polygon to + * @param w - Width of the polygon + * @param h - Height of the polygon + * @param points - Array of points to create the polygon */ -export function insertPolygonShape(parent, w, h, points) { +export function insertPolygonShape( + parent: D3Selection, + w: number, + h: number, + points: Point[] +) { return parent .insert('polygon', ':first-child') .attr( @@ -139,16 +159,23 @@ export function insertPolygonShape(parent, w, h, points) { .attr('transform', 'translate(' + -w / 2 + ',' + h / 2 + ')'); } -export const getNodeClasses = (node, extra) => +export const getNodeClasses = (node: Node, extra?: string) => (node.look === 'handDrawn' ? 'rough-node' : 'node') + ' ' + node.cssClasses + ' ' + (extra || ''); -export function createPathFromPoints(points) { +export function createPathFromPoints(points: Point[]) { const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`); pointStrings.push('Z'); return pointStrings.join(' '); } -export function generateFullSineWavePoints(x1, y1, x2, y2, amplitude, numCycles) { +export function generateFullSineWavePoints( + x1: number, + y1: number, + x2: number, + y2: number, + amplitude: number, + numCycles: number +) { const points = []; const steps = 50; // Number of segments to create a smooth curve const deltaX = x2 - x1; @@ -170,13 +197,21 @@ export function generateFullSineWavePoints(x1, y1, x2, y2, amplitude, numCycles) return points; } +/** + * @param centerX - x-coordinate of center of circle + * @param centerY - y-coordinate of center of circle + * @param radius - radius of circle + * @param numPoints - total points required + * @param startAngle - angle where arc will start + * @param endAngle - angle where arc will end + */ export function generateCirclePoints( - centerX, // x-coordinate of center of circle - centerY, // x-coordinate of center of circle - radius, // radius of circle - numPoints, // total points required - startAngle, // angle where arc will start - endAngle // angle where arc will end + centerX: number, + centerY: number, + radius: number, + numPoints: number, + startAngle: number, + endAngle: number ) { const points = []; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts index 58c481af6..ab3217d3e 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts @@ -6,11 +6,15 @@ import { createPathFromPoints, } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import rough from 'roughjs'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import type { D3Selection } from '../../../types.js'; -export const waveEdgedRectangle = async (parent: SVGAElement, node: Node) => { +export async function waveEdgedRectangle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -44,6 +48,7 @@ export const waveEdgedRectangle = async (parent: SVGAElement, node: Node) => { const widthDif = minWidth - w; const extraW = widthDif > 0 ? widthDif / 2 : 0; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -94,4 +99,4 @@ export const waveEdgedRectangle = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts index 9ed2ef592..ceadd1503 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts @@ -6,11 +6,15 @@ import { generateFullSineWavePoints, } from './util.js'; import intersect from '../intersect/index.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; -export const waveRectangle = async (parent: SVGAElement, node: Node) => { +export async function waveRectangle( + parent: D3Selection, + node: Node +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const nodePadding = node.padding ?? 0; @@ -43,6 +47,7 @@ export const waveRectangle = async (parent: SVGAElement, node: Node) => { const finalH = h + waveAmplitude * 2; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -83,4 +88,4 @@ export const waveRectangle = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts index d4da0d228..2abb85346 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts @@ -1,13 +1,14 @@ import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; -import type { Node } from '../../types.ts'; +import type { Node } from '../../types.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import intersect from '../intersect/index.js'; +import type { D3Selection } from '../../../types.js'; /// Width of the frame on the top and left of the shape const rectOffset = 5; -export const windowPane = async (parent: SVGAElement, node: Node) => { +export async function windowPane(parent: D3Selection, node: Node) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; @@ -34,6 +35,7 @@ export const windowPane = async (parent: SVGAElement, node: Node) => { const y = -h / 2; const { cssStyles } = node; + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); @@ -81,4 +83,4 @@ export const windowPane = async (parent: SVGAElement, node: Node) => { }; return shapeSvg; -}; +} diff --git a/packages/mermaid/src/rendering-util/types.ts b/packages/mermaid/src/rendering-util/types.ts index 5c6be4877..776347f75 100644 --- a/packages/mermaid/src/rendering-util/types.ts +++ b/packages/mermaid/src/rendering-util/types.ts @@ -147,5 +147,6 @@ export type LayoutMethod = export interface ShapeRenderOptions { config: MermaidConfig; - dir: string; + /** Some shapes render differently if a diagram has a direction `LR` */ + dir?: Node['dir']; } diff --git a/packages/mermaid/src/types.ts b/packages/mermaid/src/types.ts index c26130bf5..196773e6a 100644 --- a/packages/mermaid/src/types.ts +++ b/packages/mermaid/src/types.ts @@ -79,6 +79,11 @@ export interface ParseResult { // This makes it clear that we're working with a d3 selected element of some kind, even though it's hard to specify the exact type. export type D3Element = any; +/** + * Helper type for d3 selections. + */ +export type D3Selection = d3.Selection; + export interface RenderResult { /** * The svg code for the rendered graph. @@ -99,3 +104,10 @@ export interface RenderResult { */ bindFunctions?: (element: Element) => void; } + +/** + * Can be converted back to `T` by awaiting/`Awaited`. + * + * This is useful for function types that may be either synchronous or asynchronous. + */ +export type MaybePromise = T | Promise; diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index 631b6dd85..f523197ae 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -1,5 +1,5 @@ import { sanitizeUrl } from '@braintree/sanitize-url'; -import type { CurveFactory } from 'd3'; +import type { BaseType, CurveFactory } from 'd3'; import { curveBasis, curveBasisClosed, @@ -940,3 +940,15 @@ export const getEdgeId = ( ) => { return `${prefix ? `${prefix}_` : ''}${from}_${to}_${counter}${suffix ? `_${suffix}` : ''}`; }; + +/** + * D3's `selection.attr` method doesn't officially support `undefined`. + * + * However, it seems if you do pass `undefined`, it seems to be treated as `null` + * (e.g. it removes the attribute). + */ +export function handleUndefinedAttr( + attrValue: Parameters['attr']>[1] | undefined +) { + return attrValue ?? null; +}