UNPKG

@blocknote/core

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

153 lines (132 loc) 3.69 kB
import { Extension } from "@tiptap/core"; import { Plugin } from "prosemirror-state"; import type { BlockNoteEditor, BlockNoteEditorOptions, } from "../../../editor/BlockNoteEditor"; import { isMarkdown } from "../../parsers/markdown/detectMarkdown.js"; import { BlockSchema, InlineContentSchema, StyleSchema, } from "../../../schema/index.js"; import { acceptedMIMETypes } from "./acceptedMIMETypes.js"; import { handleFileInsertion } from "./handleFileInsertion.js"; import { handleVSCodePaste } from "./handleVSCodePaste.js"; function defaultPasteHandler({ event, editor, prioritizeMarkdownOverHTML, plainTextAsMarkdown, }: { event: ClipboardEvent; editor: BlockNoteEditor<any, any, any>; prioritizeMarkdownOverHTML: boolean; plainTextAsMarkdown: boolean; }) { // Special case for code blocks, as they do not support any rich text // formatting, so we force pasting plain text. const isInCodeBlock = editor.transact( (tr) => tr.selection.$from.parent.type.spec.code && tr.selection.$to.parent.type.spec.code, ); if (isInCodeBlock) { const data = event.clipboardData?.getData("text/plain"); if (data) { editor.pasteText(data); return true; } } let format: (typeof acceptedMIMETypes)[number] | undefined; for (const mimeType of acceptedMIMETypes) { if (event.clipboardData!.types.includes(mimeType)) { format = mimeType; break; } } if (!format) { return true; } if (format === "vscode-editor-data") { handleVSCodePaste(event, editor.prosemirrorView!); return true; } if (format === "Files") { handleFileInsertion(event, editor); return true; } const data = event.clipboardData!.getData(format); if (format === "blocknote/html") { // Is blocknote/html, so no need to convert it editor.pasteHTML(data, true); return true; } if (format === "text/markdown") { editor.pasteMarkdown(data); return true; } if (prioritizeMarkdownOverHTML) { // Use plain text instead of HTML if it looks like Markdown const plainText = event.clipboardData!.getData("text/plain"); if (isMarkdown(plainText)) { editor.pasteMarkdown(plainText); return true; } } if (format === "text/html") { editor.pasteHTML(data); return true; } if (plainTextAsMarkdown) { editor.pasteMarkdown(data); return true; } editor.pasteText(data); return true; } export const createPasteFromClipboardExtension = < BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema, >( editor: BlockNoteEditor<BSchema, I, S>, pasteHandler: Exclude< BlockNoteEditorOptions<any, any, any>["pasteHandler"], undefined >, ) => Extension.create({ name: "pasteFromClipboard", addProseMirrorPlugins() { return [ new Plugin({ props: { handleDOMEvents: { paste(_view, event) { event.preventDefault(); if (!editor.isEditable) { return; } return pasteHandler({ event, editor, defaultPasteHandler: ({ prioritizeMarkdownOverHTML = true, plainTextAsMarkdown = true, } = {}) => { return defaultPasteHandler({ event, editor, prioritizeMarkdownOverHTML, plainTextAsMarkdown, }); }, }); }, }, }, }), ]; }, });