Add support for classes

This commit is contained in:
yari-dewalt
2025-01-23 09:36:49 -08:00
parent 081681f05b
commit 41d7a549b0
4 changed files with 119 additions and 19 deletions

View File

@@ -37,6 +37,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
"{" return 'STRUCT_START'; "{" return 'STRUCT_START';
"}" return 'STRUCT_STOP'; "}" return 'STRUCT_STOP';
":"{3} return 'STYLE_SEPARATOR';
":" return 'COLONSEP'; ":" return 'COLONSEP';
"id" return 'ID'; "id" return 'ID';
@@ -77,13 +78,16 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<style>\w+ return 'ALPHA'; <style>\w+ return 'ALPHA';
<style>":" return 'COLON'; <style>":" return 'COLON';
<style>";" return 'SEMICOLON'; <style>";" return 'SEMICOLON';
<style>"," return 'COMMA';
<style>"%" return 'PERCENT'; <style>"%" return 'PERCENT';
<style>"-" return 'MINUS'; <style>"-" return 'MINUS';
<style>"#" return 'BRKT'; <style>"#" return 'BRKT';
<style>" " /* skip spaces */ <style>" " /* skip spaces */
<style>["] { this.begin("string"); }
<style>\n { this.popState(); } <style>\n { this.popState(); }
"classDef" { this.begin("style"); return 'CLASSDEF'; }
"class" { this.begin("style"); return 'CLASS'; }
"<-" return 'END_ARROW_L'; "<-" return 'END_ARROW_L';
"->" {return 'END_ARROW_R';} "->" {return 'END_ARROW_R';}
"-" {return 'LINE';} "-" {return 'LINE';}
@@ -92,10 +96,11 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<string>["] { this.popState(); } <string>["] { this.popState(); }
<string>[^"]* { return "qString"; } <string>[^"]* { return "qString"; }
[\w][^\r\n\{\<\>\-\=]* { yytext = yytext.trim(); return 'unqString';} [\w][^:,\r\n\{\<\>\-\=]* { yytext = yytext.trim(); return 'unqString';}
<*>\w+ return 'ALPHA'; <*>\w+ return 'ALPHA';
<*>[0-9]+ return 'NUM'; <*>[0-9]+ return 'NUM';
<*>"," return 'COMMA';
/lex /lex
@@ -122,6 +127,8 @@ diagram
| directive diagram | directive diagram
| direction diagram | direction diagram
| styleStatement diagram | styleStatement diagram
| classDefStatement diagram
| classStatement diagram
| NEWLINE diagram | NEWLINE diagram
; ;
@@ -137,8 +144,9 @@ direction
; ;
requirementDef requirementDef
: requirementType requirementName STRUCT_START NEWLINE requirementBody : requirementType requirementName STRUCT_START NEWLINE requirementBody { yy.addRequirement($2, $1) }
{ yy.addRequirement($2, $1) }; | requirementType requirementName STYLE_SEPARATOR idList STRUCT_START NEWLINE requirementBody { yy.addRequirement($2, $1); yy.setClass([$2], $4); }
;
requirementBody requirementBody
: ID COLONSEP id NEWLINE requirementBody : ID COLONSEP id NEWLINE requirementBody
@@ -182,8 +190,9 @@ verifyType
{ $$=yy.VerifyType.VERIFY_TEST;}; { $$=yy.VerifyType.VERIFY_TEST;};
elementDef elementDef
: ELEMENT elementName STRUCT_START NEWLINE elementBody : ELEMENT elementName STRUCT_START NEWLINE elementBody { yy.addElement($2) }
{ yy.addElement($2) }; | ELEMENT elementName STYLE_SEPARATOR idList STRUCT_START NEWLINE elementBody { yy.addElement($2); yy.setClass([$2], $4); }
;
elementBody elementBody
: TYPE COLONSEP type NEWLINE elementBody : TYPE COLONSEP type NEWLINE elementBody
@@ -215,8 +224,24 @@ relationship
| TRACES | TRACES
{ $$=yy.Relationships.TRACES;}; { $$=yy.Relationships.TRACES;};
classDefStatement
: CLASSDEF idList stylesOpt {$$ = $CLASSDEF;yy.defineClass($idList,$stylesOpt);}
;
classStatement
: CLASS idList idList {yy.setClass($2, $3);}
| id STYLE_SEPARATOR idList {yy.setClass([$1], $3);}
;
idList
: ALPHA { $$ = [$ALPHA]; }
| idList COMMA ALPHA = { $$ = $idList.concat([$ALPHA]); }
| id { $$ = [$id]; }
| idList COMMA id = { $$ = $idList.concat([$id]); }
;
styleStatement styleStatement
: STYLE ALPHA stylesOpt {$$ = $STYLE;yy.setCssStyle($2,$stylesOpt);} : STYLE idList stylesOpt {$$ = $STYLE;yy.setCssStyle($2,$stylesOpt);}
; ;
stylesOpt stylesOpt

View File

@@ -16,6 +16,7 @@ import type {
Relation, Relation,
RelationshipType, RelationshipType,
Requirement, Requirement,
RequirementClass,
RequirementType, RequirementType,
RiskLevel, RiskLevel,
VerifyType, VerifyType,
@@ -67,6 +68,7 @@ const getInitialRequirement = (): Requirement => ({
name: '', name: '',
type: RequirementType.REQUIREMENT as RequirementType, type: RequirementType.REQUIREMENT as RequirementType,
cssStyles: [], cssStyles: [],
classes: ['default'],
}); });
const getInitialElement = (): Element => ({ const getInitialElement = (): Element => ({
@@ -74,6 +76,7 @@ const getInitialElement = (): Element => ({
type: '', type: '',
docRef: '', docRef: '',
cssStyles: [], cssStyles: [],
classes: ['default'],
}); });
// Update initial declarations // Update initial declarations
@@ -82,6 +85,7 @@ let latestRequirement: Requirement = getInitialRequirement();
let requirements = new Map<string, Requirement>(); let requirements = new Map<string, Requirement>();
let latestElement: Element = getInitialElement(); let latestElement: Element = getInitialElement();
let elements = new Map<string, Element>(); let elements = new Map<string, Element>();
let classes = new Map<string, RequirementClass>();
// Add reset functions // Add reset functions
const resetLatestRequirement = () => { const resetLatestRequirement = () => {
@@ -102,6 +106,7 @@ const addRequirement = (name: string, type: RequirementType) => {
risk: latestRequirement.risk, risk: latestRequirement.risk,
verifyMethod: latestRequirement.verifyMethod, verifyMethod: latestRequirement.verifyMethod,
cssStyles: [], cssStyles: [],
classes: ['default'],
}); });
} }
resetLatestRequirement(); resetLatestRequirement();
@@ -142,6 +147,7 @@ const addElement = (name: string) => {
type: latestElement.type, type: latestElement.type,
docRef: latestElement.docRef, docRef: latestElement.docRef,
cssStyles: [], cssStyles: [],
classes: ['default'],
}); });
log.info('Added new element: ', name); log.info('Added new element: ', name);
} }
@@ -180,29 +186,84 @@ const clear = () => {
requirements = new Map(); requirements = new Map();
resetLatestElement(); resetLatestElement();
elements = new Map(); elements = new Map();
classes = new Map();
commonClear(); commonClear();
}; };
export const setCssStyle = function (id: string, styles: string[]) { export const setCssStyle = function (ids: string[], styles: string[]) {
const node = requirements.get(id) ?? elements.get(id); for (const id of ids) {
if (!styles || !node) { const node = requirements.get(id) ?? elements.get(id);
return; if (!styles || !node) {
} return;
for (const s of styles) { }
if (s.includes(',')) { for (const s of styles) {
node.cssStyles.push(...s.split(',')); if (s.includes(',')) {
} else { node.cssStyles.push(...s.split(','));
node.cssStyles.push(s); } else {
node.cssStyles.push(s);
}
} }
} }
}; };
export const setClass = function (ids: string[], classNames: string[]) {
for (const id of ids) {
const node = requirements.get(id) ?? elements.get(id);
if (node) {
for (const _class of classNames) {
node.classes.push(_class);
const styles = classes.get(_class)?.styles;
if (styles) {
node.cssStyles.push(...styles);
}
}
}
}
};
export const defineClass = function (ids: string[], style: string[]) {
for (const id of ids) {
let styleClass = classes.get(id);
if (styleClass === undefined) {
styleClass = { id, styles: [], textStyles: [] };
classes.set(id, styleClass);
}
if (style) {
style.forEach(function (s) {
if (/color/.exec(s)) {
const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill');
styleClass.textStyles.push(newStyle);
}
styleClass.styles.push(s);
});
}
requirements.forEach((value) => {
if (value.classes.includes(id)) {
value.cssStyles.push(...style.flatMap((s) => s.split(',')));
}
});
elements.forEach((value) => {
if (value.classes.includes(id)) {
value.cssStyles.push(...style.flatMap((s) => s.split(',')));
}
});
}
};
export const getClasses = () => {
return classes;
};
const getData = () => { const getData = () => {
const config = getConfig(); const config = getConfig();
const nodes: Node[] = []; const nodes: Node[] = [];
const edges: Edge[] = []; const edges: Edge[] = [];
for (const requirement of requirements.values()) { for (const requirement of requirements.values()) {
const node = requirement as unknown as Node; const node = requirement as unknown as Node;
node.cssStyles = requirement.cssStyles;
node.cssClasses = requirement.classes.join(' ');
node.shape = 'requirementBox'; node.shape = 'requirementBox';
node.look = config.look; node.look = config.look;
nodes.push(node); nodes.push(node);
@@ -213,6 +274,8 @@ const getData = () => {
node.shape = 'requirementBox'; node.shape = 'requirementBox';
node.look = config.look; node.look = config.look;
node.id = element.name; node.id = element.name;
node.cssStyles = element.cssStyles;
node.cssClasses = element.classes.join(' ');
nodes.push(node); nodes.push(node);
} }
@@ -270,5 +333,8 @@ export default {
getRelationships, getRelationships,
clear, clear,
setCssStyle, setCssStyle,
setClass,
defineClass,
getClasses,
getData, getData,
}; };

View File

@@ -18,6 +18,7 @@ export interface Requirement {
risk: RiskLevel; risk: RiskLevel;
verifyMethod: VerifyType; verifyMethod: VerifyType;
cssStyles: string[]; cssStyles: string[];
classes: string[];
} }
export type RelationshipType = export type RelationshipType =
@@ -40,4 +41,11 @@ export interface Element {
type: string; type: string;
docRef: string; docRef: string;
cssStyles: string[]; cssStyles: string[];
classes: string[];
}
export interface RequirementClass {
id: string;
styles: string[];
textStyles: string[];
} }

View File

@@ -1,4 +1,4 @@
import { updateNodeBounds } from './util.js'; import { getNodeClasses, 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 { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
@@ -22,11 +22,12 @@ export async function requirementBox<T extends SVGGraphicsElement>(
const PADDING = 20; const PADDING = 20;
const GAP = 20; const GAP = 20;
const isRequirementNode = 'verifyMethod' in node; const isRequirementNode = 'verifyMethod' in node;
const classes = getNodeClasses(node);
// Add outer g element // Add outer g element
const shapeSvg = parent const shapeSvg = parent
.insert('g') .insert('g')
.attr('class', '') .attr('class', classes)
.attr('id', node.domId ?? node.id); .attr('id', node.domId ?? node.id);
let typeHeight; let typeHeight;