mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-21 01:06:43 +02:00
Add styling support for requirement diagram and box shape
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
%x string
|
%x string
|
||||||
%x token
|
%x token
|
||||||
%x unqString
|
%x unqString
|
||||||
|
%x style
|
||||||
%x acc_title
|
%x acc_title
|
||||||
%x acc_descr
|
%x acc_descr
|
||||||
%x acc_descr_multiline
|
%x acc_descr_multiline
|
||||||
@@ -72,6 +73,17 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
"type" return 'TYPE';
|
"type" return 'TYPE';
|
||||||
"docref" return 'DOCREF';
|
"docref" return 'DOCREF';
|
||||||
|
|
||||||
|
"style" { this.begin("style"); return 'STYLE'; }
|
||||||
|
<style>\w+ return 'ALPHA';
|
||||||
|
<style>":" return 'COLON';
|
||||||
|
<style>";" return 'SEMICOLON';
|
||||||
|
<style>"," return 'COMMA';
|
||||||
|
<style>"%" return 'PERCENT';
|
||||||
|
<style>"-" return 'MINUS';
|
||||||
|
<style>"#" return 'BRKT';
|
||||||
|
<style>" " /* skip spaces */
|
||||||
|
<style>\n { this.popState(); }
|
||||||
|
|
||||||
"<-" return 'END_ARROW_L';
|
"<-" return 'END_ARROW_L';
|
||||||
"->" {return 'END_ARROW_R';}
|
"->" {return 'END_ARROW_R';}
|
||||||
"-" {return 'LINE';}
|
"-" {return 'LINE';}
|
||||||
@@ -82,6 +94,9 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
|
|
||||||
[\w][^\r\n\{\<\>\-\=]* { yytext = yytext.trim(); return 'unqString';}
|
[\w][^\r\n\{\<\>\-\=]* { yytext = yytext.trim(); return 'unqString';}
|
||||||
|
|
||||||
|
<*>\w+ return 'ALPHA';
|
||||||
|
<*>[0-9]+ return 'NUM';
|
||||||
|
|
||||||
/lex
|
/lex
|
||||||
|
|
||||||
%start start
|
%start start
|
||||||
@@ -106,7 +121,9 @@ diagram
|
|||||||
| relationshipDef diagram
|
| relationshipDef diagram
|
||||||
| directive diagram
|
| directive diagram
|
||||||
| direction diagram
|
| direction diagram
|
||||||
| NEWLINE diagram;
|
| styleStatement diagram
|
||||||
|
| NEWLINE diagram
|
||||||
|
;
|
||||||
|
|
||||||
direction
|
direction
|
||||||
: direction_tb
|
: direction_tb
|
||||||
@@ -198,6 +215,22 @@ relationship
|
|||||||
| TRACES
|
| TRACES
|
||||||
{ $$=yy.Relationships.TRACES;};
|
{ $$=yy.Relationships.TRACES;};
|
||||||
|
|
||||||
|
styleStatement
|
||||||
|
: STYLE ALPHA stylesOpt {$$ = $STYLE;yy.setCssStyle($2,$stylesOpt);}
|
||||||
|
;
|
||||||
|
|
||||||
|
stylesOpt
|
||||||
|
: style {$$ = [$style]}
|
||||||
|
| stylesOpt COMMA style {$stylesOpt.push($style);$$ = $stylesOpt;}
|
||||||
|
;
|
||||||
|
|
||||||
|
style
|
||||||
|
: styleComponent
|
||||||
|
| style styleComponent {$$ = $style + $styleComponent;}
|
||||||
|
;
|
||||||
|
|
||||||
|
styleComponent: ALPHA | NUM | COLON | UNIT | SPACE | BRKT | PCT | MINUS | LABEL | SEMICOLON;
|
||||||
|
|
||||||
|
|
||||||
requirementName: unqString | qString;
|
requirementName: unqString | qString;
|
||||||
id : unqString | qString;
|
id : unqString | qString;
|
||||||
|
@@ -66,12 +66,14 @@ const getInitialRequirement = (): Requirement => ({
|
|||||||
verifyMethod: VerifyType.VERIFY_ANALYSIS as VerifyType,
|
verifyMethod: VerifyType.VERIFY_ANALYSIS as VerifyType,
|
||||||
name: '',
|
name: '',
|
||||||
type: RequirementType.REQUIREMENT as RequirementType,
|
type: RequirementType.REQUIREMENT as RequirementType,
|
||||||
|
cssStyles: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const getInitialElement = (): Element => ({
|
const getInitialElement = (): Element => ({
|
||||||
name: '',
|
name: '',
|
||||||
type: '',
|
type: '',
|
||||||
docRef: '',
|
docRef: '',
|
||||||
|
cssStyles: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update initial declarations
|
// Update initial declarations
|
||||||
@@ -99,6 +101,7 @@ const addRequirement = (name: string, type: RequirementType) => {
|
|||||||
text: latestRequirement.text,
|
text: latestRequirement.text,
|
||||||
risk: latestRequirement.risk,
|
risk: latestRequirement.risk,
|
||||||
verifyMethod: latestRequirement.verifyMethod,
|
verifyMethod: latestRequirement.verifyMethod,
|
||||||
|
cssStyles: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
resetLatestRequirement();
|
resetLatestRequirement();
|
||||||
@@ -138,6 +141,7 @@ const addElement = (name: string) => {
|
|||||||
name,
|
name,
|
||||||
type: latestElement.type,
|
type: latestElement.type,
|
||||||
docRef: latestElement.docRef,
|
docRef: latestElement.docRef,
|
||||||
|
cssStyles: [],
|
||||||
});
|
});
|
||||||
log.info('Added new element: ', name);
|
log.info('Added new element: ', name);
|
||||||
}
|
}
|
||||||
@@ -179,6 +183,20 @@ const clear = () => {
|
|||||||
commonClear();
|
commonClear();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setCssStyle = function (id: string, styles: string[]) {
|
||||||
|
const node = requirements.get(id) ?? elements.get(id);
|
||||||
|
if (!styles || !node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const s of styles) {
|
||||||
|
if (s.includes(',')) {
|
||||||
|
node.cssStyles.push(...s.split(','));
|
||||||
|
} else {
|
||||||
|
node.cssStyles.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getData = () => {
|
const getData = () => {
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
const nodes: Node[] = [];
|
const nodes: Node[] = [];
|
||||||
@@ -251,5 +269,6 @@ export default {
|
|||||||
addRelationship,
|
addRelationship,
|
||||||
getRelationships,
|
getRelationships,
|
||||||
clear,
|
clear,
|
||||||
|
setCssStyle,
|
||||||
getData,
|
getData,
|
||||||
};
|
};
|
||||||
|
@@ -17,6 +17,7 @@ export interface Requirement {
|
|||||||
text: string;
|
text: string;
|
||||||
risk: RiskLevel;
|
risk: RiskLevel;
|
||||||
verifyMethod: VerifyType;
|
verifyMethod: VerifyType;
|
||||||
|
cssStyles: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RelationshipType =
|
export type RelationshipType =
|
||||||
@@ -38,4 +39,5 @@ export interface Element {
|
|||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
docRef: string;
|
docRef: string;
|
||||||
|
cssStyles: string[];
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { updateNodeBounds } from './util.js';
|
import { updateNodeBounds } from './util.js';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
import type { Node } from '../../types.js';
|
import type { Node } from '../../types.js';
|
||||||
import { userNodeOverrides } from './handDrawnShapeStyles.js';
|
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
|
||||||
import rough from 'roughjs';
|
import rough from 'roughjs';
|
||||||
import type { D3Selection } from '../../../types.js';
|
import type { D3Selection } from '../../../types.js';
|
||||||
import { calculateTextWidth, decodeEntities } from '../../../utils.js';
|
import { calculateTextWidth, decodeEntities } from '../../../utils.js';
|
||||||
@@ -14,12 +14,14 @@ export async function requirementBox<T extends SVGGraphicsElement>(
|
|||||||
parent: D3Selection<T>,
|
parent: D3Selection<T>,
|
||||||
node: Node
|
node: Node
|
||||||
) {
|
) {
|
||||||
|
const { labelStyles, nodeStyles } = styles2String(node);
|
||||||
|
node.labelStyle = labelStyles;
|
||||||
const requirementNode = node as unknown as Requirement;
|
const requirementNode = node as unknown as Requirement;
|
||||||
const elementNode = node as unknown as Element;
|
const elementNode = node as unknown as Element;
|
||||||
const config = getConfig().requirement;
|
const config = getConfig().requirement;
|
||||||
const PADDING = 20;
|
const PADDING = 20;
|
||||||
const GAP = 20;
|
const GAP = 20;
|
||||||
const isRequirementNode = 'id' in node;
|
const isRequirementNode = 'verifyMethod' in node;
|
||||||
|
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = parent
|
const shapeSvg = parent
|
||||||
@@ -29,36 +31,68 @@ export async function requirementBox<T extends SVGGraphicsElement>(
|
|||||||
|
|
||||||
let typeHeight;
|
let typeHeight;
|
||||||
if (isRequirementNode) {
|
if (isRequirementNode) {
|
||||||
typeHeight = await addText(shapeSvg, `<<${requirementNode.type}>>`, 0);
|
typeHeight = await addText(
|
||||||
|
shapeSvg,
|
||||||
|
`<<${requirementNode.type}>>`,
|
||||||
|
0,
|
||||||
|
node.labelStyle
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
typeHeight = await addText(shapeSvg, '<<Element>>', 0);
|
typeHeight = await addText(shapeSvg, '<<Element>>', 0, node.labelStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
let accumulativeHeight = typeHeight;
|
let accumulativeHeight = typeHeight;
|
||||||
const nameHeight = await addText(shapeSvg, requirementNode.name, accumulativeHeight);
|
const nameHeight = await addText(
|
||||||
|
shapeSvg,
|
||||||
|
requirementNode.name,
|
||||||
|
accumulativeHeight,
|
||||||
|
node.labelStyle
|
||||||
|
);
|
||||||
accumulativeHeight += nameHeight + GAP;
|
accumulativeHeight += nameHeight + GAP;
|
||||||
|
|
||||||
// Requirement
|
// Requirement
|
||||||
if (isRequirementNode) {
|
if (isRequirementNode) {
|
||||||
const idHeight = await addText(shapeSvg, `Id: ${requirementNode.id}`, accumulativeHeight);
|
const idHeight = await addText(
|
||||||
|
shapeSvg,
|
||||||
|
`Id: ${requirementNode.id}`,
|
||||||
|
accumulativeHeight,
|
||||||
|
node.labelStyle
|
||||||
|
);
|
||||||
accumulativeHeight += idHeight;
|
accumulativeHeight += idHeight;
|
||||||
const textHeight = await addText(shapeSvg, `Text: ${requirementNode.text}`, accumulativeHeight);
|
const textHeight = await addText(
|
||||||
|
shapeSvg,
|
||||||
|
`Text: ${requirementNode.text}`,
|
||||||
|
accumulativeHeight,
|
||||||
|
node.labelStyle
|
||||||
|
);
|
||||||
accumulativeHeight += textHeight;
|
accumulativeHeight += textHeight;
|
||||||
const riskHeight = await addText(shapeSvg, `Risk: ${requirementNode.risk}`, accumulativeHeight);
|
const riskHeight = await addText(
|
||||||
|
shapeSvg,
|
||||||
|
`Risk: ${requirementNode.risk}`,
|
||||||
|
accumulativeHeight,
|
||||||
|
node.labelStyle
|
||||||
|
);
|
||||||
accumulativeHeight += riskHeight;
|
accumulativeHeight += riskHeight;
|
||||||
await addText(shapeSvg, `Verification: ${requirementNode.verifyMethod}`, accumulativeHeight);
|
await addText(
|
||||||
|
shapeSvg,
|
||||||
|
`Verification: ${requirementNode.verifyMethod}`,
|
||||||
|
accumulativeHeight,
|
||||||
|
node.labelStyle
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Element
|
// Element
|
||||||
const typeHeight = await addText(
|
const typeHeight = await addText(
|
||||||
shapeSvg,
|
shapeSvg,
|
||||||
`Type: ${elementNode.type ? elementNode.type : 'Not specified'}`,
|
`Type: ${elementNode.type ? elementNode.type : 'Not specified'}`,
|
||||||
accumulativeHeight
|
accumulativeHeight,
|
||||||
|
node.labelStyle
|
||||||
);
|
);
|
||||||
accumulativeHeight += typeHeight;
|
accumulativeHeight += typeHeight;
|
||||||
await addText(
|
await addText(
|
||||||
shapeSvg,
|
shapeSvg,
|
||||||
`Doc Ref: ${elementNode.docRef ? elementNode.docRef : 'None'}`,
|
`Doc Ref: ${elementNode.docRef ? elementNode.docRef : 'None'}`,
|
||||||
accumulativeHeight
|
accumulativeHeight,
|
||||||
|
node.labelStyle
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +118,7 @@ export async function requirementBox<T extends SVGGraphicsElement>(
|
|||||||
const roughRect = rc.rectangle(x, y, totalWidth, totalHeight, options);
|
const roughRect = rc.rectangle(x, y, totalWidth, totalHeight, options);
|
||||||
|
|
||||||
const rect = shapeSvg.insert(() => roughRect, ':first-child');
|
const rect = shapeSvg.insert(() => roughRect, ':first-child');
|
||||||
rect.attr('class', 'basic label-container');
|
rect.attr('class', 'basic label-container').attr('style', nodeStyles);
|
||||||
|
|
||||||
// Re-translate labels now that rect is centered
|
// Re-translate labels now that rect is centered
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -123,7 +157,8 @@ export async function requirementBox<T extends SVGGraphicsElement>(
|
|||||||
y + typeHeight + nameHeight + GAP,
|
y + typeHeight + nameHeight + GAP,
|
||||||
options
|
options
|
||||||
);
|
);
|
||||||
shapeSvg.insert(() => roughLine);
|
const dividerLine = shapeSvg.insert(() => roughLine);
|
||||||
|
dividerLine.attr('style', nodeStyles);
|
||||||
|
|
||||||
updateNodeBounds(node, rect);
|
updateNodeBounds(node, rect);
|
||||||
|
|
||||||
@@ -138,9 +173,9 @@ async function addText<T extends SVGGraphicsElement>(
|
|||||||
parentGroup: D3Selection<T>,
|
parentGroup: D3Selection<T>,
|
||||||
inputText: string,
|
inputText: string,
|
||||||
yOffset: number,
|
yOffset: number,
|
||||||
styles: string[] = []
|
style = ''
|
||||||
) {
|
) {
|
||||||
const textEl = parentGroup.insert('g').attr('class', 'label').attr('style', styles.join('; '));
|
const textEl = parentGroup.insert('g').attr('class', 'label').attr('style', style);
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
const useHtmlLabels = config.htmlLabels ?? true;
|
const useHtmlLabels = config.htmlLabels ?? true;
|
||||||
|
|
||||||
@@ -151,6 +186,7 @@ async function addText<T extends SVGGraphicsElement>(
|
|||||||
width: calculateTextWidth(inputText, config) + 50, // Add room for error when splitting text into multiple lines
|
width: calculateTextWidth(inputText, config) + 50, // Add room for error when splitting text into multiple lines
|
||||||
classes: 'markdown-node-label',
|
classes: 'markdown-node-label',
|
||||||
useHtmlLabels,
|
useHtmlLabels,
|
||||||
|
style,
|
||||||
},
|
},
|
||||||
config
|
config
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user