mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-17 06:20:07 +02:00
Merge remote-tracking branch 'os_repo/5237-unified-layout-common-renderer' into alanaV11
This commit is contained in:
@@ -110,6 +110,7 @@ strikethrough
|
|||||||
stringifying
|
stringifying
|
||||||
struct
|
struct
|
||||||
STYLECLASS
|
STYLECLASS
|
||||||
|
STYLEDEF
|
||||||
STYLEOPTS
|
STYLEOPTS
|
||||||
subcomponent
|
subcomponent
|
||||||
subcomponents
|
subcomponents
|
||||||
|
@@ -558,6 +558,29 @@ stateDiagram-v2
|
|||||||
{ logLevel: 0, fontFamily: 'courier' }
|
{ logLevel: 0, fontFamily: 'courier' }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it(' can have styles applied ', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
stateDiagram-v2
|
||||||
|
AState
|
||||||
|
style AState fill:#636,border:1px solid red,color:white;
|
||||||
|
`,
|
||||||
|
{ logLevel: 0, fontFamily: 'courier' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it(' should let styles take preceedence over classes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
stateDiagram-v2
|
||||||
|
AState: Should NOT be white
|
||||||
|
BState
|
||||||
|
classDef exampleStyleClass fill:#fff,color: blue;
|
||||||
|
class AState,BState exampleStyleClass
|
||||||
|
style AState fill:#636,border:1px solid red,color:white;
|
||||||
|
`,
|
||||||
|
{ logLevel: 0, fontFamily: 'courier' }
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('1433: should render a simple state diagram with a title', () => {
|
it('1433: should render a simple state diagram with a title', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
|
@@ -75,25 +75,127 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<pre id="diagram" class="mermaid2">
|
<h4>Case 1</h4>
|
||||||
---
|
<div class="flex">
|
||||||
config:
|
<pre id="diagram" class="mermaid">
|
||||||
theme: neo
|
stateDiagram-v2
|
||||||
look: neo
|
AState: Should NOT be white
|
||||||
layout: elk
|
BState
|
||||||
elk.mergeEdges: true
|
classDef exampleStyleClass fill:#fff,color: blue;
|
||||||
themeVariables: {}
|
class AState,BState exampleStyleClass
|
||||||
---
|
style AState fill:#636,border:1px solid red,color:white;
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid">
|
||||||
|
%%{init: {"look": "classic"} }%%
|
||||||
|
stateDiagram-v2
|
||||||
|
AState: Should NOT be white
|
||||||
|
BState
|
||||||
|
classDef exampleStyleClass fill:#fff,color: blue;
|
||||||
|
class AState,BState exampleStyleClass
|
||||||
|
style AState fill:#636,border:1px solid red,color:white;
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
stateDiagram-v2
|
||||||
|
|
||||||
|
classDef exampleStyleClass background:#bbb,border:1px solid red;
|
||||||
|
a --> b
|
||||||
|
class a exampleStyleClass
|
||||||
|
%% a:::exampleStyleClass
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
stateDiagram
|
stateDiagram
|
||||||
direction TB
|
direction TB
|
||||||
A --> B
|
|
||||||
A --> C
|
accTitle: This is the accessible title
|
||||||
A --> D
|
accDescr: This is an accessible description
|
||||||
A --> E
|
|
||||||
A --> F
|
classDef notMoving fill:white
|
||||||
</pre
|
classDef movement font-style:italic;
|
||||||
>
|
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||||
|
|
||||||
|
[*] --> Still:::notMoving
|
||||||
|
Still --> [*]
|
||||||
|
Still --> Moving:::movement
|
||||||
|
Moving --> Still
|
||||||
|
Moving --> Crash:::movement
|
||||||
|
Crash:::badBadEvent --> [*]
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
stateDiagram-v2
|
||||||
|
MyState
|
||||||
|
note left of MyState : I am a leftie
|
||||||
|
note right of MyState : I am a rightie
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
stateDiagram
|
||||||
|
%% direction LR
|
||||||
|
|
||||||
|
state C0 {
|
||||||
|
A0 --> B0
|
||||||
|
}
|
||||||
|
|
||||||
|
C0 --> Apa0
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
stateDiagram
|
||||||
|
direction LR
|
||||||
|
|
||||||
|
|
||||||
|
state C1 {
|
||||||
|
A1 --> B1
|
||||||
|
}
|
||||||
|
|
||||||
|
C1 --> Apa1
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<h4>Case 2</h4>
|
||||||
|
<div class="flex">
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
stateDiagram
|
||||||
|
direction LR
|
||||||
|
state Gorilla0 {
|
||||||
|
state Apa0 {
|
||||||
|
A0 --> B0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Apa --> Gorilla0:Label
|
||||||
|
A0 --> C0
|
||||||
|
%% C1: "`This is C`"
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
stateDiagram
|
||||||
|
direction LR
|
||||||
|
state Apa1 {
|
||||||
|
A1
|
||||||
|
}
|
||||||
|
|
||||||
|
Apa11 --> Apa1
|
||||||
|
A1 --> C1
|
||||||
|
%% C1: "`This is C`"
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
stateDiagram
|
||||||
|
[*] --> Level1
|
||||||
|
|
||||||
|
state Level1 {
|
||||||
|
[*] --> Level2
|
||||||
|
|
||||||
|
state Level2 {
|
||||||
|
[*] --> level2
|
||||||
|
level2 --> Level3
|
||||||
|
|
||||||
|
state Level3 {
|
||||||
|
[*] --> level3
|
||||||
|
level3 --> [*]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
---
|
---
|
||||||
config:
|
config:
|
||||||
@@ -184,20 +286,7 @@ Apa --> C
|
|||||||
A --> B & C["C"]
|
A --> B & C["C"]
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram" class="mermaid">
|
|
||||||
stateDiagram
|
|
||||||
direction LR
|
|
||||||
state Gorilla0 {
|
|
||||||
state Apa0 {
|
|
||||||
A0 --> B0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Apa0 --> C0
|
|
||||||
A0 --> C0
|
|
||||||
C1: "`This is C`"
|
|
||||||
</pre
|
|
||||||
>
|
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
subgraph Gorilla
|
subgraph Gorilla
|
||||||
@@ -237,7 +326,7 @@ stateDiagram
|
|||||||
|
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart LR
|
flowchart LR
|
||||||
Apa --Hello--> C
|
Apa --Hello--> C
|
||||||
|
|
||||||
@@ -375,7 +464,7 @@ stateDiagram-v2
|
|||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
theme: 'neo',
|
theme: 'neo',
|
||||||
// handdrawnSeed: 12,
|
// handdrawnSeed: 12,
|
||||||
look: 'neo',
|
look: 'handdrawn',
|
||||||
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
||||||
// layout: 'dagre',
|
// layout: 'dagre',
|
||||||
// layout: 'elk',
|
// layout: 'elk',
|
||||||
|
@@ -806,7 +806,8 @@ const addNodeFromVertex = (
|
|||||||
labelStyle: '',
|
labelStyle: '',
|
||||||
parentId,
|
parentId,
|
||||||
padding: config.flowchart?.padding || 8,
|
padding: config.flowchart?.padding || 8,
|
||||||
cssStyles: vertex.styles.join(' '),
|
cssStyles: vertex.styles,
|
||||||
|
cssCompiledStyles: getCompiledStyles(vertex.classes),
|
||||||
cssClasses: vertex.classes.join(' '),
|
cssClasses: vertex.classes.join(' '),
|
||||||
shape: getTypeFromVertex(vertex),
|
shape: getTypeFromVertex(vertex),
|
||||||
dir: vertex.dir,
|
dir: vertex.dir,
|
||||||
@@ -820,6 +821,22 @@ const addNodeFromVertex = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getCompiledStyles(classDefs: string[]) {
|
||||||
|
let compiledStyles: string[] = [];
|
||||||
|
for (const customClass of classDefs) {
|
||||||
|
const cssClass = classes.get(customClass);
|
||||||
|
if (cssClass) {
|
||||||
|
if (cssClass.styles) {
|
||||||
|
compiledStyles = [...compiledStyles, ...(cssClass.styles ?? [])];
|
||||||
|
}
|
||||||
|
if (cssClass.textStyles) {
|
||||||
|
compiledStyles = [...compiledStyles, ...(cssClass.textStyles ?? [])];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compiledStyles;
|
||||||
|
}
|
||||||
|
|
||||||
export const getData = () => {
|
export const getData = () => {
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
const nodes: Node[] = [];
|
const nodes: Node[] = [];
|
||||||
@@ -844,7 +861,7 @@ export const getData = () => {
|
|||||||
labelStyle: '',
|
labelStyle: '',
|
||||||
parentId: parentDB.get(subGraph.id),
|
parentId: parentDB.get(subGraph.id),
|
||||||
padding: config.flowchart?.padding || 8,
|
padding: config.flowchart?.padding || 8,
|
||||||
cssStyles: '',
|
cssStyles: [],
|
||||||
cssClasses: '',
|
cssClasses: '',
|
||||||
shape: 'rect',
|
shape: 'rect',
|
||||||
dir: subGraph.dir,
|
dir: subGraph.dir,
|
||||||
|
@@ -54,21 +54,39 @@ export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOM
|
|||||||
return `${DOMID_STATE}-${itemId}${typeStr}-${counter}`;
|
return `${DOMID_STATE}-${itemId}${typeStr}-${counter}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const setupDoc = (parentParsedItem, doc, diagramStates, nodes, edges, altFlag, look) => {
|
const setupDoc = (parentParsedItem, doc, diagramStates, nodes, edges, altFlag, look, classes) => {
|
||||||
// graphItemCount = 0;
|
// graphItemCount = 0;
|
||||||
log.trace('items', doc);
|
log.trace('items', doc);
|
||||||
doc.forEach((item) => {
|
doc.forEach((item) => {
|
||||||
switch (item.stmt) {
|
switch (item.stmt) {
|
||||||
case STMT_STATE:
|
case STMT_STATE:
|
||||||
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look);
|
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes);
|
||||||
break;
|
break;
|
||||||
case DEFAULT_STATE_TYPE:
|
case DEFAULT_STATE_TYPE:
|
||||||
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look);
|
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes);
|
||||||
break;
|
break;
|
||||||
case STMT_RELATION:
|
case STMT_RELATION:
|
||||||
{
|
{
|
||||||
dataFetcher(parentParsedItem, item.state1, diagramStates, nodes, edges, altFlag, look);
|
dataFetcher(
|
||||||
dataFetcher(parentParsedItem, item.state2, diagramStates, nodes, edges, altFlag, look);
|
parentParsedItem,
|
||||||
|
item.state1,
|
||||||
|
diagramStates,
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
altFlag,
|
||||||
|
look,
|
||||||
|
classes
|
||||||
|
);
|
||||||
|
dataFetcher(
|
||||||
|
parentParsedItem,
|
||||||
|
item.state2,
|
||||||
|
diagramStates,
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
altFlag,
|
||||||
|
look,
|
||||||
|
classes
|
||||||
|
);
|
||||||
const edgeData = {
|
const edgeData = {
|
||||||
id: 'edge' + graphItemCount,
|
id: 'edge' + graphItemCount,
|
||||||
start: item.state1.id,
|
start: item.state1.id,
|
||||||
@@ -134,8 +152,9 @@ let cssClasses = newClassesList(); // style classes defined by a classDef
|
|||||||
*
|
*
|
||||||
* @param nodes
|
* @param nodes
|
||||||
* @param nodeData
|
* @param nodeData
|
||||||
|
* @param classes
|
||||||
*/
|
*/
|
||||||
function insertOrUpdateNode(nodes, nodeData) {
|
function insertOrUpdateNode(nodes, nodeData, classes) {
|
||||||
if (!nodeData.id || nodeData.id === '</join></fork>' || nodeData.id === '</choice>') {
|
if (!nodeData.id || nodeData.id === '</join></fork>' || nodeData.id === '</choice>') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -143,29 +162,62 @@ function insertOrUpdateNode(nodes, nodeData) {
|
|||||||
//Populate node style attributes if nodeData has classes defined
|
//Populate node style attributes if nodeData has classes defined
|
||||||
if (nodeData.cssClasses) {
|
if (nodeData.cssClasses) {
|
||||||
nodeData.cssClasses.split(' ').forEach((cssClass) => {
|
nodeData.cssClasses.split(' ').forEach((cssClass) => {
|
||||||
if (cssClasses[cssClass]) {
|
if (classes.get(cssClass)) {
|
||||||
cssClasses[cssClass].styles.forEach((style) => {
|
const classDef = classes.get(cssClass);
|
||||||
// Populate nodeData with style attributes specifically to be used by rough.js
|
// classDef.styles.forEach((style) => {
|
||||||
if (style && style.startsWith('fill:')) {
|
// nodeData.cssCompiledStyles += style + ',';
|
||||||
nodeData.backgroundColor = style.replace('fill:', '');
|
// // Populate nodeData with style attributes specifically to be used by rough.js
|
||||||
}
|
// if (style && style.startsWith('fill:')) {
|
||||||
if (style && style.startsWith('stroke:')) {
|
// nodeData.backgroundColor = style.replace('fill:', '');
|
||||||
nodeData.borderColor = style.replace('stroke:', '');
|
// }
|
||||||
}
|
// if (style && style.startsWith('stroke:')) {
|
||||||
if (style && style.startsWith('stroke-width:')) {
|
// nodeData.borderColor = style.replace('stroke:', '');
|
||||||
nodeData.borderWidth = style.replace('stroke-width:', '');
|
// }
|
||||||
}
|
// if (style && style.startsWith('stroke-width:')) {
|
||||||
|
// nodeData.borderWidth = style.replace('stroke-width:', '');
|
||||||
|
// }
|
||||||
|
|
||||||
nodeData.cssStyles += style + ';';
|
// nodeData.cssStyles += style + ';';
|
||||||
});
|
// });
|
||||||
cssClasses[cssClass].textStyles.forEach((style) => {
|
|
||||||
nodeData.labelStyle += style + ';';
|
// classDef.textStyles.forEach((style) => {
|
||||||
if (style && style.startsWith('fill:')) {
|
// nodeData.labelStyle += style + ';';
|
||||||
nodeData.labelTextColor = style.replace('fill:', '');
|
// if (style && style.startsWith('fill:')) {
|
||||||
}
|
// nodeData.labelTextColor = style.replace('fill:', '');
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
|
nodeData.cssCompiledStyles = [...nodeData.cssCompiledStyles, ...classDef.styles];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
//Populate node style attributes if nodeData has classes defined
|
||||||
|
if (nodeData.cssStyles) {
|
||||||
|
// nodeData.cssStyles.split(' ').forEach((csStyle) => {
|
||||||
|
// 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.cssStyles += style + ';';
|
||||||
|
// });
|
||||||
|
// classes[cssClass].textStyles.forEach((style) => {
|
||||||
|
// nodeData.labelStyle += style + ';';
|
||||||
|
// if (style && style.startsWith('fill:')) {
|
||||||
|
// nodeData.labelTextColor = style.replace('fill:', '');
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
// nodeData.labelTextColor = '#ffffff';
|
||||||
|
// nodeData.labelStyle = 'color:#ffffff';
|
||||||
|
// nodeData.cssStyles = 'fill:#f77';
|
||||||
}
|
}
|
||||||
const existingNodeData = nodes.find((node) => node.id === nodeData.id);
|
const existingNodeData = nodes.find((node) => node.id === nodeData.id);
|
||||||
if (existingNodeData) {
|
if (existingNodeData) {
|
||||||
@@ -197,7 +249,7 @@ function getClassesFromDbInfo(dbInfoItem) {
|
|||||||
}
|
}
|
||||||
//add comma for all other classes
|
//add comma for all other classes
|
||||||
else {
|
else {
|
||||||
classStr += dbInfoItem.classes[i] + ' ';
|
classStr += dbInfoItem.classes[i] + ',';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return classStr;
|
return classStr;
|
||||||
@@ -206,9 +258,41 @@ function getClassesFromDbInfo(dbInfoItem) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, altFlag, look) => {
|
/**
|
||||||
|
* 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 getStylesFromDbInfo(dbInfoItem) {
|
||||||
|
if (dbInfoItem === undefined || dbInfoItem === null) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (dbInfoItem.styles) {
|
||||||
|
return dbInfoItem.styles;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const dataFetcher = (
|
||||||
|
parent,
|
||||||
|
parsedItem,
|
||||||
|
diagramStates,
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
altFlag,
|
||||||
|
look,
|
||||||
|
classes
|
||||||
|
) => {
|
||||||
const itemId = parsedItem.id;
|
const itemId = parsedItem.id;
|
||||||
const classStr = getClassesFromDbInfo(diagramStates.get(itemId));
|
const dbState = diagramStates.get(itemId);
|
||||||
|
const classStr = getClassesFromDbInfo(dbState);
|
||||||
|
const style = getStylesFromDbInfo(dbState);
|
||||||
|
|
||||||
|
log.info('dataFetcher parsedItem', parsedItem, dbState, style);
|
||||||
|
|
||||||
if (itemId !== 'root') {
|
if (itemId !== 'root') {
|
||||||
let shape = SHAPE_STATE;
|
let shape = SHAPE_STATE;
|
||||||
@@ -229,6 +313,7 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
|
|||||||
shape,
|
shape,
|
||||||
description: common.sanitizeText(itemId, getConfig()),
|
description: common.sanitizeText(itemId, getConfig()),
|
||||||
cssClasses: `${classStr} ${CSS_DIAGRAM_STATE}`,
|
cssClasses: `${classStr} ${CSS_DIAGRAM_STATE}`,
|
||||||
|
cssStyles: style,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +372,8 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
|
|||||||
shape: newNode.shape,
|
shape: newNode.shape,
|
||||||
label: newNode.description,
|
label: newNode.description,
|
||||||
cssClasses: newNode.cssClasses,
|
cssClasses: newNode.cssClasses,
|
||||||
cssStyles: '',
|
cssCompiledStyles: [],
|
||||||
|
cssStyles: newNode.cssStyles,
|
||||||
id: itemId,
|
id: itemId,
|
||||||
dir: newNode.dir,
|
dir: newNode.dir,
|
||||||
domId: stateDomId(itemId, graphItemCount),
|
domId: stateDomId(itemId, graphItemCount),
|
||||||
@@ -319,7 +405,8 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
|
|||||||
label: parsedItem.note.text,
|
label: parsedItem.note.text,
|
||||||
cssClasses: CSS_DIAGRAM_NOTE,
|
cssClasses: CSS_DIAGRAM_NOTE,
|
||||||
// useHtmlLabels: false,
|
// useHtmlLabels: false,
|
||||||
cssStyles: '', // styles.style,
|
cssStyles: [],
|
||||||
|
cssCompilesStyles: [],
|
||||||
id: itemId + NOTE_ID + '-' + graphItemCount,
|
id: itemId + NOTE_ID + '-' + graphItemCount,
|
||||||
domId: stateDomId(itemId, graphItemCount, NOTE),
|
domId: stateDomId(itemId, graphItemCount, NOTE),
|
||||||
type: newNode.type,
|
type: newNode.type,
|
||||||
@@ -334,7 +421,7 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
|
|||||||
shape: SHAPE_NOTEGROUP,
|
shape: SHAPE_NOTEGROUP,
|
||||||
label: parsedItem.note.text,
|
label: parsedItem.note.text,
|
||||||
cssClasses: newNode.cssClasses,
|
cssClasses: newNode.cssClasses,
|
||||||
cssStyles: '', // styles.style,
|
cssStyles: [],
|
||||||
id: itemId + PARENT_ID,
|
id: itemId + PARENT_ID,
|
||||||
domId: stateDomId(itemId, graphItemCount, PARENT),
|
domId: stateDomId(itemId, graphItemCount, PARENT),
|
||||||
type: 'group',
|
type: 'group',
|
||||||
@@ -352,11 +439,11 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
|
|||||||
nodeData.parentId = parentNodeId;
|
nodeData.parentId = parentNodeId;
|
||||||
|
|
||||||
//insert groupData
|
//insert groupData
|
||||||
insertOrUpdateNode(nodes, groupData);
|
insertOrUpdateNode(nodes, groupData, classes);
|
||||||
//insert noteData
|
//insert noteData
|
||||||
insertOrUpdateNode(nodes, noteData);
|
insertOrUpdateNode(nodes, noteData, classes);
|
||||||
//insert nodeData
|
//insert nodeData
|
||||||
insertOrUpdateNode(nodes, nodeData);
|
insertOrUpdateNode(nodes, nodeData, classes);
|
||||||
|
|
||||||
let from = itemId;
|
let from = itemId;
|
||||||
let to = noteData.id;
|
let to = noteData.id;
|
||||||
@@ -382,12 +469,12 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
|
|||||||
look,
|
look,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
insertOrUpdateNode(nodes, nodeData);
|
insertOrUpdateNode(nodes, nodeData, classes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parsedItem.doc) {
|
if (parsedItem.doc) {
|
||||||
log.trace('Adding nodes children ');
|
log.trace('Adding nodes children ');
|
||||||
setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, look);
|
setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, look, classes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -217,4 +217,50 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('style statement for a state (style)', () => {
|
||||||
|
describe('defining (style)', () => {
|
||||||
|
it('has "style" as a keyword, an id, and can set a css style attribute', function () {
|
||||||
|
stateDiagram.parser.parse(`stateDiagram-v2
|
||||||
|
id1
|
||||||
|
style id1 background:#bbb`);
|
||||||
|
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||||
|
const data4Layout = stateDiagram.parser.yy.getData();
|
||||||
|
|
||||||
|
expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']);
|
||||||
|
});
|
||||||
|
it('has "style" as a keyword, an id, and can set a css style attribute', function () {
|
||||||
|
stateDiagram.parser.parse(`stateDiagram-v2
|
||||||
|
id1
|
||||||
|
id2
|
||||||
|
style id1,id2 background:#bbb`);
|
||||||
|
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||||
|
const data4Layout = stateDiagram.parser.yy.getData();
|
||||||
|
|
||||||
|
expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']);
|
||||||
|
expect(data4Layout.nodes[1].cssStyles).toEqual(['background:#bbb']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can define multiple attributes separated by commas', function () {
|
||||||
|
stateDiagram.parser.parse(`stateDiagram-v2
|
||||||
|
id1
|
||||||
|
id2
|
||||||
|
style id1,id2 background:#bbb, font-weight:bold, font-style:italic;`);
|
||||||
|
|
||||||
|
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||||
|
const data4Layout = stateDiagram.parser.yy.getData();
|
||||||
|
|
||||||
|
expect(data4Layout.nodes[0].cssStyles).toEqual([
|
||||||
|
'background:#bbb',
|
||||||
|
'font-weight:bold',
|
||||||
|
'font-style:italic',
|
||||||
|
]);
|
||||||
|
expect(data4Layout.nodes[1].cssStyles).toEqual([
|
||||||
|
'background:#bbb',
|
||||||
|
'font-weight:bold',
|
||||||
|
'font-style:italic',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -27,6 +27,13 @@
|
|||||||
%x CLASSDEFID
|
%x CLASSDEFID
|
||||||
%x CLASS
|
%x CLASS
|
||||||
%x CLASS_STYLE
|
%x CLASS_STYLE
|
||||||
|
|
||||||
|
// Style statement states
|
||||||
|
%x STYLE
|
||||||
|
%x STYLE_IDS
|
||||||
|
%x STYLEDEF_STYLES
|
||||||
|
%x STYLEDEF_STYLEOPTS
|
||||||
|
|
||||||
%x NOTE
|
%x NOTE
|
||||||
%x NOTE_ID
|
%x NOTE_ID
|
||||||
%x NOTE_TEXT
|
%x NOTE_TEXT
|
||||||
@@ -75,6 +82,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
<CLASS>(\w+)+((","\s*\w+)*) { this.popState(); this.pushState('CLASS_STYLE'); return 'CLASSENTITY_IDS' }
|
<CLASS>(\w+)+((","\s*\w+)*) { this.popState(); this.pushState('CLASS_STYLE'); return 'CLASSENTITY_IDS' }
|
||||||
<CLASS_STYLE>[^\n]* { this.popState(); return 'STYLECLASS' }
|
<CLASS_STYLE>[^\n]* { this.popState(); return 'STYLECLASS' }
|
||||||
|
|
||||||
|
<INITIAL,struct>"style"\s+ { this.pushState('STYLE'); return 'style'; }
|
||||||
|
<STYLE>[\w,]+\s+ { this.popState(); this.pushState('STYLEDEF_STYLES'); return 'STYLE_IDS' }
|
||||||
|
<STYLEDEF_STYLES>[^\n]* { this.popState(); return 'STYLEDEF_STYLEOPTS' }
|
||||||
|
|
||||||
"scale"\s+ { this.pushState('SCALE'); /* console.log('Got scale', yytext);*/ return 'scale'; }
|
"scale"\s+ { this.pushState('SCALE'); /* console.log('Got scale', yytext);*/ return 'scale'; }
|
||||||
<SCALE>\d+ return 'WIDTH';
|
<SCALE>\d+ return 'WIDTH';
|
||||||
<SCALE>\s+"width" {this.popState();}
|
<SCALE>\s+"width" {this.popState();}
|
||||||
@@ -168,6 +179,7 @@ line
|
|||||||
|
|
||||||
statement
|
statement
|
||||||
: classDefStatement
|
: classDefStatement
|
||||||
|
| styleStatement
|
||||||
| cssClassStatement
|
| cssClassStatement
|
||||||
| idStatement { /* console.log('got id', $1); */
|
| idStatement { /* console.log('got id', $1); */
|
||||||
$$=$1;
|
$$=$1;
|
||||||
@@ -246,6 +258,12 @@ classDefStatement
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
styleStatement
|
||||||
|
: style STYLE_IDS STYLEDEF_STYLEOPTS {
|
||||||
|
$$ = { stmt: 'style', id: $2.trim(), styleClass: $3.trim() };
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
cssClassStatement
|
cssClassStatement
|
||||||
: class CLASSENTITY_IDS STYLECLASS {
|
: class CLASSENTITY_IDS STYLECLASS {
|
||||||
//console.log('apply class: id(s): ',$2, ' style class: ', $3);
|
//console.log('apply class: id(s): ',$2, ' style class: ', $3);
|
||||||
|
@@ -14,6 +14,7 @@ export const STMT_STATE = 'state';
|
|||||||
export const STMT_RELATION = 'relation';
|
export const STMT_RELATION = 'relation';
|
||||||
// parsed statement type for a classDef
|
// parsed statement type for a classDef
|
||||||
export const STMT_CLASSDEF = 'classDef';
|
export const STMT_CLASSDEF = 'classDef';
|
||||||
|
export const STMT_STYLEDEF = 'style';
|
||||||
// parsed statement type for applyClass
|
// parsed statement type for applyClass
|
||||||
export const STMT_APPLYCLASS = 'applyClass';
|
export const STMT_APPLYCLASS = 'applyClass';
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ export default {
|
|||||||
STMT_STATE,
|
STMT_STATE,
|
||||||
STMT_RELATION,
|
STMT_RELATION,
|
||||||
STMT_CLASSDEF,
|
STMT_CLASSDEF,
|
||||||
|
STMT_STYLEDEF,
|
||||||
STMT_APPLYCLASS,
|
STMT_APPLYCLASS,
|
||||||
DEFAULT_STATE_TYPE,
|
DEFAULT_STATE_TYPE,
|
||||||
DIVIDER_TYPE,
|
DIVIDER_TYPE,
|
||||||
|
@@ -18,6 +18,7 @@ import {
|
|||||||
STMT_STATE,
|
STMT_STATE,
|
||||||
STMT_RELATION,
|
STMT_RELATION,
|
||||||
STMT_CLASSDEF,
|
STMT_CLASSDEF,
|
||||||
|
STMT_STYLEDEF,
|
||||||
STMT_APPLYCLASS,
|
STMT_APPLYCLASS,
|
||||||
DEFAULT_STATE_TYPE,
|
DEFAULT_STATE_TYPE,
|
||||||
DIVIDER_TYPE,
|
DIVIDER_TYPE,
|
||||||
@@ -171,9 +172,10 @@ const extract = (_doc) => {
|
|||||||
log.info(doc);
|
log.info(doc);
|
||||||
clear(true);
|
clear(true);
|
||||||
|
|
||||||
log.info('Extract', doc);
|
log.info('Extract initial document:', doc);
|
||||||
|
|
||||||
doc.forEach((item) => {
|
doc.forEach((item) => {
|
||||||
|
log.warn('Statement', item.stmt);
|
||||||
switch (item.stmt) {
|
switch (item.stmt) {
|
||||||
case STMT_STATE:
|
case STMT_STATE:
|
||||||
addState(
|
addState(
|
||||||
@@ -193,6 +195,18 @@ const extract = (_doc) => {
|
|||||||
case STMT_CLASSDEF:
|
case STMT_CLASSDEF:
|
||||||
addStyleClass(item.id.trim(), item.classes);
|
addStyleClass(item.id.trim(), item.classes);
|
||||||
break;
|
break;
|
||||||
|
case STMT_STYLEDEF:
|
||||||
|
{
|
||||||
|
const ids = item.id.trim().split(',');
|
||||||
|
const styles = item.styleClass.split(',');
|
||||||
|
ids.forEach((id) => {
|
||||||
|
const state = getState(id);
|
||||||
|
if (state !== undefined) {
|
||||||
|
state.styles = styles.map((s) => s.replace(/;/g, '')?.trim());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
case STMT_APPLYCLASS:
|
case STMT_APPLYCLASS:
|
||||||
setCssClass(item.id.trim(), item.styleClass);
|
setCssClass(item.id.trim(), item.styleClass);
|
||||||
break;
|
break;
|
||||||
@@ -203,7 +217,7 @@ const extract = (_doc) => {
|
|||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
const look = config.look;
|
const look = config.look;
|
||||||
resetDataFetching();
|
resetDataFetching();
|
||||||
dataFetcher(undefined, getRootDocV2(), diagramStates, nodes, edges, true, look);
|
dataFetcher(undefined, getRootDocV2(), diagramStates, nodes, edges, true, look, classes);
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
if (Array.isArray(node.label)) {
|
if (Array.isArray(node.label)) {
|
||||||
// add the rest as description
|
// add the rest as description
|
||||||
@@ -538,7 +552,7 @@ export const setCssClass = function (itemIds, cssClassName) {
|
|||||||
export const setStyle = function (itemId, styleText) {
|
export const setStyle = function (itemId, styleText) {
|
||||||
const item = getState(itemId);
|
const item = getState(itemId);
|
||||||
if (item !== undefined) {
|
if (item !== undefined) {
|
||||||
item.textStyles.push(styleText);
|
item.styles.push(styleText);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -198,7 +198,16 @@ export const createText = (
|
|||||||
} = {},
|
} = {},
|
||||||
config: MermaidConfig
|
config: MermaidConfig
|
||||||
) => {
|
) => {
|
||||||
log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground);
|
log.info(
|
||||||
|
'IPI createText',
|
||||||
|
text,
|
||||||
|
style,
|
||||||
|
isTitle,
|
||||||
|
classes,
|
||||||
|
useHtmlLabels,
|
||||||
|
isNode,
|
||||||
|
addSvgBackground
|
||||||
|
);
|
||||||
if (useHtmlLabels) {
|
if (useHtmlLabels) {
|
||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
|
|
||||||
|
@@ -2,13 +2,18 @@ import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
|
|||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node, RectOptions } from '$root/rendering-util/types.d.ts';
|
import type { Node, RectOptions } from '$root/rendering-util/types.d.ts';
|
||||||
import { createRoundedRectPathD } from './roundedRectPath.js';
|
import { createRoundedRectPathD } from './roundedRectPath.js';
|
||||||
import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
|
import {
|
||||||
|
userNodeOverrides,
|
||||||
|
styles2String,
|
||||||
|
} from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
import { getConfig } from '$root/diagram-api/diagramAPI.js';
|
||||||
|
import { log } from '$root/logger.js';
|
||||||
|
|
||||||
export const drawRect = async (parent: SVGAElement, node: Node, options: RectOptions) => {
|
export const drawRect = async (parent: SVGAElement, node: Node, options: RectOptions) => {
|
||||||
const { look } = getConfig();
|
const { look } = getConfig();
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||||
|
|
||||||
const totalWidth = Math.max(bbox.width + options.labelPaddingX * 2, node?.width || 0);
|
const totalWidth = Math.max(bbox.width + options.labelPaddingX * 2, node?.width || 0);
|
||||||
@@ -16,6 +21,8 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt
|
|||||||
const x = -totalWidth / 2;
|
const x = -totalWidth / 2;
|
||||||
const y = -totalHeight / 2;
|
const y = -totalHeight / 2;
|
||||||
|
|
||||||
|
log.info('IPI node = ', node);
|
||||||
|
|
||||||
let rect;
|
let rect;
|
||||||
node.look = look;
|
node.look = look;
|
||||||
let { rx, ry } = node;
|
let { rx, ry } = node;
|
||||||
@@ -48,8 +55,8 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt
|
|||||||
}
|
}
|
||||||
|
|
||||||
rect
|
rect
|
||||||
.attr('class', rectClass)
|
.attr('class', 'basic label-container')
|
||||||
.attr('style', cssStyles)
|
.attr('style', nodeStyles)
|
||||||
.attr('rx', rx)
|
.attr('rx', rx)
|
||||||
.attr('data-id', 'abc')
|
.attr('data-id', 'abc')
|
||||||
.attr('data-et', 'node')
|
.attr('data-et', 'node')
|
||||||
|
@@ -15,28 +15,61 @@ export const solidStateFill = (color: string) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const compileStyles = (node: Node) => {
|
||||||
|
// node.cssCompiledStyles is an array of strings in the form of 'key: value' where jey is the css property and value is the value
|
||||||
|
// the array is the styles of node node from the classes it is using
|
||||||
|
// node.cssStyles is an array of styles directly set on the node
|
||||||
|
|
||||||
|
// concat the arrays and remove duplicates such that the values from node.cssStyles are used if there are duplicates
|
||||||
|
const stylesMap = styles2Map([...(node.cssCompiledStyles || []), ...(node.cssStyles || [])]);
|
||||||
|
return { stylesMap, stylesArray: [...stylesMap] };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const styles2Map = (styles: string[]) => {
|
||||||
|
const styleMap = new Map<string, string>();
|
||||||
|
styles.forEach((style) => {
|
||||||
|
const [key, value] = style.split(':');
|
||||||
|
styleMap.set(key.trim(), value?.trim());
|
||||||
|
});
|
||||||
|
return styleMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const styles2String = (node: Node) => {
|
||||||
|
const { stylesArray } = compileStyles(node);
|
||||||
|
const labelStyles: string[] = [];
|
||||||
|
const nodeStyles: string[] = [];
|
||||||
|
|
||||||
|
stylesArray.forEach((style) => {
|
||||||
|
const key = style[0];
|
||||||
|
if (key === 'color') {
|
||||||
|
labelStyles.push(style.join(':') + ' !important');
|
||||||
|
} else {
|
||||||
|
nodeStyles.push(style.join(':') + ' !important');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { labelStyles: labelStyles.join(';'), nodeStyles: nodeStyles.join(';') };
|
||||||
|
};
|
||||||
|
|
||||||
// Striped fill like start or fork nodes in state diagrams
|
// Striped fill like start or fork nodes in state diagrams
|
||||||
// TODO remove any
|
// TODO remove any
|
||||||
export const userNodeOverrides = (node: Node, options: any) => {
|
export const userNodeOverrides = (node: Node, options: any) => {
|
||||||
const { themeVariables, handdrawnSeed } = getConfig();
|
const { themeVariables, handdrawnSeed } = getConfig();
|
||||||
const { nodeBorder, mainBkg } = themeVariables;
|
const { nodeBorder, mainBkg } = themeVariables;
|
||||||
|
const { stylesArray: styles, stylesMap } = compileStyles(node);
|
||||||
|
|
||||||
|
// index the style array to a map object
|
||||||
const result = Object.assign(
|
const result = Object.assign(
|
||||||
{
|
{
|
||||||
roughness: 0.7,
|
roughness: 0.7,
|
||||||
fill: mainBkg,
|
fill: stylesMap.get('fill') || mainBkg,
|
||||||
fillStyle: 'hachure', // solid fill
|
fillStyle: 'hachure', // solid fill
|
||||||
fillWeight: 3.5,
|
fillWeight: 3.5,
|
||||||
stroke: nodeBorder,
|
stroke: stylesMap.get('stroke') || nodeBorder,
|
||||||
seed: handdrawnSeed,
|
seed: handdrawnSeed,
|
||||||
strokeWidth: 1.3,
|
strokeWidth: 1.3,
|
||||||
},
|
},
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
if (node.backgroundColor) {
|
|
||||||
result.fill = node.backgroundColor;
|
|
||||||
}
|
|
||||||
if (node.borderColor) {
|
|
||||||
result.stroke = node.borderColor;
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@@ -35,6 +35,7 @@ export const labelHelper = async (parent, node, _classes) => {
|
|||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
width: node.width || getConfig().flowchart.wrappingWidth,
|
width: node.width || getConfig().flowchart.wrappingWidth,
|
||||||
cssClasses: 'markdown-node-label',
|
cssClasses: 'markdown-node-label',
|
||||||
|
style: node.labelStyle,
|
||||||
});
|
});
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
|
Reference in New Issue
Block a user