Improved paste handling (#4474)

* Improved paste handling

* Remove file
This commit is contained in:
Tom Moor
2022-11-24 06:50:31 -08:00
committed by GitHub
parent a6125be6f1
commit a8936039e5
4 changed files with 59 additions and 26 deletions

View File

@@ -1,8 +1,12 @@
import { observer } from "mobx-react";
import { Slice } from "prosemirror-model";
import { Selection } from "prosemirror-state";
import { __parseFromClipboard } from "prosemirror-view";
import * as React from "react";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import isMarkdown from "@shared/editor/lib/isMarkdown";
import normalizePastedMarkdown from "@shared/editor/lib/markdown/normalize";
import { light } from "@shared/styles/theme";
import {
getCurrentDateAsString,
@@ -120,9 +124,12 @@ const EditableTitle = React.forwardRef(
const handlePaste = React.useCallback(
(event: React.ClipboardEvent) => {
event.preventDefault();
const text = event.clipboardData.getData("text/plain");
const html = event.clipboardData.getData("text/html");
const [firstLine, ...rest] = text.split(`\n`);
const content = rest.join(`\n`).trim();
window.document.execCommand(
"insertText",
false,
@@ -130,11 +137,37 @@ const EditableTitle = React.forwardRef(
);
if (editor && content) {
const { view, parser } = editor;
const { view, pasteParser } = editor;
let slice;
if (isMarkdown(text)) {
const paste = pasteParser.parse(normalizePastedMarkdown(content));
slice = paste.slice(0);
} else {
const defaultSlice = __parseFromClipboard(
view,
text,
html,
false,
view.state.selection.$from
);
// remove first node from slice
slice = defaultSlice.content.firstChild
? new Slice(
defaultSlice.content.cut(
defaultSlice.content.firstChild.nodeSize
),
defaultSlice.openStart,
defaultSlice.openEnd
)
: defaultSlice;
}
view.dispatch(
view.state.tr
.setSelection(Selection.atStart(view.state.doc))
.insert(0, parser.parse(content))
.replaceSelection(slice)
);
}
},

View File

@@ -19,7 +19,7 @@ export default function isMarkdown(text: string): boolean {
}
// list-ish
const listItems = text.match(/^[\d-*].?\s\S+/gm);
const listItems = text.match(/^([-*]|\d+.)\s\S+/gm);
if (listItems && listItems.length > 1) {
return true;
}

View File

@@ -0,0 +1,22 @@
/**
* Add support for additional syntax that users paste even though it isn't
* supported by the markdown parser directly by massaging the text content.
*
* @param text The incoming pasted plain text
*/
export default function normalizePastedMarkdown(text: string): string {
const CHECKBOX_REGEX = /^\s?(\[(X|\s|_|-)\]\s(.*)?)/gim;
// find checkboxes not contained in a list and wrap them in list items
while (text.match(CHECKBOX_REGEX)) {
text = text.replace(CHECKBOX_REGEX, (match) => `- ${match.trim()}`);
}
// find multiple newlines and insert a hard break to ensure they are respected
text = text.replace(/\n{3,}/g, "\n\n\\\n");
// find single newlines and insert an extra to ensure they are treated as paragraphs
text = text.replace(/\b\n\b/g, "\n\n");
return text;
}

View File

@@ -4,6 +4,7 @@ import { isInTable } from "prosemirror-tables";
import { isUrl } from "../../utils/urls";
import Extension from "../lib/Extension";
import isMarkdown from "../lib/isMarkdown";
import normalizePastedMarkdown from "../lib/markdown/normalize";
import isInCode from "../queries/isInCode";
import { LANGUAGES } from "./Prism";
@@ -13,29 +14,6 @@ function isDropboxPaper(html: string): boolean {
return html?.includes("usually-unique-id");
}
/**
* Add support for additional syntax that users paste even though it isn't
* supported by the markdown parser directly by massaging the text content.
*
* @param text The incoming pasted plain text
*/
function normalizePastedMarkdown(text: string): string {
const CHECKBOX_REGEX = /^\s?(\[(X|\s|_|-)\]\s(.*)?)/gim;
// find checkboxes not contained in a list and wrap them in list items
while (text.match(CHECKBOX_REGEX)) {
text = text.replace(CHECKBOX_REGEX, (match) => `- ${match.trim()}`);
}
// find multiple newlines and insert a hard break to ensure they are respected
text = text.replace(/\n{2,}/g, "\n\n\\\n");
// find single newlines and insert an extra to ensure they are treated as paragraphs
text = text.replace(/\b\n\b/g, "\n\n");
return text;
}
export default class PasteHandler extends Extension {
get name() {
return "markdown-paste";