Skip to content

Commit

Permalink
fix: fix TextNode space
Browse files Browse the repository at this point in the history
  • Loading branch information
jkcs committed Oct 28, 2023
1 parent 63327d8 commit dfd2583
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,48 @@ describe('htmlTextNodeToString', () => {
describe('when text node with text and \n characters', () => {
it('should strip \n characters from start and end', () => {
const input = document.createTextNode('\n\n\ntest\n\ntest\n\n');
const output = 'test\n\ntest';
const output = 'test test';

expect(htmlTextNodeToString(input)).toEqual(output);
});
});

describe('when htmlTextNodeToString', () => {
const text = `${'\n'} ${'\t'} hello ${'\n'}world!`;
const baseInput = (whiteSpace): Text => {
const textNode = document.createTextNode(text);
const parentDom = document.createElement('div');
parentDom.style.whiteSpace = whiteSpace;

parentDom.append(textNode);

return textNode;
};

it('white-space: normal', () => {
expect(htmlTextNodeToString(baseInput('normal'))).toEqual(`hello world!`);
});

it('white-space: nowrap', () => {
expect(htmlTextNodeToString(baseInput('nowrap'))).toEqual(`hello world!`);
});

it('white-space: pre', () => {
expect(htmlTextNodeToString(baseInput('pre'))).toEqual(text);
});

it('white-space: pre-wrap', () => {
expect(htmlTextNodeToString(baseInput('pre-wrap'))).toEqual(text);
});

it('white-space: pre-line', () => {
expect(htmlTextNodeToString(baseInput('pre-line'))).toEqual(
`${'\n'} hello ${'\n'}world!`
);
});

it('white-space: break-spaces', () => {
expect(htmlTextNodeToString(baseInput('break-spaces'))).toEqual(text);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,101 @@
*/
import { isHtmlText } from './isHtmlText';

export const htmlTextNodeToString = (node: HTMLElement | ChildNode) => {
function getStyleFromNode(node: HTMLElement | ChildNode): string {
if (!(node as HTMLElement).getAttribute) return '';

const styleAttr = (node as HTMLElement).getAttribute('style');

return styleAttr ? styleAttr.trim() : '';
}

// Can use the npm package style-to-object.
// https://www.npmjs.com/package/style-to-object
function styleToObject(styleText: string): Record<string, string> {
const styleObject = {};
const declarations = styleText.split(';');

const camelCase = (str: string) => {
return str.replaceAll(/-([a-z])/g, (match: string, letter: string) => {
return letter.toUpperCase();
});
};

declarations.forEach((declaration) => {
declaration = declaration.trim();
if (declaration) {
const [property, value] = declaration.split(':');
const propertyName = camelCase(property.trim());
// @ts-ignore
styleObject[propertyName] = value.trim();
}
});

return styleObject;
}

function findParentElementWhiteSpace(node: HTMLElement | ChildNode) {
let parentNode = node.parentNode;

while (parentNode != null) {
const styleStr = getStyleFromNode(parentNode as typeof node);
const styles = styleStr ? styleToObject(styleStr) : {};

if (parentNode.nodeType === Node.ELEMENT_NODE && styles['whiteSpace']) {
// If parentNode.nodeType === 'PRE', do not perform any processing here.
return styles['whiteSpace'];
}

parentNode = parentNode.parentNode;
}

return '';
}

const mergeWhitespace = (node: HTMLElement | ChildNode) => {
let parentWhiteSpace = findParentElementWhiteSpace(node);

if (!parentWhiteSpace) {
parentWhiteSpace = 'normal';
}

switch (parentWhiteSpace) {
case 'normal':
case 'nowrap': {
node.textContent =
node.textContent && node.textContent.trim().replaceAll(/\s+/g, ' ');
break;
}
case 'pre-line': {
node.textContent =
node.textContent && node.textContent.replaceAll(/[\t ]+/g, ' ');
break;
}
case 'pre':
node.textContent =
node.textContent && node.textContent.replace(/\n+$/, '');
break;
case 'break-spaces':
case 'pre-wrap':
default: {
break;
}
}
};

export const htmlTextNodeToString = (
node: HTMLElement | ChildNode,
stripWhitespace = true
) => {
if (isHtmlText(node)) {
const trimmedText = node.textContent?.replace(/^\n+|\n+$/g, '') ?? '';

if (stripWhitespace) {
mergeWhitespace(node);
} else {
node.textContent = node.textContent?.replace(/^\n+|\n+$/g, '') ?? '';
}

const trimmedText = node.textContent ?? '';
return trimmedText.length > 0 ? trimmedText : null;
}
};

0 comments on commit dfd2583

Please sign in to comment.