mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-29 05:06:43 +02:00
add support for cloud and bang shape
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
This commit is contained in:
@@ -105,6 +105,126 @@ root))bang((
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Bang and cloud shape with dagre layout', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`---
|
||||||
|
config:
|
||||||
|
layout: dagre
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
`,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
shouldHaveRoot
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Bang and cloud shape with elk layout', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`---
|
||||||
|
config:
|
||||||
|
layout: elk
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
`,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
shouldHaveRoot
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Bang and cloud shape with cose-bilkent layout', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`---
|
||||||
|
config:
|
||||||
|
layout: cose-bilkent
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
`,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
shouldHaveRoot
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Bang and cloud shape with tidy-tree layout', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`---
|
||||||
|
config:
|
||||||
|
layout: tidy-tree
|
||||||
|
---
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
::icon(fa fa-book)
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
`,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
shouldHaveRoot
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('braches', () => {
|
it('braches', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`mindmap
|
`mindmap
|
||||||
|
@@ -206,9 +206,9 @@ const flattenNodes = (node: MindmapNode, processedNodes: MindmapLayoutNode[]): v
|
|||||||
case nodeType.ROUNDED_RECT:
|
case nodeType.ROUNDED_RECT:
|
||||||
return 'rounded';
|
return 'rounded';
|
||||||
case nodeType.CLOUD:
|
case nodeType.CLOUD:
|
||||||
return 'rounded'; // Map cloud to rounded for now
|
return 'cloud';
|
||||||
case nodeType.BANG:
|
case nodeType.BANG:
|
||||||
return 'circle'; // Map bang to circle for now
|
return 'bang';
|
||||||
case nodeType.HEXAGON:
|
case nodeType.HEXAGON:
|
||||||
return 'hexagon';
|
return 'hexagon';
|
||||||
case nodeType.DEFAULT:
|
case nodeType.DEFAULT:
|
||||||
|
@@ -61,6 +61,8 @@ import { erBox } from './shapes/erBox.js';
|
|||||||
import { classBox } from './shapes/classBox.js';
|
import { classBox } from './shapes/classBox.js';
|
||||||
import { requirementBox } from './shapes/requirementBox.js';
|
import { requirementBox } from './shapes/requirementBox.js';
|
||||||
import { kanbanItem } from './shapes/kanbanItem.js';
|
import { kanbanItem } from './shapes/kanbanItem.js';
|
||||||
|
import { bang } from './shapes/bang.js';
|
||||||
|
import { cloud } from './shapes/cloud.js';
|
||||||
|
|
||||||
type ShapeHandler = <T extends SVGGraphicsElement>(
|
type ShapeHandler = <T extends SVGGraphicsElement>(
|
||||||
parent: D3Selection<T>,
|
parent: D3Selection<T>,
|
||||||
@@ -135,6 +137,22 @@ export const shapesDefs = [
|
|||||||
aliases: ['circ'],
|
aliases: ['circ'],
|
||||||
handler: circle,
|
handler: circle,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
semanticName: 'Bang',
|
||||||
|
name: 'Bang',
|
||||||
|
shortName: 'bang',
|
||||||
|
description: 'Bang',
|
||||||
|
aliases: ['bang'],
|
||||||
|
handler: bang,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
semanticName: 'Cloud',
|
||||||
|
name: 'Cloud',
|
||||||
|
shortName: 'cloud',
|
||||||
|
description: 'cloud',
|
||||||
|
aliases: ['cloud'],
|
||||||
|
handler: cloud,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
semanticName: 'Decision',
|
semanticName: 'Decision',
|
||||||
name: 'Diamond',
|
name: 'Diamond',
|
||||||
|
@@ -0,0 +1,77 @@
|
|||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
|
||||||
|
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';
|
||||||
|
import type { Bounds, Point } from '../../../types.js';
|
||||||
|
|
||||||
|
export async function bang<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
getNodeClasses(node)
|
||||||
|
);
|
||||||
|
|
||||||
|
const w = bbox.width + 2 * halfPadding;
|
||||||
|
const h = bbox.height + 2 * halfPadding;
|
||||||
|
const r = 0.15 * w;
|
||||||
|
const { cssStyles } = node;
|
||||||
|
|
||||||
|
// Label centered around (0,0)
|
||||||
|
label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`);
|
||||||
|
|
||||||
|
let bangElem;
|
||||||
|
const path = `M0 0
|
||||||
|
a${r},${r} 1 0,0 ${w * 0.25},${-1 * h * 0.1}
|
||||||
|
a${r},${r} 1 0,0 ${w * 0.25},${0}
|
||||||
|
a${r},${r} 1 0,0 ${w * 0.25},${0}
|
||||||
|
a${r},${r} 1 0,0 ${w * 0.25},${h * 0.1}
|
||||||
|
|
||||||
|
a${r},${r} 1 0,0 ${w * 0.15},${h * 0.33}
|
||||||
|
a${r * 0.8},${r * 0.8} 1 0,0 0,${h * 0.34}
|
||||||
|
a${r},${r} 1 0,0 ${-1 * w * 0.15},${h * 0.33}
|
||||||
|
|
||||||
|
a${r},${r} 1 0,0 ${-1 * w * 0.25},${h * 0.15}
|
||||||
|
a${r},${r} 1 0,0 ${-1 * w * 0.25},0
|
||||||
|
a${r},${r} 1 0,0 ${-1 * w * 0.25},0
|
||||||
|
a${r},${r} 1 0,0 ${-1 * w * 0.25},${-1 * h * 0.15}
|
||||||
|
|
||||||
|
a${r},${r} 1 0,0 ${-1 * w * 0.1},${-1 * h * 0.33}
|
||||||
|
a${r * 0.8},${r * 0.8} 1 0,0 0,${-1 * h * 0.34}
|
||||||
|
a${r},${r} 1 0,0 ${w * 0.1},${-1 * h * 0.33}
|
||||||
|
H0 V0 Z`;
|
||||||
|
|
||||||
|
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 roughNode = rc.path(path, options);
|
||||||
|
bangElem = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
|
bangElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles));
|
||||||
|
} else {
|
||||||
|
bangElem = shapeSvg
|
||||||
|
.insert('path', ':first-child')
|
||||||
|
.attr('class', 'basic label-container')
|
||||||
|
.attr('style', nodeStyles)
|
||||||
|
.attr('d', path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate the path (center the shape)
|
||||||
|
bangElem.attr('transform', `translate(${-w / 2}, ${-h / 2})`);
|
||||||
|
|
||||||
|
updateNodeBounds(node, bangElem);
|
||||||
|
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||||
|
return intersect.rect(bounds, point);
|
||||||
|
};
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('Bang intersect', node, point);
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
}
|
@@ -0,0 +1,81 @@
|
|||||||
|
import { log } from '../../../logger.js';
|
||||||
|
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
|
||||||
|
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';
|
||||||
|
import type { Bounds, Point } from '../../../types.js';
|
||||||
|
|
||||||
|
export async function cloud<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
|
|
||||||
|
const { shapeSvg, bbox, halfPadding, label } = await labelHelper(
|
||||||
|
parent,
|
||||||
|
node,
|
||||||
|
getNodeClasses(node)
|
||||||
|
);
|
||||||
|
|
||||||
|
const w = bbox.width + 2 * halfPadding;
|
||||||
|
const h = bbox.height + 2 * halfPadding;
|
||||||
|
|
||||||
|
// Cloud radii
|
||||||
|
const r1 = 0.15 * w;
|
||||||
|
const r2 = 0.25 * w;
|
||||||
|
const r3 = 0.35 * w;
|
||||||
|
const r4 = 0.2 * w;
|
||||||
|
|
||||||
|
const { cssStyles } = node;
|
||||||
|
let cloudElem;
|
||||||
|
|
||||||
|
// Cloud path
|
||||||
|
const path = `M0 0
|
||||||
|
a${r1},${r1} 0 0,1 ${w * 0.25},${-1 * w * 0.1}
|
||||||
|
a${r3},${r3} 1 0,1 ${w * 0.4},${-1 * w * 0.1}
|
||||||
|
a${r2},${r2} 1 0,1 ${w * 0.35},${w * 0.2}
|
||||||
|
|
||||||
|
a${r1},${r1} 1 0,1 ${w * 0.15},${h * 0.35}
|
||||||
|
a${r4},${r4} 1 0,1 ${-1 * w * 0.15},${h * 0.65}
|
||||||
|
|
||||||
|
a${r2},${r1} 1 0,1 ${-1 * w * 0.25},${w * 0.15}
|
||||||
|
a${r3},${r3} 1 0,1 ${-1 * w * 0.5},0
|
||||||
|
a${r1},${r1} 1 0,1 ${-1 * w * 0.25},${-1 * w * 0.15}
|
||||||
|
|
||||||
|
a${r1},${r1} 1 0,1 ${-1 * w * 0.1},${-1 * h * 0.35}
|
||||||
|
a${r4},${r4} 1 0,1 ${w * 0.1},${-1 * h * 0.65}
|
||||||
|
H0 V0 Z`;
|
||||||
|
|
||||||
|
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 roughNode = rc.path(path, options);
|
||||||
|
cloudElem = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
|
cloudElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles));
|
||||||
|
} else {
|
||||||
|
cloudElem = shapeSvg
|
||||||
|
.insert('path', ':first-child')
|
||||||
|
.attr('class', 'basic label-container')
|
||||||
|
.attr('style', nodeStyles)
|
||||||
|
.attr('d', path);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`);
|
||||||
|
|
||||||
|
// Center the shape
|
||||||
|
cloudElem.attr('transform', `translate(${-w / 2}, ${-h / 2})`);
|
||||||
|
|
||||||
|
updateNodeBounds(node, cloudElem);
|
||||||
|
|
||||||
|
node.calcIntersect = function (bounds: Bounds, point: Point) {
|
||||||
|
return intersect.rect(bounds, point);
|
||||||
|
};
|
||||||
|
node.intersect = function (point) {
|
||||||
|
log.info('Cloud intersect', node, point);
|
||||||
|
return intersect.rect(node, point);
|
||||||
|
};
|
||||||
|
|
||||||
|
return shapeSvg;
|
||||||
|
}
|
Reference in New Issue
Block a user