Adding text wrap and logic for placing nodes in the svg
This commit is contained in:
parent
6029c5371e
commit
7de68f0bf2
|
@ -41,6 +41,16 @@ journey
|
|||
Go downstairs: 5: Me
|
||||
Sit down: 5: Mee
|
||||
</div>
|
||||
<div class="mermaid" style="width: 50%;">
|
||||
mindmap
|
||||
root[
|
||||
The root where the things
|
||||
hap<br/>
|
||||
hap<br/>
|
||||
pen!
|
||||
]
|
||||
Child1
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%;">
|
||||
pie
|
||||
accTitle: My Pie Chart Accessibility Title
|
||||
|
@ -52,8 +62,8 @@ journey
|
|||
"Magnesium" : 10.01
|
||||
"Iron" : 5
|
||||
</div>
|
||||
<div class="mermaid2" style="width: 50%;">
|
||||
gitGraph
|
||||
<div class="mermaid" style="width: 50%;">
|
||||
gitGraph TB
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
|
@ -217,7 +227,7 @@ class Class10 {
|
|||
size()
|
||||
}
|
||||
</div>
|
||||
<div class="mermaid" style="width: 100%;">
|
||||
<div class="mermaid2" style="width: 100%;">
|
||||
%%{init: {'config': {'wrap': true }}}%%
|
||||
sequenceDiagram
|
||||
participant A as Extremely utterly long line of longness which had previously overflown the actor box as it is much longer than what it should be
|
||||
|
@ -332,7 +342,7 @@ flowchart TD
|
|||
rankSpacing: 50,
|
||||
defaultRenderer: 'dagre-d3',
|
||||
},
|
||||
logLevel: 0,
|
||||
logLevel: 1,
|
||||
fontSize: 18,
|
||||
curve: 'cardinal',
|
||||
// securityLevel: 'sandbox',
|
||||
|
|
|
@ -1818,6 +1818,11 @@ const config = {
|
|||
external_component_queue_bg_color: '#CCCCCC',
|
||||
external_component_queue_border_color: '#BFBFBF',
|
||||
},
|
||||
mindmap: {
|
||||
useMaxWidth: true,
|
||||
diagramPadding: 10,
|
||||
maxNodeWidth: 200,
|
||||
},
|
||||
};
|
||||
|
||||
config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute;
|
||||
|
|
|
@ -83,8 +83,10 @@ const detectType = function (text, cnf) {
|
|||
if (cnf && cnf.flowchart && cnf.flowchart.defaultRenderer === 'dagre-wrapper')
|
||||
return 'flowchart-v2';
|
||||
const k = Object.keys(detectors);
|
||||
console.log('here', k);
|
||||
for (let i = 0; i < k.length; i++) {
|
||||
const key = k[i];
|
||||
console.log('Detecting type for', key);
|
||||
const dia = detectors[key];
|
||||
if (dia && dia.detector(text)) {
|
||||
return key;
|
||||
|
|
|
@ -1,24 +1,17 @@
|
|||
import { registerDiagram } from './diagramAPI.js';
|
||||
// import mindmapDb from '../diagrams/mindmap/mindmapDb';
|
||||
// import mindmapRenderer from '../diagrams/mindmap/mindmapRenderer';
|
||||
// import mindmapParser from '../diagrams/mindmap/parser/mindmapDiagram';
|
||||
// import mindmapDetector from '../diagrams/mindmap/mindmapDetector';
|
||||
import mindmapDb from '../diagrams/mindmap/mindmapDb';
|
||||
import mindmapRenderer from '../diagrams/mindmap/mindmapRenderer';
|
||||
import mindmapParser from '../diagrams/mindmap/parser/mindmap';
|
||||
import mindmapDetector from '../diagrams/mindmap/mindmapDetector';
|
||||
import mindmapStyles from '../diagrams/mindmap/styles';
|
||||
|
||||
import gitGraphDb from '../diagrams/git/gitGraphAst';
|
||||
import gitGraphRenderer from '../diagrams/git/gitGraphRenderer';
|
||||
import gitGraphParser from '../diagrams/git/parser/gitGraph';
|
||||
import gitGraphDetector from '../diagrams/git/gitGraphDetector';
|
||||
import gitGraphStyles from '../diagrams/git/styles';
|
||||
|
||||
// Register mindmap and other built-in diagrams
|
||||
// registerDiagram(
|
||||
// 'mindmap',
|
||||
// mindmapParser,
|
||||
// mindmapDb,
|
||||
// mindmapRenderer,
|
||||
// undefined,
|
||||
// mindmapRenderer,
|
||||
// mindmapDetector
|
||||
// );
|
||||
const addDiagrams = () => {
|
||||
registerDiagram(
|
||||
'gitGraph',
|
||||
|
@ -26,7 +19,17 @@ const addDiagrams = () => {
|
|||
gitGraphDb,
|
||||
gitGraphRenderer,
|
||||
undefined,
|
||||
gitGraphDetector
|
||||
gitGraphDetector,
|
||||
gitGraphStyles
|
||||
);
|
||||
registerDiagram(
|
||||
'mindmap',
|
||||
mindmapParser,
|
||||
mindmapDb,
|
||||
mindmapRenderer,
|
||||
undefined,
|
||||
mindmapDetector,
|
||||
mindmapStyles
|
||||
);
|
||||
};
|
||||
export default addDiagrams;
|
||||
|
|
|
@ -35,15 +35,19 @@ import journeyDb from '../diagrams/user-journey/journeyDb';
|
|||
import journeyRenderer from '../diagrams/user-journey/journeyRenderer';
|
||||
import journeyParser from '../diagrams/user-journey/parser/journey';
|
||||
import { addDetector } from './detectType';
|
||||
import { addStylesForDiagram } from '../styles';
|
||||
import { sanitizeText as _sanitizeText } from '../diagrams/common/common';
|
||||
import { getConfig as _getConfig } from '../config';
|
||||
import { log as _log } from '../logger';
|
||||
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox';
|
||||
|
||||
let title = '';
|
||||
let diagramTitle = '';
|
||||
let description = '';
|
||||
|
||||
export const getConfig = _getConfig;
|
||||
export const sanitizeText = (txt) => _sanitizeText(txt, getConfig());
|
||||
|
||||
export const log = _log;
|
||||
const diagrams = {
|
||||
c4: {
|
||||
db: c4Db,
|
||||
|
@ -171,13 +175,16 @@ const diagrams = {
|
|||
},
|
||||
},
|
||||
};
|
||||
// console.log(sequenceDb);
|
||||
export const registerDiagram = (id, parser, db, renderer, init, detector) => {
|
||||
|
||||
export const registerDiagram = (id, parser, db, renderer, init, detector, styles) => {
|
||||
diagrams[id] = { parser, db, renderer, init };
|
||||
addDetector(id, detector);
|
||||
addStylesForDiagram(id, styles);
|
||||
};
|
||||
|
||||
export const getDiagrams = () => {
|
||||
// console.log('diagrams', diagrams);
|
||||
return diagrams;
|
||||
};
|
||||
|
||||
export const setupGraphViewbox = _setupGraphViewbox;
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
export const lineBreakRegex = /<br\s*\/?>/gi;
|
||||
|
||||
/**
|
||||
* Caches results of functions based on input
|
||||
*
|
||||
* @param {Function} fn Function to run
|
||||
* @param {Function} resolver Function that resolves to an ID given arguments the `fn` takes
|
||||
* @returns {Function} An optimized caching function
|
||||
*/
|
||||
const memoize = (fn, resolver) => {
|
||||
let cache = {};
|
||||
return (...args) => {
|
||||
let n = resolver ? resolver.apply(this, args) : args[0];
|
||||
if (n in cache) {
|
||||
return cache[n];
|
||||
} else {
|
||||
let result = fn(...args);
|
||||
cache[n] = result;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* This calculates the width of the given text, font size and family.
|
||||
*
|
||||
* @param {any} text - The text to calculate the width of
|
||||
* @param {any} config - The config for fontSize, fontFamily, and fontWeight all impacting the resulting size
|
||||
* @returns {any} - The width for the given text
|
||||
*/
|
||||
export const calculateTextWidth = function (text, config) {
|
||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
||||
return calculateTextDimensions(text, config).width;
|
||||
};
|
||||
|
||||
export const getTextObj = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: undefined,
|
||||
anchor: 'start',
|
||||
style: '#666',
|
||||
width: 100,
|
||||
height: 100,
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
valign: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds text to an element
|
||||
*
|
||||
* @param {SVGElement} elem Element to add text to
|
||||
* @param {{
|
||||
* text: string;
|
||||
* x: number;
|
||||
* y: number;
|
||||
* anchor: 'start' | 'middle' | 'end';
|
||||
* fontFamily: string;
|
||||
* fontSize: string | number;
|
||||
* fontWeight: string | number;
|
||||
* fill: string;
|
||||
* class: string | undefined;
|
||||
* textMargin: number;
|
||||
* }} textData
|
||||
* @returns {SVGTextElement} Text element with given styling and content
|
||||
*/
|
||||
export const drawSimpleText = function (elem, textData) {
|
||||
// Remove and ignore br:s
|
||||
const nText = textData.text.replace(lineBreakRegex, ' ');
|
||||
|
||||
const textElem = elem.append('text');
|
||||
textElem.attr('x', textData.x);
|
||||
textElem.attr('y', textData.y);
|
||||
textElem.style('text-anchor', textData.anchor);
|
||||
textElem.style('font-family', textData.fontFamily);
|
||||
textElem.style('font-size', textData.fontSize);
|
||||
textElem.style('font-weight', textData.fontWeight);
|
||||
textElem.attr('fill', textData.fill);
|
||||
if (typeof textData.class !== 'undefined') {
|
||||
textElem.attr('class', textData.class);
|
||||
}
|
||||
|
||||
const span = textElem.append('tspan');
|
||||
span.attr('x', textData.x + textData.textMargin * 2);
|
||||
span.attr('fill', textData.fill);
|
||||
span.text(nText);
|
||||
|
||||
return textElem;
|
||||
};
|
||||
|
||||
/**
|
||||
* This calculates the dimensions of the given text, font size, font family, font weight, and margins.
|
||||
*
|
||||
* @param {any} text - The text to calculate the width of
|
||||
* @param {any} config - The config for fontSize, fontFamily, fontWeight, and margin all impacting
|
||||
* the resulting size
|
||||
* @returns - The width for the given text
|
||||
*/
|
||||
export const calculateTextDimensions = memoize(
|
||||
function (text, config) {
|
||||
config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config);
|
||||
const { fontSize, fontFamily, fontWeight } = config;
|
||||
if (!text) {
|
||||
return { width: 0, height: 0 };
|
||||
}
|
||||
|
||||
// We can't really know if the user supplied font family will render on the user agent;
|
||||
// thus, we'll take the max width between the user supplied font family, and a default
|
||||
// of sans-serif.
|
||||
const fontFamilies = ['sans-serif', fontFamily];
|
||||
const lines = text.split(common.lineBreakRegex);
|
||||
let dims = [];
|
||||
|
||||
const body = select('body');
|
||||
// We don't want to leak DOM elements - if a removal operation isn't available
|
||||
// for any reason, do not continue.
|
||||
if (!body.remove) {
|
||||
return { width: 0, height: 0, lineHeight: 0 };
|
||||
}
|
||||
|
||||
const g = body.append('svg');
|
||||
|
||||
for (let fontFamily of fontFamilies) {
|
||||
let cheight = 0;
|
||||
let dim = { width: 0, height: 0, lineHeight: 0 };
|
||||
for (let line of lines) {
|
||||
const textObj = getTextObj();
|
||||
textObj.text = line;
|
||||
const textElem = drawSimpleText(g, textObj)
|
||||
.style('font-size', fontSize)
|
||||
.style('font-weight', fontWeight)
|
||||
.style('font-family', fontFamily);
|
||||
|
||||
let bBox = (textElem._groups || textElem)[0][0].getBBox();
|
||||
dim.width = Math.round(Math.max(dim.width, bBox.width));
|
||||
cheight = Math.round(bBox.height);
|
||||
dim.height += cheight;
|
||||
dim.lineHeight = Math.round(Math.max(dim.lineHeight, cheight));
|
||||
}
|
||||
dims.push(dim);
|
||||
}
|
||||
|
||||
g.remove();
|
||||
|
||||
let index =
|
||||
isNaN(dims[1].height) ||
|
||||
isNaN(dims[1].width) ||
|
||||
isNaN(dims[1].lineHeight) ||
|
||||
(dims[0].height > dims[1].height &&
|
||||
dims[0].width > dims[1].width &&
|
||||
dims[0].lineHeight > dims[1].lineHeight)
|
||||
? 0
|
||||
: 1;
|
||||
return dims[index];
|
||||
},
|
||||
(text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
|
||||
);
|
||||
|
||||
const breakString = memoize(
|
||||
(word, maxWidth, hyphenCharacter = '-', config) => {
|
||||
config = Object.assign(
|
||||
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 0 },
|
||||
config
|
||||
);
|
||||
const characters = word.split('');
|
||||
const lines = [];
|
||||
let currentLine = '';
|
||||
characters.forEach((character, index) => {
|
||||
const nextLine = `${currentLine}${character}`;
|
||||
const lineWidth = calculateTextWidth(nextLine, config);
|
||||
if (lineWidth >= maxWidth) {
|
||||
const currentCharacter = index + 1;
|
||||
const isLastLine = characters.length === currentCharacter;
|
||||
const hyphenatedNextLine = `${nextLine}${hyphenCharacter}`;
|
||||
lines.push(isLastLine ? nextLine : hyphenatedNextLine);
|
||||
currentLine = '';
|
||||
} else {
|
||||
currentLine = nextLine;
|
||||
}
|
||||
});
|
||||
return { hyphenatedStrings: lines, remainingWord: currentLine };
|
||||
},
|
||||
(word, maxWidth, hyphenCharacter = '-', config) =>
|
||||
`${word}-${maxWidth}-${hyphenCharacter}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}`
|
||||
);
|
||||
|
||||
export const wrapLabel = memoize(
|
||||
(label, maxWidth, config) => {
|
||||
if (!label) {
|
||||
return label;
|
||||
}
|
||||
config = Object.assign(
|
||||
{ fontSize: 12, fontWeight: 400, fontFamily: 'Arial', joinWith: '<br/>' },
|
||||
config
|
||||
);
|
||||
if (lineBreakRegex.test(label)) {
|
||||
return label;
|
||||
}
|
||||
const words = label.split(' ');
|
||||
const completedLines = [];
|
||||
let nextLine = '';
|
||||
words.forEach((word, index) => {
|
||||
const wordLength = calculateTextWidth(`${word} `, config);
|
||||
const nextLineLength = calculateTextWidth(nextLine, config);
|
||||
if (wordLength > maxWidth) {
|
||||
const { hyphenatedStrings, remainingWord } = breakString(word, maxWidth, '-', config);
|
||||
completedLines.push(nextLine, ...hyphenatedStrings);
|
||||
nextLine = remainingWord;
|
||||
} else if (nextLineLength + wordLength >= maxWidth) {
|
||||
completedLines.push(nextLine);
|
||||
nextLine = word;
|
||||
} else {
|
||||
nextLine = [nextLine, word].filter(Boolean).join(' ');
|
||||
}
|
||||
const currentWord = index + 1;
|
||||
const isLastWord = currentWord === words.length;
|
||||
if (isLastWord) {
|
||||
completedLines.push(nextLine);
|
||||
}
|
||||
});
|
||||
return completedLines.filter((line) => line !== '').join(config.joinWith);
|
||||
},
|
||||
(label, maxWidth, config) =>
|
||||
`${label}-${maxWidth}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}-${config.joinWith}`
|
||||
);
|
|
@ -1,6 +1,5 @@
|
|||
/** Created by knut on 15-01-14. */
|
||||
import { log } from '../../logger';
|
||||
import { sanitizeText } from '../../diagram-api/diagramAPI';
|
||||
import { log, sanitizeText, getConfig } from '../../diagram-api/diagramAPI';
|
||||
|
||||
var message = '';
|
||||
var info = false;
|
||||
|
@ -26,7 +25,14 @@ export const getMindmap = () => {
|
|||
return nodes.length > 0 ? nodes[0] : null;
|
||||
};
|
||||
export const addNode = (level, id, descr, type) => {
|
||||
const node = { id: sanitizeText(id), level, descr: sanitizeText(descr), type, children: [] };
|
||||
const node = {
|
||||
id: sanitizeText(id),
|
||||
level,
|
||||
descr: sanitizeText(descr),
|
||||
type,
|
||||
children: [],
|
||||
width: getConfig().mindmap.maxNodeWidth,
|
||||
};
|
||||
const parent = getParent(level);
|
||||
if (parent) {
|
||||
parent.children.push(node);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const detector = (txt) => {
|
||||
const detector = function detect(txt) {
|
||||
if (txt.match(/^\s*mindmap/)) {
|
||||
return 'mindmap';
|
||||
}
|
|
@ -1,7 +1,33 @@
|
|||
/** Created by knut on 14-12-11. */
|
||||
import { select } from 'd3';
|
||||
import { log } from '../../logger';
|
||||
import { getConfig } from '../../config';
|
||||
import { log, getConfig, setupGraphViewbox } from '../../diagram-api/diagramAPI';
|
||||
import svgDraw from './svgDraw';
|
||||
|
||||
/**
|
||||
* @param {any} svg The svg element to draw the diagram onto
|
||||
* @param {object} mindmap The maindmap data and hierarchy
|
||||
* @param {object} conf The configuration object
|
||||
*/
|
||||
function drawNodes(svg, mindmap, conf) {
|
||||
svgDraw.drawNode(svg, mindmap, conf);
|
||||
if (mindmap.children) {
|
||||
mindmap.children.forEach((child) => {
|
||||
drawNodes(svg, child, conf);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function drawEdges() {}
|
||||
/**
|
||||
* @param node
|
||||
* @param isRoot
|
||||
*/
|
||||
function layoutMindmap(node, isRoot) {}
|
||||
/**
|
||||
* @param node
|
||||
* @param isRoot
|
||||
*/
|
||||
function positionNodes(node, isRoot) {}
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
|
@ -12,6 +38,7 @@ import { getConfig } from '../../config';
|
|||
* @param diagObj
|
||||
*/
|
||||
export const draw = (text, id, version, diagObj) => {
|
||||
const conf = getConfig();
|
||||
try {
|
||||
// const parser = infoParser.parser;
|
||||
// parser.yy = db;
|
||||
|
@ -30,24 +57,40 @@ export const draw = (text, id, version, diagObj) => {
|
|||
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
||||
|
||||
// Parse the graph definition
|
||||
// parser.parse(text);
|
||||
// log.debug('Parsed info diagram');
|
||||
// Fetch the default direction, use TD if none was found
|
||||
|
||||
const svg = root.select('#' + id);
|
||||
|
||||
const g = svg.append('g');
|
||||
const mm = diagObj.db.getMindmap();
|
||||
|
||||
g.append('text') // text label for the x axis
|
||||
.attr('x', 100)
|
||||
.attr('y', 40)
|
||||
.attr('class', 'version')
|
||||
.attr('font-size', '32px')
|
||||
.style('text-anchor', 'middle')
|
||||
.text('v ' + version);
|
||||
// mm.x = 0;
|
||||
// mm.y = 0;
|
||||
// svgDraw.drawNode(g, mm, getConfig());
|
||||
// mm.children.forEach((child) => {
|
||||
// child.x = 200;
|
||||
// child.y = 200;
|
||||
// child.width = 200;
|
||||
// svgDraw.drawNode(g, child, getConfig());
|
||||
// });
|
||||
|
||||
svg.attr('height', 100);
|
||||
svg.attr('width', 400);
|
||||
// svg.attr('viewBox', '0 0 300 150');
|
||||
// Draw the graph and start with drawing the nodes without proper position
|
||||
// this gives us the size of the nodes and we can set the positions later
|
||||
|
||||
const nodesElem = svg.append('g');
|
||||
nodesElem.attr('class', 'mindmap-nodes');
|
||||
drawNodes(nodesElem, mm, conf);
|
||||
|
||||
// Next step is to layout the mindmap, giving each node a position
|
||||
|
||||
// layoutMindmap(mm, conf);
|
||||
|
||||
// After this we can draw, first the edges and the then nodes with the correct position
|
||||
// drawEdges(svg, mm, conf);
|
||||
|
||||
// positionNodes(svg, mm, conf);
|
||||
|
||||
// Setup the view box and size of the svg element
|
||||
setupGraphViewbox(undefined, svg, conf.mindmap.diagramPadding, conf.mindmap.useMaxWidth);
|
||||
} catch (e) {
|
||||
log.error('Error while rendering info diagram');
|
||||
log.error(e.message);
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
const getStyles = () => ``;
|
||||
|
||||
const getStyles = (options) =>
|
||||
`
|
||||
.node{
|
||||
stroke: ${options.pieStrokeColor};
|
||||
stroke-width : ${options.pieStrokeWidth};
|
||||
opacity : ${options.pieOpacity};
|
||||
}
|
||||
`;
|
||||
export default getStyles;
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
const lineBreakRegex = /<br\s*\/?>/gi;
|
||||
import { select } from 'd3';
|
||||
/**
|
||||
* @param {string} text The text to be wrapped
|
||||
* @param {number} width The max width of the text
|
||||
*/
|
||||
function wrap(text, width) {
|
||||
text.each(function () {
|
||||
var text = select(this),
|
||||
words = text
|
||||
.text()
|
||||
.split(/(\s+|<br>)/)
|
||||
.reverse(),
|
||||
word,
|
||||
line = [],
|
||||
lineNumber = 0,
|
||||
lineHeight = 1.1, // ems
|
||||
y = text.attr('y'),
|
||||
dy = parseFloat(text.attr('dy')),
|
||||
tspan = text
|
||||
.text(null)
|
||||
.append('tspan')
|
||||
.attr('x', 0)
|
||||
.attr('y', y)
|
||||
.attr('dy', dy + 'em');
|
||||
for (let j = 0; j < words.length; j++) {
|
||||
word = words[words.length - 1 - j];
|
||||
line.push(word);
|
||||
tspan.text(line.join(' ').trim());
|
||||
if (tspan.node().getComputedTextLength() > width || word === '<br>') {
|
||||
line.pop();
|
||||
tspan.text(line.join(' ').trim());
|
||||
if (word === '<br>') {
|
||||
line = [''];
|
||||
} else {
|
||||
line = [word];
|
||||
}
|
||||
|
||||
tspan = text
|
||||
.append('tspan')
|
||||
.attr('x', 0)
|
||||
.attr('y', y)
|
||||
.attr('dy', lineHeight + 'em')
|
||||
.text(word);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {object} elem The D3 dom element in which the node is to be added
|
||||
* @param {object} node The node to be added
|
||||
* @param {object} conf The configuration object
|
||||
* @returns {number} The height nodes dom element
|
||||
*/
|
||||
export const drawNode = function (elem, node, conf) {
|
||||
const nodeElem = elem.append('g');
|
||||
nodeElem.attr('class', 'mindmap-node');
|
||||
|
||||
const rect = {
|
||||
fill: '#EDF2AE',
|
||||
stroke: '#666',
|
||||
width: node.width,
|
||||
anchor: 'start',
|
||||
height: 100,
|
||||
rx: 3,
|
||||
ry: 3,
|
||||
};
|
||||
|
||||
const r = nodeElem
|
||||
.append('rect')
|
||||
.attr('x', (-1 * node.width) / 2)
|
||||
.attr('width', node.width)
|
||||
.attr('fill', rect.fill)
|
||||
.attr('stroke', rect.stroke)
|
||||
.attr('rx', rect.rx)
|
||||
.attr('ry', rect.ry);
|
||||
|
||||
const textElem = nodeElem.append('g');
|
||||
// .attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
|
||||
|
||||
const txt = textElem
|
||||
.append('text')
|
||||
.text(node.descr)
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.call(wrap, node.width);
|
||||
const bbox = txt.node().getBBox();
|
||||
node.height = bbox.height + conf.fontSize * 1.1 * 0.5;
|
||||
r.attr('height', node.height).attr('y', (-1 * node.height) / 2);
|
||||
|
||||
txt.attr('transform', 'translate( 0,' + (-1 * node.height) / 2 + ')');
|
||||
// Position the node to its coordinate
|
||||
if (node.x || node.y) {
|
||||
nodeElem.attr('transform', 'translate(' + node.x + ',' + node.y + ')');
|
||||
}
|
||||
return node.height;
|
||||
};
|
||||
export default { drawNode };
|
|
@ -0,0 +1,102 @@
|
|||
import { log } from './logger';
|
||||
|
||||
/**
|
||||
* Applys d3 attributes
|
||||
*
|
||||
* @param {any} d3Elem D3 Element to apply the attributes onto
|
||||
* @param {[string, string][]} attrs Object.keys equivalent format of key to value mapping of attributes
|
||||
*/
|
||||
const d3Attrs = function (d3Elem, attrs) {
|
||||
for (let attr of attrs) {
|
||||
d3Elem.attr(attr[0], attr[1]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gives attributes for an SVG's size given arguments
|
||||
*
|
||||
* @param {number} height The height of the SVG
|
||||
* @param {number} width The width of the SVG
|
||||
* @param {boolean} useMaxWidth Whether or not to use max-width and set width to 100%
|
||||
* @returns {Map<'height' | 'width' | 'style', string>} Attributes for the SVG
|
||||
*/
|
||||
export const calculateSvgSizeAttrs = function (height, width, useMaxWidth) {
|
||||
let attrs = new Map();
|
||||
attrs.set('height', height);
|
||||
if (useMaxWidth) {
|
||||
attrs.set('width', '100%');
|
||||
attrs.set('style', `max-width: ${width}px;`);
|
||||
} else {
|
||||
attrs.set('width', width);
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies attributes from `calculateSvgSizeAttrs`
|
||||
*
|
||||
* @param {SVGSVGElement} svgElem The SVG Element to configure
|
||||
* @param {number} height The height of the SVG
|
||||
* @param {number} width The width of the SVG
|
||||
* @param tx
|
||||
* @param ty
|
||||
* @param {boolean} useMaxWidth Whether or not to use max-width and set width to 100%
|
||||
*/
|
||||
export const configureSvgSize = function (svgElem, height, width, tx, ty, useMaxWidth) {
|
||||
const attrs = calculateSvgSizeAttrs(height, width, useMaxWidth);
|
||||
d3Attrs(svgElem, attrs);
|
||||
};
|
||||
export const setupGraphViewbox = function (graph, svgElem, padding, useMaxWidth) {
|
||||
const svgBounds = svgElem.node().getBBox();
|
||||
const sWidth = svgBounds.width;
|
||||
const sHeight = svgBounds.height;
|
||||
|
||||
let width;
|
||||
let height;
|
||||
let tx = 0;
|
||||
let ty = 0;
|
||||
if (graph) {
|
||||
width = graph._label.width;
|
||||
height = graph._label.height;
|
||||
if (sWidth > width) {
|
||||
tx = (sWidth - width) / 2 + padding;
|
||||
width = sWidth + padding * 2;
|
||||
} else {
|
||||
if (Math.abs(sWidth - width) >= 2 * padding + 1) {
|
||||
width = width - padding;
|
||||
}
|
||||
}
|
||||
if (sHeight > height) {
|
||||
ty = (sHeight - height) / 2 + padding;
|
||||
height = sHeight + padding * 2;
|
||||
}
|
||||
} else {
|
||||
width = sWidth + padding * 2;
|
||||
height = sHeight + padding * 2;
|
||||
}
|
||||
|
||||
// Ensure the viewBox includes the whole svgBounds area with extra space for padding
|
||||
const vBox = graph
|
||||
? `0 0 ${width} ${height}`
|
||||
: `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`;
|
||||
console.log(
|
||||
'Graph.label',
|
||||
graph ? graph._label : null,
|
||||
'swidth',
|
||||
sWidth,
|
||||
'sheight',
|
||||
sHeight,
|
||||
'width',
|
||||
width,
|
||||
'height',
|
||||
height,
|
||||
'tx',
|
||||
tx,
|
||||
'ty',
|
||||
ty,
|
||||
'vBox',
|
||||
vBox
|
||||
);
|
||||
svgElem.attr('viewBox', vBox);
|
||||
svgElem.select('g').attr('transform', `translate(${tx}, ${ty})`);
|
||||
};
|
|
@ -2,7 +2,7 @@ import classDiagram from './diagrams/class/styles';
|
|||
import er from './diagrams/er/styles';
|
||||
import flowchart from './diagrams/flowchart/styles';
|
||||
import gantt from './diagrams/gantt/styles';
|
||||
import gitGraph from './diagrams/git/styles';
|
||||
// import gitGraph from './diagrams/git/styles';
|
||||
import info from './diagrams/info/styles';
|
||||
import pie from './diagrams/pie/styles';
|
||||
import requirement from './diagrams/requirement/styles';
|
||||
|
@ -22,7 +22,7 @@ const themes = {
|
|||
class: classDiagram,
|
||||
stateDiagram,
|
||||
state: stateDiagram,
|
||||
gitGraph,
|
||||
// gitGraph,
|
||||
info,
|
||||
pie,
|
||||
er,
|
||||
|
@ -89,4 +89,8 @@ const getStyles = (type, userStyles, options) => {
|
|||
`;
|
||||
};
|
||||
|
||||
export const addStylesForDiagram = (type, diagramTheme, options) => {
|
||||
themes[type] = diagramTheme;
|
||||
};
|
||||
|
||||
export default getStyles;
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import utils from './utils';
|
||||
import assignWithDepth from './assignWithDepth';
|
||||
import detectType from './diagram-api/detectType';
|
||||
import './diagram-api/diagram-orchestration';
|
||||
import addDiagrams from './diagram-api/diagram-orchestration';
|
||||
|
||||
// Orchestrating diagrams and adding the dynamic ones to the list of diagrams
|
||||
addDiagrams();
|
||||
|
||||
describe('when assignWithDepth: should merge objects within objects', function () {
|
||||
it('should handle simple, depth:1 types (identity)', function () {
|
||||
|
@ -214,7 +217,7 @@ Alice->Bob: hi`;
|
|||
const type = detectType(str);
|
||||
expect(type).toBe('flowchart');
|
||||
});
|
||||
it('should handle a graph definition for gitGraph', function () {
|
||||
fit('should handle a graph definition for gitGraph', function () {
|
||||
const str = ' \n gitGraph TB:\nbfs1:queue';
|
||||
const type = detectType(str);
|
||||
expect(type).toBe('gitGraph');
|
||||
|
|
Loading…
Reference in New Issue