diff --git a/packages/mermaid/src/diagram-api/text-wrap b/packages/mermaid/src/diagram-api/text-wrap deleted file mode 100644 index 173baecec..000000000 --- a/packages/mermaid/src/diagram-api/text-wrap +++ /dev/null @@ -1,227 +0,0 @@ -export const lineBreakRegex = //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: '
' }, - 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}` -);