Merge pull request #4416 from MikeJeffers/bug/4408

Fix #4408: Handle wrapping long words
This commit is contained in:
Sidharth Vinod
2023-06-08 11:46:09 +05:30
committed by GitHub
4 changed files with 63 additions and 2101 deletions

View File

@@ -52,6 +52,17 @@ root[A root with a long text that wraps to keep the node size in check]
); );
}); });
it('a root with wrapping text and long words that exceed width', () => {
imgSnapshotTest(
`mindmap
root[A few smaller words but then averylongsetofcharacterswithoutwhitespacetoseparate that we expect to wrapontonextlinesandnotexceedwidthparameters]
`,
{},
undefined,
shouldHaveRoot
);
});
it('a root with an icon', () => { it('a root with an icon', () => {
imgSnapshotTest( imgSnapshotTest(
`mindmap `mindmap

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -78,6 +78,22 @@ function createTspan(textElement, lineIndex, lineHeight) {
.attr('dy', lineHeight + 'em'); .attr('dy', lineHeight + 'em');
} }
/**
* Compute the width of rendered text
* @param {object} parentNode
* @param {number} lineHeight
* @param {string} text
* @returns {number}
*/
function computeWidthOfText(parentNode, lineHeight, text) {
const testElement = parentNode.append('text');
const testSpan = createTspan(testElement, 1, lineHeight);
updateTextContentAndStyles(testSpan, [{ content: text, type: 'normal' }]);
const textLength = testSpan.node().getComputedTextLength();
testElement.remove();
return textLength;
}
/** /**
* Creates a formatted text element by breaking lines and applying styles based on * Creates a formatted text element by breaking lines and applying styles based on
* the given structuredText. * the given structuredText.
@@ -95,31 +111,44 @@ function createFormattedText(width, g, structuredText, addBackground = false) {
// .attr('dominant-baseline', 'middle') // .attr('dominant-baseline', 'middle')
// .attr('text-anchor', 'middle'); // .attr('text-anchor', 'middle');
// .attr('text-anchor', 'middle'); // .attr('text-anchor', 'middle');
let lineIndex = -1; let lineIndex = 0;
structuredText.forEach((line) => { structuredText.forEach((line) => {
lineIndex++; /**
let tspan = createTspan(textElement, lineIndex, lineHeight); * Preprocess raw string content of line data
* Creating an array of strings pre-split to satisfy width limit
let words = [...line].reverse(); */
let currentWord; let fullStr = line.map((data) => data.content).join(' ');
let wrappedLine = []; let tempStr = '';
let linesUnderWidth = [];
while (words.length) { let prevIndex = 0;
currentWord = words.pop(); if (computeWidthOfText(labelGroup, lineHeight, fullStr) <= width) {
wrappedLine.push(currentWord); linesUnderWidth.push(fullStr);
} else {
updateTextContentAndStyles(tspan, wrappedLine); for (let i = 0; i <= fullStr.length; i++) {
tempStr = fullStr.slice(prevIndex, i);
if (tspan.node().getComputedTextLength() > width) { log.info(tempStr, prevIndex, i);
wrappedLine.pop(); if (computeWidthOfText(labelGroup, lineHeight, tempStr) > width) {
words.push(currentWord); const subStr = fullStr.slice(prevIndex, i);
// Break at space if any
updateTextContentAndStyles(tspan, wrappedLine); const lastSpaceIndex = subStr.lastIndexOf(' ');
if (lastSpaceIndex > -1) {
wrappedLine = []; i = prevIndex + lastSpaceIndex + 1;
lineIndex++; }
tspan = createTspan(textElement, lineIndex, lineHeight); linesUnderWidth.push(fullStr.slice(prevIndex, i).trim());
prevIndex = i;
tempStr = null;
}
} }
if (tempStr != null) {
linesUnderWidth.push(tempStr);
}
}
/** Add each prepared line as a tspan to the parent node */
const preparedLines = linesUnderWidth.map((w) => ({ content: w, type: line.type }));
for (const preparedLine of preparedLines) {
let tspan = createTspan(textElement, lineIndex, lineHeight);
updateTextContentAndStyles(tspan, [preparedLine]);
lineIndex++;
} }
}); });
if (addBackground) { if (addBackground) {