mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-27 14:46:26 +01:00
Compare commits
6 Commits
deprecate-
...
refactor/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7eb14f09bf | ||
|
|
b7c66a220a | ||
|
|
87e2d819bf | ||
|
|
93aa657578 | ||
|
|
43b7db9d6a | ||
|
|
df46b617d4 |
5
.changeset/late-bears-chew.md
Normal file
5
.changeset/late-bears-chew.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
revert: restore original hexagon and roundedRect implementations
|
||||
5
.changeset/restore-hexagon-roundedrect.md
Normal file
5
.changeset/restore-hexagon-roundedrect.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
chore: restore original hexagon and roundedRect implementations
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: Deprecate flowchart.htmlLabels in favor of root-level htmlLabels
|
||||
@@ -236,27 +236,22 @@ A --> C[End]
|
||||
|
||||
Some common flowchart configurations are:
|
||||
|
||||
- _htmlLabels_: true/false
|
||||
- _curve_: linear/curve
|
||||
- _diagramPadding_: number
|
||||
- _useMaxWidth_: number
|
||||
|
||||
**Deprecated configurations:**
|
||||
|
||||
- ~~_htmlLabels_~~: Use global `htmlLabels` instead
|
||||
|
||||
For a complete list of flowchart configurations, see [defaultConfig.ts](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/defaultConfig.ts) in the source code.
|
||||
_Soon we plan to publish a complete list of all diagram-specific configurations updated in the docs._
|
||||
|
||||
The following code snippet changes flowchart config:
|
||||
|
||||
```
|
||||
%%{init: { "htmlLabels": true, "flowchart": { "curve": "linear" } } }%%
|
||||
```
|
||||
`%%{init: { "flowchart": { "htmlLabels": true, "curve": "linear" } } }%%`
|
||||
|
||||
**Note:** `flowchart.htmlLabels` has been deprecated. Use the global `htmlLabels` configuration instead.
|
||||
Here we are overriding only the flowchart config, and not the general config, setting `htmlLabels` to `true` and `curve` to `linear`.
|
||||
|
||||
```mermaid-example
|
||||
%%{init: { "htmlLabels": true, "flowchart": { "curve": "linear" } } }%%
|
||||
%%{init: { "flowchart": { "htmlLabels": true, "curve": "linear" } } }%%
|
||||
graph TD
|
||||
A(Forest) --> B[/Another/]
|
||||
A --> C[End]
|
||||
@@ -267,7 +262,7 @@ A --> C[End]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
%%{init: { "htmlLabels": true, "flowchart": { "curve": "linear" } } }%%
|
||||
%%{init: { "flowchart": { "htmlLabels": true, "curve": "linear" } } }%%
|
||||
graph TD
|
||||
A(Forest) --> B[/Another/]
|
||||
A --> C[End]
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
- [addDirective](functions/addDirective.md)
|
||||
- [getConfig](functions/getConfig.md)
|
||||
- [getEffectiveHtmlLabels](functions/getEffectiveHtmlLabels.md)
|
||||
- [getSiteConfig](functions/getSiteConfig.md)
|
||||
- [getUserDefinedConfig](functions/getUserDefinedConfig.md)
|
||||
- [reset](functions/reset.md)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/config/functions/getEffectiveHtmlLabels.md](../../../../../packages/mermaid/src/docs/config/setup/config/functions/getEffectiveHtmlLabels.md).
|
||||
|
||||
[**mermaid**](../../README.md)
|
||||
|
||||
---
|
||||
|
||||
# Function: getEffectiveHtmlLabels()
|
||||
|
||||
> **getEffectiveHtmlLabels**(`config`): `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/config.ts:272](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L272)
|
||||
|
||||
Helper function to handle deprecated flowchart.htmlLabels
|
||||
|
||||
## Parameters
|
||||
|
||||
### config
|
||||
|
||||
[`MermaidConfig`](../../mermaid/interfaces/MermaidConfig.md)
|
||||
|
||||
The configuration object
|
||||
|
||||
## Returns
|
||||
|
||||
`boolean`
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
> **getUserDefinedConfig**(): [`MermaidConfig`](../../mermaid/interfaces/MermaidConfig.md)
|
||||
|
||||
Defined in: [packages/mermaid/src/config.ts:254](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L254)
|
||||
Defined in: [packages/mermaid/src/config.ts:252](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L252)
|
||||
|
||||
## Returns
|
||||
|
||||
|
||||
@@ -372,7 +372,7 @@ The list of configuration objects are described [in the mermaidAPI documentation
|
||||
```html
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
let config = { startOnLoad: true, htmlLabels: true, flowchart: { useMaxWidth: false } };
|
||||
let config = { startOnLoad: true, flowchart: { useMaxWidth: false, htmlLabels: true } };
|
||||
mermaid.initialize(config);
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -227,8 +227,6 @@ export const reset = (config = siteConfig): void => {
|
||||
const ConfigWarning = {
|
||||
LAZY_LOAD_DEPRECATED:
|
||||
'The configuration options lazyLoadedDiagrams and loadExternalDiagramsAtStartup are deprecated. Please use registerExternalDiagrams instead.',
|
||||
FLOWCHART_HTML_LABELS_DEPRECATED:
|
||||
'flowchart.htmlLabels is deprecated. Please use global htmlLabels instead.',
|
||||
} as const;
|
||||
|
||||
type ConfigWarningStrings = keyof typeof ConfigWarning;
|
||||
@@ -264,15 +262,3 @@ export const getUserDefinedConfig = (): MermaidConfig => {
|
||||
|
||||
return userConfig;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to handle deprecated flowchart.htmlLabels
|
||||
* @param config - The configuration object
|
||||
*/
|
||||
export const getEffectiveHtmlLabels = (config: MermaidConfig): boolean => {
|
||||
if (config.flowchart?.htmlLabels !== undefined) {
|
||||
issueWarning('FLOWCHART_HTML_LABELS_DEPRECATED');
|
||||
}
|
||||
|
||||
return config.htmlLabels ?? config.flowchart?.htmlLabels ?? true;
|
||||
};
|
||||
|
||||
@@ -248,12 +248,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
||||
*/
|
||||
diagramPadding?: number;
|
||||
/**
|
||||
* @deprecated
|
||||
* **DEPRECATED: Use global `htmlLabels` instead.**
|
||||
*
|
||||
* Flag for setting whether or not a html tag should be used for rendering labels on nodes and edges.
|
||||
* This property is deprecated.
|
||||
* Please use the global `htmlLabels` configuration instead.
|
||||
* Flag for setting whether or not a html tag should be used for rendering labels on the edges.
|
||||
*
|
||||
*/
|
||||
htmlLabels?: boolean;
|
||||
|
||||
@@ -4,8 +4,7 @@ import createLabel from './createLabel.js';
|
||||
import { createText } from '../rendering-util/createText.js';
|
||||
import { select } from 'd3';
|
||||
import { getConfig } from '../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../config.js';
|
||||
|
||||
import { evaluate } from '../diagrams/common/common.js';
|
||||
import { getSubGraphTitleMargins } from '../utils/subGraphTitleMargins.js';
|
||||
|
||||
const rect = async (parent, node) => {
|
||||
@@ -21,7 +20,7 @@ const rect = async (parent, node) => {
|
||||
// add the rect
|
||||
const rect = shapeSvg.insert('rect', ':first-child');
|
||||
|
||||
const useHtmlLabels = getEffectiveHtmlLabels(siteConfig);
|
||||
const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels);
|
||||
|
||||
// Create the label and insert it after the rect
|
||||
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
||||
@@ -39,7 +38,7 @@ const rect = async (parent, node) => {
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
|
||||
if (getEffectiveHtmlLabels(siteConfig)) {
|
||||
if (evaluate(siteConfig.flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
bbox = div.getBoundingClientRect();
|
||||
@@ -151,7 +150,7 @@ const roundedWithTitle = async (parent, node) => {
|
||||
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
if (getEffectiveHtmlLabels(siteConfig)) {
|
||||
if (evaluate(siteConfig.flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
bbox = div.getBoundingClientRect();
|
||||
@@ -191,7 +190,7 @@ const roundedWithTitle = async (parent, node) => {
|
||||
node.y -
|
||||
node.height / 2 -
|
||||
node.padding / 3 +
|
||||
(getEffectiveHtmlLabels(siteConfig) ? 5 : 3) +
|
||||
(evaluate(siteConfig.flowchart.htmlLabels) ? 5 : 3) +
|
||||
subGraphTitleTopMargin
|
||||
})`
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { select } from 'd3';
|
||||
import { getConfig } from '../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../config.js';
|
||||
import { sanitizeText } from '../diagrams/common/common.js';
|
||||
import { evaluate, sanitizeText } from '../diagrams/common/common.js';
|
||||
import { log } from '../logger.js';
|
||||
import { replaceIconSubstring } from '../rendering-util/createText.js';
|
||||
import { decodeEntities } from '../utils.js';
|
||||
@@ -51,7 +50,7 @@ const createLabel = async (_vertexText, style, isTitle, isNode) => {
|
||||
vertexText = vertexText[0];
|
||||
}
|
||||
const config = getConfig();
|
||||
if (getEffectiveHtmlLabels(config)) {
|
||||
if (evaluate(config.flowchart.htmlLabels)) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
|
||||
log.debug('vertexText' + vertexText);
|
||||
|
||||
@@ -3,9 +3,8 @@ import createLabel from './createLabel.js';
|
||||
import { createText } from '../rendering-util/createText.js';
|
||||
import { line, curveBasis, select } from 'd3';
|
||||
import { getConfig } from '../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../config.js';
|
||||
import utils from '../utils.js';
|
||||
import { getUrl } from '../diagrams/common/common.js';
|
||||
import { evaluate, getUrl } from '../diagrams/common/common.js';
|
||||
import { getLineFunctionsWithOffset } from '../utils/lineWithOffset.js';
|
||||
import { getSubGraphTitleMargins } from '../utils/subGraphTitleMargins.js';
|
||||
import { addEdgeMarkers } from './edgeMarker.js';
|
||||
@@ -20,7 +19,7 @@ export const clear = () => {
|
||||
|
||||
export const insertEdgeLabel = async (elem, edge) => {
|
||||
const config = getConfig();
|
||||
const useHtmlLabels = getEffectiveHtmlLabels(config);
|
||||
const useHtmlLabels = evaluate(config.flowchart.htmlLabels);
|
||||
// Create the actual text element
|
||||
const labelElement =
|
||||
edge.labelType === 'markdown'
|
||||
@@ -134,7 +133,7 @@ export const insertEdgeLabel = async (elem, edge) => {
|
||||
* @param {any} value
|
||||
*/
|
||||
function setTerminalWidth(fo, value) {
|
||||
if (getEffectiveHtmlLabels(getConfig()) && fo) {
|
||||
if (getConfig().flowchart.htmlLabels && fo) {
|
||||
fo.style.width = value.length * 9 + 'px';
|
||||
fo.style.height = '12px';
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { select } from 'd3';
|
||||
import { getConfig } from '../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../config.js';
|
||||
|
||||
import { evaluate } from '../diagrams/common/common.js';
|
||||
import { log } from '../logger.js';
|
||||
import { getArrowPoints } from './blockArrowHelper.js';
|
||||
import createLabel from './createLabel.js';
|
||||
@@ -589,7 +588,7 @@ const rectWithTitle = async (parent, node) => {
|
||||
|
||||
const text = label.node().appendChild(await createLabel(title, node.labelStyle, true, true));
|
||||
let bbox = { width: 0, height: 0 };
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
bbox = div.getBoundingClientRect();
|
||||
@@ -610,7 +609,7 @@ const rectWithTitle = async (parent, node) => {
|
||||
)
|
||||
);
|
||||
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
const div = descr.children[0];
|
||||
const dv = select(descr);
|
||||
bbox = div.getBoundingClientRect();
|
||||
@@ -918,7 +917,7 @@ const class_box = async (parent, node) => {
|
||||
.node()
|
||||
.appendChild(await createLabel(interfaceLabelText, node.labelStyle, true, true));
|
||||
let interfaceBBox = interfaceLabel.getBBox();
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
const div = interfaceLabel.children[0];
|
||||
const dv = select(interfaceLabel);
|
||||
interfaceBBox = div.getBoundingClientRect();
|
||||
@@ -933,7 +932,7 @@ const class_box = async (parent, node) => {
|
||||
let classTitleString = node.classData.label;
|
||||
|
||||
if (node.classData.type !== undefined && node.classData.type !== '') {
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
classTitleString += '<' + node.classData.type + '>';
|
||||
} else {
|
||||
classTitleString += '<' + node.classData.type + '>';
|
||||
@@ -944,7 +943,7 @@ const class_box = async (parent, node) => {
|
||||
.appendChild(await createLabel(classTitleString, node.labelStyle, true, true));
|
||||
select(classTitleLabel).attr('class', 'classTitle');
|
||||
let classTitleBBox = classTitleLabel.getBBox();
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
const div = classTitleLabel.children[0];
|
||||
const dv = select(classTitleLabel);
|
||||
classTitleBBox = div.getBoundingClientRect();
|
||||
@@ -959,7 +958,7 @@ const class_box = async (parent, node) => {
|
||||
node.classData.members.forEach(async (member) => {
|
||||
const parsedInfo = member.getDisplayDetails();
|
||||
let parsedText = parsedInfo.displayText;
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
parsedText = parsedText.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
const lbl = labelContainer
|
||||
@@ -973,7 +972,7 @@ const class_box = async (parent, node) => {
|
||||
)
|
||||
);
|
||||
let bbox = lbl.getBBox();
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
const div = lbl.children[0];
|
||||
const dv = select(lbl);
|
||||
bbox = div.getBoundingClientRect();
|
||||
@@ -993,7 +992,7 @@ const class_box = async (parent, node) => {
|
||||
node.classData.methods.forEach(async (member) => {
|
||||
const parsedInfo = member.getDisplayDetails();
|
||||
let displayText = parsedInfo.displayText;
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (getConfig().flowchart.htmlLabels) {
|
||||
displayText = displayText.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
const lbl = labelContainer
|
||||
@@ -1007,7 +1006,7 @@ const class_box = async (parent, node) => {
|
||||
)
|
||||
);
|
||||
let bbox = lbl.getBBox();
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
const div = lbl.children[0];
|
||||
const dv = select(lbl);
|
||||
bbox = div.getBoundingClientRect();
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { updateNodeBounds, labelHelper } from './util.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../../config.js';
|
||||
import intersect from '../intersect/index.js';
|
||||
|
||||
const note = async (parent, node) => {
|
||||
const useHtmlLabels = getEffectiveHtmlLabels(getConfig()) || node.useHtmlLabels;
|
||||
const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart.htmlLabels;
|
||||
if (!useHtmlLabels) {
|
||||
node.centerLabel = true;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import createLabel from '../createLabel.js';
|
||||
import { createText } from '../../rendering-util/createText.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../../config.js';
|
||||
import { select } from 'd3';
|
||||
import { sanitizeText } from '../../diagrams/common/common.js';
|
||||
import { evaluate, sanitizeText } from '../../diagrams/common/common.js';
|
||||
import { decodeEntities } from '../../utils.js';
|
||||
|
||||
export const labelHelper = async (parent, node, _classes, isNode) => {
|
||||
const config = getConfig();
|
||||
let classes;
|
||||
const useHtmlLabels = node.useHtmlLabels || getEffectiveHtmlLabels(config);
|
||||
const useHtmlLabels = node.useHtmlLabels || evaluate(config.flowchart.htmlLabels);
|
||||
if (!_classes) {
|
||||
classes = 'node default';
|
||||
} else {
|
||||
@@ -61,7 +60,7 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
|
||||
let bbox = text.getBBox();
|
||||
const halfPadding = node.padding / 2;
|
||||
|
||||
if (getEffectiveHtmlLabels(config)) {
|
||||
if (evaluate(config.flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { select, curveLinear } from 'd3';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../../config.js';
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
import utils, { getEdgeId } from '../../utils.js';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||
@@ -269,7 +268,7 @@ export const addRelations = function (relations: ClassRelation[], g: graphlib.Gr
|
||||
edgeData.labelpos = 'c';
|
||||
|
||||
// TODO V10: Flowchart ? Keeping flowchart for backwards compatibility. Remove in next major release
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (getConfig().flowchart?.htmlLabels ?? getConfig().htmlLabels) {
|
||||
edgeData.labelType = 'html';
|
||||
edgeData.label = '<span class="edgeLabel">' + edge.text + '</span>';
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import DOMPurify from 'dompurify';
|
||||
import { getEffectiveHtmlLabels } from '../../config.js';
|
||||
import type { MermaidConfig } from '../../config.type.js';
|
||||
|
||||
// Remove and ignore br:s
|
||||
@@ -65,7 +64,7 @@ export const removeScript = (txt: string): string => {
|
||||
};
|
||||
|
||||
const sanitizeMore = (text: string, config: MermaidConfig) => {
|
||||
if (getEffectiveHtmlLabels(config)) {
|
||||
if (config.flowchart?.htmlLabels !== false) {
|
||||
const level = config.securityLevel;
|
||||
if (level === 'antiscript' || level === 'strict' || level === 'sandbox') {
|
||||
text = removeScript(text);
|
||||
|
||||
@@ -321,7 +321,7 @@ export class RequirementDB implements DiagramDB {
|
||||
id: `${relation.src}-${relation.dst}-${counter}`,
|
||||
start: this.requirements.get(relation.src)?.name ?? this.elements.get(relation.src)?.name,
|
||||
end: this.requirements.get(relation.dst)?.name ?? this.elements.get(relation.dst)?.name,
|
||||
label: `«${relation.type}»`,
|
||||
label: `<<${relation.type}>>`,
|
||||
classes: 'relationshipLine',
|
||||
style: ['fill:none', isContains ? '' : 'stroke-dasharray: 10,7'],
|
||||
labelpos: 'c',
|
||||
|
||||
@@ -40,15 +40,6 @@ const getStyles = (options) => `
|
||||
.relationshipLabel {
|
||||
fill: ${options.relationLabelColor};
|
||||
}
|
||||
.edgeLabel {
|
||||
background-color: ${options.edgeLabelBackground};
|
||||
}
|
||||
.edgeLabel .label rect {
|
||||
fill: ${options.edgeLabelBackground};
|
||||
}
|
||||
.edgeLabel .label text {
|
||||
fill: ${options.relationLabelColor};
|
||||
}
|
||||
.divider {
|
||||
stroke: ${options.nodeBorder};
|
||||
stroke-width: 1;
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { CanonicalUrlConfig } from './canonical-urls.js';
|
||||
*/
|
||||
export const canonicalConfig: CanonicalUrlConfig = {
|
||||
// Base URL for the Mermaid documentation site
|
||||
baseUrl: 'https://mermaid.ai/open-source',
|
||||
baseUrl: 'https://docs.mermaidchart.com',
|
||||
|
||||
// Disable automatic generation - only use specificCanonicalUrls
|
||||
autoGenerate: false,
|
||||
@@ -57,6 +57,93 @@ export const canonicalConfig: CanonicalUrlConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Pages that should have specific canonical URLs
|
||||
*
|
||||
* Since autoGenerate is set to false, ONLY pages listed here will get canonical URLs.
|
||||
*
|
||||
* Usage: Add entries to this object where the key is the relative path
|
||||
* of the markdown file and the value is the desired canonical URL.
|
||||
*
|
||||
* Examples:
|
||||
* - 'intro/index.md': 'https://docs.mermaidchart.com/intro/index.html'
|
||||
* - 'syntax/flowchart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/flowchart.html'
|
||||
* - 'config/configuration.md': 'https://docs.mermaidchart.com/mermaid-oss/config/configuration.html'
|
||||
*/
|
||||
export const specificCanonicalUrls: Record<string, string> = {
|
||||
// Add your specific canonical URLs here
|
||||
// Example:
|
||||
// 'syntax/flowchart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/flowchart.html',
|
||||
|
||||
// Intro section
|
||||
'intro/index.md': 'https://docs.mermaidchart.com/intro/index.html',
|
||||
'intro/getting-started.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/intro/getting-started.html',
|
||||
'intro/syntax-reference.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/intro/syntax-reference.html',
|
||||
|
||||
// Syntax section
|
||||
'syntax/flowchart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/flowchart.html',
|
||||
'syntax/sequenceDiagram.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/syntax/sequenceDiagram.html',
|
||||
'syntax/classDiagram.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/classDiagram.html',
|
||||
'syntax/stateDiagram.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/stateDiagram.html',
|
||||
'syntax/entityRelationshipDiagram.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/syntax/entityRelationshipDiagram.html',
|
||||
'syntax/userJourney.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/userJourney.html',
|
||||
'syntax/gantt.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/gantt.html',
|
||||
'syntax/pie.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/pie.html',
|
||||
'syntax/quadrantChart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/quadrantChart.html',
|
||||
'syntax/requirementDiagram.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/syntax/requirementDiagram.html',
|
||||
'syntax/mindmap.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/mindmap.html',
|
||||
'syntax/timeline.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/timeline.html',
|
||||
'syntax/gitgraph.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/gitgraph.html',
|
||||
'syntax/c4.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/c4.html',
|
||||
'syntax/sankey.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/sankey.html',
|
||||
'syntax/xyChart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/xyChart.html',
|
||||
'syntax/block.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/block.html',
|
||||
'syntax/packet.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/packet.html',
|
||||
'syntax/kanban.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/kanban.html',
|
||||
'syntax/architecture.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/architecture.html',
|
||||
'syntax/radar.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/radar.html',
|
||||
'syntax/examples.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/examples.html',
|
||||
|
||||
// Config section
|
||||
'config/configuration.md': 'https://docs.mermaidchart.com/mermaid-oss/config/configuration.html',
|
||||
'config/usage.md': 'https://docs.mermaidchart.com/mermaid-oss/config/usage.html',
|
||||
'config/icons.md': 'https://docs.mermaidchart.com/mermaid-oss/config/icons.html',
|
||||
'config/directives.md': 'https://docs.mermaidchart.com/mermaid-oss/config/directives.html',
|
||||
'config/theming.md': 'https://docs.mermaidchart.com/mermaid-oss/config/theming.html',
|
||||
'config/math.md': 'https://docs.mermaidchart.com/mermaid-oss/config/math.html',
|
||||
'config/accessibility.md': 'https://docs.mermaidchart.com/mermaid-oss/config/accessibility.html',
|
||||
'config/mermaidCLI.md': 'https://docs.mermaidchart.com/mermaid-oss/config/mermaidCLI.html',
|
||||
'config/faq.md': 'https://docs.mermaidchart.com/mermaid-oss/config/faq.html',
|
||||
|
||||
// Ecosystem section
|
||||
'ecosystem/mermaid-chart.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/ecosystem/mermaid-chart.html',
|
||||
'ecosystem/tutorials.md': 'https://docs.mermaidchart.com/mermaid-oss/ecosystem/tutorials.html',
|
||||
'ecosystem/integrations-community.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/ecosystem/integrations-community.html',
|
||||
'ecosystem/integrations-create.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/ecosystem/integrations-create.html',
|
||||
|
||||
// Community section
|
||||
'community/intro.md': 'https://docs.mermaidchart.com/mermaid-oss/community/intro.html',
|
||||
'community/contributing.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/community/contributing.html',
|
||||
'community/new-diagram.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/community/new-diagram.html',
|
||||
'community/questions-and-suggestions.md':
|
||||
'https://docs.mermaidchart.com/mermaid-oss/community/questions-and-suggestions.html',
|
||||
'community/security.md': 'https://docs.mermaidchart.com/mermaid-oss/community/security.html',
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to get canonical URL for a specific page
|
||||
* This can be used in frontmatter or for manual overrides
|
||||
*/
|
||||
export function getCanonicalUrl(relativePath: string): string | undefined {
|
||||
return `https://mermaid.ai/open-source/${relativePath}`;
|
||||
return specificCanonicalUrls[relativePath];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PageData } from 'vitepress';
|
||||
import { canonicalConfig } from './canonical-config.js';
|
||||
import { canonicalConfig, specificCanonicalUrls } from './canonical-config.js';
|
||||
|
||||
/**
|
||||
* Configuration for canonical URL generation
|
||||
@@ -48,15 +48,31 @@ const defaultConfig: CanonicalUrlConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a path matches any of the exclude patterns
|
||||
*/
|
||||
function shouldExcludePath(relativePath: string, excludePatterns: string[] = []): boolean {
|
||||
return excludePatterns.some((pattern) => {
|
||||
// Convert glob pattern to regex
|
||||
const regexPattern = pattern
|
||||
.replace(/\*\*/g, '.*')
|
||||
.replace(/\*/g, '[^/]*')
|
||||
.replace(/\?/g, '.')
|
||||
.replace(/\./g, '\\.');
|
||||
const regex = new RegExp(`^${regexPattern}$`);
|
||||
return regex.test(relativePath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a relative path to a canonical URL path
|
||||
*/
|
||||
export function transformPath(relativePath: string, config: CanonicalUrlConfig): string {
|
||||
function transformPath(relativePath: string, config: CanonicalUrlConfig): string {
|
||||
let transformedPath = relativePath;
|
||||
|
||||
// Apply built-in transformations
|
||||
if (config.transformations?.removeMarkdownExtension) {
|
||||
transformedPath = transformedPath.replace(/\.md$/, '.html');
|
||||
transformedPath = transformedPath.replace(/\.md$/, '');
|
||||
}
|
||||
|
||||
if (config.transformations?.removeIndex) {
|
||||
@@ -100,9 +116,45 @@ function generateCanonicalUrl(relativePath: string, config: CanonicalUrlConfig):
|
||||
export function addCanonicalUrls(pageData: PageData): void {
|
||||
const config = canonicalConfig;
|
||||
|
||||
// Check for specific canonical URLs first
|
||||
const specificUrl = specificCanonicalUrls[pageData.relativePath];
|
||||
if (specificUrl) {
|
||||
addCanonicalToHead(pageData, specificUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if canonical URL is already explicitly set in frontmatter
|
||||
if (pageData.frontmatter.canonical) {
|
||||
// If it's already a full URL, use as-is
|
||||
if (pageData.frontmatter.canonical.startsWith('http')) {
|
||||
addCanonicalToHead(pageData, pageData.frontmatter.canonical);
|
||||
return;
|
||||
}
|
||||
// If it's a relative path, convert to absolute URL
|
||||
const canonicalUrl = config.baseUrl + pageData.frontmatter.canonical;
|
||||
addCanonicalToHead(pageData, canonicalUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if canonicalPath is set in frontmatter
|
||||
if (pageData.frontmatter.canonicalPath) {
|
||||
const canonicalUrl = config.baseUrl + pageData.frontmatter.canonicalPath;
|
||||
addCanonicalToHead(pageData, canonicalUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if auto-generation is disabled
|
||||
if (!config.autoGenerate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if path should be excluded
|
||||
if (shouldExcludePath(pageData.relativePath, config.excludePatterns)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate canonical URL
|
||||
const canonicalUrl = generateCanonicalUrl(pageData.relativePath, config);
|
||||
transformPath(pageData.relativePath, config);
|
||||
addCanonicalToHead(pageData, canonicalUrl);
|
||||
}
|
||||
|
||||
|
||||
@@ -185,27 +185,22 @@ A --> C[End]
|
||||
|
||||
Some common flowchart configurations are:
|
||||
|
||||
- _htmlLabels_: true/false
|
||||
- _curve_: linear/curve
|
||||
- _diagramPadding_: number
|
||||
- _useMaxWidth_: number
|
||||
|
||||
**Deprecated configurations:**
|
||||
|
||||
- ~~_htmlLabels_~~: Use global `htmlLabels` instead
|
||||
|
||||
For a complete list of flowchart configurations, see [defaultConfig.ts](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/defaultConfig.ts) in the source code.
|
||||
_Soon we plan to publish a complete list of all diagram-specific configurations updated in the docs._
|
||||
|
||||
The following code snippet changes flowchart config:
|
||||
|
||||
```
|
||||
%%{init: { "htmlLabels": true, "flowchart": { "curve": "linear" } } }%%
|
||||
```
|
||||
`%%{init: { "flowchart": { "htmlLabels": true, "curve": "linear" } } }%%`
|
||||
|
||||
**Note:** `flowchart.htmlLabels` has been deprecated. Use the global `htmlLabels` configuration instead.
|
||||
Here we are overriding only the flowchart config, and not the general config, setting `htmlLabels` to `true` and `curve` to `linear`.
|
||||
|
||||
```mermaid-example
|
||||
%%{init: { "htmlLabels": true, "flowchart": { "curve": "linear" } } }%%
|
||||
%%{init: { "flowchart": { "htmlLabels": true, "curve": "linear" } } }%%
|
||||
graph TD
|
||||
A(Forest) --> B[/Another/]
|
||||
A --> C[End]
|
||||
|
||||
@@ -368,7 +368,7 @@ The list of configuration objects are described [in the mermaidAPI documentation
|
||||
```html
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
let config = { startOnLoad: true, htmlLabels: true, flowchart: { useMaxWidth: false } };
|
||||
let config = { startOnLoad: true, flowchart: { useMaxWidth: false, htmlLabels: true } };
|
||||
mermaid.initialize(config);
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -2,7 +2,6 @@ import { assert, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import assignWithDepth from './assignWithDepth.js';
|
||||
import type { MermaidConfig } from './config.type.js';
|
||||
import { getEffectiveHtmlLabels } from './config.js';
|
||||
import mermaid from './mermaid.js';
|
||||
import mermaidAPI, {
|
||||
appendDivSvgG,
|
||||
@@ -359,11 +358,10 @@ describe('mermaidAPI', () => {
|
||||
});
|
||||
|
||||
describe('no htmlLabels in the configuration', () => {
|
||||
const mocked_config_no_htmlLabels: MermaidConfig = {
|
||||
const mocked_config_no_htmlLabels = {
|
||||
themeCSS: 'default',
|
||||
fontFamily: 'serif',
|
||||
altFontFamily: 'sans-serif',
|
||||
htmlLabels: false, // Explicitly set to false
|
||||
};
|
||||
|
||||
describe('creates styles for shape elements "rect", "polygon", "ellipse", and "circle"', () => {
|
||||
@@ -1150,63 +1148,4 @@ flowchart TD
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('flowchart.htmlLabels deprecation behavior', () => {
|
||||
beforeEach(() => {
|
||||
mermaidAPI.globalReset();
|
||||
});
|
||||
|
||||
it('should use root-level htmlLabels when only root-level is set', () => {
|
||||
const config: MermaidConfig = { htmlLabels: true };
|
||||
expect(config.htmlLabels).toBe(true);
|
||||
|
||||
const config2: MermaidConfig = { htmlLabels: false };
|
||||
expect(config2.htmlLabels).toBe(false);
|
||||
});
|
||||
|
||||
it('should check config.htmlLabels value directly when set', () => {
|
||||
const config1: MermaidConfig = { htmlLabels: true };
|
||||
expect(config1.htmlLabels).toBe(true);
|
||||
|
||||
const config2: MermaidConfig = { htmlLabels: false };
|
||||
expect(config2.htmlLabels).toBe(false);
|
||||
|
||||
const config3: MermaidConfig = { htmlLabels: undefined };
|
||||
expect(config3.htmlLabels).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should use getEffectiveHtmlLabels only when fallback logic is needed', () => {
|
||||
// Only call getEffectiveHtmlLabels when we need the fallback behavior
|
||||
const configWithDeprecated: MermaidConfig = { flowchart: { htmlLabels: true } };
|
||||
expect(getEffectiveHtmlLabels(configWithDeprecated)).toBe(true);
|
||||
|
||||
const configWithBoth: MermaidConfig = {
|
||||
htmlLabels: false,
|
||||
flowchart: { htmlLabels: true },
|
||||
};
|
||||
expect(getEffectiveHtmlLabels(configWithBoth)).toBe(false); // Root takes precedence
|
||||
|
||||
const configEmpty: MermaidConfig = {};
|
||||
expect(getEffectiveHtmlLabels(configEmpty)).toBe(true); // Default to true
|
||||
});
|
||||
|
||||
it('should verify the precedence logic: config.htmlLabels ?? config.flowchart?.htmlLabels ?? true', () => {
|
||||
// Test the exact precedence chain
|
||||
const config1: MermaidConfig = { htmlLabels: true };
|
||||
const result1 = config1.htmlLabels ?? config1.flowchart?.htmlLabels ?? true;
|
||||
expect(result1).toBe(true);
|
||||
|
||||
const config2: MermaidConfig = { htmlLabels: false };
|
||||
const result2 = config2.htmlLabels ?? config2.flowchart?.htmlLabels ?? true;
|
||||
expect(result2).toBe(false);
|
||||
|
||||
const config3: MermaidConfig = { flowchart: { htmlLabels: true } };
|
||||
const result3 = config3.htmlLabels ?? config3.flowchart?.htmlLabels ?? true;
|
||||
expect(result3).toBe(true);
|
||||
|
||||
const config4: MermaidConfig = {};
|
||||
const result4 = config4.htmlLabels ?? config4.flowchart?.htmlLabels ?? true;
|
||||
expect(result4).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,7 +10,6 @@ import isEmpty from 'lodash-es/isEmpty.js';
|
||||
import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js';
|
||||
import assignWithDepth from './assignWithDepth.js';
|
||||
import * as configApi from './config.js';
|
||||
import { getEffectiveHtmlLabels } from './config.js';
|
||||
import type { MermaidConfig } from './config.type.js';
|
||||
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
||||
import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js';
|
||||
@@ -128,7 +127,7 @@ export const createCssStyles = (
|
||||
|
||||
// classDefs defined in the diagram text
|
||||
if (classDefs instanceof Map) {
|
||||
const htmlLabels = getEffectiveHtmlLabels(config);
|
||||
const htmlLabels = config.htmlLabels ?? config.flowchart?.htmlLabels; // TODO why specifically check the Flowchart diagram config?
|
||||
|
||||
const cssHtmlElements = ['> *', 'span']; // TODO make a constant
|
||||
const cssShapeElements = ['rect', 'polygon', 'ellipse', 'circle', 'path']; // TODO make a constant
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../../config.js';
|
||||
|
||||
import { evaluate } from '../../diagrams/common/common.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js';
|
||||
import { select } from 'd3';
|
||||
@@ -26,7 +25,7 @@ const rect = async (parent, node) => {
|
||||
.attr('id', node.id)
|
||||
.attr('data-look', node.look);
|
||||
|
||||
const useHtmlLabels = getEffectiveHtmlLabels(siteConfig);
|
||||
const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels);
|
||||
|
||||
// Create the label and insert it after the rect
|
||||
const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label ');
|
||||
@@ -40,7 +39,7 @@ const rect = async (parent, node) => {
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
|
||||
if (getEffectiveHtmlLabels(siteConfig)) {
|
||||
if (evaluate(siteConfig.flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
bbox = div.getBoundingClientRect();
|
||||
@@ -189,7 +188,7 @@ const roundedWithTitle = async (parent, node) => {
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
|
||||
if (getEffectiveHtmlLabels(siteConfig)) {
|
||||
if (evaluate(siteConfig.flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
bbox = div.getBoundingClientRect();
|
||||
@@ -265,7 +264,7 @@ const roundedWithTitle = async (parent, node) => {
|
||||
|
||||
label.attr(
|
||||
'transform',
|
||||
`translate(${node.x - bbox.width / 2}, ${y + 1 - (getEffectiveHtmlLabels(siteConfig) ? 0 : 3)})`
|
||||
`translate(${node.x - bbox.width / 2}, ${y + 1 - (evaluate(siteConfig.flowchart.htmlLabels) ? 0 : 3)})`
|
||||
);
|
||||
|
||||
const rectBox = rect.node().getBBox();
|
||||
@@ -296,7 +295,7 @@ const kanbanSection = async (parent, node) => {
|
||||
.attr('id', node.id)
|
||||
.attr('data-look', node.look);
|
||||
|
||||
const useHtmlLabels = getEffectiveHtmlLabels(siteConfig);
|
||||
const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels);
|
||||
|
||||
// Create the label and insert it after the rect
|
||||
const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label ');
|
||||
@@ -311,7 +310,7 @@ const kanbanSection = async (parent, node) => {
|
||||
// Get the size of the label
|
||||
let bbox = text.getBBox();
|
||||
|
||||
if (getEffectiveHtmlLabels(siteConfig)) {
|
||||
if (evaluate(siteConfig.flowchart.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
bbox = div.getBoundingClientRect();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { select } from 'd3';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../../config.js';
|
||||
import common, {
|
||||
evaluate,
|
||||
hasKatex,
|
||||
renderKatexSanitized,
|
||||
sanitizeText,
|
||||
@@ -64,7 +64,7 @@ const createLabel = async (_vertexText, style, isTitle, isNode) => {
|
||||
vertexText = vertexText[0];
|
||||
}
|
||||
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
|
||||
log.info('vertexText' + vertexText);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../../config.js';
|
||||
import { evaluate } from '../../diagrams/common/common.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { createText } from '../createText.js';
|
||||
import utils from '../../utils.js';
|
||||
@@ -45,7 +45,7 @@ export const getLabelStyles = (styleArray) => {
|
||||
};
|
||||
|
||||
export const insertEdgeLabel = async (elem, edge) => {
|
||||
let useHtmlLabels = getEffectiveHtmlLabels(getConfig());
|
||||
let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
||||
|
||||
const { labelStyles } = styles2String(edge);
|
||||
edge.labelStyle = labelStyles;
|
||||
@@ -161,7 +161,7 @@ export const insertEdgeLabel = async (elem, edge) => {
|
||||
* @param {any} value
|
||||
*/
|
||||
function setTerminalWidth(fo, value) {
|
||||
if (getEffectiveHtmlLabels(getConfig()) && fo) {
|
||||
if (getConfig().flowchart.htmlLabels && fo) {
|
||||
fo.style.width = value.length * 9 + 'px';
|
||||
fo.style.height = '12px';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.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 { insertPolygonShape } from './insertPolygonShape.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
|
||||
export const createHexagonPathD = (
|
||||
@@ -28,50 +29,42 @@ export async function hexagon<T extends SVGGraphicsElement>(parent: D3Selection<
|
||||
node.labelStyle = labelStyles;
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||
|
||||
const h = bbox.height + (node.padding ?? 0);
|
||||
const w = bbox.width + (node.padding ?? 0) * 2.5;
|
||||
const { cssStyles } = node;
|
||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||
const rc = rough.svg(shapeSvg);
|
||||
const options = userNodeOverrides(node, {});
|
||||
|
||||
if (node.look !== 'handDrawn') {
|
||||
options.roughness = 0;
|
||||
options.fillStyle = 'solid';
|
||||
}
|
||||
|
||||
let halfWidth = w / 2;
|
||||
const m = halfWidth / 6; // Margin for label
|
||||
halfWidth = halfWidth + m; // Adjusted half width for hexagon
|
||||
|
||||
const halfHeight = h / 2;
|
||||
|
||||
const fixedLength = halfHeight / 2;
|
||||
const deducedWidth = halfWidth - fixedLength;
|
||||
|
||||
const f = 4;
|
||||
const h = bbox.height + node.padding;
|
||||
const m = h / f;
|
||||
const w = bbox.width + 2 * m + node.padding;
|
||||
const points = [
|
||||
{ x: -deducedWidth, y: -halfHeight },
|
||||
{ x: 0, y: -halfHeight },
|
||||
{ x: deducedWidth, y: -halfHeight },
|
||||
{ x: halfWidth, y: 0 },
|
||||
{ x: deducedWidth, y: halfHeight },
|
||||
{ x: 0, y: halfHeight },
|
||||
{ x: -deducedWidth, y: halfHeight },
|
||||
{ x: -halfWidth, y: 0 },
|
||||
{ x: m, y: 0 },
|
||||
{ x: w - m, y: 0 },
|
||||
{ x: w, y: -h / 2 },
|
||||
{ x: w - m, y: -h },
|
||||
{ x: m, y: -h },
|
||||
{ x: 0, y: -h / 2 },
|
||||
];
|
||||
|
||||
const pathData = createPathFromPoints(points);
|
||||
const shapeNode = rc.path(pathData, options);
|
||||
let polygon: D3Selection<SVGGElement> | Awaited<ReturnType<typeof insertPolygonShape>>;
|
||||
const { cssStyles } = node;
|
||||
|
||||
const polygon = shapeSvg.insert(() => shapeNode, ':first-child');
|
||||
polygon.attr('class', 'basic label-container');
|
||||
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 pathData = createHexagonPathD(0, 0, w, h, m);
|
||||
const roughNode = rc.path(pathData, options);
|
||||
|
||||
if (cssStyles && node.look !== 'handDrawn') {
|
||||
polygon.selectChildren('path').attr('style', cssStyles);
|
||||
polygon = shapeSvg
|
||||
.insert(() => roughNode, ':first-child')
|
||||
.attr('transform', `translate(${-w / 2}, ${h / 2})`);
|
||||
|
||||
if (cssStyles) {
|
||||
polygon.attr('style', cssStyles);
|
||||
}
|
||||
} else {
|
||||
polygon = insertPolygonShape(shapeSvg, w, h, points);
|
||||
}
|
||||
|
||||
if (nodeStyles && node.look !== 'handDrawn') {
|
||||
polygon.selectChildren('path').attr('style', nodeStyles);
|
||||
if (nodeStyles) {
|
||||
polygon.attr('style', nodeStyles);
|
||||
}
|
||||
|
||||
node.width = w;
|
||||
|
||||
@@ -5,7 +5,6 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||
import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
import { getConfig } from '../../../config.js';
|
||||
import { getEffectiveHtmlLabels } from '../../../config.js';
|
||||
|
||||
export async function note<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
@@ -14,7 +13,7 @@ export async function note<T extends SVGGraphicsElement>(
|
||||
) {
|
||||
const { labelStyles, nodeStyles } = styles2String(node);
|
||||
node.labelStyle = labelStyles;
|
||||
const useHtmlLabels = getEffectiveHtmlLabels(getConfig()) || node.useHtmlLabels;
|
||||
const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart?.htmlLabels !== false;
|
||||
if (!useHtmlLabels) {
|
||||
node.centerLabel = true;
|
||||
}
|
||||
|
||||
@@ -1,161 +1,18 @@
|
||||
import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } 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 { Node, RectOptions } from '../../types.js';
|
||||
import type { D3Selection } from '../../../types.js';
|
||||
|
||||
/**
|
||||
* Generates evenly spaced points along an elliptical arc connecting two points.
|
||||
*
|
||||
* @param x1 - x-coordinate of the start point of the arc
|
||||
* @param y1 - y-coordinate of the start point of the arc
|
||||
* @param x2 - x-coordinate of the end point of the arc
|
||||
* @param y2 - y-coordinate of the end point of the arc
|
||||
* @param rx - horizontal radius of the ellipse
|
||||
* @param ry - vertical radius of the ellipse
|
||||
* @param clockwise - direction of the arc; true for clockwise, false for counterclockwise
|
||||
* @returns Array of points `{ x, y }` along the elliptical arc
|
||||
*
|
||||
* @throws Error if the given radii are too small to draw an arc between the points
|
||||
*/
|
||||
export function generateArcPoints(
|
||||
x1: number,
|
||||
y1: number,
|
||||
x2: number,
|
||||
y2: number,
|
||||
rx: number,
|
||||
ry: number,
|
||||
clockwise: boolean
|
||||
) {
|
||||
const numPoints = 20;
|
||||
// Calculate midpoint
|
||||
const midX = (x1 + x2) / 2;
|
||||
const midY = (y1 + y2) / 2;
|
||||
|
||||
// Calculate the angle of the line connecting the points
|
||||
const angle = Math.atan2(y2 - y1, x2 - x1);
|
||||
|
||||
// Calculate transformed coordinates for the ellipse
|
||||
const dx = (x2 - x1) / 2;
|
||||
const dy = (y2 - y1) / 2;
|
||||
|
||||
// Scale to unit circle
|
||||
const transformedX = dx / rx;
|
||||
const transformedY = dy / ry;
|
||||
|
||||
// Calculate the distance between points on the unit circle
|
||||
const distance = Math.sqrt(transformedX ** 2 + transformedY ** 2);
|
||||
|
||||
// Check if the ellipse can be drawn with the given radii
|
||||
if (distance > 1) {
|
||||
throw new Error('The given radii are too small to create an arc between the points.');
|
||||
}
|
||||
|
||||
// Calculate the distance from the midpoint to the center of the ellipse
|
||||
const scaledCenterDistance = Math.sqrt(1 - distance ** 2);
|
||||
|
||||
// Calculate the center of the ellipse
|
||||
const centerX = midX + scaledCenterDistance * ry * Math.sin(angle) * (clockwise ? -1 : 1);
|
||||
const centerY = midY - scaledCenterDistance * rx * Math.cos(angle) * (clockwise ? -1 : 1);
|
||||
|
||||
// Calculate the start and end angles on the ellipse
|
||||
const startAngle = Math.atan2((y1 - centerY) / ry, (x1 - centerX) / rx);
|
||||
const endAngle = Math.atan2((y2 - centerY) / ry, (x2 - centerX) / rx);
|
||||
|
||||
// Adjust angles for clockwise/counterclockwise
|
||||
let angleRange = endAngle - startAngle;
|
||||
if (clockwise && angleRange < 0) {
|
||||
angleRange += 2 * Math.PI;
|
||||
}
|
||||
if (!clockwise && angleRange > 0) {
|
||||
angleRange -= 2 * Math.PI;
|
||||
}
|
||||
|
||||
// Generate points
|
||||
const points = [];
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
const t = i / (numPoints - 1);
|
||||
const angle = startAngle + t * angleRange;
|
||||
const x = centerX + rx * Math.cos(angle);
|
||||
const y = centerY + ry * Math.sin(angle);
|
||||
points.push({ x, y });
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
import { drawRect } from './drawRect.js';
|
||||
|
||||
export async function roundedRect<T extends SVGGraphicsElement>(
|
||||
parent: D3Selection<T>,
|
||||
node: Node
|
||||
) {
|
||||
const { labelStyles, nodeStyles } = styles2String(node);
|
||||
node.labelStyle = labelStyles;
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||
const options = {
|
||||
rx: 5,
|
||||
ry: 5,
|
||||
classes: '',
|
||||
labelPaddingX: (node?.padding || 0) * 1,
|
||||
labelPaddingY: (node?.padding || 0) * 1,
|
||||
} as RectOptions;
|
||||
|
||||
const labelPaddingX = node?.padding ?? 0;
|
||||
const labelPaddingY = node?.padding ?? 0;
|
||||
|
||||
const w = (node?.width ? node?.width : bbox.width) + labelPaddingX * 2;
|
||||
const h = (node?.height ? node?.height : bbox.height) + labelPaddingY * 2;
|
||||
const radius = node.radius || 5;
|
||||
const taper = node.taper || 5; // Taper width for the rounded corners
|
||||
const { cssStyles } = node;
|
||||
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
|
||||
const rc = rough.svg(shapeSvg);
|
||||
const options = userNodeOverrides(node, {});
|
||||
if (node.stroke) {
|
||||
options.stroke = node.stroke;
|
||||
}
|
||||
if (node.look !== 'handDrawn') {
|
||||
options.roughness = 0;
|
||||
options.fillStyle = 'solid';
|
||||
}
|
||||
const points = [
|
||||
// Top edge (left to right)
|
||||
{ x: -w / 2 + taper, y: -h / 2 }, // Top-left corner start (1)
|
||||
{ x: w / 2 - taper, y: -h / 2 }, // Top-right corner start (2)
|
||||
|
||||
...generateArcPoints(w / 2 - taper, -h / 2, w / 2, -h / 2 + taper, radius, radius, true), // Top-left arc (2 to 3)
|
||||
|
||||
// Right edge (top to bottom)
|
||||
{ x: w / 2, y: -h / 2 + taper }, // Top-right taper point (3)
|
||||
{ x: w / 2, y: h / 2 - taper }, // Bottom-right taper point (4)
|
||||
|
||||
...generateArcPoints(w / 2, h / 2 - taper, w / 2 - taper, h / 2, radius, radius, true), // Top-left arc (4 to 5)
|
||||
|
||||
// Bottom edge (right to left)
|
||||
{ x: w / 2 - taper, y: h / 2 }, // Bottom-right corner start (5)
|
||||
{ x: -w / 2 + taper, y: h / 2 }, // Bottom-left corner start (6)
|
||||
|
||||
...generateArcPoints(-w / 2 + taper, h / 2, -w / 2, h / 2 - taper, radius, radius, true), // Top-left arc (4 to 5)
|
||||
|
||||
// Left edge (bottom to top)
|
||||
{ x: -w / 2, y: h / 2 - taper }, // Bottom-left taper point (7)
|
||||
{ x: -w / 2, y: -h / 2 + taper }, // Top-left taper point (8)
|
||||
...generateArcPoints(-w / 2, -h / 2 + taper, -w / 2 + taper, -h / 2, radius, radius, true), // Top-left arc (4 to 5)
|
||||
];
|
||||
|
||||
const pathData = createPathFromPoints(points);
|
||||
const shapeNode = rc.path(pathData, options);
|
||||
|
||||
const polygon = shapeSvg.insert(() => shapeNode, ':first-child');
|
||||
polygon.attr('class', 'basic label-container outer-path');
|
||||
|
||||
if (cssStyles && node.look !== 'handDrawn') {
|
||||
polygon.selectChildren('path').attr('style', cssStyles);
|
||||
}
|
||||
|
||||
if (nodeStyles && node.look !== 'handDrawn') {
|
||||
polygon.selectChildren('path').attr('style', nodeStyles);
|
||||
}
|
||||
|
||||
updateNodeBounds(node, polygon);
|
||||
|
||||
node.intersect = function (point) {
|
||||
const pos = intersect.polygon(node, points, point);
|
||||
return pos;
|
||||
};
|
||||
|
||||
return shapeSvg;
|
||||
return drawRect(parent, node, options);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createText } from '../../createText.js';
|
||||
import type { Node } from '../../types.js';
|
||||
import { getConfig } from '../../../diagram-api/diagramAPI.js';
|
||||
import { getEffectiveHtmlLabels } from '../../../config.js';
|
||||
import { select } from 'd3';
|
||||
import defaultConfig from '../../../defaultConfig.js';
|
||||
import { evaluate, sanitizeText } from '../../../diagrams/common/common.js';
|
||||
@@ -131,7 +130,7 @@ export const insertLabel = async <T extends SVGGraphicsElement>(
|
||||
addSvgBackground?: boolean | undefined;
|
||||
}
|
||||
) => {
|
||||
const useHtmlLabels = getEffectiveHtmlLabels(getConfig()) || options.useHtmlLabels;
|
||||
const useHtmlLabels = options.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels);
|
||||
|
||||
// Create the label and insert it after the rect
|
||||
const labelEl = parent
|
||||
@@ -149,7 +148,7 @@ export const insertLabel = async <T extends SVGGraphicsElement>(
|
||||
let bbox = text.getBBox();
|
||||
const halfPadding = options.padding / 2;
|
||||
|
||||
if (getEffectiveHtmlLabels(getConfig())) {
|
||||
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {
|
||||
const div = text.children[0];
|
||||
const dv = select(text);
|
||||
|
||||
|
||||
@@ -153,8 +153,6 @@ properties:
|
||||
default: false
|
||||
htmlLabels:
|
||||
type: boolean # maybe unused, seems to be copied in each diagram config
|
||||
default: true
|
||||
|
||||
fontFamily:
|
||||
description: |
|
||||
Specifies the font to be used in the rendered diagrams.
|
||||
@@ -2054,6 +2052,7 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file)
|
||||
- titleTopMargin
|
||||
- subGraphTitleMargin
|
||||
- diagramPadding
|
||||
- htmlLabels
|
||||
- nodeSpacing
|
||||
- rankSpacing
|
||||
- curve
|
||||
@@ -2085,13 +2084,9 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file)
|
||||
default: 8
|
||||
htmlLabels:
|
||||
description: |
|
||||
**DEPRECATED: Use global `htmlLabels` instead.**
|
||||
|
||||
Flag for setting whether or not a html tag should be used for rendering labels on nodes and edges.
|
||||
This property is deprecated.
|
||||
Please use the global `htmlLabels` configuration instead.
|
||||
Flag for setting whether or not a html tag should be used for rendering labels on the edges.
|
||||
type: boolean
|
||||
deprecated: true
|
||||
default: true
|
||||
nodeSpacing:
|
||||
description: |
|
||||
Defines the spacing between nodes on the same level
|
||||
|
||||
Reference in New Issue
Block a user