diff --git a/packages/mermaid/src/rendering-util/createText.ts b/packages/mermaid/src/rendering-util/createText.ts index 7dab485b4..cc189e46e 100644 --- a/packages/mermaid/src/rendering-util/createText.ts +++ b/packages/mermaid/src/rendering-util/createText.ts @@ -204,7 +204,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/shapes/util.js b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts similarity index 66% rename from packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js rename to packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts index eda1aa9af..fcae715d8 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts @@ -1,12 +1,18 @@ import { createText } from '../../createText.js'; +import type { Node } from '../../types.js'; import { getConfig } from '../../../diagram-api/diagramAPI.js'; import { select } from 'd3'; import { evaluate, sanitizeText } from '../../../diagrams/common/common.js'; -import { decodeEntities } from '../../../utils.js'; +import { decodeEntities, handleUndefinedAttr } from '../../../utils.js'; +import type { D3Selection, Point } from '../../../types.js'; -export const labelHelper = async (parent, node, _classes) => { +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 { @@ -20,7 +26,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; @@ -30,19 +39,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 || getConfig().flowchart.wrappingWidth, + width: 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); @@ -68,7 +77,11 @@ 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 = + typeof bodyFontSize === 'number' + ? bodyFontSize + : parseInt(bodyFontSize ?? '', 10); + const width = parsedBodyFontSize * enlargingFactor + 'px'; img.style.minWidth = width; img.style.maxWidth = width; } else { @@ -106,19 +119,28 @@ 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; }; /** - * @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( @@ -133,16 +155,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; @@ -164,13 +193,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 = [];