From 355ba754080dc3db620d03e87b6673920d0b73a3 Mon Sep 17 00:00:00 2001 From: Zihua Li <635902+luin@users.noreply.github.com> Date: Sat, 20 Jul 2024 22:34:50 +0800 Subject: [PATCH] getSemanticHTML() exports white-space style when necessary --- packages/quill/src/core/editor.ts | 17 ++++- packages/quill/test/unit/core/editor.spec.ts | 65 +++++++++++++++++++- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/packages/quill/src/core/editor.ts b/packages/quill/src/core/editor.ts index a19485840d..50a5c4a45d 100644 --- a/packages/quill/src/core/editor.ts +++ b/packages/quill/src/core/editor.ts @@ -360,6 +360,10 @@ function convertListHTML( return `${convertListHTML(items, lastIndent - 1, types)}`; } +const SPACE_EXCLUDE_NBSP = '[^\\S\\u00a0]'; +const COLLAPSIBLE_SPACES_REGEX = new RegExp( + `^${SPACE_EXCLUDE_NBSP}|${SPACE_EXCLUDE_NBSP}{2,}|${SPACE_EXCLUDE_NBSP}$`, +); function convertHTML( blot: Blot, index: number, @@ -370,7 +374,18 @@ function convertHTML( return blot.html(index, length); } if (blot instanceof TextBlot) { - return escapeText(blot.value().slice(index, index + length)); + const escapedText = escapeText(blot.value().slice(index, index + length)); + + if (!COLLAPSIBLE_SPACES_REGEX.test(escapedText)) { + return escapedText; + } + + const { parentElement } = blot.domNode; + const style = + parentElement?.ownerDocument.defaultView?.getComputedStyle(parentElement); + return style?.whiteSpace + ? `${escapedText}` + : escapedText; } if (blot instanceof ParentBlot) { // TODO fix API diff --git a/packages/quill/test/unit/core/editor.spec.ts b/packages/quill/test/unit/core/editor.spec.ts index 0c595332bb..0e09122e5f 100644 --- a/packages/quill/test/unit/core/editor.spec.ts +++ b/packages/quill/test/unit/core/editor.spec.ts @@ -28,9 +28,11 @@ import { ColorClass } from '../../../src/formats/color.js'; import Quill from '../../../src/core.js'; import { normalizeHTML } from '../__helpers__/utils.js'; -const createEditor = (html: string) => { +const createEditor = (htmlOrContents: string | Delta) => { const container = document.createElement('div'); - container.innerHTML = normalizeHTML(html); + if (typeof htmlOrContents === 'string') { + container.innerHTML = normalizeHTML(htmlOrContents); + } document.body.appendChild(container); const quill = new Quill(container, { registry: createRegistry([ @@ -54,9 +56,17 @@ const createEditor = (html: string) => { SizeClass, ]), }); + if (typeof htmlOrContents !== 'string') { + quill.setContents(htmlOrContents); + } return quill.editor; }; +const applyWhiteSpace = (editor: Editor, whiteSpace: string) => { + editor.scroll.domNode.style.whiteSpace = whiteSpace; + return editor; +}; + describe('Editor', () => { describe('insert', () => { test('text', () => { @@ -1246,6 +1256,57 @@ describe('Editor', () => { ); }); + test('collapsible spaces', () => { + expect( + applyWhiteSpace( + createEditor('

123 123 123

'), + 'pre-wrap', + ).getHTML(0, 11), + ).toEqual( + '123 123 123', + ); + + expect( + applyWhiteSpace( + createEditor(new Delta().insert('1 2\n')), + 'pre-wrap', + ).getHTML(0, 5), + ).toEqual('1 2'); + + expect( + applyWhiteSpace( + createEditor( + new Delta().insert(' 123', { bold: true }).insert('\n'), + ), + 'pre-wrap', + ).getHTML(0, 5), + ).toEqual( + ' 123', + ); + }); + + test(' ', () => { + expect( + applyWhiteSpace( + createEditor( + new Delta().insert('\u00a0 123', { bold: true }).insert('\n'), + ), + 'pre-wrap', + ).getHTML(0, 5), + ).toEqual('\u00a0 123'); + + expect( + applyWhiteSpace( + createEditor( + new Delta().insert('\u00a0 123', { bold: true }).insert('\n'), + ), + 'pre-wrap', + ).getHTML(0, 6), + ).toEqual( + '\u00a0 123', + ); + }); + test('mixed list', () => { const editor = createEditor( `