mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-01 10:06:25 +01:00
Compare commits
4 Commits
v10.2.3
...
chore/clea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e92b463d9 | ||
|
|
509798c03d | ||
|
|
7dc7ecb8f1 | ||
|
|
1d5b7702b4 |
@@ -84,7 +84,6 @@
|
||||
"mkdocs",
|
||||
"mmorel",
|
||||
"mult",
|
||||
"nextra",
|
||||
"orlandoni",
|
||||
"pathe",
|
||||
"pbrolin",
|
||||
|
||||
@@ -60,7 +60,7 @@ export const renderGraph = (graphStr, options, api) => {
|
||||
openURLAndVerifyRendering(url, options);
|
||||
};
|
||||
|
||||
export const openURLAndVerifyRendering = (url, options, validation = undefined) => {
|
||||
const openURLAndVerifyRendering = (url, options, validation = undefined) => {
|
||||
const useAppli = Cypress.env('useAppli');
|
||||
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.js';
|
||||
import { urlSnapshotTest } from '../../helpers/util.js';
|
||||
|
||||
describe('CSS injections', () => {
|
||||
it('should not allow CSS injections outside of the diagram', () => {
|
||||
@@ -13,11 +13,4 @@ describe('CSS injections', () => {
|
||||
flowchart: { htmlLabels: false },
|
||||
});
|
||||
});
|
||||
it('should not allow manipulating styletags using arrowheads', () => {
|
||||
openURLAndVerifyRendering('http://localhost:9000/xss23-css.html', {
|
||||
logLevel: 1,
|
||||
arrowMarkerAbsolute: false,
|
||||
flowchart: { htmlLabels: true },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,17 +52,6 @@ root[A root with a long text that wraps to keep the node size in check]
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with wrapping text and long words that exceed width', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root[A few smaller words but then averylongsetofcharacterswithoutwhitespacetoseparate that we expect to wrapontonextlinesandnotexceedwidthparameters]
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with an icon', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
|
||||
@@ -88,16 +88,6 @@ context('Sequence diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should handle empty lines', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
sequenceDiagram
|
||||
Alice->>John: Hello John<br/>
|
||||
John-->>Alice: Great<br/><br/>day!
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('should handle line breaks and wrap annotations', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
|
||||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
/* background:#333; */
|
||||
font-family: 'Arial';
|
||||
/* font-size: 18px !important; */
|
||||
}
|
||||
h1 {
|
||||
color: grey;
|
||||
}
|
||||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
.mermaid svg {
|
||||
/* font-size: 18px !important; */
|
||||
}
|
||||
.malware {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 150px;
|
||||
background: red;
|
||||
color: black;
|
||||
display: flex;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: monospace;
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
<div class="flex">
|
||||
<div id="diagram" class="mermaid"></div>
|
||||
<div id="graph-div"></div>
|
||||
<div id="res" class=""></div>
|
||||
</div>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
theme: 'base',
|
||||
startOnLoad: false,
|
||||
flowcharts: { htmlLabels: true },
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
|
||||
let diagram = `graph TD
|
||||
A[["a marker-end=#quot;url(<s title='#<style>*{background:red}</style>'>b"]]
|
||||
`;
|
||||
const el = document.querySelector('#graph-div');
|
||||
console.log(diagram);
|
||||
const { svg } = await mermaid.render('graph-div', diagram);
|
||||
document.querySelector('#res').innerHTML = svg;
|
||||
window.rendered = true;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1054
docs/CHANGELOG.md
Normal file
1054
docs/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -96,7 +96,7 @@ mermaid.initialize(config);
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:670](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L670)
|
||||
[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667)
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -127,7 +127,7 @@ Return the last node appended
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:309](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L309)
|
||||
[mermaidAPI.ts:306](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L306)
|
||||
|
||||
---
|
||||
|
||||
@@ -295,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:288](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L288)
|
||||
[mermaidAPI.ts:285](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L285)
|
||||
|
||||
---
|
||||
|
||||
@@ -320,4 +320,4 @@ Remove any existing elements from the given document
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:359](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L359)
|
||||
[mermaidAPI.ts:356](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L356)
|
||||
|
||||
@@ -59,8 +59,6 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [hexo-filter-mermaid-diagrams](https://github.com/webappdevelp/hexo-filter-mermaid-diagrams)
|
||||
- [hexo-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
||||
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
||||
- [Nextra](https://nextra.site/)
|
||||
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||
|
||||
## CMS
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ quadrantChart
|
||||
|
||||
## Syntax
|
||||
|
||||
> **Note**
|
||||
> In place of `<text>` you can use text like `this is a sample text` or inside **double quotes** like `"This type of text may contain unicode like ❤"`.
|
||||
|
||||
> **Note**
|
||||
> If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant.
|
||||
> If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant.
|
||||
@@ -149,7 +152,7 @@ Points are used to plot a circle inside the quadrantChart. The syntax is `<text>
|
||||
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
||||
quadrantChart
|
||||
x-axis Urgent --> Not Urgent
|
||||
y-axis Not Important --> "Important ❤"
|
||||
y-axis Not Important --> important
|
||||
quadrant-1 Plan
|
||||
quadrant-2 Do
|
||||
quadrant-3 Deligate
|
||||
@@ -160,7 +163,7 @@ quadrantChart
|
||||
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
||||
quadrantChart
|
||||
x-axis Urgent --> Not Urgent
|
||||
y-axis Not Important --> "Important ❤"
|
||||
y-axis Not Important --> important
|
||||
quadrant-1 Plan
|
||||
quadrant-2 Do
|
||||
quadrant-3 Deligate
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mermaid-monorepo",
|
||||
"private": true,
|
||||
"version": "10.2.3",
|
||||
"version": "10.2.0",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@8.5.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "10.2.3",
|
||||
"version": "10.2.0",
|
||||
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
@@ -28,7 +28,7 @@
|
||||
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts",
|
||||
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts --verify",
|
||||
"docs:pre:vitepress": "rimraf src/vitepress && pnpm docs:code && ts-node-esm src/docs.mts --vitepress",
|
||||
"docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm --filter ./ install --no-frozen-lockfile --ignore-scripts && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing",
|
||||
"docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm --filter ./ install --no-frozen-lockfile && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing",
|
||||
"docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./ src/vitepress dev\" \"ts-node-esm src/docs.mts --watch --vitepress\"",
|
||||
"docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress",
|
||||
"docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* of src to dst in order.
|
||||
* @param {any} dst - The destination of the merge
|
||||
* @param {any} src - The source object(s) to merge into destination
|
||||
* @param {{ depth: number; clobber: boolean }} [config={ depth: 2, clobber: false }] - Depth: depth
|
||||
* @param {{ depth?: number; clobber?: boolean }} [config={ depth: 2, clobber: false }] - Depth: depth
|
||||
* to traverse within src and dst for merging - clobber: should dissimilar types clobber (default:
|
||||
* { depth: 2, clobber: false }). Default is `{ depth: 2, clobber: false }`
|
||||
* @returns {any}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import dayjs from 'dayjs';
|
||||
import dayjsIsoWeek from 'dayjs/plugin/isoWeek.js';
|
||||
import dayjsCustomParseFormat from 'dayjs/plugin/customParseFormat.js';
|
||||
import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat.js';
|
||||
import dayjs from 'dayjs/esm/index.js';
|
||||
import dayjsIsoWeek from 'dayjs/esm/plugin/isoWeek/index.js';
|
||||
import dayjsCustomParseFormat from 'dayjs/esm/plugin/customParseFormat/index.js';
|
||||
import dayjsAdvancedFormat from 'dayjs/esm/plugin/advancedFormat/index.js';
|
||||
import { log } from '../../logger.js';
|
||||
import * as configApi from '../../config.js';
|
||||
import utils from '../../utils.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-nocheck TODO: Fix TS
|
||||
import dayjs from 'dayjs';
|
||||
import dayjs from 'dayjs/esm/index.js';
|
||||
import ganttDb from './ganttDb.js';
|
||||
import { convert } from '../../tests/util.js';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import dayjs from 'dayjs';
|
||||
import dayjs from 'dayjs/esm/index.js';
|
||||
import { log } from '../../logger.js';
|
||||
import {
|
||||
select,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import common from '../common/common.js';
|
||||
import * as svgDrawCommon from '../common/svgDrawCommon';
|
||||
import { addFunction } from '../../interactionDb.js';
|
||||
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||
import { parseFontSize } from '../../utils.js';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
|
||||
export const drawRect = function (elem, rectData) {
|
||||
@@ -224,16 +224,15 @@ export const drawText = function (elem, textData) {
|
||||
textElem.attr('dy', dy);
|
||||
}
|
||||
|
||||
const text = line || ZERO_WIDTH_SPACE;
|
||||
if (textData.tspan) {
|
||||
const span = textElem.append('tspan');
|
||||
span.attr('x', textData.x);
|
||||
if (textData.fill !== undefined) {
|
||||
span.attr('fill', textData.fill);
|
||||
}
|
||||
span.text(text);
|
||||
span.text(line);
|
||||
} else {
|
||||
textElem.text(text);
|
||||
textElem.text(line);
|
||||
}
|
||||
if (
|
||||
textData.valign !== undefined &&
|
||||
|
||||
@@ -5,12 +5,12 @@ import { contributors } from '../contributors';
|
||||
<template>
|
||||
<div flex="~ wrap gap2" justify-center>
|
||||
<a
|
||||
v-for="{ username, avatar } of contributors"
|
||||
:key="username"
|
||||
:href="`https://github.com/${username}`"
|
||||
v-for="{ name, avatar } of contributors"
|
||||
:key="name"
|
||||
:href="`https://github.com/${name}`"
|
||||
m-0
|
||||
rel="noopener noreferrer"
|
||||
:aria-label="`${username} on GitHub`"
|
||||
:aria-label="`${name} on GitHub`"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
@@ -20,7 +20,7 @@ import { contributors } from '../contributors';
|
||||
rounded-full
|
||||
h-12
|
||||
w-12
|
||||
:alt="`${username}'s avatar`"
|
||||
:alt="`${name}'s avatar`"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-console */
|
||||
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
@@ -13,23 +12,22 @@ async function download(url: string, fileName: URL) {
|
||||
if (existsSync(fileName)) {
|
||||
return;
|
||||
}
|
||||
console.log('downloading', url);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('downloading', fileName);
|
||||
try {
|
||||
const image = await fetch(url);
|
||||
await writeFile(fileName, Buffer.from(await image.arrayBuffer()));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function fetchAvatars() {
|
||||
await mkdir(fileURLToPath(new URL(getAvatarPath('none'))).replace('none.png', ''), {
|
||||
recursive: true,
|
||||
});
|
||||
await mkdir(fileURLToPath(new URL('..', getAvatarPath('none'))), { recursive: true });
|
||||
contributors = JSON.parse(await readFile(pathContributors, { encoding: 'utf-8' }));
|
||||
for (const name of contributors) {
|
||||
await download(`https://github.com/${name}.png?size=100`, getAvatarPath(name));
|
||||
}
|
||||
await Promise.allSettled(
|
||||
contributors.map((name) =>
|
||||
download(`https://github.com/${name}.png?size=100`, getAvatarPath(name))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fetchAvatars();
|
||||
|
||||
@@ -23,8 +23,9 @@ async function fetchContributors() {
|
||||
}
|
||||
);
|
||||
data = await response.json();
|
||||
console.log(response.status, response.statusText);
|
||||
console.log(data);
|
||||
collaborators.push(...data.map((i) => i.login));
|
||||
console.log(`Fetched page ${page}`);
|
||||
page++;
|
||||
} while (data.length === 100);
|
||||
return collaborators.filter((name) => !name.includes('[bot]'));
|
||||
|
||||
1024
packages/mermaid/src/docs/CHANGELOG.md
Normal file
1024
packages/mermaid/src/docs/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -53,8 +53,6 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [hexo-filter-mermaid-diagrams](https://github.com/webappdevelp/hexo-filter-mermaid-diagrams)
|
||||
- [hexo-tag-mermaid](https://github.com/JameChou/hexo-tag-mermaid)
|
||||
- [hexo-mermaid-diagrams](https://github.com/mslxl/hexo-mermaid-diagrams)
|
||||
- [Nextra](https://nextra.site/)
|
||||
- [Mermaid](https://nextra.site/docs/guide/mermaid)
|
||||
|
||||
## CMS
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ quadrantChart
|
||||
|
||||
## Syntax
|
||||
|
||||
```note
|
||||
In place of `<text>` you can use text like `this is a sample text` or inside **double quotes** like `"This type of text may contain unicode like ❤"`.
|
||||
```
|
||||
|
||||
```note
|
||||
If there is no points available in the chart both **axis** text and **quadrant** will be rendered in the center of the respective quadrant.
|
||||
If there are points **x-axis** labels will rendered from left of the respective quadrant also they will be displayed in bottom of the chart, and **y-axis** lables will be rendered in bottom of the respective quadrant, the quadrant text will render at top of the respective quadrant.
|
||||
@@ -130,7 +134,7 @@ Points are used to plot a circle inside the quadrantChart. The syntax is `<text>
|
||||
%%{init: {"quadrantChart": {"chartWidth": 400, "chartHeight": 400}, "themeVariables": {"quadrant1TextFill": "#ff0000"} }}%%
|
||||
quadrantChart
|
||||
x-axis Urgent --> Not Urgent
|
||||
y-axis Not Important --> "Important ❤"
|
||||
y-axis Not Important --> important
|
||||
quadrant-1 Plan
|
||||
quadrant-2 Do
|
||||
quadrant-3 Deligate
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable no-console */
|
||||
import dayjs from 'dayjs';
|
||||
import dayjs from 'dayjs/esm/index.js';
|
||||
|
||||
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
|
||||
|
||||
@@ -263,10 +263,7 @@ export const cleanUpSvgCode = (
|
||||
|
||||
// Replace marker-end urls with just the # anchor (remove the preceding part of the URL)
|
||||
if (!useArrowMarkerUrls && !inSandboxMode) {
|
||||
cleanedUpSvg = cleanedUpSvg.replace(
|
||||
/marker-end="url\([\d+./:=?A-Za-z-]*?#/g,
|
||||
'marker-end="url(#'
|
||||
);
|
||||
cleanedUpSvg = cleanedUpSvg.replace(/marker-end="url\(.*?#/g, 'marker-end="url(#');
|
||||
}
|
||||
|
||||
cleanedUpSvg = decodeEntities(cleanedUpSvg);
|
||||
|
||||
@@ -78,22 +78,6 @@ function createTspan(textElement, lineIndex, lineHeight) {
|
||||
.attr('dy', lineHeight + 'em');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the width of rendered text
|
||||
* @param {object} parentNode
|
||||
* @param {number} lineHeight
|
||||
* @param {string} text
|
||||
* @returns {number}
|
||||
*/
|
||||
function computeWidthOfText(parentNode, lineHeight, text) {
|
||||
const testElement = parentNode.append('text');
|
||||
const testSpan = createTspan(testElement, 1, lineHeight);
|
||||
updateTextContentAndStyles(testSpan, [{ content: text, type: 'normal' }]);
|
||||
const textLength = testSpan.node().getComputedTextLength();
|
||||
testElement.remove();
|
||||
return textLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a formatted text element by breaking lines and applying styles based on
|
||||
* the given structuredText.
|
||||
@@ -111,44 +95,31 @@ function createFormattedText(width, g, structuredText, addBackground = false) {
|
||||
// .attr('dominant-baseline', 'middle')
|
||||
// .attr('text-anchor', 'middle');
|
||||
// .attr('text-anchor', 'middle');
|
||||
let lineIndex = 0;
|
||||
let lineIndex = -1;
|
||||
structuredText.forEach((line) => {
|
||||
/**
|
||||
* Preprocess raw string content of line data
|
||||
* Creating an array of strings pre-split to satisfy width limit
|
||||
*/
|
||||
let fullStr = line.map((data) => data.content).join(' ');
|
||||
let tempStr = '';
|
||||
let linesUnderWidth = [];
|
||||
let prevIndex = 0;
|
||||
if (computeWidthOfText(labelGroup, lineHeight, fullStr) <= width) {
|
||||
linesUnderWidth.push(fullStr);
|
||||
} else {
|
||||
for (let i = 0; i <= fullStr.length; i++) {
|
||||
tempStr = fullStr.slice(prevIndex, i);
|
||||
log.info(tempStr, prevIndex, i);
|
||||
if (computeWidthOfText(labelGroup, lineHeight, tempStr) > width) {
|
||||
const subStr = fullStr.slice(prevIndex, i);
|
||||
// Break at space if any
|
||||
const lastSpaceIndex = subStr.lastIndexOf(' ');
|
||||
if (lastSpaceIndex > -1) {
|
||||
i = prevIndex + lastSpaceIndex + 1;
|
||||
}
|
||||
linesUnderWidth.push(fullStr.slice(prevIndex, i).trim());
|
||||
prevIndex = i;
|
||||
tempStr = null;
|
||||
}
|
||||
lineIndex++;
|
||||
let tspan = createTspan(textElement, lineIndex, lineHeight);
|
||||
|
||||
let words = [...line].reverse();
|
||||
let currentWord;
|
||||
let wrappedLine = [];
|
||||
|
||||
while (words.length) {
|
||||
currentWord = words.pop();
|
||||
wrappedLine.push(currentWord);
|
||||
|
||||
updateTextContentAndStyles(tspan, wrappedLine);
|
||||
|
||||
if (tspan.node().getComputedTextLength() > width) {
|
||||
wrappedLine.pop();
|
||||
words.push(currentWord);
|
||||
|
||||
updateTextContentAndStyles(tspan, wrappedLine);
|
||||
|
||||
wrappedLine = [];
|
||||
lineIndex++;
|
||||
tspan = createTspan(textElement, lineIndex, lineHeight);
|
||||
}
|
||||
if (tempStr != null) {
|
||||
linesUnderWidth.push(tempStr);
|
||||
}
|
||||
}
|
||||
/** Add each prepared line as a tspan to the parent node */
|
||||
const preparedLines = linesUnderWidth.map((w) => ({ content: w, type: line.type }));
|
||||
for (const preparedLine of preparedLines) {
|
||||
let tspan = createTspan(textElement, lineIndex, lineHeight);
|
||||
updateTextContentAndStyles(tspan, [preparedLine]);
|
||||
lineIndex++;
|
||||
}
|
||||
});
|
||||
if (addBackground) {
|
||||
|
||||
@@ -133,6 +133,7 @@ export class MockedD3 {
|
||||
// --------------------------------------------------------------------------------
|
||||
// The following functions are here for completeness. They simply return a vi.fn()
|
||||
|
||||
remove = vi.fn();
|
||||
insertBefore = vi.fn();
|
||||
curveBasis = vi.fn();
|
||||
curveBasisClosed = vi.fn();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { vi } from 'vitest';
|
||||
import { vi, describe, it, expect } from 'vitest';
|
||||
import utils from './utils.js';
|
||||
import assignWithDepth from './assignWithDepth.js';
|
||||
import { detectType } from './diagram-api/detectType.js';
|
||||
@@ -10,51 +10,51 @@ addDiagrams();
|
||||
|
||||
describe('when assignWithDepth: should merge objects within objects', function () {
|
||||
it('should handle simple, depth:1 types (identity)', function () {
|
||||
let config_0 = { foo: 'bar', bar: 0 };
|
||||
let config_1 = { foo: 'bar', bar: 0 };
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = { foo: 'bar', bar: 0 };
|
||||
const config_1 = { foo: 'bar', bar: 0 };
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual(config_1);
|
||||
});
|
||||
it('should handle simple, depth:1 types (dst: undefined)', function () {
|
||||
let config_0 = undefined;
|
||||
let config_1 = { foo: 'bar', bar: 0 };
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = undefined;
|
||||
const config_1 = { foo: 'bar', bar: 0 };
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual(config_1);
|
||||
});
|
||||
it('should handle simple, depth:1 types (src: undefined)', function () {
|
||||
let config_0 = { foo: 'bar', bar: 0 };
|
||||
let config_1 = undefined;
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = { foo: 'bar', bar: 0 };
|
||||
const config_1 = undefined;
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual(config_0);
|
||||
});
|
||||
it('should handle simple, depth:1 types (merge)', function () {
|
||||
let config_0 = { foo: 'bar', bar: 0 };
|
||||
let config_1 = { foo: 'foo' };
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = { foo: 'bar', bar: 0 };
|
||||
const config_1 = { foo: 'foo' };
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual({ foo: 'foo', bar: 0 });
|
||||
});
|
||||
it('should handle depth:2 types (dst: orphan)', function () {
|
||||
let config_0 = { foo: 'bar', bar: { foo: 'bar' } };
|
||||
let config_1 = { foo: 'bar' };
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = { foo: 'bar', bar: { foo: 'bar' } };
|
||||
const config_1 = { foo: 'bar' };
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual(config_0);
|
||||
});
|
||||
it('should handle depth:2 types (dst: object, src: simple type)', function () {
|
||||
let config_0 = { foo: 'bar', bar: { foo: 'bar' } };
|
||||
let config_1 = { foo: 'foo', bar: 'should NOT clobber' };
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = { foo: 'bar', bar: { foo: 'bar' } };
|
||||
const config_1 = { foo: 'foo', bar: 'should NOT clobber' };
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual({ foo: 'foo', bar: { foo: 'bar' } });
|
||||
});
|
||||
it('should handle depth:2 types (src: orphan)', function () {
|
||||
let config_0 = { foo: 'bar' };
|
||||
let config_1 = { foo: 'bar', bar: { foo: 'bar' } };
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = { foo: 'bar' };
|
||||
const config_1 = { foo: 'bar', bar: { foo: 'bar' } };
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual(config_1);
|
||||
});
|
||||
it('should handle depth:2 types (merge)', function () {
|
||||
let config_0 = { foo: 'bar', bar: { foo: 'bar' }, boofar: 1 };
|
||||
let config_1 = { foo: 'foo', bar: { bar: 0 }, foobar: 'foobar' };
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const config_0 = { foo: 'bar', bar: { foo: 'bar' }, boofar: 1 };
|
||||
const config_1 = { foo: 'foo', bar: { bar: 0 }, foobar: 'foobar' };
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual({
|
||||
foo: 'foo',
|
||||
bar: { foo: 'bar', bar: 0 },
|
||||
@@ -63,17 +63,17 @@ describe('when assignWithDepth: should merge objects within objects', function (
|
||||
});
|
||||
});
|
||||
it('should handle depth:3 types (merge with clobber because assignWithDepth::depth == 2)', function () {
|
||||
let config_0 = {
|
||||
const config_0 = {
|
||||
foo: 'bar',
|
||||
bar: { foo: 'bar', bar: { foo: { message: 'this', willbe: 'clobbered' } } },
|
||||
boofar: 1,
|
||||
};
|
||||
let config_1 = {
|
||||
const config_1 = {
|
||||
foo: 'foo',
|
||||
bar: { foo: 'foo', bar: { foo: { message: 'clobbered other foo' } } },
|
||||
foobar: 'foobar',
|
||||
};
|
||||
let result = assignWithDepth(config_0, config_1);
|
||||
const result = assignWithDepth(config_0, config_1);
|
||||
expect(result).toEqual({
|
||||
foo: 'foo',
|
||||
bar: { foo: 'foo', bar: { foo: { message: 'clobbered other foo' } } },
|
||||
@@ -82,7 +82,7 @@ describe('when assignWithDepth: should merge objects within objects', function (
|
||||
});
|
||||
});
|
||||
it('should handle depth:3 types (merge with clobber because assignWithDepth::depth == 1)', function () {
|
||||
let config_0 = {
|
||||
const config_0 = {
|
||||
foo: 'bar',
|
||||
bar: {
|
||||
foo: 'bar',
|
||||
@@ -90,12 +90,12 @@ describe('when assignWithDepth: should merge objects within objects', function (
|
||||
},
|
||||
boofar: 1,
|
||||
};
|
||||
let config_1 = {
|
||||
const config_1 = {
|
||||
foo: 'foo',
|
||||
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
|
||||
foobar: 'foobar',
|
||||
};
|
||||
let result = assignWithDepth(config_0, config_1, { depth: 1 });
|
||||
const result = assignWithDepth(config_0, config_1, { depth: 1 });
|
||||
expect(result).toEqual({
|
||||
foo: 'foo',
|
||||
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
|
||||
@@ -104,17 +104,17 @@ describe('when assignWithDepth: should merge objects within objects', function (
|
||||
});
|
||||
});
|
||||
it('should handle depth:3 types (merge with no clobber because assignWithDepth::depth == 3)', function () {
|
||||
let config_0 = {
|
||||
const config_0 = {
|
||||
foo: 'bar',
|
||||
bar: { foo: 'bar', bar: { foo: { message: '', willbe: 'present' } } },
|
||||
boofar: 1,
|
||||
};
|
||||
let config_1 = {
|
||||
const config_1 = {
|
||||
foo: 'foo',
|
||||
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
|
||||
foobar: 'foobar',
|
||||
};
|
||||
let result = assignWithDepth(config_0, config_1, { depth: 3 });
|
||||
const result = assignWithDepth(config_0, config_1, { depth: 3 });
|
||||
expect(result).toEqual({
|
||||
foo: 'foo',
|
||||
bar: { foo: 'foo', bar: { foo: { message: 'this', willbe: 'present' } } },
|
||||
@@ -125,7 +125,7 @@ describe('when assignWithDepth: should merge objects within objects', function (
|
||||
});
|
||||
describe('when memoizing', function () {
|
||||
it('should return the same value', function () {
|
||||
const fib = memoize(
|
||||
const fib: (n: number, x: unknown, canary: { flag: boolean }) => number = memoize(
|
||||
function (n, x, canary) {
|
||||
canary.flag = true;
|
||||
if (n < 2) {
|
||||
@@ -260,7 +260,7 @@ describe('when formatting urls', function () {
|
||||
it('should handle links', function () {
|
||||
const url = 'https://mermaid-js.github.io/mermaid/#/';
|
||||
|
||||
let config = { securityLevel: 'loose' };
|
||||
const config = { securityLevel: 'loose' };
|
||||
let result = utils.formatUrl(url, config);
|
||||
expect(result).toEqual(url);
|
||||
|
||||
@@ -271,7 +271,7 @@ describe('when formatting urls', function () {
|
||||
it('should handle anchors', function () {
|
||||
const url = '#interaction';
|
||||
|
||||
let config = { securityLevel: 'loose' };
|
||||
const config = { securityLevel: 'loose' };
|
||||
let result = utils.formatUrl(url, config);
|
||||
expect(result).toEqual(url);
|
||||
|
||||
@@ -282,7 +282,7 @@ describe('when formatting urls', function () {
|
||||
it('should handle mailto', function () {
|
||||
const url = 'mailto:user@user.user';
|
||||
|
||||
let config = { securityLevel: 'loose' };
|
||||
const config = { securityLevel: 'loose' };
|
||||
let result = utils.formatUrl(url, config);
|
||||
expect(result).toEqual(url);
|
||||
|
||||
@@ -293,7 +293,7 @@ describe('when formatting urls', function () {
|
||||
it('should handle other protocols', function () {
|
||||
const url = 'notes://do-your-thing/id';
|
||||
|
||||
let config = { securityLevel: 'loose' };
|
||||
const config = { securityLevel: 'loose' };
|
||||
let result = utils.formatUrl(url, config);
|
||||
expect(result).toEqual(url);
|
||||
|
||||
@@ -304,7 +304,7 @@ describe('when formatting urls', function () {
|
||||
it('should handle scripts', function () {
|
||||
const url = 'javascript:alert("test")';
|
||||
|
||||
let config = { securityLevel: 'loose' };
|
||||
const config = { securityLevel: 'loose' };
|
||||
let result = utils.formatUrl(url, config);
|
||||
expect(result).toEqual(url);
|
||||
|
||||
@@ -425,6 +425,7 @@ describe('when parsing font sizes', function () {
|
||||
});
|
||||
|
||||
it('handles unparseable input', function () {
|
||||
// @ts-expect-error This is testing an invalid case
|
||||
expect(utils.parseFontSize({ fontSize: 14 })).toEqual([undefined, undefined]);
|
||||
});
|
||||
});
|
||||
@@ -32,8 +32,6 @@ import assignWithDepth from './assignWithDepth.js';
|
||||
import { MermaidConfig } from './config.type.js';
|
||||
import memoize from 'lodash-es/memoize.js';
|
||||
|
||||
export const ZERO_WIDTH_SPACE = '\u200b';
|
||||
|
||||
// Effectively an enum of the supported curve types, accessible by name
|
||||
const d3CurveTypes = {
|
||||
curveBasis: curveBasis,
|
||||
@@ -767,7 +765,7 @@ export const calculateTextDimensions: (
|
||||
const dim = { width: 0, height: 0, lineHeight: 0 };
|
||||
for (const line of lines) {
|
||||
const textObj = getTextObj();
|
||||
textObj.text = line || ZERO_WIDTH_SPACE;
|
||||
textObj.text = line;
|
||||
const textElem = drawSimpleText(g, textObj)
|
||||
.style('font-size', _fontSizePx)
|
||||
.style('font-weight', fontWeight)
|
||||
@@ -802,7 +800,7 @@ export const calculateTextDimensions: (
|
||||
);
|
||||
|
||||
export const initIdGenerator = class iterator {
|
||||
constructor(deterministic, seed) {
|
||||
constructor(deterministic: boolean, seed?: string) {
|
||||
this.deterministic = deterministic;
|
||||
// TODO: Seed is only used for length?
|
||||
this.seed = seed;
|
||||
|
||||
94
pnpm-lock.yaml
generated
94
pnpm-lock.yaml
generated
@@ -435,6 +435,58 @@ importers:
|
||||
specifier: ^6.5.4
|
||||
version: 6.5.4
|
||||
|
||||
packages/mermaid/src/vitepress:
|
||||
dependencies:
|
||||
'@vueuse/core':
|
||||
specifier: ^10.1.0
|
||||
version: 10.1.0(vue@3.2.47)
|
||||
jiti:
|
||||
specifier: ^1.18.2
|
||||
version: 1.18.2
|
||||
vue:
|
||||
specifier: ^3.2.47
|
||||
version: 3.2.47
|
||||
devDependencies:
|
||||
'@iconify-json/carbon':
|
||||
specifier: ^1.1.16
|
||||
version: 1.1.16
|
||||
'@unocss/reset':
|
||||
specifier: ^0.52.0
|
||||
version: 0.52.0
|
||||
'@vite-pwa/vitepress':
|
||||
specifier: ^0.0.5
|
||||
version: 0.0.5(vite-plugin-pwa@0.15.0)
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(vite@4.3.3)(vue@3.2.47)
|
||||
fast-glob:
|
||||
specifier: ^3.2.12
|
||||
version: 3.2.12
|
||||
https-localhost:
|
||||
specifier: ^4.7.1
|
||||
version: 4.7.1
|
||||
pathe:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
unocss:
|
||||
specifier: ^0.52.0
|
||||
version: 0.52.0(postcss@8.4.23)(rollup@2.79.1)(vite@4.3.3)
|
||||
unplugin-vue-components:
|
||||
specifier: ^0.24.1
|
||||
version: 0.24.1(rollup@2.79.1)(vue@3.2.47)
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.0)
|
||||
vite-plugin-pwa:
|
||||
specifier: ^0.15.0
|
||||
version: 0.15.0(vite@4.3.3)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
||||
vitepress:
|
||||
specifier: 1.0.0-alpha.76
|
||||
version: 1.0.0-alpha.76(@algolia/client-search@4.14.2)(@types/node@18.16.0)
|
||||
workbox-window:
|
||||
specifier: ^6.5.4
|
||||
version: 6.5.4
|
||||
|
||||
tests/webpack:
|
||||
dependencies:
|
||||
'@mermaid-js/mermaid-example-diagram':
|
||||
@@ -14605,6 +14657,48 @@ packages:
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/vitepress@1.0.0-alpha.76(@algolia/client-search@4.14.2)(@types/node@18.16.0):
|
||||
resolution: {integrity: sha512-fzR1pDpGnSMeCJ+AnDdMe/ETD2G0Go+g6mTxDv9ps7Hmr1JjVqw97nasCyZg3jgfQxi2nt78EJ/bw7hY5n/rlw==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@docsearch/css': 3.3.5
|
||||
'@docsearch/js': 3.3.5(@algolia/client-search@4.14.2)
|
||||
'@vitejs/plugin-vue': 4.2.3(vite@4.3.8)(vue@3.3.4)
|
||||
'@vue/devtools-api': 6.5.0
|
||||
'@vueuse/core': 10.1.2(vue@3.3.4)
|
||||
'@vueuse/integrations': 10.1.2(focus-trap@7.4.3)(vue@3.3.4)
|
||||
body-scroll-lock: 4.0.0-beta.0
|
||||
focus-trap: 7.4.3
|
||||
mark.js: 8.11.1
|
||||
minisearch: 6.1.0
|
||||
shiki: 0.14.2
|
||||
vite: 4.3.8(@types/node@18.16.0)
|
||||
vue: 3.3.4
|
||||
transitivePeerDependencies:
|
||||
- '@algolia/client-search'
|
||||
- '@types/node'
|
||||
- '@types/react'
|
||||
- '@vue/composition-api'
|
||||
- async-validator
|
||||
- axios
|
||||
- change-case
|
||||
- drauu
|
||||
- fuse.js
|
||||
- idb-keyval
|
||||
- jwt-decode
|
||||
- less
|
||||
- nprogress
|
||||
- qrcode
|
||||
- react
|
||||
- react-dom
|
||||
- sass
|
||||
- sortablejs
|
||||
- stylus
|
||||
- sugarss
|
||||
- terser
|
||||
- universal-cookie
|
||||
dev: true
|
||||
|
||||
/vitepress@1.0.0-beta.1(@algolia/client-search@4.14.2)(@types/node@18.16.0):
|
||||
resolution: {integrity: sha512-V2yyCwQ+v9fh7rbnGDLp8M7vHa9sLElexXf/JHtBOsOwv7ed9wt1QI4WUagYgKR3TeoJT9v2s6f0UaQSne0EvQ==}
|
||||
hasBin: true
|
||||
|
||||
Reference in New Issue
Block a user