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 7adc0c465..fc56cd5ac 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/icon.ts @@ -1,60 +1,77 @@ import { log } from '../../../logger.js'; -import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds } from './util.js'; import type { Node } from '../../types.d.ts'; import type { SVG } from '../../../diagram-api/types.js'; -import { styles2String } from './handDrawnShapeStyles.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; import intersect from '../intersect/index.js'; import { getIconSVG } from '../../icons.js'; +import { getConfig } from '../../../diagram-api/diagramAPI.js'; export const icon = async (parent: SVG, node: Node) => { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; + const assetHeight = node.assetHeight ?? 48; + const assetWidth = node.assetWidth ?? 48; + const iconSize = Math.max(assetHeight, assetWidth); + const defaultWidth = getConfig()?.flowchart?.wrappingWidth; + node.width = Math.max(iconSize, defaultWidth ?? 0); const { shapeSvg, bbox, halfPadding, label } = await labelHelper( parent, node, - getNodeClasses(node) + 'icon-square default' ); - const { cssStyles } = node; - const topLabel = node.pos === 't'; - const labelWidth = Math.max(bbox.width + halfPadding * 2, node?.width ?? 0); - const labelHeight = Math.max(bbox.height + halfPadding * 2, node?.height ?? 0); - const iconHeight = node.assetHeight ?? 0; - const iconWidth = node.assetWidth ?? 0; - const iconSize = - iconWidth || iconHeight - ? Math.max(iconHeight, iconWidth) - : Math.max(labelHeight - halfPadding, labelWidth - halfPadding, 48); - // const width = Math.max(labelWidth, iconSize); - const height = labelHeight + iconSize; + const topLabel = node.pos === 't'; + + const height = (node.label ? iconSize + bbox.height : iconSize) + halfPadding * 2; + const width = Math.max(iconSize, bbox.width) + halfPadding * 2; + + const x = -width / 2; + const y = -height / 2; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { stroke: 'none', fill: 'none' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const iconNode = rc.rectangle(x, y, width, height, options); + + const iconShape = shapeSvg.insert(() => iconNode, ':first-child'); if (node.icon) { const iconElem = shapeSvg.append('g'); iconElem.html( `${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}` ); - const iconWidth = iconElem.node().getBBox().width; - const iconHeight = iconElem.node().getBBox().height; + const iconBBox = iconElem.node().getBBox(); + const iconWidth = iconBBox.width; + const iconHeight = iconBBox.height; iconElem.attr( 'transform', - `translate(${-iconWidth / 2}, ${-iconHeight / 2 - labelHeight / 2 - halfPadding + (topLabel ? labelHeight : halfPadding * 2)})` + `translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight - halfPadding / 2 : -height / 2 + halfPadding / 2})` ); } - if (cssStyles && node.look !== 'handDrawn') { - shapeSvg.selectAll('path').attr('style', cssStyles); - } - - if (nodeStyles && node.look !== 'handDrawn') { - shapeSvg.selectAll('path').attr('style', nodeStyles); - } - label.attr( 'transform', - `translate(${-labelWidth / 2 + halfPadding - (bbox.x - (bbox.left ?? 0))},${-height / 2 + iconSize - (topLabel ? iconSize - halfPadding : -halfPadding) - (bbox.y - (bbox.top ?? 0))})` + `translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 + halfPadding / 2 : -height / 2 + halfPadding * 1.5 + iconSize})` ); - updateNodeBounds(node, shapeSvg); + + if (cssStyles && node.look !== 'handDrawn') { + iconShape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + iconShape.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, iconShape); node.intersect = function (point) { log.info('iconSquare intersect', node, point); 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 648ecfd50..1b069cf38 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts @@ -1,69 +1,71 @@ import { log } from '../../../logger.js'; -import { createPathFromPoints, getNodeClasses, labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds } from './util.js'; import type { Node } from '../../types.d.ts'; import type { SVG } from '../../../diagram-api/types.js'; -import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import intersect from '../intersect/index.js'; import { getIconSVG } from '../../icons.js'; +import { getConfig } from '../../../diagram-api/diagramAPI.js'; export const iconSquare = async (parent: SVG, node: Node) => { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; + const assetHeight = node.assetHeight ?? 48; + const assetWidth = node.assetWidth ?? 48; + const iconSize = Math.max(assetHeight, assetWidth); + const defaultWidth = getConfig()?.flowchart?.wrappingWidth; + node.width = Math.max(iconSize, defaultWidth ?? 0); const { shapeSvg, bbox, halfPadding, label } = await labelHelper( parent, node, - getNodeClasses(node) + 'icon-square default' ); - const { cssStyles } = node; + const topLabel = node.pos === 't'; - const labelWidth = Math.max(bbox.width + halfPadding * 2, node?.width ?? 0); - const labelHeight = Math.max(bbox.height + halfPadding * 2, node?.height ?? 0); - const iconHeight = node.assetHeight ?? 0; - const iconWidth = node.assetWidth ?? 0; - const iconSize = - iconWidth || iconHeight - ? Math.max(iconHeight, iconWidth) - : Math.max(labelHeight - halfPadding, labelWidth - halfPadding, 48); - const width = Math.max(labelWidth, iconSize); - const height = labelHeight + iconSize; + const height = (node.label ? iconSize + bbox.height : iconSize) + halfPadding * 2; + const width = Math.max(iconSize, bbox.width) + halfPadding * 2; + const { themeVariables } = getConfig(); + const { mainBkg } = themeVariables; + const { stylesMap } = compileStyles(node); - const points = [ - { x: -width / 2, y: -height / 2 }, - { x: -width / 2, y: height / 2 }, - { x: width / 2, y: height / 2 }, - { x: width / 2, y: -height / 2 }, - ]; + const x = -width / 2; + const y = -height / 2; // @ts-ignore - rough is not typed const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, {}); + const options = userNodeOverrides(node, { stroke: stylesMap.get('fill') || mainBkg }); if (node.look !== 'handDrawn') { options.roughness = 0; options.fillStyle = 'solid'; } - const linePath = createPathFromPoints(points); - const lineNode = rc.path(linePath, options); + const iconNode = rc.rectangle(x, y, width, height, options); - const iconShape = shapeSvg.insert(() => lineNode, ':first-child'); + const iconShape = shapeSvg.insert(() => iconNode, ':first-child'); if (node.icon) { const iconElem = shapeSvg.append('g'); iconElem.html( `${await getIconSVG(node.icon, { height: iconSize, fallbackPrefix: '' })}` ); - const iconWidth = iconElem.node().getBBox().width; - const iconHeight = iconElem.node().getBBox().height; + const iconBBox = iconElem.node().getBBox(); + const iconWidth = iconBBox.width; + const iconHeight = iconBBox.height; iconElem.attr( 'transform', - `translate(${-iconWidth / 2}, ${-iconHeight / 2 - labelHeight / 2 - halfPadding + (topLabel ? labelHeight : halfPadding * 2)})` + `translate(${-iconWidth / 2},${topLabel ? height / 2 - iconHeight - halfPadding / 2 : -height / 2 + halfPadding / 2})` ); } + label.attr( + 'transform', + `translate(${-width / 2 + width / 2 - bbox.width / 2},${topLabel ? -height / 2 + halfPadding / 2 : -height / 2 + halfPadding * 1.5 + iconSize})` + ); + if (cssStyles && node.look !== 'handDrawn') { iconShape.selectAll('path').attr('style', cssStyles); } @@ -72,15 +74,11 @@ export const iconSquare = async (parent: SVG, node: Node) => { iconShape.selectAll('path').attr('style', nodeStyles); } - label.attr( - 'transform', - `translate(${-labelWidth / 2 + halfPadding - (bbox.x - (bbox.left ?? 0))},${-height / 2 + iconSize - (topLabel ? iconSize - halfPadding : -halfPadding) - (bbox.y - (bbox.top ?? 0))})` - ); updateNodeBounds(node, iconShape); node.intersect = function (point) { log.info('iconSquare intersect', node, point); - const pos = intersect.polygon(node, points, point); + const pos = intersect.rect(node, point); return pos; };