From c2ad69951f6a1b1a715164703dade34e28d6ddeb Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Thu, 10 Aug 2023 12:54:51 -0700 Subject: [PATCH] Eliminate extra newlines when pasting composer text back into composer --- patches/quill+1.3.7.patch | 36 +++++++++++++++++++++++++------ ts/quill/signal-clipboard/util.ts | 13 +++++++---- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/patches/quill+1.3.7.patch b/patches/quill+1.3.7.patch index b21d23edd..a8d2360d1 100644 --- a/patches/quill+1.3.7.patch +++ b/patches/quill+1.3.7.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/quill/dist/quill.js b/node_modules/quill/dist/quill.js -index 811b3d0..135dfb2 100644 +index 811b3d0..fe5d034 100644 --- a/node_modules/quill/dist/quill.js +++ b/node_modules/quill/dist/quill.js @@ -8896,7 +8896,8 @@ var debug = (0, _logger2.default)('quill:clipboard'); @@ -8,7 +8,7 @@ index 811b3d0..135dfb2 100644 -var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]]; +// var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchBlot], [Node.ELEMENT_NODE, matchSpacing], [Node.ELEMENT_NODE, matchAttributor], [Node.ELEMENT_NODE, matchStyles], ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], ['style', matchIgnore]]; -+var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline], [Node.ELEMENT_NODE, matchSpacing]]; ++var CLIPBOARD_CONFIG = [[Node.TEXT_NODE, matchText], [Node.TEXT_NODE, matchNewline], ['br', matchBreak], [Node.ELEMENT_NODE, matchNewline]]; var ATTRIBUTE_ATTRIBUTORS = [_align.AlignAttribute, _direction.DirectionAttribute].reduce(function (memo, attr) { memo[attr.keyName] = attr; @@ -83,7 +83,20 @@ index 811b3d0..135dfb2 100644 } function deltaEndsWith(delta, text) { -@@ -9074,24 +9081,30 @@ function deltaEndsWith(delta, text) { +@@ -9070,28 +9077,43 @@ function deltaEndsWith(delta, text) { + } + return endText.slice(-1 * text.length) === text; + } ++function deltaIs(delta, text) { ++ var allText = ""; ++ for (var i = delta.ops.length - 1; i >= 0 && allText.length <= text.length; --i) { ++ var op = delta.ops[i]; ++ if (typeof op.insert !== 'string') break; ++ allText = op.insert + allText; ++ } ++ return allText === text; ++} + function isLine(node) { if (node.childNodes.length === 0) return false; // Exclude embed blocks var style = computeStyle(node); @@ -120,20 +133,29 @@ index 811b3d0..135dfb2 100644 }, childrenDelta); } return delta.concat(childrenDelta); -@@ -9177,8 +9190,10 @@ function matchIndent(node, delta) { +@@ -9177,8 +9199,10 @@ function matchIndent(node, delta) { } function matchNewline(node, delta) { - if (!deltaEndsWith(delta, '\n')) { - if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) { + // if (!deltaEndsWith(delta, '\n')) { -+ if (!deltaEndsWith(delta, '\n\n')) { ++ if (!deltaIs(delta, '\n') && !deltaEndsWith(delta, '\n\n')) { + // if (isLine(node) || delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) { + if (delta.length() > 0 && isLine(node)) { delta.insert('\n'); } } -@@ -9214,7 +9229,7 @@ function matchStyles(node, delta) { +@@ -9186,7 +9210,7 @@ function matchNewline(node, delta) { + } + + function matchSpacing(node, delta) { +- if (isLine(node) && node.nextElementSibling != null && !deltaEndsWith(delta, '\n\n')) { ++ if (isLine(node) && node.nextElementSibling != null && !deltaIs(delta, '\n') && !deltaEndsWith(delta, '\n\n')) { + var nodeHeight = node.offsetHeight + parseFloat(computeStyle(node).marginTop) + parseFloat(computeStyle(node).marginBottom); + if (node.nextElementSibling.offsetTop > node.offsetTop + nodeHeight * 1.5) { + delta.insert('\n'); +@@ -9214,7 +9238,7 @@ function matchStyles(node, delta) { return delta; } @@ -142,7 +164,7 @@ index 811b3d0..135dfb2 100644 var text = node.data; // Word represents empty line with   if (node.parentNode.tagName === 'O:P') { -@@ -9238,7 +9253,7 @@ function matchText(node, delta) { +@@ -9238,7 +9262,7 @@ function matchText(node, delta) { text = text.replace(/\s+$/, replacer.bind(replacer, false)); } } diff --git a/ts/quill/signal-clipboard/util.ts b/ts/quill/signal-clipboard/util.ts index 35ab000b8..b5553cdbf 100644 --- a/ts/quill/signal-clipboard/util.ts +++ b/ts/quill/signal-clipboard/util.ts @@ -79,7 +79,13 @@ function getStringFromNode( ) { return element.ariaLabel || ''; } - if (nextSibling && element.nodeName === 'BR') { + + // Sometimes we need to add multiple newlines to represent nested divs, and other times + // we only want to add a newline if we know there's another node after this. + const shouldAddNewline = + parent && (nextSibling || parent.childNodes.length === 1); + + if (shouldAddNewline && element.nodeName === 'BR') { return '\n'; } const childCount = element.childNodes.length; @@ -94,13 +100,12 @@ function getStringFromNode( } if ( - parent && - parent.childNodes.length > 1 && + shouldAddNewline && (element.nodeName === 'P' || element.nodeName === 'DIV' || element.nodeName === 'TIME') ) { - if (result.length > 0 && !result.endsWith('\n\n')) { + if (result.length > 0 && result !== '\n' && !result.endsWith('\n\n')) { result += '\n'; } }