From d42a0be080ed6b1cce4a65de008279318c8844dc Mon Sep 17 00:00:00 2001 From: Daniel Eder Date: Mon, 1 Aug 2022 15:55:10 +0200 Subject: [PATCH] Add quill paste matchers for MS Word for bullet and ordered lists --- frontend/components/Editor/index.js | 5 ++ .../services/quill/msWordPasteMatchers.js | 52 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 frontend/services/quill/msWordPasteMatchers.js diff --git a/frontend/components/Editor/index.js b/frontend/components/Editor/index.js index a5c00171..2991fac7 100644 --- a/frontend/components/Editor/index.js +++ b/frontend/components/Editor/index.js @@ -1,5 +1,6 @@ import standardToolbarContainer from '~/services/quill/standardToolbarContainer'; import dropOrPasteImageHandler from '~/services/quill/handlers/dropOrPasteImageHandler'; +import msWordPasteMatchers from '~/services/quill/msWordPasteMatchers'; import uploadImageHandler from '~/services/quill/handlers/uploadImageHandler'; import markAsAnswerHandler from '~/services/quill/handlers/markAsAnswerHandler'; import codeBlockHandler from '~/services/quill/handlers/codeBlockHandler'; @@ -73,6 +74,10 @@ const bindings = { handler: formulaHandler }, + clipboard: { + matchers: msWordPasteMatchers + } + // blurOnEsc: { // key: 'Escape', // handler: () => { diff --git a/frontend/services/quill/msWordPasteMatchers.js b/frontend/services/quill/msWordPasteMatchers.js new file mode 100644 index 00000000..c8041783 --- /dev/null +++ b/frontend/services/quill/msWordPasteMatchers.js @@ -0,0 +1,52 @@ +// See https://github.com/quilljs/quill/issues/1225#issuecomment-1000785590 + +import Delta from 'quill-delta'; + +function matchMsWordList(node, delta) { + // Clone the operations + let ops = delta.ops.map((op) => Object.assign({}, op)); + + // Trim the front of the first op to remove the bullet/number + let bulletOp = ops.find((op) => op.insert && op.insert.trim().length); + if (!bulletOp) { return delta } + + bulletOp.insert = bulletOp.insert.trimLeft(); + let listPrefix = bulletOp.insert.match(/^.*?(^·|\.)/) || bulletOp.insert[0]; + bulletOp.insert = bulletOp.insert.substring(listPrefix[0].length, bulletOp.insert.length); + + // Trim the newline off the last op + let last = ops[ops.length-1]; + last.insert = last.insert.substring(0, last.insert.length - 1); + + // Determine the list type + let listType = listPrefix[0].length === 1 ? 'bullet' : 'ordered'; + + // Determine the list indent + let style = node.getAttribute('style').replace(/\n+/g, ''); + let levelMatch = style.match(/level(\d+)/); + let indent = levelMatch ? levelMatch[1] - 1 : 0; + + // Add the list attribute + ops.push({insert: '\n', attributes: {list: listType, indent}}) + + return new Delta(ops); +} + +function maybeMatchMsWordList(node, delta) { + if (delta.ops[0].insert.trimLeft()[0] === '·') { + return matchMsWordList(node, delta); + } + + return delta; +} + +const msWordPasteMatchers = [ + ['p.MsoListParagraphCxSpFirst', matchMsWordList], + ['p.MsoListParagraphCxSpMiddle', matchMsWordList], + ['p.MsoListParagraphCxSpLast', matchMsWordList], + ['p.MsoListParagraph', matchMsWordList], + ['p.msolistparagraph', matchMsWordList], + ['p.MsoNormal', maybeMatchMsWordList] +]; + +export default msWordPasteMatchers;