mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-17 14:29:48 +02:00
cleanup
This commit is contained in:
@@ -1,227 +0,0 @@
|
|||||||
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}`
|
|
||||||
);
|
|
Reference in New Issue
Block a user