#4220 Handling paragraphs and html labels with classes in mindmaps.

This commit is contained in:
Knut Sveidqvist
2023-03-22 18:41:31 +01:00
parent a1c50b8079
commit fd9ad95346
6 changed files with 106 additions and 39 deletions

View File

@@ -66,21 +66,24 @@ h --3i -->a
b --> d(The dog in the hog)
c --> d
</pre>
<pre id="diagram" class="mermaid">
<pre id="diagram" class="mermaid2">
mindmap
id1["`Start`"]
id1["`**Start2**
second line 😎 with long text that is wrapping to the next line`"]
id2["`Child **with bold** text`"]
id3["`Children of which some
is using *italic type of* text`"]
id4[Child]
id5["`Child
Row
and another
`"]
</pre>
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
mindmap
id1["`Start
second line 😎`"]
id2[Child]
id3[Child]
id4[Child]
id1["`**Start** with
a second line 😎`"]
</pre>
<pre id="diagram" class="mermaid2">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%

View File

@@ -70,5 +70,12 @@ const getStyles = (options) =>
.edge {
fill: none;
}
.mindmap-node-label {
dy: 1em;
alignment-baseline: middle;
text-anchor: middle;
dominant-baseline: middle;
text-align: center;
}
`;
export default getStyles;

View File

@@ -204,7 +204,7 @@ const roundedRectBkg = function (elem, node) {
* @returns {number} The height nodes dom element
*/
export const drawNode = function (elem, node, fullSection, conf) {
const htmlLabels = false;
const htmlLabels = conf.htmlLabels;
const section = fullSection % (MAX_SECTIONS - 1);
const nodeElem = elem.append('g');
node.section = section;
@@ -217,19 +217,12 @@ export const drawNode = function (elem, node, fullSection, conf) {
// Create the wrapped text element
const textElem = nodeElem.append('g');
const newEl = createText(textElem, node.descr, { useHtmlLabels: htmlLabels, width: node.width });
// const txt = textElem.node().appendChild(newEl);
// const txt = textElem.append(newEl);
// 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 newerEl = textElem.node().appendChild(newEl);
// setSize(textElem);
const newEl = createText(textElem, node.descr, {
useHtmlLabels: htmlLabels,
width: node.width,
classes: 'mindmap-node-label',
});
if (!htmlLabels) {
textElem
.attr('dy', '1em')

View File

@@ -17,19 +17,22 @@ function applyStyle(dom, styleFn) {
/**
* @param element
* @param {any} node
* @param width
* @param classes
* @returns {SVGForeignObjectElement} Node
*/
function addHtmlSpan(element, node) {
function addHtmlSpan(element, node, width, classes) {
const fo = element.append('foreignObject');
const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
const div = fo.append('xhtml:div');
// const div = body.append('div');
// const div = fo.append('div');
const label = node.label;
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
div.html(
'<span class="' +
labelClass +
'" ' +
`<span class="${labelClass} ${classes}" ` +
(node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
'>' +
label +
@@ -37,16 +40,22 @@ function addHtmlSpan(element, node) {
);
applyStyle(div, node.labelStyle);
div.style('display', 'inline-block');
const bbox = div.node().getBoundingClientRect();
div.style('display', 'table-cell');
div.style('white-space', 'nowrap');
div.style('max-width', width + 'px');
div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
let bbox = div.node().getBoundingClientRect();
if (bbox.width === width) {
div.style('display', 'table');
div.style('white-space', 'break-spaces');
div.style('width', '200px');
bbox = div.node().getBoundingClientRect();
}
fo.style('width', bbox.width);
fo.style('height', bbox.height);
const divNode = div.node();
window.divNode = divNode;
// Fix for firefox
div.style('white-space', 'nowrap');
div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
return fo.node();
}
@@ -158,7 +167,7 @@ export const createText = (
),
labelStyle: style.replace('fill:', 'color:'),
};
let vertexNode = addHtmlSpan(el, node);
let vertexNode = addHtmlSpan(el, node, width, classes);
return vertexNode;
} else {
const structuredText = markdownToLines(text);

View File

@@ -1,12 +1,25 @@
import SimpleMarkdown from '@khanacademy/simple-markdown';
/**
*
* @param markdown
*/
function preprocessMarkdown(markdown) {
// Replace multiple newlines with a single newline
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
// Remove extra spaces at the beginning of each line
const withoutExtraSpaces = withoutMultipleNewlines.replace(/^\s+/gm, '');
return withoutExtraSpaces;
}
/**
*
* @param markdown
*/
export function markdownToLines(markdown) {
const preprocessedMarkdown = preprocessMarkdown(markdown);
const mdParse = SimpleMarkdown.defaultBlockParse;
const syntaxTree = mdParse(markdown);
const syntaxTree = mdParse(preprocessedMarkdown);
let lines = [[]];
let currentLine = 0;
@@ -19,6 +32,7 @@ export function markdownToLines(markdown) {
function processNode(node, parentType) {
if (node.type === 'text') {
const textLines = node.content.split('\n');
textLines.forEach((textLine, index) => {
if (index !== 0) {
currentLine++;
@@ -62,7 +76,7 @@ export function markdownToHTML(markdown) {
*/
function output(node) {
if (node.type === 'text') {
return node.content.replace(/\n/g, '<br>');
return node.content.replace(/\n/g, '<br/>');
} else if (node.type === 'strong') {
return `<strong>${node.content.map(output).join('')}</strong>`;
} else if (node.type === 'em') {

View File

@@ -95,6 +95,47 @@ test('markdownToLines - Only bold formatting', () => {
expect(output).toEqual(expectedOutput);
});
test('markdownToLines - paragraph 1', () => {
const input = `**Start** with
a second line`;
const expectedOutput = [
[
{ content: 'Start', type: 'strong' },
{ content: 'with', type: 'normal' },
],
[
{ content: 'a', type: 'normal' },
{ content: 'second', type: 'normal' },
{ content: 'line', type: 'normal' },
],
];
const output = markdownToLines(input);
expect(output).toEqual(expectedOutput);
});
test('markdownToLines - paragraph', () => {
const input = `**Start** with
a second line`;
const expectedOutput = [
[
{ content: 'Start', type: 'strong' },
{ content: 'with', type: 'normal' },
],
[
{ content: 'a', type: 'normal' },
{ content: 'second', type: 'normal' },
{ content: 'line', type: 'normal' },
],
];
const output = markdownToLines(input);
expect(output).toEqual(expectedOutput);
});
test('markdownToLines - Only italic formatting', () => {
const input = `This is an *italic* test`;
@@ -134,7 +175,7 @@ Here is a new line
There is some words **with a bold** section
Here is a line *with an italic* section`;
const expectedOutput = `<p>This is regular text<br>Here is a new line<br>There is some words <strong>with a bold</strong> section<br>Here is a line <em>with an italic</em> section</p>`;
const expectedOutput = `<p>This is regular text<br/>Here is a new line<br/>There is some words <strong>with a bold</strong> section<br/>Here is a line <em>with an italic</em> section</p>`;
const output = markdownToHTML(input);
expect(output).toEqual(expectedOutput);
@@ -151,7 +192,7 @@ test('markdownToHTML - No formatting', () => {
const input = `This is a simple test
with no formatting`;
const expectedOutput = `<p>This is a simple test<br>with no formatting</p>`;
const expectedOutput = `<p>This is a simple test<br/>with no formatting</p>`;
const output = markdownToHTML(input);
expect(output).toEqual(expectedOutput);
});