mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-15 05:19:58 +02:00
#5237 Removed style, class, labelText and props from node
This commit is contained in:
@@ -76,7 +76,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid">
|
||||||
%%{init: {"layout": "stress"} }%%
|
%%{init: {"layout": "elk"} }%%
|
||||||
stateDiagram
|
stateDiagram
|
||||||
[*] --> T1
|
[*] --> T1
|
||||||
T1 --> T2
|
T1 --> T2
|
||||||
@@ -531,7 +531,7 @@ mindmap
|
|||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
// theme: 'dark',
|
// theme: 'dark',
|
||||||
handdrawnSeed: 12,
|
handdrawnSeed: 12,
|
||||||
look: 'handdrawn',
|
// look: 'handdrawn',
|
||||||
// layout: 'dagre',
|
// layout: 'dagre',
|
||||||
layout: 'elk',
|
layout: 'elk',
|
||||||
flowchart: { titleTopMargin: 10 },
|
flowchart: { titleTopMargin: 10 },
|
||||||
|
392
packages/mermaid/src/diagrams/state/dataFetcher.js
Normal file
392
packages/mermaid/src/diagrams/state/dataFetcher.js
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
import { log } from '../../logger.js';
|
||||||
|
import common from '../common/common.js';
|
||||||
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
DEFAULT_DIAGRAM_DIRECTION,
|
||||||
|
STMT_STATE,
|
||||||
|
STMT_RELATION,
|
||||||
|
STMT_CLASSDEF,
|
||||||
|
STMT_APPLYCLASS,
|
||||||
|
DEFAULT_STATE_TYPE,
|
||||||
|
DIVIDER_TYPE,
|
||||||
|
G_EDGE_STYLE,
|
||||||
|
G_EDGE_ARROWHEADSTYLE,
|
||||||
|
G_EDGE_LABELPOS,
|
||||||
|
G_EDGE_LABELTYPE,
|
||||||
|
G_EDGE_THICKNESS,
|
||||||
|
CSS_EDGE,
|
||||||
|
DEFAULT_NESTED_DOC_DIR,
|
||||||
|
SHAPE_DIVIDER,
|
||||||
|
SHAPE_GROUP,
|
||||||
|
CSS_DIAGRAM_CLUSTER,
|
||||||
|
CSS_DIAGRAM_CLUSTER_ALT,
|
||||||
|
CSS_DIAGRAM_STATE,
|
||||||
|
SHAPE_STATE_WITH_DESC,
|
||||||
|
SHAPE_STATE,
|
||||||
|
SHAPE_START,
|
||||||
|
SHAPE_END,
|
||||||
|
SHAPE_NOTE,
|
||||||
|
SHAPE_NOTEGROUP,
|
||||||
|
CSS_DIAGRAM_NOTE,
|
||||||
|
DOMID_TYPE_SPACER,
|
||||||
|
DOMID_STATE,
|
||||||
|
NOTE_ID,
|
||||||
|
PARENT_ID,
|
||||||
|
NOTE,
|
||||||
|
PARENT,
|
||||||
|
CSS_EDGE_NOTE_EDGE,
|
||||||
|
} from './stateCommon.js';
|
||||||
|
|
||||||
|
// List of nodes created from the parsed diagram statement items
|
||||||
|
let nodeDb = {};
|
||||||
|
|
||||||
|
let graphItemCount = 0; // used to construct ids, etc.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a standard string for the dom ID of an item.
|
||||||
|
* If a type is given, insert that before the counter, preceded by the type spacer
|
||||||
|
*
|
||||||
|
* @param itemId
|
||||||
|
* @param counter
|
||||||
|
* @param {string | null} type
|
||||||
|
* @param typeSpacer
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOMID_TYPE_SPACER) {
|
||||||
|
const typeStr = type !== null && type.length > 0 ? `${typeSpacer}${type}` : '';
|
||||||
|
return `${DOMID_STATE}-${itemId}${typeStr}-${counter}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupDoc = (parentParsedItem, doc, diagramStates, nodes, edges, altFlag, useRough) => {
|
||||||
|
// graphItemCount = 0;
|
||||||
|
log.trace('items', doc);
|
||||||
|
doc.forEach((item) => {
|
||||||
|
switch (item.stmt) {
|
||||||
|
case STMT_STATE:
|
||||||
|
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, useRough);
|
||||||
|
break;
|
||||||
|
case DEFAULT_STATE_TYPE:
|
||||||
|
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, useRough);
|
||||||
|
break;
|
||||||
|
case STMT_RELATION:
|
||||||
|
{
|
||||||
|
dataFetcher(
|
||||||
|
parentParsedItem,
|
||||||
|
item.state1,
|
||||||
|
diagramStates,
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
altFlag,
|
||||||
|
useRough
|
||||||
|
);
|
||||||
|
dataFetcher(
|
||||||
|
parentParsedItem,
|
||||||
|
item.state2,
|
||||||
|
diagramStates,
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
altFlag,
|
||||||
|
useRough
|
||||||
|
);
|
||||||
|
const edgeData = {
|
||||||
|
id: 'edge' + graphItemCount,
|
||||||
|
start: item.state1.id,
|
||||||
|
end: item.state2.id,
|
||||||
|
arrowhead: 'normal',
|
||||||
|
arrowTypeEnd: 'arrow_barb',
|
||||||
|
style: G_EDGE_STYLE,
|
||||||
|
labelStyle: '',
|
||||||
|
label: common.sanitizeText(item.description, getConfig()),
|
||||||
|
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
||||||
|
labelpos: G_EDGE_LABELPOS,
|
||||||
|
labelType: G_EDGE_LABELTYPE,
|
||||||
|
thickness: G_EDGE_THICKNESS,
|
||||||
|
classes: CSS_EDGE,
|
||||||
|
useRough,
|
||||||
|
};
|
||||||
|
edges.push(edgeData);
|
||||||
|
//g.setEdge(item.state1.id, item.state2.id, edgeData, graphItemCount);
|
||||||
|
graphItemCount++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the direction from the statement items.
|
||||||
|
* Look through all of the documents (docs) in the parsedItems
|
||||||
|
* Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction.
|
||||||
|
* @param {object[]} parsedItem - the parsed statement item to look through
|
||||||
|
* @param [defaultDir] - the direction to use if none is found
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
|
||||||
|
let dir = defaultDir;
|
||||||
|
if (parsedItem.doc) {
|
||||||
|
for (let i = 0; i < parsedItem.doc.length; i++) {
|
||||||
|
const parsedItemDoc = parsedItem.doc[i];
|
||||||
|
if (parsedItemDoc.stmt === 'dir') {
|
||||||
|
dir = parsedItemDoc.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new list of classes.
|
||||||
|
* In the future, this can be replaced with a class common to all diagrams.
|
||||||
|
* ClassDef information = { id: id, styles: [], textStyles: [] }
|
||||||
|
*
|
||||||
|
* @returns {{}}
|
||||||
|
*/
|
||||||
|
function newClassesList() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// let direction = DEFAULT_DIAGRAM_DIRECTION;
|
||||||
|
// let rootDoc = [];
|
||||||
|
let cssClasses = newClassesList(); // style classes defined by a classDef
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param nodes
|
||||||
|
* @param nodeData
|
||||||
|
*/
|
||||||
|
function insertOrUpdateNode(nodes, nodeData) {
|
||||||
|
if (!nodeData.id || nodeData.id === '</join></fork>' || nodeData.id === '</choice>') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Populate node style attributes if nodeData has classes defined
|
||||||
|
if (nodeData.cssClasses) {
|
||||||
|
nodeData.cssClasses.split(' ').forEach((cssClass) => {
|
||||||
|
if (cssClasses[cssClass]) {
|
||||||
|
cssClasses[cssClass].styles.forEach((style) => {
|
||||||
|
// Populate nodeData with style attributes specifically to be used by rough.js
|
||||||
|
if (style && style.startsWith('fill:')) {
|
||||||
|
nodeData.backgroundColor = style.replace('fill:', '');
|
||||||
|
}
|
||||||
|
if (style && style.startsWith('stroke:')) {
|
||||||
|
nodeData.borderColor = style.replace('stroke:', '');
|
||||||
|
}
|
||||||
|
if (style && style.startsWith('stroke-width:')) {
|
||||||
|
nodeData.borderWidth = style.replace('stroke-width:', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeData.cssStyles += style + ';';
|
||||||
|
});
|
||||||
|
cssClasses[cssClass].textStyles.forEach((style) => {
|
||||||
|
nodeData.labelStyle += style + ';';
|
||||||
|
if (style && style.startsWith('fill:')) {
|
||||||
|
nodeData.labelTextColor = style.replace('fill:', '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const existingNodeData = nodes.find((node) => node.id === nodeData.id);
|
||||||
|
if (existingNodeData) {
|
||||||
|
//update the existing nodeData
|
||||||
|
Object.assign(existingNodeData, nodeData);
|
||||||
|
} else {
|
||||||
|
nodes.push(nodeData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get classes from the db for the info item.
|
||||||
|
* If there aren't any or if dbInfoItem isn't defined, return an empty string.
|
||||||
|
* Else create 1 string from the list of classes found
|
||||||
|
*
|
||||||
|
* @param {undefined | null | object} dbInfoItem
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getClassesFromDbInfo(dbInfoItem) {
|
||||||
|
if (dbInfoItem === undefined || dbInfoItem === null) {
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
if (dbInfoItem.cssClasses) {
|
||||||
|
return dbInfoItem.cssClasses.join(' ');
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, altFlag, useRough) => {
|
||||||
|
const itemId = parsedItem.id;
|
||||||
|
const classStr = getClassesFromDbInfo(diagramStates[itemId]);
|
||||||
|
|
||||||
|
if (itemId !== 'root') {
|
||||||
|
let shape = SHAPE_STATE;
|
||||||
|
if (parsedItem.start === true) {
|
||||||
|
shape = SHAPE_START;
|
||||||
|
}
|
||||||
|
if (parsedItem.start === false) {
|
||||||
|
shape = SHAPE_END;
|
||||||
|
}
|
||||||
|
if (parsedItem.type !== DEFAULT_STATE_TYPE) {
|
||||||
|
shape = parsedItem.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the node to our list (nodeDb)
|
||||||
|
if (!nodeDb[itemId]) {
|
||||||
|
nodeDb[itemId] = {
|
||||||
|
id: itemId,
|
||||||
|
shape,
|
||||||
|
description: common.sanitizeText(itemId, getConfig()),
|
||||||
|
cssClasses: `${classStr} ${CSS_DIAGRAM_STATE}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const newNode = nodeDb[itemId];
|
||||||
|
console.log('New Node:', newNode);
|
||||||
|
|
||||||
|
// Save data for description and group so that for instance a statement without description overwrites
|
||||||
|
// one with description @todo TODO What does this mean? If important, add a test for it
|
||||||
|
|
||||||
|
// Build of the array of description strings
|
||||||
|
if (parsedItem.description) {
|
||||||
|
if (Array.isArray(newNode.description)) {
|
||||||
|
// There already is an array of strings,add to it
|
||||||
|
newNode.shape = SHAPE_STATE_WITH_DESC;
|
||||||
|
newNode.description.push(parsedItem.description);
|
||||||
|
} else {
|
||||||
|
if (newNode.description?.length > 0) {
|
||||||
|
// if there is a description already transform it to an array
|
||||||
|
newNode.shape = SHAPE_STATE_WITH_DESC;
|
||||||
|
if (newNode.description === itemId) {
|
||||||
|
// If the previous description was this, remove it
|
||||||
|
newNode.description = [parsedItem.description];
|
||||||
|
} else {
|
||||||
|
newNode.description = [newNode.description, parsedItem.description];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newNode.shape = SHAPE_STATE;
|
||||||
|
newNode.description = parsedItem.description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newNode.description = common.sanitizeTextOrArray(newNode.description, getConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's only 1 description entry, just use a regular state shape
|
||||||
|
if (newNode.description?.length === 1 && newNode.shape === SHAPE_STATE_WITH_DESC) {
|
||||||
|
newNode.shape = SHAPE_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// group
|
||||||
|
if (!newNode.type && parsedItem.doc) {
|
||||||
|
log.info('Setting cluster for ', itemId, getDir(parsedItem));
|
||||||
|
newNode.type = 'group';
|
||||||
|
newNode.dir = getDir(parsedItem);
|
||||||
|
newNode.shape = parsedItem.type === DIVIDER_TYPE ? SHAPE_DIVIDER : SHAPE_GROUP;
|
||||||
|
newNode.cssClasses =
|
||||||
|
newNode.cssClasses +
|
||||||
|
' ' +
|
||||||
|
CSS_DIAGRAM_CLUSTER +
|
||||||
|
' ' +
|
||||||
|
(altFlag ? CSS_DIAGRAM_CLUSTER_ALT : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is what will be added to the graph
|
||||||
|
const nodeData = {
|
||||||
|
labelStyle: '',
|
||||||
|
shape: newNode.shape,
|
||||||
|
label: newNode.description,
|
||||||
|
cssClasses: newNode.cssClasses,
|
||||||
|
cssStyles: '',
|
||||||
|
id: itemId,
|
||||||
|
dir: newNode.dir,
|
||||||
|
domId: stateDomId(itemId, graphItemCount),
|
||||||
|
type: newNode.type,
|
||||||
|
padding: 15,
|
||||||
|
rx: 10,
|
||||||
|
ry: 10,
|
||||||
|
useRough,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (parent && parent.id !== 'root') {
|
||||||
|
log.trace('Setting node ', itemId, ' to be child of its parent ', parent.id);
|
||||||
|
nodeData.parentId = parent.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeData.centerLabel = true;
|
||||||
|
|
||||||
|
if (parsedItem.note) {
|
||||||
|
// Todo: set random id
|
||||||
|
const noteData = {
|
||||||
|
labelStyle: '',
|
||||||
|
shape: SHAPE_NOTE,
|
||||||
|
label: parsedItem.note.text,
|
||||||
|
cssClasses: CSS_DIAGRAM_NOTE,
|
||||||
|
// useHtmlLabels: false,
|
||||||
|
cssStyles: '', // styles.style,
|
||||||
|
id: itemId + NOTE_ID + '-' + graphItemCount,
|
||||||
|
domId: stateDomId(itemId, graphItemCount, NOTE),
|
||||||
|
type: newNode.type,
|
||||||
|
padding: 15, //getConfig().flowchart.padding
|
||||||
|
useRough,
|
||||||
|
};
|
||||||
|
const groupData = {
|
||||||
|
labelStyle: '',
|
||||||
|
shape: SHAPE_NOTEGROUP,
|
||||||
|
label: parsedItem.note.text,
|
||||||
|
cssClasses: newNode.cssClasses,
|
||||||
|
cssStyles: '', // styles.style,
|
||||||
|
id: itemId + PARENT_ID,
|
||||||
|
domId: stateDomId(itemId, graphItemCount, PARENT),
|
||||||
|
type: 'group',
|
||||||
|
padding: 0, //getConfig().flowchart.padding
|
||||||
|
useRough,
|
||||||
|
};
|
||||||
|
graphItemCount++;
|
||||||
|
|
||||||
|
const parentNodeId = itemId + PARENT_ID;
|
||||||
|
|
||||||
|
//add parent id to groupData
|
||||||
|
groupData.id = parentNodeId;
|
||||||
|
//add parent id to noteData
|
||||||
|
noteData.parentId = parentNodeId;
|
||||||
|
|
||||||
|
//insert groupData
|
||||||
|
insertOrUpdateNode(nodes, groupData);
|
||||||
|
//insert noteData
|
||||||
|
insertOrUpdateNode(nodes, noteData);
|
||||||
|
//insert nodeData
|
||||||
|
insertOrUpdateNode(nodes, nodeData);
|
||||||
|
|
||||||
|
let from = itemId;
|
||||||
|
let to = noteData.id;
|
||||||
|
|
||||||
|
if (parsedItem.note.position === 'left of') {
|
||||||
|
from = noteData.id;
|
||||||
|
to = itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
edges.push({
|
||||||
|
id: from + '-' + to,
|
||||||
|
start: from,
|
||||||
|
end: to,
|
||||||
|
arrowhead: 'none',
|
||||||
|
arrowTypeEnd: '',
|
||||||
|
style: G_EDGE_STYLE,
|
||||||
|
labelStyle: '',
|
||||||
|
classes: CSS_EDGE_NOTE_EDGE,
|
||||||
|
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
||||||
|
labelpos: G_EDGE_LABELPOS,
|
||||||
|
labelType: G_EDGE_LABELTYPE,
|
||||||
|
thickness: G_EDGE_THICKNESS,
|
||||||
|
useRough,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
insertOrUpdateNode(nodes, nodeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Nodes:', nodes);
|
||||||
|
}
|
||||||
|
if (parsedItem.doc) {
|
||||||
|
log.trace('Adding nodes children ');
|
||||||
|
setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, useRough);
|
||||||
|
}
|
||||||
|
};
|
@@ -11,6 +11,7 @@ import {
|
|||||||
setDiagramTitle,
|
setDiagramTitle,
|
||||||
getDiagramTitle,
|
getDiagramTitle,
|
||||||
} from '../common/commonDb.js';
|
} from '../common/commonDb.js';
|
||||||
|
import { dataFetcher } from './dataFetcher.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_DIAGRAM_DIRECTION,
|
DEFAULT_DIAGRAM_DIRECTION,
|
||||||
@@ -75,10 +76,6 @@ let rootDoc = [];
|
|||||||
let classes = newClassesList(); // style classes defined by a classDef
|
let classes = newClassesList(); // style classes defined by a classDef
|
||||||
|
|
||||||
// --------------------------------------
|
// --------------------------------------
|
||||||
// List of nodes created from the parsed diagram statement items
|
|
||||||
let nodeDb = {};
|
|
||||||
|
|
||||||
let graphItemCount = 0; // used to construct ids, etc.
|
|
||||||
|
|
||||||
const newDoc = () => {
|
const newDoc = () => {
|
||||||
return {
|
return {
|
||||||
@@ -574,300 +571,6 @@ const setDirection = (dir) => {
|
|||||||
|
|
||||||
const trimColon = (str) => (str && str[0] === ':' ? str.substr(1).trim() : str.trim());
|
const trimColon = (str) => (str && str[0] === ':' ? str.substr(1).trim() : str.trim());
|
||||||
|
|
||||||
const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, altFlag, useRough) => {
|
|
||||||
const itemId = parsedItem.id;
|
|
||||||
const classStr = getClassesFromDbInfo(diagramStates[itemId]);
|
|
||||||
|
|
||||||
if (itemId !== 'root') {
|
|
||||||
let shape = SHAPE_STATE;
|
|
||||||
if (parsedItem.start === true) {
|
|
||||||
shape = SHAPE_START;
|
|
||||||
}
|
|
||||||
if (parsedItem.start === false) {
|
|
||||||
shape = SHAPE_END;
|
|
||||||
}
|
|
||||||
if (parsedItem.type !== DEFAULT_STATE_TYPE) {
|
|
||||||
shape = parsedItem.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the node to our list (nodeDb)
|
|
||||||
if (!nodeDb[itemId]) {
|
|
||||||
nodeDb[itemId] = {
|
|
||||||
id: itemId,
|
|
||||||
shape,
|
|
||||||
description: common.sanitizeText(itemId, getConfig()),
|
|
||||||
classes: `${classStr} ${CSS_DIAGRAM_STATE}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const newNode = nodeDb[itemId];
|
|
||||||
console.log('New Node:', newNode);
|
|
||||||
|
|
||||||
// Save data for description and group so that for instance a statement without description overwrites
|
|
||||||
// one with description @todo TODO What does this mean? If important, add a test for it
|
|
||||||
|
|
||||||
// Build of the array of description strings
|
|
||||||
if (parsedItem.description) {
|
|
||||||
if (Array.isArray(newNode.description)) {
|
|
||||||
// There already is an array of strings,add to it
|
|
||||||
newNode.shape = SHAPE_STATE_WITH_DESC;
|
|
||||||
newNode.description.push(parsedItem.description);
|
|
||||||
} else {
|
|
||||||
if (newNode.description?.length > 0) {
|
|
||||||
// if there is a description already transform it to an array
|
|
||||||
newNode.shape = SHAPE_STATE_WITH_DESC;
|
|
||||||
if (newNode.description === itemId) {
|
|
||||||
// If the previous description was this, remove it
|
|
||||||
newNode.description = [parsedItem.description];
|
|
||||||
} else {
|
|
||||||
newNode.description = [newNode.description, parsedItem.description];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newNode.shape = SHAPE_STATE;
|
|
||||||
newNode.description = parsedItem.description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newNode.description = common.sanitizeTextOrArray(newNode.description, getConfig());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's only 1 description entry, just use a regular state shape
|
|
||||||
if (newNode.description?.length === 1 && newNode.shape === SHAPE_STATE_WITH_DESC) {
|
|
||||||
newNode.shape = SHAPE_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// group
|
|
||||||
if (!newNode.type && parsedItem.doc) {
|
|
||||||
log.info('Setting cluster for ', itemId, getDir(parsedItem));
|
|
||||||
newNode.type = 'group';
|
|
||||||
newNode.dir = getDir(parsedItem);
|
|
||||||
newNode.shape = parsedItem.type === DIVIDER_TYPE ? SHAPE_DIVIDER : SHAPE_GROUP;
|
|
||||||
newNode.classes =
|
|
||||||
newNode.classes +
|
|
||||||
' ' +
|
|
||||||
CSS_DIAGRAM_CLUSTER +
|
|
||||||
' ' +
|
|
||||||
(altFlag ? CSS_DIAGRAM_CLUSTER_ALT : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is what will be added to the graph
|
|
||||||
const nodeData = {
|
|
||||||
labelStyle: '',
|
|
||||||
shape: newNode.shape,
|
|
||||||
labelText: newNode.description,
|
|
||||||
classes: newNode.classes,
|
|
||||||
style: '',
|
|
||||||
id: itemId,
|
|
||||||
dir: newNode.dir,
|
|
||||||
domId: stateDomId(itemId, graphItemCount),
|
|
||||||
type: newNode.type,
|
|
||||||
padding: 15,
|
|
||||||
rx: 10,
|
|
||||||
ry: 10,
|
|
||||||
useRough,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (parent && parent.id !== 'root') {
|
|
||||||
log.trace('Setting node ', itemId, ' to be child of its parent ', parent.id);
|
|
||||||
nodeData.parentId = parent.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeData.centerLabel = true;
|
|
||||||
|
|
||||||
if (parsedItem.note) {
|
|
||||||
// Todo: set random id
|
|
||||||
const noteData = {
|
|
||||||
labelStyle: '',
|
|
||||||
shape: SHAPE_NOTE,
|
|
||||||
labelText: parsedItem.note.text,
|
|
||||||
classes: CSS_DIAGRAM_NOTE,
|
|
||||||
// useHtmlLabels: false,
|
|
||||||
style: '', // styles.style,
|
|
||||||
id: itemId + NOTE_ID + '-' + graphItemCount,
|
|
||||||
domId: stateDomId(itemId, graphItemCount, NOTE),
|
|
||||||
type: newNode.type,
|
|
||||||
padding: 15, //getConfig().flowchart.padding
|
|
||||||
useRough,
|
|
||||||
};
|
|
||||||
const groupData = {
|
|
||||||
labelStyle: '',
|
|
||||||
shape: SHAPE_NOTEGROUP,
|
|
||||||
labelText: parsedItem.note.text,
|
|
||||||
classes: newNode.classes,
|
|
||||||
style: '', // styles.style,
|
|
||||||
id: itemId + PARENT_ID,
|
|
||||||
domId: stateDomId(itemId, graphItemCount, PARENT),
|
|
||||||
type: 'group',
|
|
||||||
padding: 0, //getConfig().flowchart.padding
|
|
||||||
useRough,
|
|
||||||
};
|
|
||||||
graphItemCount++;
|
|
||||||
|
|
||||||
const parentNodeId = itemId + PARENT_ID;
|
|
||||||
|
|
||||||
//add parent id to groupData
|
|
||||||
groupData.id = parentNodeId;
|
|
||||||
//add parent id to noteData
|
|
||||||
noteData.parentId = parentNodeId;
|
|
||||||
|
|
||||||
//insert groupData
|
|
||||||
insertOrUpdateNode(nodes, groupData);
|
|
||||||
//insert noteData
|
|
||||||
insertOrUpdateNode(nodes, noteData);
|
|
||||||
//insert nodeData
|
|
||||||
insertOrUpdateNode(nodes, nodeData);
|
|
||||||
|
|
||||||
let from = itemId;
|
|
||||||
let to = noteData.id;
|
|
||||||
|
|
||||||
if (parsedItem.note.position === 'left of') {
|
|
||||||
from = noteData.id;
|
|
||||||
to = itemId;
|
|
||||||
}
|
|
||||||
|
|
||||||
edges.push({
|
|
||||||
id: from + '-' + to,
|
|
||||||
start: from,
|
|
||||||
end: to,
|
|
||||||
arrowhead: 'none',
|
|
||||||
arrowTypeEnd: '',
|
|
||||||
style: G_EDGE_STYLE,
|
|
||||||
labelStyle: '',
|
|
||||||
classes: CSS_EDGE_NOTE_EDGE,
|
|
||||||
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
|
||||||
labelpos: G_EDGE_LABELPOS,
|
|
||||||
labelType: G_EDGE_LABELTYPE,
|
|
||||||
thickness: G_EDGE_THICKNESS,
|
|
||||||
useRough,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
insertOrUpdateNode(nodes, nodeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Nodes:', nodes);
|
|
||||||
}
|
|
||||||
if (parsedItem.doc) {
|
|
||||||
log.trace('Adding nodes children ');
|
|
||||||
setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, useRough);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param nodes
|
|
||||||
* @param nodeData
|
|
||||||
*/
|
|
||||||
function insertOrUpdateNode(nodes, nodeData) {
|
|
||||||
if (!nodeData.id || nodeData.id === '</join></fork>' || nodeData.id === '</choice>') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Populate node style attributes if nodeData has classes defined
|
|
||||||
if (nodeData.classes) {
|
|
||||||
nodeData.classes.split(' ').forEach((cssClass) => {
|
|
||||||
if (classes[cssClass]) {
|
|
||||||
classes[cssClass].styles.forEach((style) => {
|
|
||||||
// Populate nodeData with style attributes specifically to be used by rough.js
|
|
||||||
if (style && style.startsWith('fill:')) {
|
|
||||||
nodeData.backgroundColor = style.replace('fill:', '');
|
|
||||||
}
|
|
||||||
if (style && style.startsWith('stroke:')) {
|
|
||||||
nodeData.borderColor = style.replace('stroke:', '');
|
|
||||||
}
|
|
||||||
if (style && style.startsWith('stroke-width:')) {
|
|
||||||
nodeData.borderWidth = style.replace('stroke-width:', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeData.style += style + ';';
|
|
||||||
});
|
|
||||||
classes[cssClass].textStyles.forEach((style) => {
|
|
||||||
nodeData.labelStyle += style + ';';
|
|
||||||
if (style && style.startsWith('fill:')) {
|
|
||||||
nodeData.labelTextColor = style.replace('fill:', '');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const existingNodeData = nodes.find((node) => node.id === nodeData.id);
|
|
||||||
if (existingNodeData) {
|
|
||||||
//update the existing nodeData
|
|
||||||
Object.assign(existingNodeData, nodeData);
|
|
||||||
} else {
|
|
||||||
nodes.push(nodeData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a standard string for the dom ID of an item.
|
|
||||||
* If a type is given, insert that before the counter, preceded by the type spacer
|
|
||||||
*
|
|
||||||
* @param itemId
|
|
||||||
* @param counter
|
|
||||||
* @param {string | null} type
|
|
||||||
* @param typeSpacer
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOMID_TYPE_SPACER) {
|
|
||||||
const typeStr = type !== null && type.length > 0 ? `${typeSpacer}${type}` : '';
|
|
||||||
return `${DOMID_STATE}-${itemId}${typeStr}-${counter}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupDoc = (parentParsedItem, doc, diagramStates, nodes, edges, altFlag, useRough) => {
|
|
||||||
// graphItemCount = 0;
|
|
||||||
log.trace('items', doc);
|
|
||||||
doc.forEach((item) => {
|
|
||||||
switch (item.stmt) {
|
|
||||||
case STMT_STATE:
|
|
||||||
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, useRough);
|
|
||||||
break;
|
|
||||||
case DEFAULT_STATE_TYPE:
|
|
||||||
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, useRough);
|
|
||||||
break;
|
|
||||||
case STMT_RELATION:
|
|
||||||
{
|
|
||||||
dataFetcher(
|
|
||||||
parentParsedItem,
|
|
||||||
item.state1,
|
|
||||||
diagramStates,
|
|
||||||
nodes,
|
|
||||||
edges,
|
|
||||||
altFlag,
|
|
||||||
useRough
|
|
||||||
);
|
|
||||||
dataFetcher(
|
|
||||||
parentParsedItem,
|
|
||||||
item.state2,
|
|
||||||
diagramStates,
|
|
||||||
nodes,
|
|
||||||
edges,
|
|
||||||
altFlag,
|
|
||||||
useRough
|
|
||||||
);
|
|
||||||
const edgeData = {
|
|
||||||
id: 'edge' + graphItemCount,
|
|
||||||
start: item.state1.id,
|
|
||||||
end: item.state2.id,
|
|
||||||
arrowhead: 'normal',
|
|
||||||
arrowTypeEnd: 'arrow_barb',
|
|
||||||
style: G_EDGE_STYLE,
|
|
||||||
labelStyle: '',
|
|
||||||
label: common.sanitizeText(item.description, getConfig()),
|
|
||||||
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
|
||||||
labelpos: G_EDGE_LABELPOS,
|
|
||||||
labelType: G_EDGE_LABELTYPE,
|
|
||||||
thickness: G_EDGE_THICKNESS,
|
|
||||||
classes: CSS_EDGE,
|
|
||||||
useRough,
|
|
||||||
};
|
|
||||||
edges.push(edgeData);
|
|
||||||
//g.setEdge(item.state1.id, item.state2.id, edgeData, graphItemCount);
|
|
||||||
graphItemCount++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getData = () => {
|
export const getData = () => {
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
const edges = [];
|
const edges = [];
|
||||||
@@ -886,47 +589,6 @@ export const getData = () => {
|
|||||||
return { nodes, edges, other: {} };
|
return { nodes, edges, other: {} };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the direction from the statement items.
|
|
||||||
* Look through all of the documents (docs) in the parsedItems
|
|
||||||
* Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction.
|
|
||||||
* @param {object[]} parsedItem - the parsed statement item to look through
|
|
||||||
* @param [defaultDir] - the direction to use if none is found
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
|
|
||||||
let dir = defaultDir;
|
|
||||||
if (parsedItem.doc) {
|
|
||||||
for (let i = 0; i < parsedItem.doc.length; i++) {
|
|
||||||
const parsedItemDoc = parsedItem.doc[i];
|
|
||||||
if (parsedItemDoc.stmt === 'dir') {
|
|
||||||
dir = parsedItemDoc.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dir;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get classes from the db for the info item.
|
|
||||||
* If there aren't any or if dbInfoItem isn't defined, return an empty string.
|
|
||||||
* Else create 1 string from the list of classes found
|
|
||||||
*
|
|
||||||
* @param {undefined | null | object} dbInfoItem
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function getClassesFromDbInfo(dbInfoItem) {
|
|
||||||
if (dbInfoItem === undefined || dbInfoItem === null) {
|
|
||||||
return '';
|
|
||||||
} else {
|
|
||||||
if (dbInfoItem.classes) {
|
|
||||||
return dbInfoItem.classes.join(' ');
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getConfig: () => getConfig().state,
|
getConfig: () => getConfig().state,
|
||||||
getData,
|
getData,
|
||||||
|
@@ -288,7 +288,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
|||||||
domId: specialId,
|
domId: specialId,
|
||||||
id: specialId,
|
id: specialId,
|
||||||
labelStyle: '',
|
labelStyle: '',
|
||||||
labelText: edge.label,
|
label: edge.label,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
shape: 'labelRect',
|
shape: 'labelRect',
|
||||||
style: '',
|
style: '',
|
||||||
@@ -416,7 +416,7 @@ export const extractor = (graph, depth) => {
|
|||||||
clusterNode: true,
|
clusterNode: true,
|
||||||
id: node,
|
id: node,
|
||||||
clusterData: clusterDb[node].clusterData,
|
clusterData: clusterDb[node].clusterData,
|
||||||
labelText: clusterDb[node].labelText,
|
label: clusterDb[node].label,
|
||||||
graph: clusterGraph,
|
graph: clusterGraph,
|
||||||
});
|
});
|
||||||
log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph));
|
log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph));
|
||||||
|
@@ -386,7 +386,7 @@ flowchart TB
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const exportedGraph = JSON.parse(
|
const exportedGraph = JSON.parse(
|
||||||
'{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"class":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","classes":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","classes":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}'
|
'{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"cssClass":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","cssClasses":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","cssClasses":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}'
|
||||||
);
|
);
|
||||||
const gr = graphlibJson.read(exportedGraph);
|
const gr = graphlibJson.read(exportedGraph);
|
||||||
|
|
||||||
|
@@ -14,10 +14,7 @@ const rect = (parent, node) => {
|
|||||||
const siteConfig = getConfig();
|
const siteConfig = getConfig();
|
||||||
|
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = parent
|
const shapeSvg = parent.insert('g').attr('class', 'cluster').attr('id', node.id);
|
||||||
.insert('g')
|
|
||||||
.attr('class', 'cluster' + (node.class ? ' ' + node.class : ''))
|
|
||||||
.attr('id', node.id);
|
|
||||||
|
|
||||||
// add the rect
|
// add the rect
|
||||||
const rect = shapeSvg.insert('rect', ':first-child');
|
const rect = shapeSvg.insert('rect', ':first-child');
|
||||||
@@ -25,15 +22,15 @@ const rect = (parent, node) => {
|
|||||||
const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels);
|
const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels);
|
||||||
|
|
||||||
// Create the label and insert it after the rect
|
// Create the label and insert it after the rect
|
||||||
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label');
|
||||||
|
|
||||||
// const text = label
|
// const text = label
|
||||||
// .node()
|
// .node()
|
||||||
// .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
|
// .appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
||||||
const text =
|
const text =
|
||||||
node.labelType === 'markdown'
|
node.labelType === 'markdown'
|
||||||
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels })
|
? createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels })
|
||||||
: label.node().appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
|
: labelEl.node().appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
@@ -59,7 +56,7 @@ const rect = (parent, node) => {
|
|||||||
log.trace('Data ', node, JSON.stringify(node));
|
log.trace('Data ', node, JSON.stringify(node));
|
||||||
// center the rect around its coordinate
|
// center the rect around its coordinate
|
||||||
rect
|
rect
|
||||||
.attr('style', node.style)
|
.attr('style', node.cssStyles)
|
||||||
.attr('rx', node.rx)
|
.attr('rx', node.rx)
|
||||||
.attr('ry', node.ry)
|
.attr('ry', node.ry)
|
||||||
.attr('x', node.x - width / 2)
|
.attr('x', node.x - width / 2)
|
||||||
@@ -69,13 +66,13 @@ const rect = (parent, node) => {
|
|||||||
|
|
||||||
const { subGraphTitleTopMargin } = getSubGraphTitleMargins(siteConfig);
|
const { subGraphTitleTopMargin } = getSubGraphTitleMargins(siteConfig);
|
||||||
if (useHtmlLabels) {
|
if (useHtmlLabels) {
|
||||||
label.attr(
|
labelEl.attr(
|
||||||
'transform',
|
'transform',
|
||||||
// This puts the label on top of the box instead of inside it
|
// This puts the label on top of the box instead of inside it
|
||||||
`translate(${node.x - bbox.width / 2}, ${node.y - node.height / 2 + subGraphTitleTopMargin})`
|
`translate(${node.x - bbox.width / 2}, ${node.y - node.height / 2 + subGraphTitleTopMargin})`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
label.attr(
|
labelEl.attr(
|
||||||
'transform',
|
'transform',
|
||||||
// This puts the label on top of the box instead of inside it
|
// This puts the label on top of the box instead of inside it
|
||||||
`translate(${node.x}, ${node.y - node.height / 2 + subGraphTitleTopMargin})`
|
`translate(${node.x}, ${node.y - node.height / 2 + subGraphTitleTopMargin})`
|
||||||
@@ -154,7 +151,7 @@ const roundedWithTitle = (parent, node) => {
|
|||||||
themeVariables;
|
themeVariables;
|
||||||
|
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = parent.insert('g').attr('class', node.classes).attr('id', node.id);
|
const shapeSvg = parent.insert('g').attr('class', node.cssClasses).attr('id', node.id);
|
||||||
|
|
||||||
// add the rect
|
// add the rect
|
||||||
const outerRectG = shapeSvg.insert('g', ':first-child');
|
const outerRectG = shapeSvg.insert('g', ':first-child');
|
||||||
@@ -163,9 +160,7 @@ const roundedWithTitle = (parent, node) => {
|
|||||||
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
||||||
let innerRect = shapeSvg.append('rect');
|
let innerRect = shapeSvg.append('rect');
|
||||||
|
|
||||||
const text = label
|
const text = label.node().appendChild(createLabel(node.label, node.labelStyle, undefined, true));
|
||||||
.node()
|
|
||||||
.appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
|
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
@@ -197,7 +192,7 @@ const roundedWithTitle = (parent, node) => {
|
|||||||
// add the rect
|
// add the rect
|
||||||
let rect;
|
let rect;
|
||||||
if (node.useRough) {
|
if (node.useRough) {
|
||||||
const isAlt = node.classes.includes('statediagram-cluster-alt');
|
const isAlt = node.cssClasses.includes('statediagram-cluster-alt');
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const roughOuterNode =
|
const roughOuterNode =
|
||||||
node.rx || node.ry
|
node.rx || node.ry
|
||||||
@@ -263,7 +258,7 @@ const roundedWithTitle = (parent, node) => {
|
|||||||
const divider = (parent, node) => {
|
const divider = (parent, node) => {
|
||||||
const { handdrawnSeed } = getConfig();
|
const { handdrawnSeed } = getConfig();
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = parent.insert('g').attr('class', node.classes).attr('id', node.id);
|
const shapeSvg = parent.insert('g').attr('class', node.cssClasses).attr('id', node.id);
|
||||||
|
|
||||||
// add the rect
|
// add the rect
|
||||||
let rect;
|
let rect;
|
||||||
@@ -318,7 +313,7 @@ export const insertCluster = (elem, node) => {
|
|||||||
return cluster;
|
return cluster;
|
||||||
};
|
};
|
||||||
export const getClusterTitleWidth = (elem, node) => {
|
export const getClusterTitleWidth = (elem, node) => {
|
||||||
const label = createLabel(node.labelText, node.labelStyle, undefined, true);
|
const label = createLabel(node.label, node.labelStyle, undefined, true);
|
||||||
elem.node().appendChild(label);
|
elem.node().appendChild(label);
|
||||||
const width = label.getBBox().width;
|
const width = label.getBBox().width;
|
||||||
elem.node().removeChild(label);
|
elem.node().removeChild(label);
|
||||||
|
@@ -48,9 +48,9 @@ export const insertNode = async (elem, node, dir) => {
|
|||||||
if (node.tooltip) {
|
if (node.tooltip) {
|
||||||
el.attr('title', node.tooltip);
|
el.attr('title', node.tooltip);
|
||||||
}
|
}
|
||||||
if (node.class) {
|
// if (node.class) {
|
||||||
el.attr('class', 'node default ' + node.class);
|
// el.attr('class', 'node default ' + node.class);
|
||||||
}
|
// }
|
||||||
|
|
||||||
nodeElems[node.id] = newEl;
|
nodeElems[node.id] = newEl;
|
||||||
|
|
||||||
|
@@ -16,12 +16,12 @@ export const note = async (parent: SVGAElement, node: Node) => {
|
|||||||
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
||||||
parent,
|
parent,
|
||||||
node,
|
node,
|
||||||
'node ' + node.classes,
|
'node ' + node.cssClasses,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
log.info('Classes = ', node.classes);
|
log.info('Classes = ', node.cssClasses);
|
||||||
const { style, useRough } = node;
|
const { cssStyles, useRough } = node;
|
||||||
let rect;
|
let rect;
|
||||||
const totalWidth = bbox.width + node.padding;
|
const totalWidth = bbox.width + node.padding;
|
||||||
const totalHeight = bbox.height + node.padding;
|
const totalHeight = bbox.height + node.padding;
|
||||||
@@ -41,7 +41,7 @@ export const note = async (parent: SVGAElement, node: Node) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rect = shapeSvg.insert(() => roughNode, ':first-child');
|
rect = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
rect.attr('class', 'basic label-container').attr('style', style);
|
rect.attr('class', 'basic label-container').attr('style', cssStyles);
|
||||||
} else {
|
} else {
|
||||||
rect = shapeSvg.insert('rect', ':first-child');
|
rect = shapeSvg.insert('rect', ':first-child');
|
||||||
rect
|
rect
|
||||||
|
@@ -65,7 +65,7 @@ export const rect = async (parent: SVGAElement, node: Node) => {
|
|||||||
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
const { shapeSvg, bbox, halfPadding } = await labelHelper(
|
||||||
parent,
|
parent,
|
||||||
node,
|
node,
|
||||||
'node ' + node.classes + ' ' + node.class,
|
'node ' + node.cssClasses, // + ' ' + node.class,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ export const rect = async (parent: SVGAElement, node: Node) => {
|
|||||||
const y = -bbox.height / 2 - halfPadding;
|
const y = -bbox.height / 2 - halfPadding;
|
||||||
|
|
||||||
let rect;
|
let rect;
|
||||||
const { rx, ry, style, useRough } = node;
|
const { rx, ry, style: cssStyles, useRough } = node;
|
||||||
if (useRough) {
|
if (useRough) {
|
||||||
const rc = rough.svg(shapeSvg);
|
const rc = rough.svg(shapeSvg);
|
||||||
const options = userNodeOverrides(node, {
|
const options = userNodeOverrides(node, {
|
||||||
@@ -93,13 +93,13 @@ export const rect = async (parent: SVGAElement, node: Node) => {
|
|||||||
: rc.rectangle(x, y, totalWidth, totalHeight, options);
|
: rc.rectangle(x, y, totalWidth, totalHeight, options);
|
||||||
|
|
||||||
rect = shapeSvg.insert(() => roughNode, ':first-child');
|
rect = shapeSvg.insert(() => roughNode, ':first-child');
|
||||||
rect.attr('class', 'basic label-container').attr('style', style);
|
rect.attr('class', 'basic label-container').attr('style', cssStyles);
|
||||||
} else {
|
} else {
|
||||||
rect = shapeSvg.insert('rect', ':first-child');
|
rect = shapeSvg.insert('rect', ':first-child');
|
||||||
|
|
||||||
rect
|
rect
|
||||||
.attr('class', 'basic label-container')
|
.attr('class', 'basic label-container')
|
||||||
.attr('style', style)
|
.attr('style', cssStyles)
|
||||||
.attr('rx', rx)
|
.attr('rx', rx)
|
||||||
.attr('ry', ry)
|
.attr('ry', ry)
|
||||||
.attr('x', x)
|
.attr('x', x)
|
||||||
@@ -108,16 +108,16 @@ export const rect = async (parent: SVGAElement, node: Node) => {
|
|||||||
.attr('height', totalHeight);
|
.attr('height', totalHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.props) {
|
// if (node.props) {
|
||||||
const propKeys = new Set(Object.keys(node.props));
|
// const propKeys = new Set(Object.keys(node.props));
|
||||||
if (node.props.borders) {
|
// if (node.props.borders) {
|
||||||
applyNodePropertyBorders(rect, node.props.borders + '', totalWidth, totalHeight);
|
// applyNodePropertyBorders(rect, node.props.borders + '', totalWidth, totalHeight);
|
||||||
propKeys.delete('borders');
|
// propKeys.delete('borders');
|
||||||
}
|
// }
|
||||||
propKeys.forEach((propKey) => {
|
// propKeys.forEach((propKey) => {
|
||||||
log.warn(`Unknown node property ${propKey}`);
|
// log.warn(`Unknown node property ${propKey}`);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ export const rect = async (parent: SVGAElement, node: Node) => {
|
|||||||
export const labelRect = async (parent: SVGElement, node: Node) => {
|
export const labelRect = async (parent: SVGElement, node: Node) => {
|
||||||
const { shapeSvg } = await labelHelper(parent, node, 'label', true);
|
const { shapeSvg } = await labelHelper(parent, node, 'label', true);
|
||||||
|
|
||||||
log.trace('Classes = ', node.class);
|
// log.trace('Classes = ', node.class);
|
||||||
// add the rect
|
// add the rect
|
||||||
const rect = shapeSvg.insert('rect', ':first-child');
|
const rect = shapeSvg.insert('rect', ':first-child');
|
||||||
|
|
||||||
@@ -141,16 +141,16 @@ export const labelRect = async (parent: SVGElement, node: Node) => {
|
|||||||
rect.attr('width', totalWidth).attr('height', totalHeight);
|
rect.attr('width', totalWidth).attr('height', totalHeight);
|
||||||
shapeSvg.attr('class', 'label edgeLabel');
|
shapeSvg.attr('class', 'label edgeLabel');
|
||||||
|
|
||||||
if (node.props) {
|
// if (node.props) {
|
||||||
const propKeys = new Set(Object.keys(node.props));
|
// const propKeys = new Set(Object.keys(node.props));
|
||||||
if (node.props.borders) {
|
// if (node.props.borders) {
|
||||||
applyNodePropertyBorders(rect, node.borders, totalWidth, totalHeight);
|
// applyNodePropertyBorders(rect, node.borders, totalWidth, totalHeight);
|
||||||
propKeys.delete('borders');
|
// propKeys.delete('borders');
|
||||||
}
|
// }
|
||||||
propKeys.forEach((propKey) => {
|
// propKeys.forEach((propKey) => {
|
||||||
log.warn(`Unknown node property ${propKey}`);
|
// log.warn(`Unknown node property ${propKey}`);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
|
@@ -6,48 +6,43 @@ import { evaluate, sanitizeText } from '$root/diagrams/common/common.js';
|
|||||||
import { decodeEntities } from '$root/utils.js';
|
import { decodeEntities } from '$root/utils.js';
|
||||||
|
|
||||||
export const labelHelper = async (parent, node, _classes, isNode) => {
|
export const labelHelper = async (parent, node, _classes, isNode) => {
|
||||||
let classes;
|
let cssClasses;
|
||||||
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
|
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
|
||||||
if (!_classes) {
|
if (!_classes) {
|
||||||
classes = 'node default';
|
cssClasses = 'node default';
|
||||||
} else {
|
} else {
|
||||||
classes = _classes;
|
cssClasses = _classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = parent
|
const shapeSvg = parent
|
||||||
.insert('g')
|
.insert('g')
|
||||||
.attr('class', classes)
|
.attr('class', cssClasses)
|
||||||
.attr('id', node.domId || node.id);
|
.attr('id', node.domId || node.id);
|
||||||
|
|
||||||
// Create the label and insert it after the rect
|
// Create the label and insert it after the rect
|
||||||
const label = shapeSvg.insert('g').attr('class', 'label').attr('style', node.labelStyle);
|
const labelEl = shapeSvg.insert('g').attr('class', 'label').attr('style', node.labelStyle);
|
||||||
|
|
||||||
// Replace labelText with default value if undefined
|
// Replace label with default value if undefined
|
||||||
let labelText;
|
let label;
|
||||||
if (node.labelText === undefined) {
|
if (node.label === undefined) {
|
||||||
labelText = '';
|
label = '';
|
||||||
} else {
|
} else {
|
||||||
labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];
|
label = typeof node.label === 'string' ? node.label : node.label[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const textNode = label.node();
|
const textNode = labelEl.node();
|
||||||
let text;
|
let text;
|
||||||
if (node.labelType === 'markdown') {
|
if (node.labelType === 'markdown') {
|
||||||
// text = textNode;
|
// text = textNode;
|
||||||
text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
|
text = createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
width: node.width || getConfig().flowchart.wrappingWidth,
|
width: node.width || getConfig().flowchart.wrappingWidth,
|
||||||
classes: 'markdown-node-label',
|
cssClasses: 'markdown-node-label',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
text = textNode.appendChild(
|
text = textNode.appendChild(
|
||||||
createLabel(
|
createLabel(sanitizeText(decodeEntities(label), getConfig()), node.labelStyle, false, isNode)
|
||||||
sanitizeText(decodeEntities(labelText), getConfig()),
|
|
||||||
node.labelStyle,
|
|
||||||
false,
|
|
||||||
isNode
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
@@ -61,7 +56,7 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
|
|||||||
// if there are images, need to wait for them to load before getting the bounding box
|
// if there are images, need to wait for them to load before getting the bounding box
|
||||||
const images = div.getElementsByTagName('img');
|
const images = div.getElementsByTagName('img');
|
||||||
if (images) {
|
if (images) {
|
||||||
const noImgText = labelText.replace(/<img[^>]*>/g, '').trim() === '';
|
const noImgText = label.replace(/<img[^>]*>/g, '').trim() === '';
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[...images].map(
|
[...images].map(
|
||||||
@@ -107,15 +102,15 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
|
|||||||
|
|
||||||
// Center the label
|
// Center the label
|
||||||
if (useHtmlLabels) {
|
if (useHtmlLabels) {
|
||||||
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
|
labelEl.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
|
||||||
} else {
|
} else {
|
||||||
label.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')');
|
labelEl.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')');
|
||||||
}
|
}
|
||||||
if (node.centerLabel) {
|
if (node.centerLabel) {
|
||||||
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
|
labelEl.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
|
||||||
}
|
}
|
||||||
label.insert('rect', ':first-child');
|
labelEl.insert('rect', ':first-child');
|
||||||
return { shapeSvg, bbox, halfPadding, label };
|
return { shapeSvg, bbox, halfPadding, label: labelEl };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateNodeBounds = (node, element) => {
|
export const updateNodeBounds = (node, element) => {
|
||||||
|
19
packages/mermaid/src/rendering-util/types.d.ts
vendored
19
packages/mermaid/src/rendering-util/types.d.ts
vendored
@@ -14,25 +14,24 @@ interface Node {
|
|||||||
id: string;
|
id: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
parentId?: string;
|
parentId?: string;
|
||||||
position?: string; //REMOVE
|
position?: string; // Keep, this is for notes 'left of', 'right of', etc.
|
||||||
cssStyles?: string; // Renamed from `styles` to `cssStyles`
|
cssStyles?: string; // Renamed from `styles` to `cssStyles`
|
||||||
style?: string; //REMOVE
|
|
||||||
cssClasses?: string; // Renamed from `classes` to `cssClasses`
|
cssClasses?: string; // Renamed from `classes` to `cssClasses`
|
||||||
classes?: string; //REMOVE
|
// style?: string; //REMOVE
|
||||||
class?: string; //REMOVE
|
// class?: string; //REMOVE
|
||||||
|
// labelText?: string; //REMOVE, use `label` instead
|
||||||
|
// props?: Record<string, unknown>; //REMOVE
|
||||||
labelStyle?: string;
|
labelStyle?: string;
|
||||||
labelText?: string; //REMOVE, use `label` instead
|
|
||||||
|
|
||||||
// Flowchart specific properties
|
// Flowchart specific properties
|
||||||
labelType?: string; // REMOVE? Always use markdown string, need to check for KaTeX
|
labelType?: string; // REMOVE? Always use markdown string, need to check for KaTeX - wait with this one
|
||||||
domId: string;
|
domId: string;
|
||||||
// Rendering specific properties for both Flowchart and State Diagram nodes
|
// Rendering specific properties for both Flowchart and State Diagram nodes
|
||||||
dir?: string; // Only relevant for isGroup true, i.e. a sub-graph or composite state.
|
dir?: string; // Only relevant for isGroup true, i.e. a sub-graph or composite state.
|
||||||
haveCallback?: boolean;
|
haveCallback?: boolean;
|
||||||
link?: string;
|
link?: string;
|
||||||
linkTarget?: string;
|
linkTarget?: string;
|
||||||
padding?: number; //REMOVE, use from LayoutData.config
|
padding?: number; //REMOVE?, use from LayoutData.config - Keep, this could be shape specific
|
||||||
props?: Record<string, unknown>; //REMOVE
|
|
||||||
shape?: string;
|
shape?: string;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
type: string; // REMOVE, replace with isGroup: boolean, default false
|
type: string; // REMOVE, replace with isGroup: boolean, default false
|
||||||
@@ -137,8 +136,8 @@ export function createDomElement(node: Node): Node {
|
|||||||
element.id = node.domId;
|
element.id = node.domId;
|
||||||
|
|
||||||
// Optional: Apply styles and classes to the element
|
// Optional: Apply styles and classes to the element
|
||||||
if (node.styles) {
|
if (node.cssStyles) {
|
||||||
element.style.cssText = node.styles;
|
element.style.cssText = node.cssStyles;
|
||||||
}
|
}
|
||||||
if (node.classes) {
|
if (node.classes) {
|
||||||
element.className = node.classes;
|
element.className = node.classes;
|
||||||
|
945
pnpm-lock.yaml
generated
945
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user