diff --git a/packages/quill/src/modules/clipboard.ts b/packages/quill/src/modules/clipboard.ts index e4c3f755b5..5ee008ae9e 100644 --- a/packages/quill/src/modules/clipboard.ts +++ b/packages/quill/src/modules/clipboard.ts @@ -364,18 +364,48 @@ function isBetweenInlineElements(node: HTMLElement, scroll: ScrollBlot) { ); } -const preNodes = new WeakMap(); -function isPre(node: Node | null) { - if (node == null) return false; - if (!preNodes.has(node)) { - // @ts-expect-error +enum WhiteSpaceStyle { + Collapsed, + Preserved, + Inherited, +} +const getWhiteSpaceStyle = (node: Node) => { + if (node instanceof HTMLElement) { + const { whiteSpace } = node.style; + if ( + whiteSpace === 'pre' || + whiteSpace === 'pre-wrap' || + whiteSpace === 'break-spaces' + ) { + return WhiteSpaceStyle.Preserved; + } + if (whiteSpace && whiteSpace !== 'inherit') { + return WhiteSpaceStyle.Collapsed; + } if (node.tagName === 'PRE') { - preNodes.set(node, true); - } else { - preNodes.set(node, isPre(node.parentNode)); + return WhiteSpaceStyle.Preserved; } } - return preNodes.get(node); + return WhiteSpaceStyle.Inherited; +}; + +const whiteSpacePreservationMap = new WeakMap(); +function getShouldPreserveWhiteSpaces(node: Node | null) { + if (node == null) return false; + + if (!whiteSpacePreservationMap.has(node)) { + let shouldPreserve = false; + + const style = getWhiteSpaceStyle(node); + if (style === WhiteSpaceStyle.Preserved) { + shouldPreserve = true; + } else if (style === WhiteSpaceStyle.Inherited) { + shouldPreserve = getShouldPreserveWhiteSpaces(node.parentNode); + } + + whiteSpacePreservationMap.set(node, shouldPreserve); + } + return whiteSpacePreservationMap.get(node); } function traverse( @@ -631,7 +661,7 @@ function matchText(node: HTMLElement, delta: Delta, scroll: ScrollBlot) { if (node.parentElement?.tagName === 'O:P') { return delta.insert(text.trim()); } - if (!isPre(node)) { + if (!getShouldPreserveWhiteSpaces(node)) { if ( text.trim().length === 0 && text.includes('\n') && diff --git a/packages/quill/test/unit/modules/clipboard.spec.ts b/packages/quill/test/unit/modules/clipboard.spec.ts index 0ba7c159ed..44e0126758 100644 --- a/packages/quill/test/unit/modules/clipboard.spec.ts +++ b/packages/quill/test/unit/modules/clipboard.spec.ts @@ -361,6 +361,22 @@ describe('Clipboard', () => { ); }); + test('white-space', () => { + const html = '0 1'; + expect(createClipboard().convert({ html })).toEqual( + new Delta().insert('0 1'), + ); + }); + + test('white-space with nested levels', () => { + const html = ` +
0 10 10 1
+ `; + expect(createClipboard().convert({ html })).toEqual( + new Delta().insert('0 10 10 1'), + ); + }); + test('nested list', () => { const delta = createClipboard().convert({ html: '
  1. One
  2. Alpha
',