From 665eef981bc71635a8c4ce579fb740ee86a541f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=20=D0=A2=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D0=BD=D1=86=D0=B5=D0=B2?= Date: Wed, 15 Feb 2023 18:39:45 +0530 Subject: [PATCH] feat: get markup from html that has only text --- .../behavior/Clipboard/clipboard.ts | 27 ++++++++++++++++--- src/extensions/behavior/Clipboard/utils.ts | 22 +++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/extensions/behavior/Clipboard/clipboard.ts b/src/extensions/behavior/Clipboard/clipboard.ts index 61d79b140..844bbf77d 100644 --- a/src/extensions/behavior/Clipboard/clipboard.ts +++ b/src/extensions/behavior/Clipboard/clipboard.ts @@ -19,7 +19,7 @@ import {isTextSelection, isNodeSelection, isWholeSelection} from '../../../utils import {BaseNode, pType} from '../../base/BaseSchema'; import {isInsideCode} from './code'; -import {DataTransferType, isIosSafariShare} from './utils'; +import {DataTransferType, extractTextContentFromHtml, isIosSafariShare} from './utils'; export type ClipboardPluginOptions = { yfmParser: Parser; @@ -99,12 +99,31 @@ export const clipboard = ({ !e.clipboardData.types.includes(DataTransferType.Yfm) && (data = e.clipboardData.getData(DataTransferType.Html)) ) { - return false; // default html pasting + const textFromHtml = extractTextContentFromHtml(data); + if (textFromHtml) { + const res = tryCatch(() => textParser.parse(textFromHtml)); + if (res.success) { + const docNode = res.result; + const slice = getSliceFromMarkupFragment(docNode.content); + view.dispatch( + trackTransactionMetrics( + view.state.tr.replaceSelection(slice), + 'paste', + {clipboardDataFormat: DataTransferType.Html}, + ), + ); + isPasteHandled = true; + } else { + logger.error(res.error); + console.error(res.error); + } + } else return false; // default html pasting } if ( - (data = e.clipboardData.getData(DataTransferType.Yfm)) || - (data = e.clipboardData.getData(DataTransferType.Text)) + !isPasteHandled && + ((data = e.clipboardData.getData(DataTransferType.Yfm)) || + (data = e.clipboardData.getData(DataTransferType.Text))) ) { let parser: Parser; let dataFormat: string; diff --git a/src/extensions/behavior/Clipboard/utils.ts b/src/extensions/behavior/Clipboard/utils.ts index 0f36f6444..abb010d1d 100644 --- a/src/extensions/behavior/Clipboard/utils.ts +++ b/src/extensions/behavior/Clipboard/utils.ts @@ -33,3 +33,25 @@ export function isFilesFromHtml({types}: DataTransfer): boolean { export function isImageFile(file: File): boolean { return file.type.startsWith('image/'); } + +export function extractTextContentFromHtml(html: string) { + const element = document.createElement('div'); + element.innerHTML = html; + element.replaceChildren(...Array.from(element.children).filter((v) => v.nodeName !== 'META')); + + // Look if all nodes are div or span. That they don't have any classname and have only text child. + if ( + Array.from(element.children).every(({nodeName, classList, childNodes}) => { + return ( + (nodeName === 'DIV' || nodeName === 'SPAN') && + !classList.length && + childNodes.length === 1 && + (childNodes[0].nodeName === '#text' || childNodes[0].nodeName === 'BR') + ); + }) + ) { + return Array.from(element.children).reduce((a, v) => a + v.textContent + '\n', ''); + } + + return null; +}