Merge branch 'alanaV11' into pebr/neo-style

This commit is contained in:
Per Brolin
2024-06-18 13:02:22 +02:00
17 changed files with 10369 additions and 8259 deletions

View File

@@ -110,6 +110,7 @@ strikethrough
stringifying
struct
STYLECLASS
STYLEDEF
STYLEOPTS
subcomponent
subcomponents

View File

@@ -558,6 +558,29 @@ stateDiagram-v2
{ 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', () => {
imgSnapshotTest(

View File

@@ -75,295 +75,45 @@
</style>
</head>
<body>
<pre id="diagram" class="mermaid2">
---
config:
theme: neo
look: neo
layout: elk
elk.mergeEdges: true
themeVariables: {}
---
stateDiagram
direction TB
A --> B
A --> C
A --> D
A --> E
A --> F
</pre
>
<pre id="diagram" class="mermaid2">
---
config:
theme: default
look: classic
layout: elk
---
flowchart TD
subgraph subgraph_ao83d50qa["Subgraph"]
C("Let me think")
end
A["Easter"] -- Get eggs --> B("Go shopping")
B o--o C
B --> H["H"]
C -- One --> D["Laptop"]
C -- Two --> E["iPhone"]
C -- Three --> F["fa:fa-car Car"]
C --> G["G"]
G --> H
H --> C & E
D --> E
F --> E
style B stroke:#FF6D00,stroke-width:4px,stroke-dasharray: 0,fill:#FFFFFF
</pre
>
<pre id="diagram" class="mermaid2">
---
config:
theme: default
look: classic
layout: elk
---
flowchart TD
subgraph subgraph_ao83d50qa["Subgraph1"]
C("Let me think")
end
A["Easter"] -- Get eggs --> B("Go shopping")
B o--o C
B --> H["H"]
C -- One --> D["Laptop"]
C -- Two --> E["iPhone"]
C -- Three --> F["fa:fa-car Car"]
C --> G["G"]
G --> H
H --> C & E
D --> E
F --> E
style B stroke:#FF6D00,stroke-width:4px,stroke-dasharray: 0,fill:#FFFFFF
</pre
>
<pre id="diagram" class="mermaid">
flowchart RL
subgraph Apa["Apa"]
subgraph Gorilla
A["Start"]
B["This is B"]
end
end
A --> B & C["C"]
Gorilla --> C
flowchart
a_a(Aftonbladet) --> b_b[gorilla]:::apa --> c_c{chimp}:::apa -->a_a
a_a --> c --> d_d --> c_c
classDef apa fill:#f9f,stroke:#333,stroke-width:4px;
class a_a apa;
click a_a "http://www.aftonbladet.se" "bookmark"
click c_c callback "new tooltip"
</pre
>
<pre id="diagram" class="mermaid2">
<pre id="diagram2" class="mermaid">
flowchart LR
subgraph Apa["Apa"]
subgraph Gorilla
A["Start"]
B["This is B"]
end
end
A --> B & C["C"]
Apa --> C
</pre
>
<pre id="diagram" class="mermaid2">
flowchart LR
subgraph Apa["Apa"]
subgraph Gorilla
B["This is B"]
A["Start"]
end
end
Apa --> C
A --> B & C["C"]
</pre
>
<pre id="diagram" class="mermaid">
stateDiagram
direction LR
state Gorilla0 {
state Apa0 {
A0 --> B0
}
id1(Start)-->id2(Stop)
style id1 fill:#f9f,stroke:#333,stroke-width:4px
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
}
Apa0 --> C0
A0 --> C0
C1: "`This is C`"
</pre
>
<pre id="diagram" class="mermaid2">
flowchart LR
subgraph Gorilla
subgraph Apa
A[A] --- B
end
end
Apa --- C
A --x C
</pre>
<pre id="diagram" class="mermaid2">
---
config:
look: neo
theme: neo
layout: elk
elk.mergeEdges: true
themeVariables: {}
---
%% 'elk.stress',
%% 'elk.force'
%%'elk.mrtree'
%% 'elk.sporeOverlap
stateDiagram
direction TB
A --> B
A --> C
A --> D
A --> E
A --> F
state F {
Another
}
Another --> A
<pre id="diagram3" class="mermaid">
flowchart LR
A:::foo & B:::bar --> C:::foobar
classDef foo stroke:#f00
classDef bar stroke:#0f0
classDef ash color:red
class C ash
style C stroke:#00f, fill:black
</pre>
</pre
>
<pre id="diagram" class="mermaid">
flowchart LR
Apa --Hello--> C
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "dagre", "mergeEdges": false} }%%
flowchart LR
A ==> B(This is B)
A[Start] --> B(Is it?)
B -- Yes --> C[OK]
C --> D[Rethink]
D --> B
B -. No ...-> E[End]
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "elk", "mergeEdges": true} }%%
flowchart
A --> B(This is B)
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "elk", "mergeEdges": false, "elk.nodePlacement.strategy": "SIMPLE"} }%%
<pre id="diagram4" class="mermaid">
stateDiagram
state if_state &lt;&lt;choice&gt;&gt;
[*] --> IsPositive
IsPositive --> if_state
if_state --> False: if n < 0
if_state --> True : if n >= 0
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "elk", "mergeEdges": false, "elk.nodePlacement.strategy": "SIMPLE"} }%%
stateDiagram
state if_state &lt;&lt;choice&gt;&gt;
[*] --> IsPositive
IsPositive --> if_state
if_state --> False: if n < 0
if_state --> True : if n >= 0
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "dagre", "mergeEdges": true} }%%
stateDiagram
direction TB
State T1 {
T12--> T11
}
T1 --> T2
T11 --> T2
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "dagre", "mergeEdges": true} }%%
stateDiagram
State T1 {
T21
--
T22
}
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layouts": "elk2", "mergeEdges": true} }%%
stateDiagram
State S1 {
direction TB
S11
}
S1 --> S2
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "elk", "mergeEdges": true} }%%
stateDiagram
State T1 {
T21
--
T22
}
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "elk", "mergeEdges": true} }%%
stateDiagram
[*] --> T1
T1 --> T2
T1 --> T3
T1 --> T4
</pre
>
<pre id="diagram" class="mermaid2">
%%{init: {"layout": "elk"} }%%
stateDiagram
[*] --> T1
T1 --> T2
T2 --> T3
T3 --> T1
T1 --> T3
</pre
>
<pre id="diagram" class="mermaid2">
stateDiagram
State1: The state with a note
note right of State1
Important information! You can write
notes.
end note
</pre
>
<pre id="diagram" class="mermaid2">
stateDiagram-v2
direction LR
[*] --> Active
A:::foo
B:::bar --> C:::foobar
classDef foo stroke:#f00
classDef bar stroke:#0f0
style C stroke:#00f, fill:black, color:white
state Active {
direction BT
[*] --> Inner
Inner --> NumLockOn : EvNumLockPressed
}
%% Outer --> Inner
</pre
>
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
@@ -372,10 +122,13 @@ stateDiagram-v2
mermaid.parseError = function (err, hash) {
console.error('Mermaid error: ', err);
};
window.callback = function () {
alert('A callback was triggered');
};
mermaid.initialize({
theme: 'neo',
// handdrawnSeed: 12,
look: 'neo',
// look: 'handdrawn',
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
// layout: 'dagre',
// layout: 'elk',
@@ -393,6 +146,7 @@ stateDiagram-v2
},
fontSize: 12,
logLevel: 0,
securityLevel: 'loose',
});
function callback() {
alert('It worked');

View File

@@ -806,7 +806,8 @@ const addNodeFromVertex = (
labelStyle: '',
parentId,
padding: config.flowchart?.padding || 8,
cssStyles: vertex.styles.join(' '),
cssStyles: vertex.styles,
cssCompiledStyles: getCompiledStyles(vertex.classes),
cssClasses: vertex.classes.join(' '),
shape: getTypeFromVertex(vertex),
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 = () => {
const config = getConfig();
const nodes: Node[] = [];
@@ -844,7 +861,7 @@ export const getData = () => {
labelStyle: '',
parentId: parentDB.get(subGraph.id),
padding: config.flowchart?.padding || 8,
cssStyles: '',
cssStyles: [],
cssClasses: '',
shape: 'rect',
dir: subGraph.dir,

View File

@@ -1,8 +1,8 @@
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js';
// import flowRendererV3 from './flowRenderer-v3-unified.js';
//import flowRendererV2 from './flowRenderer-v2.js';
import flowRendererV3 from './flowRenderer-v3-unified.js';
import flowStyles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
@@ -10,8 +10,8 @@ import { setConfig } from '../../diagram-api/diagramAPI.js';
export const diagram = {
parser: flowParser,
db: flowDb,
renderer: flowRendererV2,
// renderer: flowRendererV3,
// renderer: flowRendererV2,
renderer: flowRendererV3,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
if (!cnf.flowchart) {
@@ -20,8 +20,8 @@ export const diagram = {
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
// flowchart-v2 uses dagre-wrapper, which doesn't have access to flowchart cnf
setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
flowRendererV2.setConf(cnf.flowchart);
// flowRendererV3.setConf(cnf.flowchart);
// flowRendererV2.setConf(cnf.flowchart);
flowRendererV3.setConf(cnf.flowchart);
flowDb.clear();
flowDb.setGen('gen-2');
},

View File

@@ -38,11 +38,11 @@ const getStyles = (options: FlowChartStyleOptions) =>
.cluster-label text {
fill: ${options.titleColor};
}
.cluster-label span,p {
.cluster-label span {
color: ${options.titleColor};
}
.label text,span,p {
.label text,span {
fill: ${options.nodeTextColor || options.textColor};
color: ${options.nodeTextColor || options.textColor};
}
@@ -119,7 +119,7 @@ const getStyles = (options: FlowChartStyleOptions) =>
fill: ${options.titleColor};
}
.cluster span,p {
.cluster span {
color: ${options.titleColor};
}
/* .cluster div {

View File

@@ -54,21 +54,39 @@ export function stateDomId(itemId = '', counter = 0, type = '', typeSpacer = DOM
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;
log.trace('items', doc);
doc.forEach((item) => {
switch (item.stmt) {
case STMT_STATE:
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look);
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes);
break;
case DEFAULT_STATE_TYPE:
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look);
dataFetcher(parentParsedItem, item, diagramStates, nodes, edges, altFlag, look, classes);
break;
case STMT_RELATION:
{
dataFetcher(parentParsedItem, item.state1, diagramStates, nodes, edges, altFlag, look);
dataFetcher(parentParsedItem, item.state2, diagramStates, nodes, edges, altFlag, look);
dataFetcher(
parentParsedItem,
item.state1,
diagramStates,
nodes,
edges,
altFlag,
look,
classes
);
dataFetcher(
parentParsedItem,
item.state2,
diagramStates,
nodes,
edges,
altFlag,
look,
classes
);
const edgeData = {
id: 'edge' + graphItemCount,
start: item.state1.id,
@@ -134,8 +152,9 @@ let cssClasses = newClassesList(); // style classes defined by a classDef
*
* @param nodes
* @param nodeData
* @param classes
*/
function insertOrUpdateNode(nodes, nodeData) {
function insertOrUpdateNode(nodes, nodeData, classes) {
if (!nodeData.id || nodeData.id === '</join></fork>' || nodeData.id === '</choice>') {
return;
}
@@ -143,29 +162,62 @@ function insertOrUpdateNode(nodes, nodeData) {
//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:', '');
}
if (classes.get(cssClass)) {
const classDef = classes.get(cssClass);
// classDef.styles.forEach((style) => {
// nodeData.cssCompiledStyles += 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:', '');
}
});
// nodeData.cssStyles += style + ';';
// });
// classDef.textStyles.forEach((style) => {
// nodeData.labelStyle += style + ';';
// 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);
if (existingNodeData) {
@@ -197,7 +249,7 @@ function getClassesFromDbInfo(dbInfoItem) {
}
//add comma for all other classes
else {
classStr += dbInfoItem.classes[i] + ' ';
classStr += dbInfoItem.classes[i] + ',';
}
}
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 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') {
let shape = SHAPE_STATE;
@@ -229,6 +313,7 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
shape,
description: common.sanitizeText(itemId, getConfig()),
cssClasses: `${classStr} ${CSS_DIAGRAM_STATE}`,
cssStyles: style,
};
}
@@ -287,7 +372,8 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
shape: newNode.shape,
label: newNode.description,
cssClasses: newNode.cssClasses,
cssStyles: '',
cssCompiledStyles: [],
cssStyles: newNode.cssStyles,
id: itemId,
dir: newNode.dir,
domId: stateDomId(itemId, graphItemCount),
@@ -319,7 +405,8 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
label: parsedItem.note.text,
cssClasses: CSS_DIAGRAM_NOTE,
// useHtmlLabels: false,
cssStyles: '', // styles.style,
cssStyles: [],
cssCompilesStyles: [],
id: itemId + NOTE_ID + '-' + graphItemCount,
domId: stateDomId(itemId, graphItemCount, NOTE),
type: newNode.type,
@@ -334,7 +421,7 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
shape: SHAPE_NOTEGROUP,
label: parsedItem.note.text,
cssClasses: newNode.cssClasses,
cssStyles: '', // styles.style,
cssStyles: [],
id: itemId + PARENT_ID,
domId: stateDomId(itemId, graphItemCount, PARENT),
type: 'group',
@@ -352,11 +439,11 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
nodeData.parentId = parentNodeId;
//insert groupData
insertOrUpdateNode(nodes, groupData);
insertOrUpdateNode(nodes, groupData, classes);
//insert noteData
insertOrUpdateNode(nodes, noteData);
insertOrUpdateNode(nodes, noteData, classes);
//insert nodeData
insertOrUpdateNode(nodes, nodeData);
insertOrUpdateNode(nodes, nodeData, classes);
let from = itemId;
let to = noteData.id;
@@ -382,12 +469,12 @@ export const dataFetcher = (parent, parsedItem, diagramStates, nodes, edges, alt
look,
});
} else {
insertOrUpdateNode(nodes, nodeData);
insertOrUpdateNode(nodes, nodeData, classes);
}
}
if (parsedItem.doc) {
log.trace('Adding nodes children ');
setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, look);
setupDoc(parsedItem, parsedItem.doc, diagramStates, nodes, edges, !altFlag, look, classes);
}
};

View File

@@ -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',
]);
});
});
});
});

View File

@@ -27,6 +27,13 @@
%x CLASSDEFID
%x CLASS
%x CLASS_STYLE
// Style statement states
%x STYLE
%x STYLE_IDS
%x STYLEDEF_STYLES
%x STYLEDEF_STYLEOPTS
%x NOTE
%x NOTE_ID
%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_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>\d+ return 'WIDTH';
<SCALE>\s+"width" {this.popState();}
@@ -168,6 +179,7 @@ line
statement
: classDefStatement
| styleStatement
| cssClassStatement
| idStatement { /* console.log('got id', $1); */
$$=$1;
@@ -246,6 +258,12 @@ classDefStatement
}
;
styleStatement
: style STYLE_IDS STYLEDEF_STYLEOPTS {
$$ = { stmt: 'style', id: $2.trim(), styleClass: $3.trim() };
}
;
cssClassStatement
: class CLASSENTITY_IDS STYLECLASS {
//console.log('apply class: id(s): ',$2, ' style class: ', $3);

View File

@@ -14,6 +14,7 @@ export const STMT_STATE = 'state';
export const STMT_RELATION = 'relation';
// parsed statement type for a classDef
export const STMT_CLASSDEF = 'classDef';
export const STMT_STYLEDEF = 'style';
// parsed statement type for applyClass
export const STMT_APPLYCLASS = 'applyClass';
@@ -64,6 +65,7 @@ export default {
STMT_STATE,
STMT_RELATION,
STMT_CLASSDEF,
STMT_STYLEDEF,
STMT_APPLYCLASS,
DEFAULT_STATE_TYPE,
DIVIDER_TYPE,

View File

@@ -18,6 +18,7 @@ import {
STMT_STATE,
STMT_RELATION,
STMT_CLASSDEF,
STMT_STYLEDEF,
STMT_APPLYCLASS,
DEFAULT_STATE_TYPE,
DIVIDER_TYPE,
@@ -171,9 +172,10 @@ const extract = (_doc) => {
log.info(doc);
clear(true);
log.info('Extract', doc);
log.info('Extract initial document:', doc);
doc.forEach((item) => {
log.warn('Statement', item.stmt);
switch (item.stmt) {
case STMT_STATE:
addState(
@@ -193,6 +195,18 @@ const extract = (_doc) => {
case STMT_CLASSDEF:
addStyleClass(item.id.trim(), item.classes);
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:
setCssClass(item.id.trim(), item.styleClass);
break;
@@ -203,7 +217,7 @@ const extract = (_doc) => {
const config = getConfig();
const look = config.look;
resetDataFetching();
dataFetcher(undefined, getRootDocV2(), diagramStates, nodes, edges, true, look);
dataFetcher(undefined, getRootDocV2(), diagramStates, nodes, edges, true, look, classes);
nodes.forEach((node) => {
if (Array.isArray(node.label)) {
// add the rest as description
@@ -538,7 +552,7 @@ export const setCssClass = function (itemIds, cssClassName) {
export const setStyle = function (itemId, styleText) {
const item = getState(itemId);
if (item !== undefined) {
item.textStyles.push(styleText);
item.styles.push(styleText);
}
};

View File

@@ -58,6 +58,7 @@ export const draw = async function (
// The getData method provided in all supported diagrams is used to extract the data from the parsed structure
// into the Layout data format
const data4Layout = diag.db.getData() as LayoutData;
// Create the root SVG - the element is the div containing the SVG element
const { element, svg } = getDiagramElements(id, securityLevel);

View File

@@ -198,7 +198,16 @@ export const createText = (
} = {},
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) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?

View File

@@ -2,13 +2,18 @@ import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
import intersect from '../intersect/index.js';
import type { Node, RectOptions } from '$root/rendering-util/types.d.ts';
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 { getConfig } from '$root/diagram-api/diagramAPI.js';
import { log } from '$root/logger.js';
export const drawRect = async (parent: SVGAElement, node: Node, options: RectOptions) => {
const { look } = getConfig();
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
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 y = -totalHeight / 2;
log.info('IPI node = ', node);
let rect;
let { rx, ry } = node;
const { cssStyles } = node;
@@ -62,7 +69,7 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt
rect
.attr('class', rectClass)
.attr('style', cssStyles)
.attr('style', nodeStyles)
.attr('rx', rx)
.attr('data-id', 'abc')
.attr('data-et', 'node')

View File

@@ -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
// TODO remove any
export const userNodeOverrides = (node: Node, options: any) => {
const { themeVariables, handdrawnSeed } = getConfig();
const { nodeBorder, mainBkg } = themeVariables;
const { stylesArray: styles, stylesMap } = compileStyles(node);
// index the style array to a map object
const result = Object.assign(
{
roughness: 0.7,
fill: mainBkg,
fill: stylesMap.get('fill') || mainBkg,
fillStyle: 'hachure', // solid fill
fillWeight: 3.5,
stroke: nodeBorder,
stroke: stylesMap.get('stroke') || nodeBorder,
seed: handdrawnSeed,
strokeWidth: 1.3,
},
options
);
if (node.backgroundColor) {
result.fill = node.backgroundColor;
}
if (node.borderColor) {
result.stroke = node.borderColor;
}
return result;
};

View File

@@ -35,6 +35,7 @@ export const labelHelper = async (parent, node, _classes) => {
useHtmlLabels,
width: node.width || getConfig().flowchart.wrappingWidth,
cssClasses: 'markdown-node-label',
style: node.labelStyle,
});
// Get the size of the label
let bbox = text.getBBox();

17931
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff