mirror of https://github.com/fantasticit/think.git
tiptap: dynamic placeholder
parent
ef2ed7eea9
commit
5146b0f5a2
|
@ -1,3 +1,95 @@
|
||||||
import BuiltInPlaceholder from '@tiptap/extension-placeholder';
|
import { Editor, Extension } from '@tiptap/core';
|
||||||
|
import { Node as ProsemirrorNode } from 'prosemirror-model';
|
||||||
|
import { Plugin } from 'prosemirror-state';
|
||||||
|
import { Decoration, DecorationSet } from 'prosemirror-view';
|
||||||
|
|
||||||
export const Placeholder = BuiltInPlaceholder;
|
export interface PlaceholderOptions {
|
||||||
|
emptyEditorClass: string;
|
||||||
|
emptyNodeClass: string;
|
||||||
|
placeholder: ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number }) => string) | string;
|
||||||
|
showOnlyWhenEditable: boolean;
|
||||||
|
showOnlyCurrent: boolean;
|
||||||
|
includeChildren: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Placeholder = Extension.create<PlaceholderOptions>({
|
||||||
|
name: 'placeholder',
|
||||||
|
|
||||||
|
addOptions() {
|
||||||
|
return {
|
||||||
|
emptyEditorClass: 'is-editor-empty',
|
||||||
|
emptyNodeClass: 'is-empty',
|
||||||
|
placeholder: 'Write something …',
|
||||||
|
showOnlyWhenEditable: true,
|
||||||
|
showOnlyCurrent: true,
|
||||||
|
includeChildren: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
addStorage() {
|
||||||
|
return new Map();
|
||||||
|
},
|
||||||
|
|
||||||
|
addProseMirrorPlugins() {
|
||||||
|
return [
|
||||||
|
new Plugin({
|
||||||
|
props: {
|
||||||
|
decorations: ({ doc, selection }) => {
|
||||||
|
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
|
||||||
|
const { anchor } = selection;
|
||||||
|
const decorations: Decoration[] = [];
|
||||||
|
|
||||||
|
if (!active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.descendants((node, pos) => {
|
||||||
|
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
|
||||||
|
const isEmpty = !node.isLeaf && !node.childCount;
|
||||||
|
|
||||||
|
if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
|
||||||
|
const classes = [this.options.emptyNodeClass];
|
||||||
|
|
||||||
|
if (this.editor.isEmpty) {
|
||||||
|
classes.push(this.options.emptyEditorClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = pos;
|
||||||
|
const end = pos + node.nodeSize;
|
||||||
|
const key = `${start}-${end}`;
|
||||||
|
|
||||||
|
if (!this.editor.storage[this.name].has(key)) {
|
||||||
|
this.editor.storage[this.name].set(
|
||||||
|
key,
|
||||||
|
typeof this.options.placeholder === 'function'
|
||||||
|
? this.options.placeholder({
|
||||||
|
editor: this.editor,
|
||||||
|
node,
|
||||||
|
pos,
|
||||||
|
})
|
||||||
|
: this.options.placeholder
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decoration = Decoration.node(start, end, {
|
||||||
|
'class': classes.join(' '),
|
||||||
|
'data-placeholder': this.editor.storage[this.name].get(key),
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.storage[this.name].delete(key);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
decorations.push(decoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.options.includeChildren;
|
||||||
|
});
|
||||||
|
|
||||||
|
return DecorationSet.create(doc, decorations);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Toast } from '@douyinfe/semi-ui';
|
import { Toast } from '@douyinfe/semi-ui';
|
||||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
|
||||||
// 自定义节点扩展
|
// 自定义节点扩展
|
||||||
import { Attachment } from 'tiptap/core/extensions/attachment';
|
import { Attachment } from 'tiptap/core/extensions/attachment';
|
||||||
import { BackgroundColor } from 'tiptap/core/extensions/background-color';
|
import { BackgroundColor } from 'tiptap/core/extensions/background-color';
|
||||||
|
@ -78,6 +77,14 @@ const DocumentWithTitle = Document.extend({
|
||||||
|
|
||||||
export { Document };
|
export { Document };
|
||||||
|
|
||||||
|
const placeholders = [
|
||||||
|
'输入 / 唤起更多',
|
||||||
|
'使用 markdown 语法进行输入',
|
||||||
|
'输入 @ 来提及他人',
|
||||||
|
'输入 : 来插入表情',
|
||||||
|
'你知道吗?输入 $katex 然后按一下空格就可以快速插入数学公式,其他节点操作类似哦',
|
||||||
|
];
|
||||||
|
|
||||||
export const CollaborationKit = [
|
export const CollaborationKit = [
|
||||||
Paragraph,
|
Paragraph,
|
||||||
Placeholder.configure({
|
Placeholder.configure({
|
||||||
|
@ -88,7 +95,7 @@ export const CollaborationKit = [
|
||||||
|
|
||||||
if (!editor.isEditable) return;
|
if (!editor.isEditable) return;
|
||||||
|
|
||||||
return '输入 / 唤起更多';
|
return placeholders[~~(Math.random() * placeholders.length)];
|
||||||
},
|
},
|
||||||
showOnlyCurrent: false,
|
showOnlyCurrent: false,
|
||||||
showOnlyWhenEditable: false,
|
showOnlyWhenEditable: false,
|
||||||
|
|
Loading…
Reference in New Issue