common consts; add consts in stateRenderer-v2 (will esp. make theme usage easier)
This commit is contained in:
parent
85ba4549fb
commit
589dd70356
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Constants common to all State Diagram code
|
||||
*/
|
||||
|
||||
// default diagram direction
|
||||
export const DEFAULT_DIAGRAM_DIRECTION = 'LR';
|
||||
|
||||
// default direction for any nested documents (composites)
|
||||
export const DEFAULT_NESTED_DOC_DIR = 'TB';
|
||||
|
||||
// parsed statement type for a state
|
||||
export const STMT_STATE = 'state';
|
||||
// parsed statement type for a relation
|
||||
export const STMT_RELATION = 'relation';
|
||||
// parsed statement type for a classDef
|
||||
export const STMT_CLASSDEF = 'classDef';
|
||||
// parsed statement type for applyClass
|
||||
export const STMT_APPLYCLASS = 'applyClass';
|
||||
|
||||
export const DEFAULT_STATE_TYPE = 'default';
|
||||
export const DIVIDER_TYPE = 'divider';
|
||||
|
||||
export default {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
DEFAULT_NESTED_DOC_DIR,
|
||||
STMT_STATE,
|
||||
STMT_RELATION,
|
||||
STMT_CLASSDEF,
|
||||
STMT_APPLYCLASS,
|
||||
DEFAULT_STATE_TYPE,
|
||||
DIVIDER_TYPE,
|
||||
};
|
|
@ -11,19 +11,27 @@ import {
|
|||
clear as commonClear,
|
||||
} from '../../commonDb';
|
||||
|
||||
const DEFAULT_DIRECTION = 'TB';
|
||||
import {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
STMT_STATE,
|
||||
STMT_RELATION,
|
||||
STMT_CLASSDEF,
|
||||
STMT_APPLYCLASS,
|
||||
DEFAULT_STATE_TYPE,
|
||||
DIVIDER_TYPE,
|
||||
} from './stateCommon';
|
||||
|
||||
const START_NODE = '[*]';
|
||||
const START_TYPE = 'start';
|
||||
const END_NODE = START_NODE;
|
||||
const END_TYPE = 'end';
|
||||
const DEFAULT_TYPE = 'default';
|
||||
|
||||
const COLOR_KEYWORD = 'color';
|
||||
const FILL_KEYWORD = 'fill';
|
||||
const BG_FILL = 'bgFill';
|
||||
const STYLECLASS_SEP = ',';
|
||||
|
||||
let direction = DEFAULT_DIRECTION;
|
||||
let direction = DEFAULT_DIAGRAM_DIRECTION;
|
||||
let rootDoc = [];
|
||||
let classes = []; // style classes defined by a classDef
|
||||
|
||||
|
@ -69,11 +77,11 @@ const setRootDoc = (o) => {
|
|||
const getRootDoc = () => rootDoc;
|
||||
|
||||
const docTranslator = (parent, node, first) => {
|
||||
if (node.stmt === 'relation') {
|
||||
if (node.stmt === STMT_RELATION) {
|
||||
docTranslator(parent, node.state1, true);
|
||||
docTranslator(parent, node.state2, false);
|
||||
} else {
|
||||
if (node.stmt === 'state') {
|
||||
if (node.stmt === STMT_STATE) {
|
||||
if (node.id === '[*]') {
|
||||
node.id = first ? parent.id + '_start' : parent.id + '_end';
|
||||
node.start = first;
|
||||
|
@ -86,7 +94,7 @@ const docTranslator = (parent, node, first) => {
|
|||
let currentDoc = [];
|
||||
let i;
|
||||
for (i = 0; i < node.doc.length; i++) {
|
||||
if (node.doc[i].type === 'divider') {
|
||||
if (node.doc[i].type === DIVIDER_TYPE) {
|
||||
// debugger;
|
||||
const newNode = clone(node.doc[i]);
|
||||
newNode.doc = clone(currentDoc);
|
||||
|
@ -100,7 +108,7 @@ const docTranslator = (parent, node, first) => {
|
|||
// If any divider was encountered
|
||||
if (doc.length > 0 && currentDoc.length > 0) {
|
||||
const newNode = {
|
||||
stmt: 'state',
|
||||
stmt: STMT_STATE,
|
||||
id: generateId(),
|
||||
type: 'divider',
|
||||
doc: clone(currentDoc),
|
||||
|
@ -149,7 +157,7 @@ const extract = (_doc) => {
|
|||
|
||||
doc.forEach((item) => {
|
||||
switch (item.stmt) {
|
||||
case 'state':
|
||||
case STMT_STATE:
|
||||
addState(
|
||||
item.id,
|
||||
item.type,
|
||||
|
@ -161,13 +169,13 @@ const extract = (_doc) => {
|
|||
item.textStyles
|
||||
);
|
||||
break;
|
||||
case 'relation':
|
||||
case STMT_RELATION:
|
||||
addRelation(item.state1, item.state2, item.description);
|
||||
break;
|
||||
case 'classDef':
|
||||
case STMT_CLASSDEF:
|
||||
addStyleClass(item.id, item.classes);
|
||||
break;
|
||||
case 'applyClass':
|
||||
case STMT_APPLYCLASS:
|
||||
setCssClass(item.id, item.styleClass);
|
||||
break;
|
||||
}
|
||||
|
@ -188,7 +196,7 @@ const extract = (_doc) => {
|
|||
*/
|
||||
export const addState = function (
|
||||
id,
|
||||
type = DEFAULT_TYPE,
|
||||
type = DEFAULT_STATE_TYPE,
|
||||
doc = null,
|
||||
descr = null,
|
||||
note = null,
|
||||
|
@ -309,7 +317,7 @@ function startIdIfNeeded(id = '') {
|
|||
* @param {string} type
|
||||
* @returns {string} - the type that should be used
|
||||
*/
|
||||
function startTypeIfNeeded(id = '', type = DEFAULT_TYPE) {
|
||||
function startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
|
||||
return id === START_NODE ? START_TYPE : type;
|
||||
}
|
||||
|
||||
|
@ -338,7 +346,7 @@ function endIdIfNeeded(id = '') {
|
|||
* @param {string} type
|
||||
* @returns {string} - the type that should be used
|
||||
*/
|
||||
function endTypeIfNeeded(id = '', type = DEFAULT_TYPE) {
|
||||
function endTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
|
||||
return id === END_NODE ? END_TYPE : type;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,58 @@ import { log } from '../../logger';
|
|||
import { configureSvgSize } from '../../setupGraphViewbox';
|
||||
import common from '../common/common';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
import {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
DEFAULT_NESTED_DOC_DIR,
|
||||
STMT_STATE,
|
||||
STMT_RELATION,
|
||||
DEFAULT_STATE_TYPE,
|
||||
DIVIDER_TYPE,
|
||||
} from './stateCommon';
|
||||
|
||||
const DEFAULT_DIR = 'TD';
|
||||
// --------------------------------------
|
||||
// Shapes
|
||||
const SHAPE_STATE = 'rect';
|
||||
const SHAPE_STATE_WITH_DESC = 'rectWithTitle';
|
||||
const SHAPE_START = 'start';
|
||||
const SHAPE_END = 'end';
|
||||
const SHAPE_DIVIDER = 'divider';
|
||||
const SHAPE_GROUP = 'roundedWithTitle';
|
||||
const SHAPE_NOTE = 'note';
|
||||
const SHAPE_NOTEGROUP = 'noteGroup';
|
||||
|
||||
// --------------------------------------
|
||||
// CSS classes
|
||||
const CSS_DIAGRAM = 'statediagram';
|
||||
const CSS_STATE = 'state';
|
||||
const CSS_DIAGRAM_STATE = `${CSS_DIAGRAM}-${CSS_STATE}`;
|
||||
const CSS_EDGE = 'transition';
|
||||
const CSS_NOTE = 'note';
|
||||
const CSS_NOTE_EDGE = 'note-edge';
|
||||
const CSS_EDGE_NOTE_EDGE = `${CSS_EDGE} ${CSS_NOTE_EDGE}`;
|
||||
const CSS_DIAGRAM_NOTE = `${CSS_DIAGRAM}-${CSS_NOTE}`;
|
||||
const CSS_CLUSTER = 'cluster';
|
||||
const CSS_DIAGRAM_CLUSTER = `${CSS_DIAGRAM}-${CSS_CLUSTER}`;
|
||||
const CSS_CLUSTER_ALT = 'cluster-alt';
|
||||
const CSS_DIAGRAM_CLUSTER_ALT = `${CSS_DIAGRAM}-${CSS_CLUSTER_ALT}`;
|
||||
|
||||
// --------------------------------------
|
||||
// DOM and element IDs
|
||||
const PARENT = 'parent';
|
||||
const NOTE = 'note';
|
||||
const DOMID_STATE = 'state';
|
||||
const DOMID_TYPE_SPACER = '----';
|
||||
const NOTE_ID = `${DOMID_TYPE_SPACER}${NOTE}`;
|
||||
const PARENT_ID = `${DOMID_TYPE_SPACER}${PARENT}`;
|
||||
// --------------------------------------
|
||||
// Graph edge settings
|
||||
const G_EDGE_STYLE = 'fill:none';
|
||||
const G_EDGE_ARROWHEADSTYLE = 'fill: #333';
|
||||
const G_EDGE_LABELPOS = 'c';
|
||||
const G_EDGE_LABELTYPE = 'text';
|
||||
const G_EDGE_THICKNESS = 'normal';
|
||||
|
||||
// --------------------------------------
|
||||
// When information is parsed and processed (extracted) by stateDb.extract()
|
||||
// These are globals so the information can be accessed as needed (e.g. in setUpNode, etc.)
|
||||
let diagramStates = [];
|
||||
|
@ -72,6 +121,21 @@ function getClassesFromDbInfo(dbInfoItem) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a graph node based on the statement information
|
||||
*
|
||||
|
@ -80,23 +144,16 @@ function getClassesFromDbInfo(dbInfoItem) {
|
|||
* @param {object} parsedItem - parsed statement item
|
||||
* @param {object} diagramDb
|
||||
* @param {boolean} altFlag - for clusters, add the "statediagram-cluster-alt" CSS class
|
||||
* @todo This duplicates some of what is done in stateDb.js extract method
|
||||
*/
|
||||
const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
|
||||
const itemId = parsedItem.id;
|
||||
const classStr = getClassesFromDbInfo(diagramStates[itemId]);
|
||||
|
||||
if (itemId !== 'root') {
|
||||
let shape = 'rect';
|
||||
if (parsedItem.start === true) {
|
||||
shape = 'start';
|
||||
}
|
||||
if (parsedItem.start === false) {
|
||||
shape = 'end';
|
||||
}
|
||||
if (parsedItem.type !== 'default') {
|
||||
shape = parsedItem.type;
|
||||
}
|
||||
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]) {
|
||||
|
@ -104,55 +161,56 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
|
|||
id: itemId,
|
||||
shape,
|
||||
description: common.sanitizeText(itemId, getConfig()),
|
||||
classes: classStr + ' statediagram-state',
|
||||
classes: `${classStr} ${CSS_DIAGRAM_STATE}`,
|
||||
};
|
||||
}
|
||||
|
||||
const newNode = nodeDb[itemId];
|
||||
|
||||
// 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 = 'rectWithTitle';
|
||||
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 = 'rectWithTitle';
|
||||
newNode.shape = SHAPE_STATE_WITH_DESC;
|
||||
if (newNode.description === itemId) {
|
||||
// If the previous description was the is, remove it
|
||||
// If the previous description was this, remove it
|
||||
newNode.description = [parsedItem.description];
|
||||
} else {
|
||||
newNode.description = [newNode.description, parsedItem.description];
|
||||
}
|
||||
} else {
|
||||
newNode.shape = 'rect';
|
||||
newNode.shape = SHAPE_STATE;
|
||||
newNode.description = parsedItem.description;
|
||||
}
|
||||
}
|
||||
newNode.description = common.sanitizeTextOrArray(newNode.description, getConfig());
|
||||
}
|
||||
|
||||
// update the node shape
|
||||
if (newNode.description.length === 1 && newNode.shape === 'rectWithTitle') {
|
||||
newNode.shape = 'rect';
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Save data for description and group so that for instance a statement without description overwrites
|
||||
// one with description
|
||||
|
||||
// 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' ? 'divider' : 'roundedWithTitle';
|
||||
|
||||
newNode.shape = parsedItem.type === DIVIDER_TYPE ? SHAPE_DIVIDER : SHAPE_GROUP;
|
||||
newNode.classes =
|
||||
newNode.classes +
|
||||
' ' +
|
||||
(altFlag ? 'statediagram-cluster statediagram-cluster-alt' : 'statediagram-cluster');
|
||||
CSS_DIAGRAM_CLUSTER +
|
||||
' ' +
|
||||
(altFlag ? CSS_DIAGRAM_CLUSTER_ALT : '');
|
||||
}
|
||||
|
||||
// This is what will be added to the graph
|
||||
|
@ -167,7 +225,7 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
|
|||
style: '', //styles.style,
|
||||
id: itemId,
|
||||
dir: newNode.dir,
|
||||
domId: 'state-' + itemId + '-' + graphItemCount,
|
||||
domId: stateDomId(itemId, graphItemCount),
|
||||
type: newNode.type,
|
||||
padding: 15, //getConfig().flowchart.padding
|
||||
};
|
||||
|
@ -176,35 +234,36 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
|
|||
// Todo: set random id
|
||||
const noteData = {
|
||||
labelStyle: '',
|
||||
shape: 'note',
|
||||
shape: SHAPE_NOTE,
|
||||
labelText: parsedItem.note.text,
|
||||
classes: 'statediagram-note', //classStr,
|
||||
classes: CSS_DIAGRAM_NOTE,
|
||||
style: '', // styles.style,
|
||||
id: itemId + '----note-' + graphItemCount,
|
||||
domId: 'state-' + itemId + '----note-' + graphItemCount,
|
||||
id: itemId + NOTE_ID + '-' + graphItemCount,
|
||||
domId: stateDomId(itemId, graphItemCount, NOTE),
|
||||
type: newNode.type,
|
||||
padding: 15, //getConfig().flowchart.padding
|
||||
};
|
||||
const groupData = {
|
||||
labelStyle: '',
|
||||
shape: 'noteGroup',
|
||||
shape: SHAPE_NOTEGROUP,
|
||||
labelText: parsedItem.note.text,
|
||||
classes: newNode.classes, //classStr,
|
||||
classes: newNode.classes,
|
||||
style: '', // styles.style,
|
||||
id: itemId + '----parent',
|
||||
domId: 'state-' + itemId + '----parent-' + graphItemCount,
|
||||
id: itemId + PARENT_ID,
|
||||
domId: stateDomId(itemId, graphItemCount, PARENT),
|
||||
type: 'group',
|
||||
padding: 0, //getConfig().flowchart.padding
|
||||
};
|
||||
graphItemCount++;
|
||||
|
||||
g.setNode(itemId + '----parent', groupData);
|
||||
const parentNodeId = itemId + PARENT_ID;
|
||||
g.setNode(parentNodeId, groupData);
|
||||
|
||||
g.setNode(noteData.id, noteData);
|
||||
g.setNode(itemId, nodeData);
|
||||
|
||||
g.setParent(itemId, itemId + '----parent');
|
||||
g.setParent(noteData.id, itemId + '----parent');
|
||||
g.setParent(itemId, parentNodeId);
|
||||
g.setParent(noteData.id, parentNodeId);
|
||||
|
||||
let from = itemId;
|
||||
let to = noteData.id;
|
||||
|
@ -216,13 +275,13 @@ const setupNode = (g, parent, parsedItem, diagramDb, altFlag) => {
|
|||
g.setEdge(from, to, {
|
||||
arrowhead: 'none',
|
||||
arrowType: '',
|
||||
style: 'fill:none',
|
||||
style: G_EDGE_STYLE,
|
||||
labelStyle: '',
|
||||
classes: 'transition note-edge',
|
||||
arrowheadStyle: 'fill: #333',
|
||||
labelpos: 'c',
|
||||
labelType: 'text',
|
||||
thickness: 'normal',
|
||||
classes: CSS_EDGE_NOTE_EDGE,
|
||||
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
||||
labelpos: G_EDGE_LABELPOS,
|
||||
labelType: G_EDGE_LABELTYPE,
|
||||
thickness: G_EDGE_THICKNESS,
|
||||
});
|
||||
} else {
|
||||
g.setNode(itemId, nodeData);
|
||||
|
@ -257,13 +316,13 @@ const setupDoc = (g, parentParsedItem, doc, diagramDb, altFlag) => {
|
|||
log.trace('items', doc);
|
||||
doc.forEach((item) => {
|
||||
switch (item.stmt) {
|
||||
case 'state':
|
||||
case STMT_STATE:
|
||||
setupNode(g, parentParsedItem, item, diagramDb, altFlag);
|
||||
break;
|
||||
case 'default':
|
||||
case DEFAULT_STATE_TYPE:
|
||||
setupNode(g, parentParsedItem, item, diagramDb, altFlag);
|
||||
break;
|
||||
case 'relation':
|
||||
case STMT_RELATION:
|
||||
{
|
||||
setupNode(g, parentParsedItem, item.state1, diagramDb, altFlag);
|
||||
setupNode(g, parentParsedItem, item.state2, diagramDb, altFlag);
|
||||
|
@ -271,14 +330,14 @@ const setupDoc = (g, parentParsedItem, doc, diagramDb, altFlag) => {
|
|||
id: 'edge' + graphItemCount,
|
||||
arrowhead: 'normal',
|
||||
arrowTypeEnd: 'arrow_barb',
|
||||
style: 'fill:none',
|
||||
style: G_EDGE_STYLE,
|
||||
labelStyle: '',
|
||||
label: common.sanitizeText(item.description, getConfig()),
|
||||
arrowheadStyle: 'fill: #333',
|
||||
labelpos: 'c',
|
||||
labelType: 'text',
|
||||
thickness: 'normal',
|
||||
classes: 'transition',
|
||||
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
||||
labelpos: G_EDGE_LABELPOS,
|
||||
labelType: G_EDGE_LABELTYPE,
|
||||
thickness: G_EDGE_THICKNESS,
|
||||
classes: CSS_EDGE,
|
||||
};
|
||||
g.setEdge(item.state1.id, item.state2.id, edgeData, graphItemCount);
|
||||
graphItemCount++;
|
||||
|
@ -289,14 +348,14 @@ const setupDoc = (g, parentParsedItem, doc, diagramDb, altFlag) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Get the direction from the statement items. Default is TB (top to bottom).
|
||||
* 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='TB'] - the direction to use if none is found
|
||||
* @param [defaultDir=DEFAULT_NESTED_DOC_DIR] - the direction to use if none is found
|
||||
* @returns {string}
|
||||
*/
|
||||
const getDir = (parsedItem, defaultDir = DEFAULT_DIR) => {
|
||||
const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
|
||||
let dir = defaultDir;
|
||||
if (parsedItem.doc) {
|
||||
for (let i = 0; i < parsedItem.doc.length; i++) {
|
||||
|
@ -323,7 +382,7 @@ export const draw = function (text, id, _version, diag) {
|
|||
nodeDb = {};
|
||||
// Fetch the default direction, use TD if none was found
|
||||
let dir = diag.db.getDirection();
|
||||
if (typeof dir === 'undefined') dir = DEFAULT_DIR;
|
||||
if (typeof dir === 'undefined') dir = DEFAULT_DIAGRAM_DIRECTION;
|
||||
|
||||
const { securityLevel, state: conf } = getConfig();
|
||||
const nodeSpacing = conf.nodeSpacing || 50;
|
||||
|
@ -370,7 +429,7 @@ export const draw = function (text, id, _version, diag) {
|
|||
// Run the renderer. This is what draws the final graph.
|
||||
|
||||
const element = root.select('#' + id + ' g');
|
||||
render(element, g, ['barb'], 'statediagram', id);
|
||||
render(element, g, ['barb'], CSS_DIAGRAM, id);
|
||||
|
||||
const padding = 8;
|
||||
|
||||
|
@ -380,7 +439,7 @@ export const draw = function (text, id, _version, diag) {
|
|||
const height = bounds.height + padding * 2;
|
||||
|
||||
// Zoom in a bit
|
||||
svg.attr('class', 'statediagram');
|
||||
svg.attr('class', CSS_DIAGRAM);
|
||||
|
||||
const svgBounds = svg.node().getBBox();
|
||||
|
||||
|
@ -400,7 +459,7 @@ export const draw = function (text, id, _version, diag) {
|
|||
// Get dimensions of label
|
||||
const dim = label.getBBox();
|
||||
|
||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
||||
const rect = document.createElementNS('http://www.w3.org/2000/svg', SHAPE_STATE);
|
||||
rect.setAttribute('rx', 0);
|
||||
rect.setAttribute('ry', 0);
|
||||
rect.setAttribute('width', dim.width);
|
||||
|
|
Loading…
Reference in New Issue