mirror of https://github.com/fantasticit/think.git
tiptap: add clipboard extension
This commit is contained in:
parent
c6fc458ae3
commit
6c1c29ee1a
|
@ -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;
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
});
|
|
@ -5,7 +5,6 @@ import { Fragment, Schema } from 'prosemirror-model';
|
|||
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||
import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/core/constants';
|
||||
import {
|
||||
copyNode,
|
||||
handleFileEvent,
|
||||
isInCode,
|
||||
isMarkdown,
|
||||
|
@ -36,22 +35,6 @@ interface IPasteOptions {
|
|||
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>({
|
||||
name: 'paste',
|
||||
priority: EXTENSION_PRIORITY_HIGHEST,
|
||||
|
@ -102,13 +85,6 @@ export const Paste = Extension.create<IPasteOptions>({
|
|||
|
||||
const { markdownToProsemirror } = extensionThis.options;
|
||||
|
||||
console.log('p', {
|
||||
text,
|
||||
html,
|
||||
node,
|
||||
markdownText,
|
||||
});
|
||||
|
||||
// 直接复制节点
|
||||
if (node) {
|
||||
const json = safeJSONParse(node);
|
||||
|
@ -178,7 +154,6 @@ export const Paste = Extension.create<IPasteOptions>({
|
|||
content: normalizeMarkdown(markdownText || text),
|
||||
needTitle: hasTitleExtension && !hasTitle,
|
||||
});
|
||||
console.log('p', markdownText, text);
|
||||
|
||||
let tr = view.state.tr;
|
||||
const selection = tr.selection;
|
||||
|
@ -220,50 +195,6 @@ export const Paste = Extension.create<IPasteOptions>({
|
|||
|
||||
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;
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
|
|
@ -103,6 +103,20 @@ export const SelectionExtension = Extension.create({
|
|||
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({
|
||||
key: new PluginKey('preventSelection'),
|
||||
|
|
|
@ -6,6 +6,7 @@ import { Blockquote } from 'tiptap/core/extensions/blockquote';
|
|||
import { Bold } from 'tiptap/core/extensions/bold';
|
||||
import { BulletList } from 'tiptap/core/extensions/bullet-list';
|
||||
import { Callout } from 'tiptap/core/extensions/callout';
|
||||
import { Clipboard } from 'tiptap/core/extensions/clipboard';
|
||||
import { Code, CodeMarkPlugin } from 'tiptap/core/extensions/code';
|
||||
import { CodeBlock } from 'tiptap/core/extensions/code-block';
|
||||
import { Color } from 'tiptap/core/extensions/color';
|
||||
|
@ -88,6 +89,9 @@ export const CollaborationKit = [
|
|||
Blockquote,
|
||||
Bold,
|
||||
BulletList,
|
||||
Clipboard.configure({
|
||||
prosemirrorToMarkdown,
|
||||
}),
|
||||
Code,
|
||||
CodeMarkPlugin,
|
||||
CodeBlock,
|
||||
|
|
|
@ -3,6 +3,7 @@ import { BackgroundColor } from 'tiptap/core/extensions/background-color';
|
|||
import { Blockquote } from 'tiptap/core/extensions/blockquote';
|
||||
import { Bold } from 'tiptap/core/extensions/bold';
|
||||
import { BulletList } from 'tiptap/core/extensions/bullet-list';
|
||||
import { Clipboard } from 'tiptap/core/extensions/clipboard';
|
||||
import { Code } from 'tiptap/core/extensions/code';
|
||||
import { CodeBlock } from 'tiptap/core/extensions/code-block';
|
||||
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 { HorizontalRule } from 'tiptap/core/extensions/horizontal-rule';
|
||||
import { HTMLMarks } from 'tiptap/core/extensions/html-marks';
|
||||
import { Image } from 'tiptap/core/extensions/image';
|
||||
import { Indent } from 'tiptap/core/extensions/indent';
|
||||
import { Italic } from 'tiptap/core/extensions/italic';
|
||||
import { Katex } from 'tiptap/core/extensions/katex';
|
||||
|
@ -53,6 +55,9 @@ export const CommentKit = [
|
|||
Blockquote,
|
||||
Bold,
|
||||
BulletList,
|
||||
Clipboard.configure({
|
||||
prosemirrorToMarkdown,
|
||||
}),
|
||||
Code,
|
||||
CodeBlock,
|
||||
Color,
|
||||
|
@ -70,6 +75,7 @@ export const CommentKit = [
|
|||
HorizontalRule,
|
||||
...HTMLMarks,
|
||||
Indent,
|
||||
Image,
|
||||
Italic,
|
||||
Katex,
|
||||
Link,
|
||||
|
|
Loading…
Reference in New Issue