@chayns-components/emoji-input 5.5.10 → 5.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/utils/text.js +10 -0
- package/lib/cjs/utils/text.js.map +1 -1
- package/lib/cjs/utils/text.test.js +34 -0
- package/lib/cjs/utils/text.test.js.map +1 -0
- package/lib/esm/utils/text.js +10 -0
- package/lib/esm/utils/text.js.map +1 -1
- package/lib/esm/utils/text.test.js +32 -0
- package/lib/esm/utils/text.test.js.map +1 -0
- package/lib/types/utils/text.test.d.ts +1 -0
- package/package.json +8 -5
package/lib/cjs/utils/text.js
CHANGED
|
@@ -27,6 +27,13 @@ const serializeBlockElementToText = element => {
|
|
|
27
27
|
}
|
|
28
28
|
return serializedText;
|
|
29
29
|
};
|
|
30
|
+
const serializeSpanElementToText = element => {
|
|
31
|
+
const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);
|
|
32
|
+
if (childNodes.length === 0) {
|
|
33
|
+
return BLOCK_SEPARATOR;
|
|
34
|
+
}
|
|
35
|
+
return serializeHTMLToText(childNodes);
|
|
36
|
+
};
|
|
30
37
|
const serializeHTMLNodeToText = node => {
|
|
31
38
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
32
39
|
return node.textContent ?? '';
|
|
@@ -38,6 +45,9 @@ const serializeHTMLNodeToText = node => {
|
|
|
38
45
|
if (element.tagName === 'BR') {
|
|
39
46
|
return '<br>';
|
|
40
47
|
}
|
|
48
|
+
if (element.tagName === 'SPAN') {
|
|
49
|
+
return serializeSpanElementToText(element);
|
|
50
|
+
}
|
|
41
51
|
if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {
|
|
42
52
|
return serializeBlockElementToText(element);
|
|
43
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":["_regex","require","_emoji","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","_node$textContent","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","exports","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","unescapeHTML","replace","HTML_A_TAG_REGEX","BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","_","prefix","type","value","entity","prefixAttr","BB_IGNORE_EMOJI_REGEX","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","container","escapeHTML","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/ /g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"text.js","names":["_regex","require","_emoji","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","_node$textContent","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeSpanElementToText","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","exports","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","unescapeHTML","replace","HTML_A_TAG_REGEX","BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","_","prefix","type","value","entity","prefixAttr","BB_IGNORE_EMOJI_REGEX","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","container","escapeHTML","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeSpanElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return BLOCK_SEPARATOR;\n }\n\n return serializeHTMLToText(childNodes);\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (element.tagName === 'SPAN') {\n return serializeSpanElementToText(element);\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/ /g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _vitest = require("vitest");
|
|
4
|
+
var _text = require("./text");
|
|
5
|
+
const serializeHTML = html => {
|
|
6
|
+
const container = document.createElement('div');
|
|
7
|
+
container.innerHTML = html;
|
|
8
|
+
return (0, _text.serializeHTMLToText)(container.childNodes);
|
|
9
|
+
};
|
|
10
|
+
(0, _vitest.describe)('serializeHTMLToText', () => {
|
|
11
|
+
(0, _vitest.it)('normalizes Safari styled spans without losing an empty line', () => {
|
|
12
|
+
const result = serializeHTML('question?<span style="font-size: var(--base-font-size, 15px);"></span><span style="font-size: var(--base-font-size, 15px);">Unsere Logs…</span>');
|
|
13
|
+
(0, _vitest.expect)(result).toBe('question?<br>Unsere Logs…');
|
|
14
|
+
(0, _vitest.expect)(result).not.toContain('<span');
|
|
15
|
+
(0, _vitest.expect)(result).not.toContain('</span>');
|
|
16
|
+
(0, _vitest.expect)(result).not.toContain('style=');
|
|
17
|
+
(0, _vitest.expect)(result.match(/<br>/g) ?? []).toHaveLength(1);
|
|
18
|
+
});
|
|
19
|
+
(0, _vitest.it)('preserves line breaks, nesting, and block structure inside spans', () => {
|
|
20
|
+
(0, _vitest.expect)(serializeHTML('<span><br></span>')).toBe('<br>');
|
|
21
|
+
(0, _vitest.expect)(serializeHTML('<span>before<span>nested</span>after</span>')).toBe('beforenestedafter');
|
|
22
|
+
(0, _vitest.expect)(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');
|
|
23
|
+
});
|
|
24
|
+
(0, _vitest.it)('keeps semantic replacements and unsupported-element fallback behavior', () => {
|
|
25
|
+
(0, _vitest.expect)((0, _text.convertHTMLToText)('<span><span class="no-emoji-convert">:smile:</span></span>', {
|
|
26
|
+
preserveSpaces: true
|
|
27
|
+
})).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');
|
|
28
|
+
(0, _vitest.expect)((0, _text.convertHTMLToText)('<span><lc_mention id="user"><span>@</span>Jane</lc_mention></span>', {
|
|
29
|
+
preserveSpaces: true
|
|
30
|
+
})).toBe('[lc_mention id="user"]Jane[/lc_mention]');
|
|
31
|
+
(0, _vitest.expect)(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=text.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.test.js","names":["_vitest","require","_text","serializeHTML","html","container","document","createElement","innerHTML","serializeHTMLToText","childNodes","describe","it","result","expect","toBe","not","toContain","match","toHaveLength","convertHTMLToText","preserveSpaces"],"sources":["../../../src/utils/text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { convertHTMLToText, serializeHTMLToText } from './text';\n\nconst serializeHTML = (html: string) => {\n const container = document.createElement('div');\n container.innerHTML = html;\n\n return serializeHTMLToText(container.childNodes);\n};\n\ndescribe('serializeHTMLToText', () => {\n it('normalizes Safari styled spans without losing an empty line', () => {\n const result = serializeHTML(\n 'question?<span style=\"font-size: var(--base-font-size, 15px);\"></span><span style=\"font-size: var(--base-font-size, 15px);\">Unsere Logs…</span>',\n );\n\n expect(result).toBe('question?<br>Unsere Logs…');\n expect(result).not.toContain('<span');\n expect(result).not.toContain('</span>');\n expect(result).not.toContain('style=');\n expect(result.match(/<br>/g) ?? []).toHaveLength(1);\n });\n\n it('preserves line breaks, nesting, and block structure inside spans', () => {\n expect(serializeHTML('<span><br></span>')).toBe('<br>');\n expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe(\n 'beforenestedafter',\n );\n expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');\n });\n\n it('keeps semantic replacements and unsupported-element fallback behavior', () => {\n expect(\n convertHTMLToText('<span><span class=\"no-emoji-convert\">:smile:</span></span>', {\n preserveSpaces: true,\n }),\n ).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');\n expect(\n convertHTMLToText(\n '<span><lc_mention id=\"user\"><span>@</span>Jane</lc_mention></span>',\n { preserveSpaces: true },\n ),\n ).toBe('[lc_mention id=\"user\"]Jane[/lc_mention]');\n expect(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');\n });\n});\n"],"mappings":";;AAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,KAAA,GAAAD,OAAA;AAEA,MAAME,aAAa,GAAIC,IAAY,IAAK;EACpC,MAAMC,SAAS,GAAGC,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC/CF,SAAS,CAACG,SAAS,GAAGJ,IAAI;EAE1B,OAAO,IAAAK,yBAAmB,EAACJ,SAAS,CAACK,UAAU,CAAC;AACpD,CAAC;AAED,IAAAC,gBAAQ,EAAC,qBAAqB,EAAE,MAAM;EAClC,IAAAC,UAAE,EAAC,6DAA6D,EAAE,MAAM;IACpE,MAAMC,MAAM,GAAGV,aAAa,CACxB,iJACJ,CAAC;IAED,IAAAW,cAAM,EAACD,MAAM,CAAC,CAACE,IAAI,CAAC,2BAA2B,CAAC;IAChD,IAAAD,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,OAAO,CAAC;IACrC,IAAAH,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,SAAS,CAAC;IACvC,IAAAH,cAAM,EAACD,MAAM,CAAC,CAACG,GAAG,CAACC,SAAS,CAAC,QAAQ,CAAC;IACtC,IAAAH,cAAM,EAACD,MAAM,CAACK,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAACC,YAAY,CAAC,CAAC,CAAC;EACvD,CAAC,CAAC;EAEF,IAAAP,UAAE,EAAC,kEAAkE,EAAE,MAAM;IACzE,IAAAE,cAAM,EAACX,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAACY,IAAI,CAAC,MAAM,CAAC;IACvD,IAAAD,cAAM,EAACX,aAAa,CAAC,6CAA6C,CAAC,CAAC,CAACY,IAAI,CACrE,mBACJ,CAAC;IACD,IAAAD,cAAM,EAACX,aAAa,CAAC,4CAA4C,CAAC,CAAC,CAACY,IAAI,CAAC,iBAAiB,CAAC;EAC/F,CAAC,CAAC;EAEF,IAAAH,UAAE,EAAC,uEAAuE,EAAE,MAAM;IAC9E,IAAAE,cAAM,EACF,IAAAM,uBAAiB,EAAC,4DAA4D,EAAE;MAC5EC,cAAc,EAAE;IACpB,CAAC,CACL,CAAC,CAACN,IAAI,CAAC,oCAAoC,CAAC;IAC5C,IAAAD,cAAM,EACF,IAAAM,uBAAiB,EACb,oEAAoE,EACpE;MAAEC,cAAc,EAAE;IAAK,CAC3B,CACJ,CAAC,CAACN,IAAI,CAAC,yCAAyC,CAAC;IACjD,IAAAD,cAAM,EAACX,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAACY,IAAI,CAAC,0BAA0B,CAAC;EACtF,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
|
package/lib/esm/utils/text.js
CHANGED
|
@@ -18,6 +18,13 @@ const serializeBlockElementToText = element => {
|
|
|
18
18
|
}
|
|
19
19
|
return serializedText;
|
|
20
20
|
};
|
|
21
|
+
const serializeSpanElementToText = element => {
|
|
22
|
+
const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);
|
|
23
|
+
if (childNodes.length === 0) {
|
|
24
|
+
return BLOCK_SEPARATOR;
|
|
25
|
+
}
|
|
26
|
+
return serializeHTMLToText(childNodes);
|
|
27
|
+
};
|
|
21
28
|
const serializeHTMLNodeToText = node => {
|
|
22
29
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
23
30
|
return node.textContent ?? '';
|
|
@@ -29,6 +36,9 @@ const serializeHTMLNodeToText = node => {
|
|
|
29
36
|
if (element.tagName === 'BR') {
|
|
30
37
|
return '<br>';
|
|
31
38
|
}
|
|
39
|
+
if (element.tagName === 'SPAN') {
|
|
40
|
+
return serializeSpanElementToText(element);
|
|
41
|
+
}
|
|
32
42
|
if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {
|
|
33
43
|
return serializeBlockElementToText(element);
|
|
34
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":["BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","BB_IGNORE_EMOJI_REGEX","HTML_A_TAG_REGEX","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","escapeHTML","unescapeHTML","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","replace","_","prefix","type","value","entity","prefixAttr","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","container","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/ /g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"text.js","names":["BB_LC_MENTION_REGEX","BB_NER_IGNORE_REGEX","BB_NER_REPLACE_REGEX","BB_IGNORE_EMOJI_REGEX","HTML_A_TAG_REGEX","HTML_BOLD_REGEX","HTML_LC_MENTION_REGEX","HTML_NER_IGNORE_REGEX","HTML_NER_REPLACE_REGEX","HTML_NO_EMOJI_REGEX","escapeHTML","unescapeHTML","BLOCK_ELEMENT_TAG_NAMES","Set","BLOCK_SEPARATOR","isMeaningfulTextNode","node","nodeType","Node","TEXT_NODE","textContent","length","isBreakElement","ELEMENT_NODE","tagName","serializeBlockElementToText","element","childNodes","Array","from","filter","serializedText","serializeHTMLToText","startsWith","slice","serializeSpanElementToText","serializeHTMLNodeToText","has","outerHTML","nodes","serializedNodes","result","didSerializeBlockElement","forEach","serializedNode","isBlockElement","convertTextToHTML","text","document","createElement","style","position","opacity","contentEditable","innerText","body","appendChild","innerHTML","removeChild","replace","_","prefix","type","value","entity","prefixAttr","convertHTMLToText","preserveSpaces","shouldSerializeNoEmojiToBBCode","container","getElementTextLength","textLength","e","cleanupEmptyIgnoreEmojiSpans","html"],"sources":["../../../src/utils/text.ts"],"sourcesContent":["import {\n BB_LC_MENTION_REGEX,\n BB_NER_IGNORE_REGEX,\n BB_NER_REPLACE_REGEX,\n BB_IGNORE_EMOJI_REGEX,\n HTML_A_TAG_REGEX,\n HTML_BOLD_REGEX,\n HTML_LC_MENTION_REGEX,\n HTML_NER_IGNORE_REGEX,\n HTML_NER_REPLACE_REGEX,\n HTML_NO_EMOJI_REGEX,\n} from '../constants/regex';\nimport { escapeHTML, unescapeHTML } from './emoji';\n\ninterface ConvertHTMLToTextOptions {\n preserveSpaces?: boolean;\n shouldSerializeNoEmojiToBBCode?: boolean;\n}\n\nconst BLOCK_ELEMENT_TAG_NAMES = new Set(['DIV', 'P']);\nconst BLOCK_SEPARATOR = '<br>';\n\nconst isMeaningfulTextNode = (node: ChildNode) =>\n node.nodeType !== Node.TEXT_NODE || (node.textContent?.length ?? 0) > 0;\n\nconst isBreakElement = (node: ChildNode) =>\n node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).tagName === 'BR';\n\nconst serializeBlockElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return '';\n }\n\n if (childNodes.length === 1 && isBreakElement(childNodes[0]!)) {\n return '';\n }\n\n let serializedText = serializeHTMLToText(childNodes);\n\n if (\n childNodes.length > 1 &&\n isBreakElement(childNodes[0]!) &&\n serializedText.startsWith(BLOCK_SEPARATOR)\n ) {\n serializedText = serializedText.slice(BLOCK_SEPARATOR.length);\n }\n\n return serializedText;\n};\n\nconst serializeSpanElementToText = (element: HTMLElement) => {\n const childNodes = Array.from(element.childNodes).filter(isMeaningfulTextNode);\n\n if (childNodes.length === 0) {\n return BLOCK_SEPARATOR;\n }\n\n return serializeHTMLToText(childNodes);\n};\n\nconst serializeHTMLNodeToText = (node: ChildNode): string => {\n if (node.nodeType === Node.TEXT_NODE) {\n return node.textContent ?? '';\n }\n\n if (node.nodeType !== Node.ELEMENT_NODE) {\n return '';\n }\n\n const element = node as HTMLElement;\n\n if (element.tagName === 'BR') {\n return '<br>';\n }\n\n if (element.tagName === 'SPAN') {\n return serializeSpanElementToText(element);\n }\n\n if (BLOCK_ELEMENT_TAG_NAMES.has(element.tagName)) {\n return serializeBlockElementToText(element);\n }\n\n return element.outerHTML;\n};\n\nexport const serializeHTMLToText = (nodes: NodeListOf<ChildNode> | ChildNode[]) => {\n const serializedNodes = Array.from(nodes);\n\n let result = '';\n let didSerializeBlockElement = false;\n\n serializedNodes.forEach((node) => {\n const serializedNode = serializeHTMLNodeToText(node);\n\n const isBlockElement =\n node.nodeType === Node.ELEMENT_NODE &&\n BLOCK_ELEMENT_TAG_NAMES.has((node as HTMLElement).tagName);\n\n if (!serializedNode && !isBlockElement) {\n return;\n }\n\n if (result && (isBlockElement || didSerializeBlockElement)) {\n result += BLOCK_SEPARATOR;\n }\n\n result += serializedNode;\n didSerializeBlockElement = isBlockElement;\n });\n\n return result;\n};\n\nexport const convertTextToHTML = (text: string) => {\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerText = text;\n\n document.body.appendChild(element);\n\n let result = element.innerHTML;\n\n document.body.removeChild(element);\n\n result = unescapeHTML(result);\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(\n BB_LC_MENTION_REGEX,\n '<lc_mention contenteditable=\"false\" id=\"$1\"><span>@</span>$2</lc_mention>\\u200B',\n )\n .replace(BB_NER_IGNORE_REGEX, '<nerIgnore contenteditable=\"false\">$1</nerIgnore>')\n .replace(\n BB_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `<nerReplace contenteditable=\"false\" ${prefixAttr}type=\"${type}\" value=\"${value}\">${entity}</nerReplace>`;\n },\n )\n .replace(BB_IGNORE_EMOJI_REGEX, '<span class=\"no-emoji-convert\">$1</span>');\n\n return result;\n};\n\nexport const convertHTMLToText = (\n text: string,\n {\n preserveSpaces = false,\n shouldSerializeNoEmojiToBBCode = true,\n }: ConvertHTMLToTextOptions = {},\n) => {\n let result = text;\n\n result = result\n .replace(HTML_A_TAG_REGEX, '$1')\n .replace(HTML_BOLD_REGEX, '[b]$1[/b]')\n .replace(HTML_LC_MENTION_REGEX, '[lc_mention id=\"$1\"]$2[/lc_mention]')\n .replace(HTML_NER_IGNORE_REGEX, '[nerIgnore]$1[/nerIgnore]')\n .replace(\n HTML_NER_REPLACE_REGEX,\n (_, prefix: string | undefined, type: string, value: string, entity: string) => {\n const prefixAttr = prefix ? `prefix=\"${prefix}\" ` : '';\n\n return `[nerReplace ${prefixAttr}type=\"${type}\" value=\"${value}\"]${entity}[/nerReplace]`;\n },\n )\n .replace(\n HTML_NO_EMOJI_REGEX,\n shouldSerializeNoEmojiToBBCode ? '[ignoreEmoji]$1[/ignoreEmoji]' : '$1',\n );\n\n const container = document.createElement('div');\n\n container.innerHTML = result;\n\n result = serializeHTMLToText(container.childNodes);\n\n if (preserveSpaces) {\n return result\n .replace(/ /g, '\\u00A0') // non-breaking space\n .replace(/\\u200B/g, '\\u200B'); // zero-width space (sichtbar gemacht)\n }\n\n // eslint-disable-next-line no-irregular-whitespace\n result = result.replace(/\\u200B/g, '');\n result = escapeHTML(result);\n\n const element = document.createElement('div');\n\n element.style.position = 'absolute';\n element.style.opacity = '0';\n\n element.contentEditable = 'true';\n element.innerHTML = result;\n\n document.body.appendChild(element);\n\n result = element.innerText;\n\n document.body.removeChild(element);\n\n return result;\n};\n\nexport const getElementTextLength = (element: Element) => {\n let textLength = 0;\n\n try {\n textLength = convertHTMLToText(element.outerHTML, {\n shouldSerializeNoEmojiToBBCode: false,\n }).length;\n } catch (e) {\n // Do nothing\n }\n\n return textLength;\n};\n\nexport const cleanupEmptyIgnoreEmojiSpans = (html: string) => {\n // Remove empty no-emoji-convert spans that have no text content\n return html.replace(/<span class=\"no-emoji-convert\"><\\/span>/g, '');\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { convertHTMLToText, serializeHTMLToText } from './text';
|
|
3
|
+
const serializeHTML = html => {
|
|
4
|
+
const container = document.createElement('div');
|
|
5
|
+
container.innerHTML = html;
|
|
6
|
+
return serializeHTMLToText(container.childNodes);
|
|
7
|
+
};
|
|
8
|
+
describe('serializeHTMLToText', () => {
|
|
9
|
+
it('normalizes Safari styled spans without losing an empty line', () => {
|
|
10
|
+
const result = serializeHTML('question?<span style="font-size: var(--base-font-size, 15px);"></span><span style="font-size: var(--base-font-size, 15px);">Unsere Logs…</span>');
|
|
11
|
+
expect(result).toBe('question?<br>Unsere Logs…');
|
|
12
|
+
expect(result).not.toContain('<span');
|
|
13
|
+
expect(result).not.toContain('</span>');
|
|
14
|
+
expect(result).not.toContain('style=');
|
|
15
|
+
expect(result.match(/<br>/g) ?? []).toHaveLength(1);
|
|
16
|
+
});
|
|
17
|
+
it('preserves line breaks, nesting, and block structure inside spans', () => {
|
|
18
|
+
expect(serializeHTML('<span><br></span>')).toBe('<br>');
|
|
19
|
+
expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe('beforenestedafter');
|
|
20
|
+
expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');
|
|
21
|
+
});
|
|
22
|
+
it('keeps semantic replacements and unsupported-element fallback behavior', () => {
|
|
23
|
+
expect(convertHTMLToText('<span><span class="no-emoji-convert">:smile:</span></span>', {
|
|
24
|
+
preserveSpaces: true
|
|
25
|
+
})).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');
|
|
26
|
+
expect(convertHTMLToText('<span><lc_mention id="user"><span>@</span>Jane</lc_mention></span>', {
|
|
27
|
+
preserveSpaces: true
|
|
28
|
+
})).toBe('[lc_mention id="user"]Jane[/lc_mention]');
|
|
29
|
+
expect(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=text.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.test.js","names":["describe","expect","it","convertHTMLToText","serializeHTMLToText","serializeHTML","html","container","document","createElement","innerHTML","childNodes","result","toBe","not","toContain","match","toHaveLength","preserveSpaces"],"sources":["../../../src/utils/text.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { convertHTMLToText, serializeHTMLToText } from './text';\n\nconst serializeHTML = (html: string) => {\n const container = document.createElement('div');\n container.innerHTML = html;\n\n return serializeHTMLToText(container.childNodes);\n};\n\ndescribe('serializeHTMLToText', () => {\n it('normalizes Safari styled spans without losing an empty line', () => {\n const result = serializeHTML(\n 'question?<span style=\"font-size: var(--base-font-size, 15px);\"></span><span style=\"font-size: var(--base-font-size, 15px);\">Unsere Logs…</span>',\n );\n\n expect(result).toBe('question?<br>Unsere Logs…');\n expect(result).not.toContain('<span');\n expect(result).not.toContain('</span>');\n expect(result).not.toContain('style=');\n expect(result.match(/<br>/g) ?? []).toHaveLength(1);\n });\n\n it('preserves line breaks, nesting, and block structure inside spans', () => {\n expect(serializeHTML('<span><br></span>')).toBe('<br>');\n expect(serializeHTML('<span>before<span>nested</span>after</span>')).toBe(\n 'beforenestedafter',\n );\n expect(serializeHTML('<span><div>first</div><p>second</p></span>')).toBe('first<br>second');\n });\n\n it('keeps semantic replacements and unsupported-element fallback behavior', () => {\n expect(\n convertHTMLToText('<span><span class=\"no-emoji-convert\">:smile:</span></span>', {\n preserveSpaces: true,\n }),\n ).toBe('[ignoreEmoji]:smile:[/ignoreEmoji]');\n expect(\n convertHTMLToText(\n '<span><lc_mention id=\"user\"><span>@</span>Jane</lc_mention></span>',\n { preserveSpaces: true },\n ),\n ).toBe('[lc_mention id=\"user\"]Jane[/lc_mention]');\n expect(serializeHTML('<mark>highlighted</mark>')).toBe('<mark>highlighted</mark>');\n });\n});\n"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAC7C,SAASC,iBAAiB,EAAEC,mBAAmB,QAAQ,QAAQ;AAE/D,MAAMC,aAAa,GAAIC,IAAY,IAAK;EACpC,MAAMC,SAAS,GAAGC,QAAQ,CAACC,aAAa,CAAC,KAAK,CAAC;EAC/CF,SAAS,CAACG,SAAS,GAAGJ,IAAI;EAE1B,OAAOF,mBAAmB,CAACG,SAAS,CAACI,UAAU,CAAC;AACpD,CAAC;AAEDX,QAAQ,CAAC,qBAAqB,EAAE,MAAM;EAClCE,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACpE,MAAMU,MAAM,GAAGP,aAAa,CACxB,iJACJ,CAAC;IAEDJ,MAAM,CAACW,MAAM,CAAC,CAACC,IAAI,CAAC,2BAA2B,CAAC;IAChDZ,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,OAAO,CAAC;IACrCd,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,SAAS,CAAC;IACvCd,MAAM,CAACW,MAAM,CAAC,CAACE,GAAG,CAACC,SAAS,CAAC,QAAQ,CAAC;IACtCd,MAAM,CAACW,MAAM,CAACI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAACC,YAAY,CAAC,CAAC,CAAC;EACvD,CAAC,CAAC;EAEFf,EAAE,CAAC,kEAAkE,EAAE,MAAM;IACzED,MAAM,CAACI,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAACQ,IAAI,CAAC,MAAM,CAAC;IACvDZ,MAAM,CAACI,aAAa,CAAC,6CAA6C,CAAC,CAAC,CAACQ,IAAI,CACrE,mBACJ,CAAC;IACDZ,MAAM,CAACI,aAAa,CAAC,4CAA4C,CAAC,CAAC,CAACQ,IAAI,CAAC,iBAAiB,CAAC;EAC/F,CAAC,CAAC;EAEFX,EAAE,CAAC,uEAAuE,EAAE,MAAM;IAC9ED,MAAM,CACFE,iBAAiB,CAAC,4DAA4D,EAAE;MAC5Ee,cAAc,EAAE;IACpB,CAAC,CACL,CAAC,CAACL,IAAI,CAAC,oCAAoC,CAAC;IAC5CZ,MAAM,CACFE,iBAAiB,CACb,oEAAoE,EACpE;MAAEe,cAAc,EAAE;IAAK,CAC3B,CACJ,CAAC,CAACL,IAAI,CAAC,yCAAyC,CAAC;IACjDZ,MAAM,CAACI,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAACQ,IAAI,CAAC,0BAA0B,CAAC;EACtF,CAAC,CAAC;AACN,CAAC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chayns-components/emoji-input",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.13",
|
|
4
4
|
"description": "Input field that supports HTML elements and emojis",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"browserslist": [
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"build:types": "tsc",
|
|
49
49
|
"build:cjs": "cross-env NODE_ENV=cjs babel src --out-dir lib/cjs --extensions=.ts,.tsx --source-maps --ignore=src/stories --copy-files",
|
|
50
50
|
"build:esm": "cross-env NODE_ENV=esm babel src --out-dir lib/esm --extensions=.ts,.tsx --source-maps --ignore=src/stories --copy-files",
|
|
51
|
+
"test": "vitest --environment jsdom",
|
|
51
52
|
"watch:js": "npm run build:esm -- --watch",
|
|
52
53
|
"link": "npm link && npm run watch:js"
|
|
53
54
|
},
|
|
@@ -66,15 +67,17 @@
|
|
|
66
67
|
"@types/uuid": "^10.0.0",
|
|
67
68
|
"babel-loader": "^9.2.1",
|
|
68
69
|
"cross-env": "^7.0.3",
|
|
70
|
+
"jsdom": "^26.1.0",
|
|
69
71
|
"lerna": "^8.2.4",
|
|
70
72
|
"react": "^18.3.1",
|
|
71
73
|
"react-dom": "^18.3.1",
|
|
72
74
|
"styled-components": "^6.4.4",
|
|
73
|
-
"typescript": "^5.9.3"
|
|
75
|
+
"typescript": "^5.9.3",
|
|
76
|
+
"vitest": "^1.6.1"
|
|
74
77
|
},
|
|
75
78
|
"dependencies": {
|
|
76
|
-
"@chayns-components/core": "^5.5.
|
|
77
|
-
"@chayns-components/textstring": "^5.5.
|
|
79
|
+
"@chayns-components/core": "^5.5.12",
|
|
80
|
+
"@chayns-components/textstring": "^5.5.12",
|
|
78
81
|
"emojilib": "^3.0.12",
|
|
79
82
|
"react-compiler-runtime": "^1.0.0",
|
|
80
83
|
"unicode-emoji-json": "^0.9.0"
|
|
@@ -89,5 +92,5 @@
|
|
|
89
92
|
"publishConfig": {
|
|
90
93
|
"access": "public"
|
|
91
94
|
},
|
|
92
|
-
"gitHead": "
|
|
95
|
+
"gitHead": "70e18cadff10e014b1325d613c345fb9bee9be58"
|
|
93
96
|
}
|