diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js index d4de32889..bd04e1268 100644 --- a/packages/mermaid/src/diagrams/state/stateDb.js +++ b/packages/mermaid/src/diagrams/state/stateDb.js @@ -231,6 +231,15 @@ const extract = (_doc) => { const look = config.look; resetDataFetching(); dataFetcher(undefined, getRootDocV2(), diagramStates, nodes, edges, true, look); + nodes.forEach((node) => { + if (Array.isArray(node.label)) { + // add the rest as description + node.description = node.label.slice(1); + // add first description as label + node.label = node.label[0]; + } + }); + console.log('nodes after extract', nodes); }; /** diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js index c9fb0c529..c1caa697e 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js @@ -8,6 +8,7 @@ import { forkJoin } from './shapes/forkJoin.ts'; import { choice } from './shapes/choice.ts'; import { note } from './shapes/note.ts'; import { stadium } from './shapes/stadium.js'; +import { rectWithTitle } from './shapes/rectWithTitle.js'; import { getConfig } from '$root/diagram-api/diagramAPI.js'; import { subroutine } from './shapes/subroutine.js'; import { cylinder } from './shapes/cylinder.js'; @@ -36,7 +37,7 @@ const shapes = { choice, note, roundedRect, - rectWithTitle: roundedRect, + rectWithTitle, squareRect, stadium, subroutine, @@ -58,6 +59,8 @@ export const insertNode = async (elem, node, dir) => { let newEl; let el; + console.log('node DDD', node); + //special check for rect shape (with or without rounded corners) if (node.shape === 'rect') { if (node.rx && node.ry) { diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts new file mode 100644 index 000000000..431c4ebce --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectWithTitle.ts @@ -0,0 +1,146 @@ +import type { Node, RectOptions } from '$root/rendering-util/types.d.ts'; +import { select } from 'd3'; +import { evaluate } from '$root/diagrams/common/common.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import createLabel from '../createLabel.js'; +import intersect from '../intersect/index.js'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import { createRoundedRectPathD } from './roundedRectPath.js'; +import { log } from '$root/logger.js'; + +export const rectWithTitle = async (parent: SVGElement, node: Node) => { + let classes; + if (!node.cssClasses) { + classes = 'node default'; + } else { + classes = 'node ' + node.cssClasses; + } + + // Add outer g element + const shapeSvg = parent + // @ts-ignore - d3 typings are not correct + .insert('g') + .attr('class', classes) + .attr('id', node.domId || node.id); + + // Create the title label and insert it after the rect + const g = shapeSvg.insert('g'); + + const label = shapeSvg.insert('g').attr('class', 'label'); + + const description = node.description; + + let title = node.label; + + const text = label.node().appendChild(createLabel(title, node.labelStyle, true, true)); + let bbox = { width: 0, height: 0 }; + if (evaluate(getConfig()?.flowchart?.htmlLabels)) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + log.info('Text 2', description); + const textRows = description || []; + let titleBox = text.getBBox(); + const descr = label + .node() + .appendChild( + createLabel(textRows.join ? textRows.join('
') : textRows, node.labelStyle, true, true) + ); + + if (evaluate(getConfig()?.flowchart?.htmlLabels)) { + const div = descr.children[0]; + const dv = select(descr); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + + const halfPadding = (node.padding || 0) / 2; + select(descr).attr( + 'transform', + 'translate( ' + + (bbox.width > titleBox.width ? 0 : (titleBox.width - bbox.width) / 2) + + ', ' + + (titleBox.height + halfPadding + 5) + + ')' + ); + select(text).attr( + 'transform', + 'translate( ' + + (bbox.width < titleBox.width ? 0 : -(titleBox.width - bbox.width) / 2) + + ', ' + + 0 + + ')' + ); + // Get the size of the label + + // Bounding box for title and text + bbox = label.node().getBBox(); + + // Center the label + label.attr( + 'transform', + 'translate(' + -bbox.width / 2 + ', ' + (-bbox.height / 2 - halfPadding + 3) + ')' + ); + + const totalWidth = bbox.width + (node.padding || 0); + const totalHeight = bbox.height + (node.padding || 0); + const x = -bbox.width / 2 - halfPadding; + const y = -bbox.height / 2 - halfPadding; + let rect; + let innerLine; + if (node.look === 'handdrawn') { + // @ts-ignore No typings for rough + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const roughNode = rc.path( + createRoundedRectPathD(x, y, totalWidth, totalHeight, node.rx || 0), + options + ); + + const roughLine = rc.line( + -bbox.width / 2 - halfPadding, + -bbox.height / 2 - halfPadding + titleBox.height + halfPadding, + bbox.width / 2 + halfPadding, + -bbox.height / 2 - halfPadding + titleBox.height + halfPadding, + options + ); + + innerLine = shapeSvg.insert(() => { + log.debug('Rough node insert CXC', roughNode); + return roughLine; + }, ':first-child'); + rect = shapeSvg.insert(() => { + log.debug('Rough node insert CXC', roughNode); + return roughNode; + }, ':first-child'); + } else { + rect = g.insert('rect', ':first-child'); + innerLine = g.insert('line'); + rect + .attr('class', 'outer title-state') + .attr('x', -bbox.width / 2 - halfPadding) + .attr('y', -bbox.height / 2 - halfPadding) + .attr('width', bbox.width + (node.padding || 0)) + .attr('height', bbox.height + (node.padding || 0)); + + innerLine + .attr('class', 'divider') + .attr('x1', -bbox.width / 2 - halfPadding) + .attr('x2', bbox.width / 2 + halfPadding) + .attr('y1', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding) + .attr('y2', -bbox.height / 2 - halfPadding + titleBox.height + halfPadding); + } + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/types.d.ts b/packages/mermaid/src/rendering-util/types.d.ts index c7efdc504..55aafd20a 100644 --- a/packages/mermaid/src/rendering-util/types.d.ts +++ b/packages/mermaid/src/rendering-util/types.d.ts @@ -13,6 +13,7 @@ export type CheckFitFunction = (text: MarkdownLine) => boolean; interface Node { id: string; label?: string; + description?: string[]; parentId?: string; position?: string; // Keep, this is for notes 'left of', 'right of', etc. Move into nodeNode cssStyles?: string; // Renamed from `styles` to `cssStyles`