tiptap: add clipboard extension

This commit is contained in:
fantasticit 2022-06-13 18:03:45 +08:00
parent c6fc458ae3
commit 6c1c29ee1a
5 changed files with 121 additions and 69 deletions

View File

@ -0,0 +1,97 @@
import { Extension } from '@tiptap/core';
import { Fragment } from 'prosemirror-model';
import { Plugin, PluginKey } from 'prosemirror-state';
import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/core/constants';
import { copyNode } from 'tiptap/prose-utils';
const isPureText = (content): boolean => {
if (!content) return false;
if (Array.isArray(content)) {
if (content.length > 1) return false;
return isPureText(content[0]);
}
const child = content['content'];
if (child) {
return isPureText(child);
}
return content['type'] === 'text';
};
interface IClipboardOptions {
/**
* prosemirror markdown
*/
prosemirrorToMarkdown: (arg: { content: Fragment }) => string;
}
export const Clipboard = Extension.create<IClipboardOptions>({
name: 'clipboard',
priority: EXTENSION_PRIORITY_HIGHEST,
addOptions() {
return {
prosemirrorToMarkdown: (arg) => String(arg.content),
};
},
addProseMirrorPlugins() {
const extensionThis = this;
return [
new Plugin({
key: new PluginKey('clipboard'),
props: {
handleKeyDown(view, event) {
/**
* Command + C
* Ctrl + C
*/
if ((event.ctrlKey || event.metaKey) && event.keyCode == 67) {
const { state } = view;
// @ts-ignore
const currentNode = state.selection.node;
if (currentNode) {
event.preventDefault();
copyNode(currentNode);
return true;
}
}
return false;
},
clipboardTextSerializer: (slice) => {
const json = slice.content.toJSON();
const isSelectAll = slice.openStart === slice.openEnd && slice.openEnd === 0;
if (typeof json === 'object' || isSelectAll) {
return extensionThis.options.prosemirrorToMarkdown({
content: slice.content,
});
} else {
const isText = isPureText(json) && !isSelectAll;
if (isText) {
return slice.content.textBetween(0, slice.content.size, '\n\n');
}
const doc = slice.content;
if (!doc) {
return '';
}
const content = extensionThis.options.prosemirrorToMarkdown({
content: doc,
});
return content;
}
},
},
}),
];
},
});

View File

@ -5,7 +5,6 @@ import { Fragment, Schema } from 'prosemirror-model';
import { Plugin, PluginKey } from 'prosemirror-state'; import { Plugin, PluginKey } from 'prosemirror-state';
import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/core/constants'; import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/core/constants';
import { import {
copyNode,
handleFileEvent, handleFileEvent,
isInCode, isInCode,
isMarkdown, isMarkdown,
@ -36,22 +35,6 @@ interface IPasteOptions {
prosemirrorToMarkdown: (arg: { content: Fragment }) => string; prosemirrorToMarkdown: (arg: { content: Fragment }) => string;
} }
const isPureText = (content): boolean => {
if (!content) return false;
if (Array.isArray(content)) {
if (content.length > 1) return false;
return isPureText(content[0]);
}
const child = content['content'];
if (child) {
return isPureText(child);
}
return content['type'] === 'text';
};
export const Paste = Extension.create<IPasteOptions>({ export const Paste = Extension.create<IPasteOptions>({
name: 'paste', name: 'paste',
priority: EXTENSION_PRIORITY_HIGHEST, priority: EXTENSION_PRIORITY_HIGHEST,
@ -102,13 +85,6 @@ export const Paste = Extension.create<IPasteOptions>({
const { markdownToProsemirror } = extensionThis.options; const { markdownToProsemirror } = extensionThis.options;
console.log('p', {
text,
html,
node,
markdownText,
});
// 直接复制节点 // 直接复制节点
if (node) { if (node) {
const json = safeJSONParse(node); const json = safeJSONParse(node);
@ -178,7 +154,6 @@ export const Paste = Extension.create<IPasteOptions>({
content: normalizeMarkdown(markdownText || text), content: normalizeMarkdown(markdownText || text),
needTitle: hasTitleExtension && !hasTitle, needTitle: hasTitleExtension && !hasTitle,
}); });
console.log('p', markdownText, text);
let tr = view.state.tr; let tr = view.state.tr;
const selection = tr.selection; const selection = tr.selection;
@ -220,50 +195,6 @@ export const Paste = Extension.create<IPasteOptions>({
return false; return false;
}, },
handleKeyDown(view, event) {
/**
* Command + C
* Ctrl + C
*/
if ((event.ctrlKey || event.metaKey) && event.keyCode == 67) {
const { state } = view;
// @ts-ignore
const currentNode = state.selection.node;
if (currentNode) {
event.preventDefault();
copyNode(currentNode);
return true;
}
}
return false;
},
clipboardTextSerializer: (slice) => {
const json = slice.content.toJSON();
const isSelectAll = slice.openStart === slice.openEnd && slice.openEnd === 0;
if (typeof json === 'object' || isSelectAll) {
return extensionThis.options.prosemirrorToMarkdown({
content: slice.content,
});
} else {
const isText = isPureText(json) && !isSelectAll;
if (isText) {
return slice.content.textBetween(0, slice.content.size, '\n\n');
}
const doc = slice.content;
if (!doc) {
return '';
}
const content = extensionThis.options.prosemirrorToMarkdown({
content: doc,
});
return content;
}
},
}, },
}), }),
]; ];

View File

@ -103,6 +103,20 @@ export const SelectionExtension = Extension.create({
return decorationSet; return decorationSet;
}, },
}, },
filterTransaction(tr, state) {
// Prevent prosemirror's mutation observer overriding a node selection with a text selection
// for exact same range - this was cause of being unable to change dates in collab:
// https://product-fabric.atlassian.net/browse/ED-10645
if (
state.selection instanceof NodeSelection &&
tr.selection instanceof TextSelection &&
state.selection.from === tr.selection.from &&
state.selection.to === tr.selection.to
) {
return false;
}
return true;
},
}), }),
new Plugin({ new Plugin({
key: new PluginKey('preventSelection'), key: new PluginKey('preventSelection'),

View File

@ -6,6 +6,7 @@ import { Blockquote } from 'tiptap/core/extensions/blockquote';
import { Bold } from 'tiptap/core/extensions/bold'; import { Bold } from 'tiptap/core/extensions/bold';
import { BulletList } from 'tiptap/core/extensions/bullet-list'; import { BulletList } from 'tiptap/core/extensions/bullet-list';
import { Callout } from 'tiptap/core/extensions/callout'; import { Callout } from 'tiptap/core/extensions/callout';
import { Clipboard } from 'tiptap/core/extensions/clipboard';
import { Code, CodeMarkPlugin } from 'tiptap/core/extensions/code'; import { Code, CodeMarkPlugin } from 'tiptap/core/extensions/code';
import { CodeBlock } from 'tiptap/core/extensions/code-block'; import { CodeBlock } from 'tiptap/core/extensions/code-block';
import { Color } from 'tiptap/core/extensions/color'; import { Color } from 'tiptap/core/extensions/color';
@ -88,6 +89,9 @@ export const CollaborationKit = [
Blockquote, Blockquote,
Bold, Bold,
BulletList, BulletList,
Clipboard.configure({
prosemirrorToMarkdown,
}),
Code, Code,
CodeMarkPlugin, CodeMarkPlugin,
CodeBlock, CodeBlock,

View File

@ -3,6 +3,7 @@ import { BackgroundColor } from 'tiptap/core/extensions/background-color';
import { Blockquote } from 'tiptap/core/extensions/blockquote'; import { Blockquote } from 'tiptap/core/extensions/blockquote';
import { Bold } from 'tiptap/core/extensions/bold'; import { Bold } from 'tiptap/core/extensions/bold';
import { BulletList } from 'tiptap/core/extensions/bullet-list'; import { BulletList } from 'tiptap/core/extensions/bullet-list';
import { Clipboard } from 'tiptap/core/extensions/clipboard';
import { Code } from 'tiptap/core/extensions/code'; import { Code } from 'tiptap/core/extensions/code';
import { CodeBlock } from 'tiptap/core/extensions/code-block'; import { CodeBlock } from 'tiptap/core/extensions/code-block';
import { Color } from 'tiptap/core/extensions/color'; import { Color } from 'tiptap/core/extensions/color';
@ -18,6 +19,7 @@ import { HardBreak } from 'tiptap/core/extensions/hard-break';
import { Heading } from 'tiptap/core/extensions/heading'; import { Heading } from 'tiptap/core/extensions/heading';
import { HorizontalRule } from 'tiptap/core/extensions/horizontal-rule'; import { HorizontalRule } from 'tiptap/core/extensions/horizontal-rule';
import { HTMLMarks } from 'tiptap/core/extensions/html-marks'; import { HTMLMarks } from 'tiptap/core/extensions/html-marks';
import { Image } from 'tiptap/core/extensions/image';
import { Indent } from 'tiptap/core/extensions/indent'; import { Indent } from 'tiptap/core/extensions/indent';
import { Italic } from 'tiptap/core/extensions/italic'; import { Italic } from 'tiptap/core/extensions/italic';
import { Katex } from 'tiptap/core/extensions/katex'; import { Katex } from 'tiptap/core/extensions/katex';
@ -53,6 +55,9 @@ export const CommentKit = [
Blockquote, Blockquote,
Bold, Bold,
BulletList, BulletList,
Clipboard.configure({
prosemirrorToMarkdown,
}),
Code, Code,
CodeBlock, CodeBlock,
Color, Color,
@ -70,6 +75,7 @@ export const CommentKit = [
HorizontalRule, HorizontalRule,
...HTMLMarks, ...HTMLMarks,
Indent, Indent,
Image,
Italic, Italic,
Katex, Katex,
Link, Link,