Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/eager-cobras-slide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'mermaid': minor
---

feat: add datastore shape for flowchart

In Data flow diagrams, a datastore/warehouse/file/database is used to represent data persistence. It is denoted by a rectangle with top and bottom border. This change adds that as a shape in flowcharts.
2 changes: 2 additions & 0 deletions cypress/integration/rendering/flowchart-shape-alias.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const aliasSet38 = ['tag-doc', 'tagged-document'] as const;
const aliasSet39 = ['tag-rect', 'tag-proc', 'tagged-rectangle', 'tagged-process'] as const;

const aliasSet40 = ['collate', 'hourglass'] as const;
const aliasSet41 = ['datastore', 'data-store'] as const;

// Aggregate all alias sets into a single array
const aliasSets = [
Expand Down Expand Up @@ -127,6 +128,7 @@ const aliasSets = [
aliasSet37,
aliasSet38,
aliasSet39,
aliasSet41,
] as const;

aliasSets.forEach((aliasSet) => {
Expand Down
1 change: 1 addition & 0 deletions cypress/integration/rendering/newShapes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const newShapesSet2 = [
'lightning-bolt',
'filled-circle',
'window-pane',
'datastore',
] as const;

const newShapesSet3 = [
Expand Down
15 changes: 1 addition & 14 deletions demos/dataflowchart.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,7 @@ <h1>Data Flow Diagram demos</h1>
flowchart LR
accTitle: A simple linear flowchart.
accDescr: A Database has input to a circle System has output to a square Customer.
DataStore[|borders:tb|Database] -->|input| Process((System)) -->|output| Entity[Customer];
</pre>

<hr />

<h2>Borders Example</h2>
<pre class="mermaid">
flowchart TD
allSides[ stroke all sides ];
allSides2[|borders:ltrb| stroke all sides ];
rbSides[|borders:rb| stroke right and bottom sides ];
ltSides[|borders:lt| stroke left and top sides ];
lrSides[|borders:lr| stroke left and right sides ];
noSide[|borders:no| stroke no side ];
DataStore@{shape: datastore, label: "Datastore"} -->|input| Process((System)) -->|output| Entity[Customer];
</pre>

<script type="module">
Expand Down
13 changes: 13 additions & 0 deletions docs/syntax/flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ Below is a comprehensive list of the newly introduced shapes and their correspon
| Comment with braces on both sides | Curly Braces | `braces` | Adds a comment | |
| Data Input/Output | Lean Right | `lean-r` | Represents input or output | `in-out`, `lean-right` |
| Data Input/Output | Lean Left | `lean-l` | Represents output or input | `lean-left`, `out-in` |
| Data Store | Data Store | `datastore` | Data flow diagram data store | `data-store` |
| Database | Cylinder | `cyl` | Database storage | `cylinder`, `database`, `db` |
| Decision | Diamond | `diam` | Decision-making step | `decision`, `diamond`, `question` |
| Delay | Half-Rounded Rectangle | `delay` | Represents a delay | `half-rounded-rectangle` |
Expand Down Expand Up @@ -526,6 +527,18 @@ flowchart TD
A@{ shape: lean-l, label: "Output/Input" }
```

### Datastore (Top and bottom border)

```mermaid-example
flowchart TD
A@{ shape: datastore, label: "Datastore" }
```

```mermaid
flowchart TD
A@{ shape: datastore, label: "Datastore" }
```

### Priority Action (Trapezoid Base Bottom)

```mermaid-example
Expand Down
1 change: 1 addition & 0 deletions packages/mermaid/scripts/docs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ This Markdown should be kept.
| Comment with braces on both sides | Curly Braces | \`braces\` | Adds a comment | |
| Data Input/Output | Lean Right | \`lean-r\` | Represents input or output | \`in-out\`, \`lean-right\` |
| Data Input/Output | Lean Left | \`lean-l\` | Represents output or input | \`lean-left\`, \`out-in\` |
| Data Store | Data Store | \`datastore\` | Data flow diagram data store | \`data-store\` |
| Database | Cylinder | \`cyl\` | Database storage | \`cylinder\`, \`database\`, \`db\` |
| Decision | Diamond | \`diam\` | Decision-making step | \`decision\`, \`diamond\`, \`question\` |
| Delay | Half-Rounded Rectangle | \`delay\` | Represents a delay | \`half-rounded-rectangle\` |
Expand Down
7 changes: 7 additions & 0 deletions packages/mermaid/src/docs/syntax/flowchart.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,13 @@ flowchart TD
A@{ shape: lean-l, label: "Output/Input" }
```

### Datastore (Top and bottom border)

```mermaid-example
flowchart TD
A@{ shape: datastore, label: "Datastore" }
```

### Priority Action (Trapezoid Base Bottom)

```mermaid-example
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ describe('Test Alias for shapes', function () {
expect(shapes.cyl).toBe(shapes.cylinder);
});

// datastore | data-store
it('should support alias for datastore shape ', function () {
expect(shapes['data-store']).toBe(shapes.datastore);
});

// diam | decision | diamond
it('should support alias for diamond shape ', function () {
expect(shapes.diam).toBe(shapes.decision);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { curlyBraceRight } from './shapes/curlyBraceRight.js';
import { curlyBraces } from './shapes/curlyBraces.js';
import { curvedTrapezoid } from './shapes/curvedTrapezoid.js';
import { cylinder } from './shapes/cylinder.js';
import { datastore } from './shapes/datastore.js';
import { dividedRectangle } from './shapes/dividedRect.js';
import { doublecircle } from './shapes/doubleCircle.js';
import { filledCircle } from './shapes/filledCircle.js';
Expand Down Expand Up @@ -131,6 +132,14 @@ export const shapesDefs = [
aliases: ['db', 'database', 'cylinder'],
handler: cylinder,
},
{
semanticName: 'Data Store',
name: 'Data Store',
shortName: 'datastore',
description: 'Data flow diagram data store',
aliases: ['data-store'],
handler: datastore,
},
{
semanticName: 'Start',
name: 'Circle',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { Node, RectOptions } from '../../types.js';
import type { D3Selection } from '../../../types.js';
import { drawRect } from './drawRect.js';
import { userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';

export async function datastore<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const { cssClasses, labelPaddingX, labelPaddingY, padding, width, height } = node;

const rectOptions: RectOptions = {
rx: 0,
ry: 0,
classes: cssClasses ?? '',
labelPaddingX: labelPaddingX ?? (padding ?? 0) * 2,
labelPaddingY: labelPaddingY ?? padding ?? 0,
};

const rect = await drawRect(parent, node, rectOptions);

if (node.look === 'handDrawn') {
// @ts-ignore TODO: Fix rough typings
const rc = rough.svg(rect);
const nodeOverrideOptions = userNodeOverrides(node, {});

const borderSelection = rect.select('.basic.label-container > path:nth-child(2)');
const borderPath = borderSelection.node();
if (!borderPath) {
return rect;
}

let bbox: DOMRect | null = null;
if (borderPath instanceof SVGGraphicsElement) {
bbox = borderPath.getBBox();
} else {
return rect;
}

rect.insert(
() => rc.line(bbox.x, bbox.y, bbox.x + bbox.width, bbox.y, nodeOverrideOptions),
'.basic.label-container g.label'
);
rect.insert(
() =>
rc.line(
bbox.x,
bbox.y + bbox.height,
bbox.x + bbox.width,
bbox.y + bbox.height,
nodeOverrideOptions
),
'.basic.label-container g.label'
);
borderSelection.remove();
return rect;
}

const selection = rect.select('.basic.label-container');
const datastoreWidth = (Number(selection.attr('width')) || width) ?? 0;
const datastoreHeight = (Number(selection.attr('height')) || height) ?? 0;
if (datastoreWidth > 0 && datastoreHeight > 0) {
selection.attr('stroke-dasharray', `${datastoreWidth} ${datastoreHeight}`);
}

return rect;
}
Loading