@shenjipo/mention-editor 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ import { Editor } from '@tiptap/core';
2
+ import { EditorState, Plugin, Transaction } from 'prosemirror-state';
3
+ import { EditorView } from 'prosemirror-view';
4
+
5
+ interface ImageInputPayload {
6
+ file: File;
7
+ base64: string;
8
+ width?: number;
9
+ height?: number;
10
+ }
11
+ interface ImageInputOptions {
12
+ onImageInput?: (payload: ImageInputPayload) => void;
13
+ onFileReject?: (file: File) => void;
14
+ }
15
+
16
+ type StringKeyOf<T> = Extract<keyof T, string>;
17
+ type CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[] ? T[EventName] : [T[EventName]];
18
+ type CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any;
19
+ declare class EventEmitter<T extends Record<string, any>> {
20
+ private callbacks;
21
+ on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): () => void;
22
+ protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>): void;
23
+ off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>): void;
24
+ protected removeAllListeners(): void;
25
+ }
26
+
27
+ type UiElementPosition = {
28
+ show: boolean;
29
+ referencePos: DOMRect;
30
+ };
31
+
32
+ type SuggestionMenuState = UiElementPosition & {
33
+ query: string;
34
+ ignoreQueryLength?: boolean;
35
+ };
36
+ interface MentionItem {
37
+ id: string;
38
+ label: string;
39
+ }
40
+
41
+ type SuggestionPluginState = {
42
+ triggerCharacter: string;
43
+ deleteTriggerCharacter: boolean;
44
+ queryStartPos: number;
45
+ query: string;
46
+ decorationId: string;
47
+ ignoreQueryLength?: boolean;
48
+ } | undefined;
49
+ declare class SuggestionMenuView {
50
+ private readonly editor;
51
+ pluginState: SuggestionPluginState;
52
+ state?: SuggestionMenuState;
53
+ emitUpdate: (triggerCharacter: string) => void;
54
+ private rootEl?;
55
+ constructor(editor: MEditor, emitUpdate: (menuName: string, state: SuggestionMenuState) => void);
56
+ update(view: EditorView, prevState: EditorState): void;
57
+ destroy(): void;
58
+ closeMenu: () => void;
59
+ clearQuery: () => void;
60
+ insertMention: (item: MentionItem) => void;
61
+ getReplaceRange(): {
62
+ from: number;
63
+ to: number;
64
+ };
65
+ }
66
+ declare class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {
67
+ view: SuggestionMenuView;
68
+ readonly plugin: Plugin;
69
+ private triggerCharacters;
70
+ constructor(editor: MEditor);
71
+ onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void): () => void;
72
+ addTriggerCharacter: (triggerCharacter: string) => void;
73
+ removeTriggerCharacter: (triggerCharacter: string) => void;
74
+ closeMenu: () => void;
75
+ clearQuery: () => void;
76
+ insertMention: (item: MentionItem) => void;
77
+ get shown(): boolean;
78
+ }
79
+
80
+ interface MentionEditorOptions {
81
+ element: HTMLElement;
82
+ onChange: (text: string) => void;
83
+ placeholder?: string;
84
+ onImageInput?: (payload: ImageInputPayload) => void;
85
+ onFileReject?: (file: File) => void;
86
+ }
87
+ declare class MEditor {
88
+ _tiptapEditor: Editor;
89
+ readonly suggestionMenus: SuggestionMenuProseMirrorPlugin;
90
+ constructor(options: MentionEditorOptions);
91
+ dispatch(tr: Transaction): void;
92
+ inserMentionBlock(item: MentionItem): void;
93
+ get isEditable(): boolean;
94
+ get domElement(): HTMLDivElement;
95
+ clear(): void;
96
+ }
97
+
98
+ export { type ImageInputOptions, type ImageInputPayload, type MentionItem, type SuggestionMenuState, MEditor as default };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ var Q=Object.create;var p=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var A=(r,t,e)=>t in r?p(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var H=(r,t)=>{for(var e in t)p(r,e,{get:t[e],enumerable:!0})},I=(r,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of K(t))!z.call(r,i)&&i!==e&&p(r,i,{get:()=>t[i],enumerable:!(n=U(t,i))||n.enumerable});return r};var m=(r,t,e)=>(e=r!=null?Q(j(r)):{},I(t||!r||!r.__esModule?p(e,"default",{value:r,enumerable:!0}):e,r)),$=r=>I(p({},"__esModule",{value:!0}),r);var a=(r,t,e)=>A(r,typeof t!="symbol"?t+"":t,e);var W={};H(W,{default:()=>G});module.exports=$(W);var S=require("@tiptap/core");var T=m(require("@tiptap/extension-document")),x=m(require("@tiptap/extension-paragraph")),P=m(require("@tiptap/extension-text")),C=[T.default,x.default,P.default];var B=m(require("@tiptap/extension-history")),D=m(require("@tiptap/extension-placeholder"));var N=require("@tiptap/core"),_=require("prosemirror-state");function w(r){return r.type.startsWith("image/")}function k(r){return new Promise((t,e)=>{let n=new FileReader;n.onload=()=>t(n.result),n.onerror=e,n.readAsDataURL(r)})}function v(r){return new Promise(t=>{let e=new Image;e.onload=()=>t({width:e.width,height:e.height}),e.src=r})}var F=N.Extension.create({name:"imageInput",addOptions(){return{onImageInput:void 0,onFileReject:void 0}},addProseMirrorPlugins(){let r=async t=>{var i,o,s,l;if(!w(t)){(o=(i=this.options).onFileReject)==null||o.call(i,t);return}let e=await k(t),n=await v(e);(l=(s=this.options).onImageInput)==null||l.call(s,{file:t,base64:e,...n})};return[new _.Plugin({props:{handlePaste:(t,e)=>{var i;let n=(i=e.clipboardData)==null?void 0:i.items;if(!n)return!1;for(let o of n){if(o.kind!=="file")continue;let s=o.getAsFile();if(s)return r(s),!0}return!1},handleDrop:(t,e)=>{var i;let n=(i=e.dataTransfer)==null?void 0:i.files;return!n||n.length===0?!1:(r(n[0]),!0)}}})]}});var R=require("@tiptap/core"),E=require("prosemirror-state"),g=require("prosemirror-view");var f=class{constructor(){a(this,"callbacks",{})}on(t,e){return this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push(e),()=>this.off(t,e)}emit(t,...e){let n=this.callbacks[t];n&&n.forEach(i=>i.apply(this,e))}off(t,e){let n=this.callbacks[t];n&&(e?this.callbacks[t]=n.filter(i=>i!==e):delete this.callbacks[t])}removeAllListeners(){this.callbacks={}}};var d=new E.PluginKey("SuggestionMenuPlugin"),V=(0,R.findParentNode)(r=>r.type.name==="blockContainer"),b=class{constructor(t,e){this.editor=t;a(this,"pluginState");a(this,"state");a(this,"emitUpdate");a(this,"rootEl");a(this,"closeMenu",()=>{this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(d,null))});a(this,"clearQuery",()=>{this.pluginState!==void 0&&this.editor._tiptapEditor.chain().focus().deleteRange({from:this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),to:this.editor._tiptapEditor.state.selection.from}).run()});a(this,"insertMention",t=>{this.editor._tiptapEditor.chain().focus().insertContent({type:"mention",attrs:t}).insertContent(" ").run()});this.pluginState=void 0,this.emitUpdate=n=>{var i;if(!this.state)throw new Error("Attempting to update uninitialized suggestions menu");e(n,{...this.state,ignoreQueryLength:(i=this.pluginState)==null?void 0:i.ignoreQueryLength})},setTimeout(()=>{this.rootEl=this.editor._tiptapEditor.view.root})}update(t,e){var c;let n=d.getState(e),i=d.getState(t.state),o=n===void 0&&i!==void 0,s=n!==void 0&&i===void 0;if(!o&&!(n!==void 0&&i!==void 0)&&!s)return;if(this.pluginState=s?n:i,s||!this.editor.isEditable){this.state.show=!1,this.emitUpdate(this.pluginState.triggerCharacter);return}let u=(c=this.rootEl)==null?void 0:c.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`);this.editor.isEditable&&u&&(this.state={show:!0,referencePos:u.getBoundingClientRect(),query:this.pluginState.query},this.emitUpdate(this.pluginState.triggerCharacter))}destroy(){}getReplaceRange(){if(!this.pluginState)return null;let t=this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),e=this.editor._tiptapEditor.state.selection.from;return{from:t,to:e}}},y=class extends f{constructor(e){super();a(this,"view");a(this,"plugin");a(this,"triggerCharacters",[]);a(this,"addTriggerCharacter",e=>{this.triggerCharacters.push(e)});a(this,"removeTriggerCharacter",e=>{this.triggerCharacters=this.triggerCharacters.filter(n=>n!==e)});a(this,"closeMenu",()=>this.view.closeMenu());a(this,"clearQuery",()=>this.view.clearQuery());a(this,"insertMention",e=>this.view.insertMention(e));let n=this.triggerCharacters;this.plugin=new E.Plugin({key:d,view:()=>(this.view=new b(e,(i,o)=>{this.emit(`update ${i}`,o)}),this.view),state:{init(){},apply(i,o,s,l){var M;let u=i.getMeta(d);if(typeof u=="object"&&u!==null&&o===void 0)return{triggerCharacter:u.triggerCharacter,deleteTriggerCharacter:u.deleteTriggerCharacter!==!1,queryStartPos:((M=i.selection)==null?void 0:M.from)||l.selection.from,query:"",decorationId:`id_${Math.floor(Math.random()*4294967295)}`,ignoreQueryLength:u==null?void 0:u.ignoreQueryLength};if(o===void 0)return o;if(l.selection.from!==l.selection.to||u===null||i.getMeta("focus")||i.getMeta("blur")||i.getMeta("pointer")||o.triggerCharacter!==void 0&&l.selection.from<o.queryStartPos)return;let c={...o};return c.query=l.doc.textBetween(o.queryStartPos,l.selection.from),c}},props:{handleTextInput(i,o,s,l){let u=this.getState(i.state);return n.includes(l)&&u===void 0?(i.dispatch(i.state.tr.insertText(l).scrollIntoView().setMeta(d,{triggerCharacter:l})),!0):!1},decorations(i){let o=this.getState(i);if(o===void 0)return null;if(!o.deleteTriggerCharacter){let s=V(i.selection);if(s)return g.DecorationSet.create(i.doc,[g.Decoration.node(s.pos,s.pos+s.node.nodeSize,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}return g.DecorationSet.create(i.doc,[g.Decoration.inline(o.queryStartPos-o.triggerCharacter.length,o.queryStartPos,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}}})}onUpdate(e,n){return this.triggerCharacters.includes(e)||this.addTriggerCharacter(e),this.on(`update ${e}`,n)}get shown(){var e,n;return((n=(e=this.view)==null?void 0:e.state)==null?void 0:n.show)||!1}};var q=require("@tiptap/core"),L=q.Node.create({name:"mention",inline:!0,group:"inline",atom:!0,selectable:!1,addAttributes(){return{id:{default:null},label:{default:null}}},parseHTML(){return[{tag:'span[data-type="mention"]'}]},renderHTML({node:r}){return["span",{class:"mention","data-type":"mention","data-id":r.attrs.id,"data-label":r.attrs.label,contenteditable:"false"},r.attrs.label]},renderText({node:r}){return""}});function O(r,t){r._tiptapEditor.chain().insertContent({type:"mention",attrs:{id:t.id,label:t.label}}).run()}var h=class{constructor(t){a(this,"_tiptapEditor");a(this,"suggestionMenus");this.suggestionMenus=new y(this);let e=S.Extension.create({name:"MEditorUIExtension",addProseMirrorPlugins:()=>[this.suggestionMenus.plugin]});this._tiptapEditor=new S.Editor({element:t.element,extensions:[...C,B.default.configure({depth:100,newGroupDelay:500}),L,D.default.configure({placeholder:t.placeholder||"\u8BF7\u8F93\u5165...",emptyEditorClass:"is-editor-empty"}),F.configure({onImageInput:t.onImageInput,onFileReject:t.onFileReject}),e],onUpdate:({editor:n})=>{t.onChange(n.getText())}})}dispatch(t){this._tiptapEditor.view.dispatch(t)}inserMentionBlock(t){O(this,t)}get isEditable(){return this._tiptapEditor.isEditable===void 0?!0:this._tiptapEditor.isEditable}get domElement(){return this._tiptapEditor.view.dom}clear(){this._tiptapEditor.commands.clearContent(!0),this._tiptapEditor.commands.focus()}};var G=h;
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/editor.ts","../src/schema/base.ts","../src/extensions/ImageInput/ImageInput.ts","../src/utils/file.ts","../src/extensions/SuggestionMenu/SuggestionPlugin.ts","../src/utils/EventEmitter.ts","../src/blocks/MentionBlock.ts","../src/api/InsertMentionBlock.ts"],"sourcesContent":["import MEditor from \"./editor\";\n\nexport default MEditor\n\nexport * from \"./extensions/SuggestionMenu/types\"\nexport * from \"./extensions/ImageInput/types\"","import { Editor, Extension } from '@tiptap/core'\nimport { baseExtensions } from './schema/base'\nimport History from '@tiptap/extension-history'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport { ImageInputPayload } from './extensions/ImageInput/types'\nimport { ImageInputExtension } from './extensions/ImageInput/ImageInput'\nimport { Transaction } from 'prosemirror-state'\nimport { SuggestionMenuProseMirrorPlugin } from './extensions/SuggestionMenu/SuggestionPlugin'\nimport { MentionBlock } from './blocks/MentionBlock'\nimport { type MentionItem } from './extensions/SuggestionMenu/types'\nimport { InsertMentionBlock } from './api/InsertMentionBlock'\n\nexport interface MentionEditorOptions {\n element: HTMLElement\n onChange: (text: string) => void\n placeholder?: string\n onImageInput?: (payload: ImageInputPayload) => void\n onFileReject?: (file: File) => void\n}\n\n\nexport default class MEditor {\n _tiptapEditor: Editor\n public readonly suggestionMenus: SuggestionMenuProseMirrorPlugin\n\n constructor(options: MentionEditorOptions) {\n this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this)\n const MEditorUIExtension = Extension.create({\n name: 'MEditorUIExtension',\n addProseMirrorPlugins: () => {\n return [\n this.suggestionMenus.plugin,\n ]\n },\n })\n\n this._tiptapEditor = new Editor({\n element: options.element,\n extensions: [\n ...baseExtensions,\n History.configure({\n depth: 100,\n newGroupDelay: 500,\n }),\n MentionBlock,\n Placeholder.configure({\n placeholder: options.placeholder || '请输入...',\n emptyEditorClass: 'is-editor-empty'\n }),\n ImageInputExtension.configure({\n onImageInput: options.onImageInput,\n onFileReject: options.onFileReject,\n }),\n MEditorUIExtension,\n ],\n onUpdate: ({ editor }) => {\n options.onChange(editor.getText())\n }\n })\n }\n\n\n dispatch(tr: Transaction) {\n this._tiptapEditor.view.dispatch(tr)\n }\n\n public inserMentionBlock(item: MentionItem) {\n InsertMentionBlock(this, item)\n // this._tiptapEditor\n // .chain()\n // .focus()\n // .insertContent({\n // type: 'mention',\n // attrs: item\n // })\n // .insertContent(' ')\n // .run()\n }\n\n public get isEditable(): boolean {\n return this._tiptapEditor.isEditable === undefined ? true : this._tiptapEditor.isEditable\n }\n\n public get domElement() {\n return this._tiptapEditor.view.dom as HTMLDivElement\n }\n\n public clear() {\n this._tiptapEditor.commands.clearContent(true)\n this._tiptapEditor.commands.focus()\n }\n}\n","import Document from '@tiptap/extension-document'\nimport Paragraph from '@tiptap/extension-paragraph'\nimport Text from '@tiptap/extension-text'\n\nexport const baseExtensions = [\n Document,\n Paragraph,\n Text,\n]\n","import { Extension } from '@tiptap/core'\nimport { Plugin } from 'prosemirror-state'\nimport {\n isImageFile,\n fileToBase64,\n getImageSize,\n} from '../../utils/file'\nimport { ImageInputPayload, ImageInputOptions } from './types'\n\n\n\nexport const ImageInputExtension = Extension.create<ImageInputOptions>({\n name: 'imageInput',\n\n addOptions() {\n return {\n onImageInput: undefined,\n onFileReject: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n const handleFile = async (file: File) => {\n if (!isImageFile(file)) {\n this.options.onFileReject?.(file)\n return\n }\n\n const base64 = await fileToBase64(file)\n const size = await getImageSize(base64)\n\n this.options.onImageInput?.({\n file,\n base64,\n ...size,\n })\n }\n\n return [\n new Plugin({\n props: {\n handlePaste: (_, event) => {\n const items = event.clipboardData?.items\n if (!items) return false\n\n for (const item of items) {\n if (item.kind !== 'file') continue\n const file = item.getAsFile()\n if (!file) continue\n\n handleFile(file)\n return true\n }\n\n return false\n },\n\n handleDrop: (_, event) => {\n const files = event.dataTransfer?.files\n if (!files || files.length === 0) return false\n\n handleFile(files[0])\n return true\n },\n },\n }),\n ]\n },\n})\n","export function isImageFile(file: File) {\n return file.type.startsWith('image/')\n}\n\nexport function fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nexport function getImageSize(base64: string): Promise<{ width: number; height: number }> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve({ width: img.width, height: img.height })\n img.src = base64\n })\n}\n","import { findParentNode } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport MEditor from '../../editor'\nimport { EventEmitter } from '../../utils/EventEmitter'\nimport { SuggestionMenuState, MentionItem } from './types'\n\ntype SuggestionPluginState =\n | {\n triggerCharacter: string\n deleteTriggerCharacter: boolean\n queryStartPos: number\n query: string\n decorationId: string\n ignoreQueryLength?: boolean\n }\n | undefined\n\nexport const suggestionMenuPluginKey = new PluginKey('SuggestionMenuPlugin')\nconst findBlock = findParentNode(node => node.type.name === 'blockContainer')\n\nclass SuggestionMenuView {\n pluginState: SuggestionPluginState\n public state?: SuggestionMenuState\n public emitUpdate: (triggerCharacter: string) => void\n private rootEl?: Document | ShadowRoot\n\n constructor(\n private readonly editor: MEditor,\n emitUpdate: (menuName: string, state: SuggestionMenuState) => void\n ) {\n this.pluginState = undefined\n\n this.emitUpdate = (menuName: string) => {\n if (!this.state) {\n throw new Error('Attempting to update uninitialized suggestions menu')\n }\n emitUpdate(menuName, {\n ...this.state,\n ignoreQueryLength: this.pluginState?.ignoreQueryLength,\n })\n }\n setTimeout(() => {\n this.rootEl = this.editor._tiptapEditor.view.root\n })\n\n }\n\n update(view: EditorView, prevState: EditorState) {\n\n const prev: SuggestionPluginState = suggestionMenuPluginKey.getState(prevState)\n const next: SuggestionPluginState = suggestionMenuPluginKey.getState(view.state)\n\n \n const started = prev === undefined && next !== undefined\n const stopped = prev !== undefined && next === undefined\n const changed = prev !== undefined && next !== undefined\n\n \n if (!started && !changed && !stopped) {\n return\n }\n\n this.pluginState = stopped ? prev : next\n\n if (stopped || !this.editor.isEditable) {\n\n this.state!.show = false\n this.emitUpdate(this.pluginState!.triggerCharacter)\n\n return\n }\n\n const decorationNode = this.rootEl?.querySelector(`[data-decoration-id=\"${this.pluginState!.decorationId}\"]`)\n\n if (this.editor.isEditable && decorationNode) {\n\n this.state = {\n show: true,\n referencePos: decorationNode.getBoundingClientRect(),\n query: this.pluginState!.query,\n }\n\n this.emitUpdate(this.pluginState!.triggerCharacter!)\n }\n }\n\n destroy() {\n\n }\n\n closeMenu = () => {\n this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(suggestionMenuPluginKey, null))\n }\n\n clearQuery = () => {\n if (this.pluginState === undefined) {\n return\n }\n\n this.editor._tiptapEditor\n .chain()\n .focus()\n .deleteRange({\n from:\n this.pluginState.queryStartPos! -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter!.length : 0),\n to: this.editor._tiptapEditor.state.selection.from,\n })\n .run()\n }\n\n insertMention = (item: MentionItem) => {\n this.editor._tiptapEditor\n .chain()\n .focus()\n .insertContent({\n type: 'mention',\n attrs: item\n })\n .insertContent(' ')\n .run()\n }\n\n getReplaceRange() {\n if (!this.pluginState) return null\n\n const from = this.pluginState.queryStartPos -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter.length : 0)\n\n const to = this.editor._tiptapEditor.state.selection.from\n\n return { from, to }\n }\n\n}\n\nexport class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {\n public view: SuggestionMenuView\n public readonly plugin: Plugin\n private triggerCharacters: string[] = []\n\n constructor(editor: MEditor) {\n super()\n const triggerCharacters = this.triggerCharacters\n\n this.plugin = new Plugin({\n key: suggestionMenuPluginKey,\n view: () => {\n this.view = new SuggestionMenuView(editor, (triggerCharacter, state) => {\n this.emit(`update ${triggerCharacter}`, state)\n })\n return this.view\n },\n state: {\n init(): SuggestionPluginState {\n return undefined\n },\n apply(transaction, prev, _oldState, newState): SuggestionPluginState {\n const suggestionPluginTransactionMeta: {\n triggerCharacter: string\n deleteTriggerCharacter?: boolean\n ignoreQueryLength?: boolean\n } | null = transaction.getMeta(suggestionMenuPluginKey)\n\n\n if (\n typeof suggestionPluginTransactionMeta === 'object' &&\n suggestionPluginTransactionMeta !== null &&\n prev === undefined\n ) {\n return {\n triggerCharacter: suggestionPluginTransactionMeta.triggerCharacter,\n deleteTriggerCharacter: suggestionPluginTransactionMeta.deleteTriggerCharacter !== false,\n queryStartPos: transaction.selection?.from || newState.selection.from,\n query: '',\n decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,\n ignoreQueryLength: suggestionPluginTransactionMeta?.ignoreQueryLength,\n }\n }\n\n\n if (prev === undefined) {\n return prev\n }\n\n\n if (\n newState.selection.from !== newState.selection.to ||\n suggestionPluginTransactionMeta === null ||\n transaction.getMeta('focus') ||\n transaction.getMeta('blur') ||\n transaction.getMeta('pointer') ||\n (prev.triggerCharacter !== undefined && newState.selection.from < prev.queryStartPos!)\n ) {\n\n return undefined\n }\n\n const next = { ...prev }\n\n\n next.query = newState.doc.textBetween(prev.queryStartPos!, newState.selection.from)\n\n return next\n }\n },\n\n props: {\n // 当用户输入一个 trigger 字符,且当前没有 suggestion 激活时:\n // 插件拦截这次输入,自己插入字符,并通过 transaction meta 通知 state 打开 suggestion 菜单;\n // 其余情况下,完全不干预编辑器行为。\n handleTextInput(view, _from, _to, text) {\n\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(view.state)\n\n if (triggerCharacters.includes(text) && suggestionPluginState === undefined) {\n view.dispatch(\n view.state.tr.insertText(text).scrollIntoView().setMeta(suggestionMenuPluginKey, {\n triggerCharacter: text,\n })\n )\n\n return true\n }\n return false\n },\n\n decorations(state) {\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(state)\n\n if (suggestionPluginState === undefined) {\n return null\n }\n\n \n if (!suggestionPluginState.deleteTriggerCharacter) {\n const blockNode = findBlock(state.selection)\n if (blockNode) {\n return DecorationSet.create(state.doc, [\n Decoration.node(blockNode.pos, blockNode.pos + blockNode.node.nodeSize, {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }),\n ])\n }\n }\n \n return DecorationSet.create(state.doc, [\n Decoration.inline(\n suggestionPluginState.queryStartPos! - suggestionPluginState.triggerCharacter!.length,\n suggestionPluginState.queryStartPos!,\n {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }\n ),\n ])\n },\n }\n })\n }\n\n public onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void) {\n if (!this.triggerCharacters.includes(triggerCharacter)) {\n this.addTriggerCharacter(triggerCharacter)\n }\n \n return this.on(`update ${triggerCharacter}`, callback)\n }\n\n addTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters.push(triggerCharacter)\n }\n\n \n removeTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters = this.triggerCharacters.filter(c => c !== triggerCharacter)\n }\n\n closeMenu = () => this.view!.closeMenu()\n\n clearQuery = () => this.view!.clearQuery()\n\n insertMention = (item: MentionItem) => this.view!.insertMention(item)\n\n public get shown() {\n return this.view?.state?.show || false\n }\n\n}\n","type StringKeyOf<T> = Extract<keyof T, string>\ntype CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]\n ? T[EventName]\n : [T[EventName]]\ntype CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any\n\nexport class EventEmitter<T extends Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n private callbacks: { [key: string]: Function[] } = {}\n\n public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>) {\n if (!this.callbacks[event]) {\n this.callbacks[event] = []\n }\n\n this.callbacks[event].push(fn)\n\n return () => this.off(event, fn)\n }\n\n protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n callbacks.forEach(callback => callback.apply(this, args))\n }\n }\n\n public off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n if (fn) {\n this.callbacks[event] = callbacks.filter(callback => callback !== fn)\n } else {\n delete this.callbacks[event]\n }\n }\n }\n\n protected removeAllListeners(): void {\n this.callbacks = {}\n }\n}\n","import { Node } from '@tiptap/core'\n\nexport const MentionBlock = Node.create({\n name: 'mention',\n inline: true,\n group: 'inline',\n atom: true,\n selectable: false,\n\n addAttributes() {\n return {\n id: { default: null },\n label: { default: null },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'span[data-type=\"mention\"]',\n },\n ]\n },\n\n renderHTML({ node }) {\n return [\n 'span',\n {\n class: 'mention',\n 'data-type': 'mention',\n 'data-id': node.attrs.id,\n 'data-label': node.attrs.label,\n contenteditable: 'false', // 非编辑\n },\n node.attrs.label,\n ]\n },\n\n // 新增:确保mention节点可以正确序列化/反序列化\n renderText({ node }) {\n return ''\n },\n})\n\n\n","import { Selection } from 'prosemirror-state'\nimport MEditor from '../editor'\n\nexport function InsertMentionBlock(\n editor: MEditor,\n item: { id: string; label: string }\n) {\n\n editor._tiptapEditor.chain().insertContent({\n type: 'mention',\n attrs: {\n id: item.id,\n label: item.label,\n }\n }).run() \n\n}\n\n"],"mappings":"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","names":["index_exports","__export","index_default","__toCommonJS","import_core","import_extension_document","import_extension_paragraph","import_extension_text","baseExtensions","Document","Paragraph","Text","import_extension_history","import_extension_placeholder","import_core","import_prosemirror_state","isImageFile","file","fileToBase64","resolve","reject","reader","getImageSize","base64","img","ImageInputExtension","handleFile","file","_a","_b","_c","_d","isImageFile","base64","fileToBase64","size","getImageSize","_","event","items","item","files","import_core","import_prosemirror_state","import_prosemirror_view","EventEmitter","__publicField","event","fn","args","callbacks","callback","suggestionMenuPluginKey","findBlock","node","SuggestionMenuView","editor","emitUpdate","__publicField","item","menuName","_a","view","prevState","prev","next","started","stopped","decorationNode","from","to","SuggestionMenuProseMirrorPlugin","EventEmitter","triggerCharacter","c","triggerCharacters","state","transaction","_oldState","newState","suggestionPluginTransactionMeta","_from","_to","text","suggestionPluginState","blockNode","callback","_b","import_core","MentionBlock","node","InsertMentionBlock","editor","item","MEditor","options","__publicField","SuggestionMenuProseMirrorPlugin","MEditorUIExtension","baseExtensions","History","MentionBlock","Placeholder","ImageInputExtension","editor","tr","item","InsertMentionBlock","index_default","MEditor"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ var C=Object.defineProperty;var w=(n,t,e)=>t in n?C(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var a=(n,t,e)=>w(n,typeof t!="symbol"?t+"":t,e);import{Editor as D,Extension as Q}from"@tiptap/core";import k from"@tiptap/extension-document";import v from"@tiptap/extension-paragraph";import N from"@tiptap/extension-text";var y=[k,v,N];import U from"@tiptap/extension-history";import K from"@tiptap/extension-placeholder";import{Extension as _}from"@tiptap/core";import{Plugin as F}from"prosemirror-state";function E(n){return n.type.startsWith("image/")}function S(n){return new Promise((t,e)=>{let i=new FileReader;i.onload=()=>t(i.result),i.onerror=e,i.readAsDataURL(n)})}function b(n){return new Promise(t=>{let e=new Image;e.onload=()=>t({width:e.width,height:e.height}),e.src=n})}var M=_.create({name:"imageInput",addOptions(){return{onImageInput:void 0,onFileReject:void 0}},addProseMirrorPlugins(){let n=async t=>{var r,o,s,l;if(!E(t)){(o=(r=this.options).onFileReject)==null||o.call(r,t);return}let e=await S(t),i=await b(e);(l=(s=this.options).onImageInput)==null||l.call(s,{file:t,base64:e,...i})};return[new F({props:{handlePaste:(t,e)=>{var r;let i=(r=e.clipboardData)==null?void 0:r.items;if(!i)return!1;for(let o of i){if(o.kind!=="file")continue;let s=o.getAsFile();if(s)return n(s),!0}return!1},handleDrop:(t,e)=>{var r;let i=(r=e.dataTransfer)==null?void 0:r.files;return!i||i.length===0?!1:(n(i[0]),!0)}}})]}});import{findParentNode as R}from"@tiptap/core";import{Plugin as q,PluginKey as L}from"prosemirror-state";import{Decoration as I,DecorationSet as T}from"prosemirror-view";var p=class{constructor(){a(this,"callbacks",{})}on(t,e){return this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push(e),()=>this.off(t,e)}emit(t,...e){let i=this.callbacks[t];i&&i.forEach(r=>r.apply(this,e))}off(t,e){let i=this.callbacks[t];i&&(e?this.callbacks[t]=i.filter(r=>r!==e):delete this.callbacks[t])}removeAllListeners(){this.callbacks={}}};var d=new L("SuggestionMenuPlugin"),O=R(n=>n.type.name==="blockContainer"),h=class{constructor(t,e){this.editor=t;a(this,"pluginState");a(this,"state");a(this,"emitUpdate");a(this,"rootEl");a(this,"closeMenu",()=>{this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(d,null))});a(this,"clearQuery",()=>{this.pluginState!==void 0&&this.editor._tiptapEditor.chain().focus().deleteRange({from:this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),to:this.editor._tiptapEditor.state.selection.from}).run()});a(this,"insertMention",t=>{this.editor._tiptapEditor.chain().focus().insertContent({type:"mention",attrs:t}).insertContent(" ").run()});this.pluginState=void 0,this.emitUpdate=i=>{var r;if(!this.state)throw new Error("Attempting to update uninitialized suggestions menu");e(i,{...this.state,ignoreQueryLength:(r=this.pluginState)==null?void 0:r.ignoreQueryLength})},setTimeout(()=>{this.rootEl=this.editor._tiptapEditor.view.root})}update(t,e){var g;let i=d.getState(e),r=d.getState(t.state),o=i===void 0&&r!==void 0,s=i!==void 0&&r===void 0;if(!o&&!(i!==void 0&&r!==void 0)&&!s)return;if(this.pluginState=s?i:r,s||!this.editor.isEditable){this.state.show=!1,this.emitUpdate(this.pluginState.triggerCharacter);return}let u=(g=this.rootEl)==null?void 0:g.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`);this.editor.isEditable&&u&&(this.state={show:!0,referencePos:u.getBoundingClientRect(),query:this.pluginState.query},this.emitUpdate(this.pluginState.triggerCharacter))}destroy(){}getReplaceRange(){if(!this.pluginState)return null;let t=this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),e=this.editor._tiptapEditor.state.selection.from;return{from:t,to:e}}},m=class extends p{constructor(e){super();a(this,"view");a(this,"plugin");a(this,"triggerCharacters",[]);a(this,"addTriggerCharacter",e=>{this.triggerCharacters.push(e)});a(this,"removeTriggerCharacter",e=>{this.triggerCharacters=this.triggerCharacters.filter(i=>i!==e)});a(this,"closeMenu",()=>this.view.closeMenu());a(this,"clearQuery",()=>this.view.clearQuery());a(this,"insertMention",e=>this.view.insertMention(e));let i=this.triggerCharacters;this.plugin=new q({key:d,view:()=>(this.view=new h(e,(r,o)=>{this.emit(`update ${r}`,o)}),this.view),state:{init(){},apply(r,o,s,l){var f;let u=r.getMeta(d);if(typeof u=="object"&&u!==null&&o===void 0)return{triggerCharacter:u.triggerCharacter,deleteTriggerCharacter:u.deleteTriggerCharacter!==!1,queryStartPos:((f=r.selection)==null?void 0:f.from)||l.selection.from,query:"",decorationId:`id_${Math.floor(Math.random()*4294967295)}`,ignoreQueryLength:u==null?void 0:u.ignoreQueryLength};if(o===void 0)return o;if(l.selection.from!==l.selection.to||u===null||r.getMeta("focus")||r.getMeta("blur")||r.getMeta("pointer")||o.triggerCharacter!==void 0&&l.selection.from<o.queryStartPos)return;let g={...o};return g.query=l.doc.textBetween(o.queryStartPos,l.selection.from),g}},props:{handleTextInput(r,o,s,l){let u=this.getState(r.state);return i.includes(l)&&u===void 0?(r.dispatch(r.state.tr.insertText(l).scrollIntoView().setMeta(d,{triggerCharacter:l})),!0):!1},decorations(r){let o=this.getState(r);if(o===void 0)return null;if(!o.deleteTriggerCharacter){let s=O(r.selection);if(s)return T.create(r.doc,[I.node(s.pos,s.pos+s.node.nodeSize,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}return T.create(r.doc,[I.inline(o.queryStartPos-o.triggerCharacter.length,o.queryStartPos,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}}})}onUpdate(e,i){return this.triggerCharacters.includes(e)||this.addTriggerCharacter(e),this.on(`update ${e}`,i)}get shown(){var e,i;return((i=(e=this.view)==null?void 0:e.state)==null?void 0:i.show)||!1}};import{Node as B}from"@tiptap/core";var x=B.create({name:"mention",inline:!0,group:"inline",atom:!0,selectable:!1,addAttributes(){return{id:{default:null},label:{default:null}}},parseHTML(){return[{tag:'span[data-type="mention"]'}]},renderHTML({node:n}){return["span",{class:"mention","data-type":"mention","data-id":n.attrs.id,"data-label":n.attrs.label,contenteditable:"false"},n.attrs.label]},renderText({node:n}){return""}});function P(n,t){n._tiptapEditor.chain().insertContent({type:"mention",attrs:{id:t.id,label:t.label}}).run()}var c=class{constructor(t){a(this,"_tiptapEditor");a(this,"suggestionMenus");this.suggestionMenus=new m(this);let e=Q.create({name:"MEditorUIExtension",addProseMirrorPlugins:()=>[this.suggestionMenus.plugin]});this._tiptapEditor=new D({element:t.element,extensions:[...y,U.configure({depth:100,newGroupDelay:500}),x,K.configure({placeholder:t.placeholder||"\u8BF7\u8F93\u5165...",emptyEditorClass:"is-editor-empty"}),M.configure({onImageInput:t.onImageInput,onFileReject:t.onFileReject}),e],onUpdate:({editor:i})=>{t.onChange(i.getText())}})}dispatch(t){this._tiptapEditor.view.dispatch(t)}inserMentionBlock(t){P(this,t)}get isEditable(){return this._tiptapEditor.isEditable===void 0?!0:this._tiptapEditor.isEditable}get domElement(){return this._tiptapEditor.view.dom}clear(){this._tiptapEditor.commands.clearContent(!0),this._tiptapEditor.commands.focus()}};var It=c;export{It as default};
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/editor.ts","../src/schema/base.ts","../src/extensions/ImageInput/ImageInput.ts","../src/utils/file.ts","../src/extensions/SuggestionMenu/SuggestionPlugin.ts","../src/utils/EventEmitter.ts","../src/blocks/MentionBlock.ts","../src/api/InsertMentionBlock.ts","../src/index.ts"],"sourcesContent":["import { Editor, Extension } from '@tiptap/core'\nimport { baseExtensions } from './schema/base'\nimport History from '@tiptap/extension-history'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport { ImageInputPayload } from './extensions/ImageInput/types'\nimport { ImageInputExtension } from './extensions/ImageInput/ImageInput'\nimport { Transaction } from 'prosemirror-state'\nimport { SuggestionMenuProseMirrorPlugin } from './extensions/SuggestionMenu/SuggestionPlugin'\nimport { MentionBlock } from './blocks/MentionBlock'\nimport { type MentionItem } from './extensions/SuggestionMenu/types'\nimport { InsertMentionBlock } from './api/InsertMentionBlock'\n\nexport interface MentionEditorOptions {\n element: HTMLElement\n onChange: (text: string) => void\n placeholder?: string\n onImageInput?: (payload: ImageInputPayload) => void\n onFileReject?: (file: File) => void\n}\n\n\nexport default class MEditor {\n _tiptapEditor: Editor\n public readonly suggestionMenus: SuggestionMenuProseMirrorPlugin\n\n constructor(options: MentionEditorOptions) {\n this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this)\n const MEditorUIExtension = Extension.create({\n name: 'MEditorUIExtension',\n addProseMirrorPlugins: () => {\n return [\n this.suggestionMenus.plugin,\n ]\n },\n })\n\n this._tiptapEditor = new Editor({\n element: options.element,\n extensions: [\n ...baseExtensions,\n History.configure({\n depth: 100,\n newGroupDelay: 500,\n }),\n MentionBlock,\n Placeholder.configure({\n placeholder: options.placeholder || '请输入...',\n emptyEditorClass: 'is-editor-empty'\n }),\n ImageInputExtension.configure({\n onImageInput: options.onImageInput,\n onFileReject: options.onFileReject,\n }),\n MEditorUIExtension,\n ],\n onUpdate: ({ editor }) => {\n options.onChange(editor.getText())\n }\n })\n }\n\n\n dispatch(tr: Transaction) {\n this._tiptapEditor.view.dispatch(tr)\n }\n\n public inserMentionBlock(item: MentionItem) {\n InsertMentionBlock(this, item)\n // this._tiptapEditor\n // .chain()\n // .focus()\n // .insertContent({\n // type: 'mention',\n // attrs: item\n // })\n // .insertContent(' ')\n // .run()\n }\n\n public get isEditable(): boolean {\n return this._tiptapEditor.isEditable === undefined ? true : this._tiptapEditor.isEditable\n }\n\n public get domElement() {\n return this._tiptapEditor.view.dom as HTMLDivElement\n }\n\n public clear() {\n this._tiptapEditor.commands.clearContent(true)\n this._tiptapEditor.commands.focus()\n }\n}\n","import Document from '@tiptap/extension-document'\nimport Paragraph from '@tiptap/extension-paragraph'\nimport Text from '@tiptap/extension-text'\n\nexport const baseExtensions = [\n Document,\n Paragraph,\n Text,\n]\n","import { Extension } from '@tiptap/core'\nimport { Plugin } from 'prosemirror-state'\nimport {\n isImageFile,\n fileToBase64,\n getImageSize,\n} from '../../utils/file'\nimport { ImageInputPayload, ImageInputOptions } from './types'\n\n\n\nexport const ImageInputExtension = Extension.create<ImageInputOptions>({\n name: 'imageInput',\n\n addOptions() {\n return {\n onImageInput: undefined,\n onFileReject: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n const handleFile = async (file: File) => {\n if (!isImageFile(file)) {\n this.options.onFileReject?.(file)\n return\n }\n\n const base64 = await fileToBase64(file)\n const size = await getImageSize(base64)\n\n this.options.onImageInput?.({\n file,\n base64,\n ...size,\n })\n }\n\n return [\n new Plugin({\n props: {\n handlePaste: (_, event) => {\n const items = event.clipboardData?.items\n if (!items) return false\n\n for (const item of items) {\n if (item.kind !== 'file') continue\n const file = item.getAsFile()\n if (!file) continue\n\n handleFile(file)\n return true\n }\n\n return false\n },\n\n handleDrop: (_, event) => {\n const files = event.dataTransfer?.files\n if (!files || files.length === 0) return false\n\n handleFile(files[0])\n return true\n },\n },\n }),\n ]\n },\n})\n","export function isImageFile(file: File) {\n return file.type.startsWith('image/')\n}\n\nexport function fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nexport function getImageSize(base64: string): Promise<{ width: number; height: number }> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve({ width: img.width, height: img.height })\n img.src = base64\n })\n}\n","import { findParentNode } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport MEditor from '../../editor'\nimport { EventEmitter } from '../../utils/EventEmitter'\nimport { SuggestionMenuState, MentionItem } from './types'\n\ntype SuggestionPluginState =\n | {\n triggerCharacter: string\n deleteTriggerCharacter: boolean\n queryStartPos: number\n query: string\n decorationId: string\n ignoreQueryLength?: boolean\n }\n | undefined\n\nexport const suggestionMenuPluginKey = new PluginKey('SuggestionMenuPlugin')\nconst findBlock = findParentNode(node => node.type.name === 'blockContainer')\n\nclass SuggestionMenuView {\n pluginState: SuggestionPluginState\n public state?: SuggestionMenuState\n public emitUpdate: (triggerCharacter: string) => void\n private rootEl?: Document | ShadowRoot\n\n constructor(\n private readonly editor: MEditor,\n emitUpdate: (menuName: string, state: SuggestionMenuState) => void\n ) {\n this.pluginState = undefined\n\n this.emitUpdate = (menuName: string) => {\n if (!this.state) {\n throw new Error('Attempting to update uninitialized suggestions menu')\n }\n emitUpdate(menuName, {\n ...this.state,\n ignoreQueryLength: this.pluginState?.ignoreQueryLength,\n })\n }\n setTimeout(() => {\n this.rootEl = this.editor._tiptapEditor.view.root\n })\n\n }\n\n update(view: EditorView, prevState: EditorState) {\n\n const prev: SuggestionPluginState = suggestionMenuPluginKey.getState(prevState)\n const next: SuggestionPluginState = suggestionMenuPluginKey.getState(view.state)\n\n \n const started = prev === undefined && next !== undefined\n const stopped = prev !== undefined && next === undefined\n const changed = prev !== undefined && next !== undefined\n\n \n if (!started && !changed && !stopped) {\n return\n }\n\n this.pluginState = stopped ? prev : next\n\n if (stopped || !this.editor.isEditable) {\n\n this.state!.show = false\n this.emitUpdate(this.pluginState!.triggerCharacter)\n\n return\n }\n\n const decorationNode = this.rootEl?.querySelector(`[data-decoration-id=\"${this.pluginState!.decorationId}\"]`)\n\n if (this.editor.isEditable && decorationNode) {\n\n this.state = {\n show: true,\n referencePos: decorationNode.getBoundingClientRect(),\n query: this.pluginState!.query,\n }\n\n this.emitUpdate(this.pluginState!.triggerCharacter!)\n }\n }\n\n destroy() {\n\n }\n\n closeMenu = () => {\n this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(suggestionMenuPluginKey, null))\n }\n\n clearQuery = () => {\n if (this.pluginState === undefined) {\n return\n }\n\n this.editor._tiptapEditor\n .chain()\n .focus()\n .deleteRange({\n from:\n this.pluginState.queryStartPos! -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter!.length : 0),\n to: this.editor._tiptapEditor.state.selection.from,\n })\n .run()\n }\n\n insertMention = (item: MentionItem) => {\n this.editor._tiptapEditor\n .chain()\n .focus()\n .insertContent({\n type: 'mention',\n attrs: item\n })\n .insertContent(' ')\n .run()\n }\n\n getReplaceRange() {\n if (!this.pluginState) return null\n\n const from = this.pluginState.queryStartPos -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter.length : 0)\n\n const to = this.editor._tiptapEditor.state.selection.from\n\n return { from, to }\n }\n\n}\n\nexport class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {\n public view: SuggestionMenuView\n public readonly plugin: Plugin\n private triggerCharacters: string[] = []\n\n constructor(editor: MEditor) {\n super()\n const triggerCharacters = this.triggerCharacters\n\n this.plugin = new Plugin({\n key: suggestionMenuPluginKey,\n view: () => {\n this.view = new SuggestionMenuView(editor, (triggerCharacter, state) => {\n this.emit(`update ${triggerCharacter}`, state)\n })\n return this.view\n },\n state: {\n init(): SuggestionPluginState {\n return undefined\n },\n apply(transaction, prev, _oldState, newState): SuggestionPluginState {\n const suggestionPluginTransactionMeta: {\n triggerCharacter: string\n deleteTriggerCharacter?: boolean\n ignoreQueryLength?: boolean\n } | null = transaction.getMeta(suggestionMenuPluginKey)\n\n\n if (\n typeof suggestionPluginTransactionMeta === 'object' &&\n suggestionPluginTransactionMeta !== null &&\n prev === undefined\n ) {\n return {\n triggerCharacter: suggestionPluginTransactionMeta.triggerCharacter,\n deleteTriggerCharacter: suggestionPluginTransactionMeta.deleteTriggerCharacter !== false,\n queryStartPos: transaction.selection?.from || newState.selection.from,\n query: '',\n decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,\n ignoreQueryLength: suggestionPluginTransactionMeta?.ignoreQueryLength,\n }\n }\n\n\n if (prev === undefined) {\n return prev\n }\n\n\n if (\n newState.selection.from !== newState.selection.to ||\n suggestionPluginTransactionMeta === null ||\n transaction.getMeta('focus') ||\n transaction.getMeta('blur') ||\n transaction.getMeta('pointer') ||\n (prev.triggerCharacter !== undefined && newState.selection.from < prev.queryStartPos!)\n ) {\n\n return undefined\n }\n\n const next = { ...prev }\n\n\n next.query = newState.doc.textBetween(prev.queryStartPos!, newState.selection.from)\n\n return next\n }\n },\n\n props: {\n // 当用户输入一个 trigger 字符,且当前没有 suggestion 激活时:\n // 插件拦截这次输入,自己插入字符,并通过 transaction meta 通知 state 打开 suggestion 菜单;\n // 其余情况下,完全不干预编辑器行为。\n handleTextInput(view, _from, _to, text) {\n\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(view.state)\n\n if (triggerCharacters.includes(text) && suggestionPluginState === undefined) {\n view.dispatch(\n view.state.tr.insertText(text).scrollIntoView().setMeta(suggestionMenuPluginKey, {\n triggerCharacter: text,\n })\n )\n\n return true\n }\n return false\n },\n\n decorations(state) {\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(state)\n\n if (suggestionPluginState === undefined) {\n return null\n }\n\n \n if (!suggestionPluginState.deleteTriggerCharacter) {\n const blockNode = findBlock(state.selection)\n if (blockNode) {\n return DecorationSet.create(state.doc, [\n Decoration.node(blockNode.pos, blockNode.pos + blockNode.node.nodeSize, {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }),\n ])\n }\n }\n \n return DecorationSet.create(state.doc, [\n Decoration.inline(\n suggestionPluginState.queryStartPos! - suggestionPluginState.triggerCharacter!.length,\n suggestionPluginState.queryStartPos!,\n {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }\n ),\n ])\n },\n }\n })\n }\n\n public onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void) {\n if (!this.triggerCharacters.includes(triggerCharacter)) {\n this.addTriggerCharacter(triggerCharacter)\n }\n \n return this.on(`update ${triggerCharacter}`, callback)\n }\n\n addTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters.push(triggerCharacter)\n }\n\n \n removeTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters = this.triggerCharacters.filter(c => c !== triggerCharacter)\n }\n\n closeMenu = () => this.view!.closeMenu()\n\n clearQuery = () => this.view!.clearQuery()\n\n insertMention = (item: MentionItem) => this.view!.insertMention(item)\n\n public get shown() {\n return this.view?.state?.show || false\n }\n\n}\n","type StringKeyOf<T> = Extract<keyof T, string>\ntype CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]\n ? T[EventName]\n : [T[EventName]]\ntype CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any\n\nexport class EventEmitter<T extends Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n private callbacks: { [key: string]: Function[] } = {}\n\n public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>) {\n if (!this.callbacks[event]) {\n this.callbacks[event] = []\n }\n\n this.callbacks[event].push(fn)\n\n return () => this.off(event, fn)\n }\n\n protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n callbacks.forEach(callback => callback.apply(this, args))\n }\n }\n\n public off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n if (fn) {\n this.callbacks[event] = callbacks.filter(callback => callback !== fn)\n } else {\n delete this.callbacks[event]\n }\n }\n }\n\n protected removeAllListeners(): void {\n this.callbacks = {}\n }\n}\n","import { Node } from '@tiptap/core'\n\nexport const MentionBlock = Node.create({\n name: 'mention',\n inline: true,\n group: 'inline',\n atom: true,\n selectable: false,\n\n addAttributes() {\n return {\n id: { default: null },\n label: { default: null },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'span[data-type=\"mention\"]',\n },\n ]\n },\n\n renderHTML({ node }) {\n return [\n 'span',\n {\n class: 'mention',\n 'data-type': 'mention',\n 'data-id': node.attrs.id,\n 'data-label': node.attrs.label,\n contenteditable: 'false', // 非编辑\n },\n node.attrs.label,\n ]\n },\n\n // 新增:确保mention节点可以正确序列化/反序列化\n renderText({ node }) {\n return ''\n },\n})\n\n\n","import { Selection } from 'prosemirror-state'\nimport MEditor from '../editor'\n\nexport function InsertMentionBlock(\n editor: MEditor,\n item: { id: string; label: string }\n) {\n\n editor._tiptapEditor.chain().insertContent({\n type: 'mention',\n attrs: {\n id: item.id,\n label: item.label,\n }\n }).run() \n\n}\n\n","import MEditor from \"./editor\";\n\nexport default MEditor\n\nexport * from \"./extensions/SuggestionMenu/types\"\nexport * from \"./extensions/ImageInput/types\""],"mappings":"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","names":["Editor","Extension","Document","Paragraph","Text","baseExtensions","History","Placeholder","Extension","Plugin","isImageFile","file","fileToBase64","resolve","reject","reader","getImageSize","base64","img","ImageInputExtension","Extension","handleFile","file","_a","_b","_c","_d","isImageFile","base64","fileToBase64","size","getImageSize","Plugin","_","event","items","item","files","findParentNode","Plugin","PluginKey","Decoration","DecorationSet","EventEmitter","__publicField","event","fn","args","callbacks","callback","suggestionMenuPluginKey","PluginKey","findBlock","findParentNode","node","SuggestionMenuView","editor","emitUpdate","__publicField","item","menuName","_a","view","prevState","prev","next","started","stopped","decorationNode","from","to","SuggestionMenuProseMirrorPlugin","EventEmitter","triggerCharacter","c","triggerCharacters","Plugin","state","transaction","_oldState","newState","suggestionPluginTransactionMeta","_from","_to","text","suggestionPluginState","blockNode","DecorationSet","Decoration","callback","_b","Node","MentionBlock","node","InsertMentionBlock","editor","item","MEditor","options","__publicField","SuggestionMenuProseMirrorPlugin","MEditorUIExtension","Extension","Editor","baseExtensions","History","MentionBlock","Placeholder","ImageInputExtension","editor","tr","item","InsertMentionBlock","index_default","MEditor"]}
package/package.json CHANGED
@@ -1,38 +1,40 @@
1
1
  {
2
- "name": "@shenjipo/mention-editor",
3
- "version": "2.0.0",
4
- "module": "dist/esm/index.mjs",
5
- "exports": {
6
- ".": {
7
- "import": "./dist/esm/index.mjs",
8
- "types": "./dist/esm/index.d.mts"
9
- }
10
- },
11
- "files": [
12
- "dist"
13
- ],
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
- "keywords": [],
18
- "author": "",
19
- "license": "ISC",
20
- "dependencies": {
21
- "@tiptap/core": "2.7.1",
22
- "@tiptap/extension-document": "2.7.1",
23
- "@tiptap/extension-text": "2.7.1",
24
- "@tiptap/extension-paragraph": "2.7.1",
25
- "@tiptap/extension-placeholder": "2.7.1",
26
- "@tiptap/extension-history": "2.7.1",
27
- "prosemirror-state": "^1.4.3",
28
- "prosemirror-model": "^1.21.0",
29
- "prosemirror-view": "^1.33.7"
30
- },
31
- "main": "index.js",
32
- "devDependencies": {},
33
- "description": "",
34
- "scripts": {
35
- "build:watch": "tsup --watch",
36
- "build": "tsup"
37
- }
2
+ "name": "@shenjipo/mention-editor",
3
+ "version": "2.1.0",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.mjs",
10
+ "types": "./dist/index.d.mts"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "build:watch": "tsup --watch",
21
+ "build": "tsup"
22
+ },
23
+ "keywords": [],
24
+ "author": "",
25
+ "license": "ISC",
26
+ "packageManager": "pnpm@10.10.0",
27
+ "dependencies": {
28
+ "@tiptap/core": "2.7.1",
29
+ "@tiptap/extension-document": "2.7.1",
30
+ "@tiptap/extension-text": "2.7.1",
31
+ "@tiptap/extension-paragraph": "2.7.1",
32
+ "@tiptap/extension-placeholder": "2.7.1",
33
+ "@tiptap/extension-history": "2.7.1",
34
+ "prosemirror-state": "^1.4.3",
35
+ "prosemirror-model": "^1.21.0",
36
+ "prosemirror-view": "^1.33.7"
37
+ },
38
+ "devDependencies": {},
39
+ "description": ""
38
40
  }
@@ -1,2 +0,0 @@
1
- var I=Object.defineProperty;var C=(n,t,e)=>t in n?I(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var a=(n,t,e)=>C(n,typeof t!="symbol"?t+"":t,e);import{Editor as O,Extension as D}from"@tiptap/core";import x from"@tiptap/extension-document";import w from"@tiptap/extension-paragraph";import v from"@tiptap/extension-text";var f=[x,w,v];import Q from"@tiptap/extension-history";import U from"@tiptap/extension-placeholder";import{Extension as k}from"@tiptap/core";import{Plugin as N}from"prosemirror-state";function y(n){return n.type.startsWith("image/")}function E(n){return new Promise((t,e)=>{let i=new FileReader;i.onload=()=>t(i.result),i.onerror=e,i.readAsDataURL(n)})}function S(n){return new Promise(t=>{let e=new Image;e.onload=()=>t({width:e.width,height:e.height}),e.src=n})}var b=k.create({name:"imageInput",addOptions(){return{onImageInput:void 0,onFileReject:void 0}},addProseMirrorPlugins(){let n=async t=>{if(!y(t)){this.options.onFileReject?.(t);return}let e=await E(t),i=await S(e);this.options.onImageInput?.({file:t,base64:e,...i})};return[new N({props:{handlePaste:(t,e)=>{let i=e.clipboardData?.items;if(!i)return!1;for(let r of i){if(r.kind!=="file")continue;let o=r.getAsFile();if(o)return n(o),!0}return!1},handleDrop:(t,e)=>{let i=e.dataTransfer?.files;return!i||i.length===0?!1:(n(i[0]),!0)}}})]}});import{findParentNode as _}from"@tiptap/core";import{Plugin as F,PluginKey as R}from"prosemirror-state";import{Decoration as M,DecorationSet as T}from"prosemirror-view";var c=class{constructor(){a(this,"callbacks",{})}on(t,e){return this.callbacks[t]||(this.callbacks[t]=[]),this.callbacks[t].push(e),()=>this.off(t,e)}emit(t,...e){let i=this.callbacks[t];i&&i.forEach(r=>r.apply(this,e))}off(t,e){let i=this.callbacks[t];i&&(e?this.callbacks[t]=i.filter(r=>r!==e):delete this.callbacks[t])}removeAllListeners(){this.callbacks={}}};var g=new R("SuggestionMenuPlugin"),q=_(n=>n.type.name==="blockContainer"),m=class{constructor(t,e){this.editor=t;a(this,"pluginState");a(this,"state");a(this,"emitUpdate");a(this,"rootEl");a(this,"closeMenu",()=>{this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(g,null))});a(this,"clearQuery",()=>{this.pluginState!==void 0&&this.editor._tiptapEditor.chain().focus().deleteRange({from:this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),to:this.editor._tiptapEditor.state.selection.from}).run()});a(this,"insertMention",t=>{this.editor._tiptapEditor.chain().focus().insertContent({type:"mention",attrs:t}).insertContent(" ").run()});this.pluginState=void 0,this.emitUpdate=i=>{if(!this.state)throw new Error("Attempting to update uninitialized suggestions menu");e(i,{...this.state,ignoreQueryLength:this.pluginState?.ignoreQueryLength})},setTimeout(()=>{this.rootEl=this.editor._tiptapEditor.view.root})}update(t,e){let i=g.getState(e),r=g.getState(t.state),o=i===void 0&&r!==void 0,u=i!==void 0&&r===void 0;if(!o&&!(i!==void 0&&r!==void 0)&&!u)return;if(this.pluginState=u?i:r,u||!this.editor.isEditable){this.state.show=!1,this.emitUpdate(this.pluginState.triggerCharacter);return}let l=this.rootEl?.querySelector(`[data-decoration-id="${this.pluginState.decorationId}"]`);this.editor.isEditable&&l&&(this.state={show:!0,referencePos:l.getBoundingClientRect(),query:this.pluginState.query},this.emitUpdate(this.pluginState.triggerCharacter))}destroy(){}getReplaceRange(){if(!this.pluginState)return null;let t=this.pluginState.queryStartPos-(this.pluginState.deleteTriggerCharacter?this.pluginState.triggerCharacter.length:0),e=this.editor._tiptapEditor.state.selection.from;return{from:t,to:e}}},p=class extends c{constructor(e){super();a(this,"view");a(this,"plugin");a(this,"triggerCharacters",[]);a(this,"addTriggerCharacter",e=>{this.triggerCharacters.push(e)});a(this,"removeTriggerCharacter",e=>{this.triggerCharacters=this.triggerCharacters.filter(i=>i!==e)});a(this,"closeMenu",()=>this.view.closeMenu());a(this,"clearQuery",()=>this.view.clearQuery());a(this,"insertMention",e=>this.view.insertMention(e));let i=this.triggerCharacters;this.plugin=new F({key:g,view:()=>(this.view=new m(e,(r,o)=>{this.emit(`update ${r}`,o)}),this.view),state:{init(){},apply(r,o,u,s){let l=r.getMeta(g);if(typeof l=="object"&&l!==null&&o===void 0)return{triggerCharacter:l.triggerCharacter,deleteTriggerCharacter:l.deleteTriggerCharacter!==!1,queryStartPos:r.selection?.from||s.selection.from,query:"",decorationId:`id_${Math.floor(Math.random()*4294967295)}`,ignoreQueryLength:l?.ignoreQueryLength};if(o===void 0)return o;if(s.selection.from!==s.selection.to||l===null||r.getMeta("focus")||r.getMeta("blur")||r.getMeta("pointer")||o.triggerCharacter!==void 0&&s.selection.from<o.queryStartPos)return;let h={...o};return h.query=s.doc.textBetween(o.queryStartPos,s.selection.from),h}},props:{handleTextInput(r,o,u,s){let l=this.getState(r.state);return i.includes(s)&&l===void 0?(r.dispatch(r.state.tr.insertText(s).scrollIntoView().setMeta(g,{triggerCharacter:s})),!0):!1},decorations(r){let o=this.getState(r);if(o===void 0)return null;if(!o.deleteTriggerCharacter){let u=q(r.selection);if(u)return T.create(r.doc,[M.node(u.pos,u.pos+u.node.nodeSize,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}return T.create(r.doc,[M.inline(o.queryStartPos-o.triggerCharacter.length,o.queryStartPos,{nodeName:"span",class:"bn-suggestion-decorator","data-decoration-id":o.decorationId})])}}})}onUpdate(e,i){return this.triggerCharacters.includes(e)||this.addTriggerCharacter(e),this.on(`update ${e}`,i)}get shown(){return this.view?.state?.show||!1}};import{Node as L}from"@tiptap/core";var P=L.create({name:"mention",inline:!0,group:"inline",atom:!0,selectable:!1,addAttributes(){return{id:{default:null},label:{default:null}}},parseHTML(){return[{tag:'span[data-type="mention"]'}]},renderHTML({node:n}){return["span",{class:"mention","data-type":"mention","data-id":n.attrs.id,"data-label":n.attrs.label,contenteditable:"false"},n.attrs.label]},renderText({node:n}){return""}});var d=class{constructor(t){a(this,"_tiptapEditor");a(this,"suggestionMenus");this.suggestionMenus=new p(this);let e=D.create({name:"MEditorUIExtension",addProseMirrorPlugins:()=>[this.suggestionMenus.plugin]});this._tiptapEditor=new O({element:t.element,extensions:[...f,Q.configure({depth:100,newGroupDelay:500}),P,U.configure({placeholder:t.placeholder||"\u8BF7\u8F93\u5165...",emptyEditorClass:"is-editor-empty"}),b.configure({onImageInput:t.onImageInput,onFileReject:t.onFileReject}),e],onUpdate:({editor:i})=>{t.onChange(i.getText())}})}dispatch(t){this._tiptapEditor.view.dispatch(t)}inserMentionBlock(t){this._tiptapEditor.chain().focus().insertContent({type:"mention",attrs:t}).insertContent(" ").run()}get isEditable(){return this._tiptapEditor.isEditable===void 0?!0:this._tiptapEditor.isEditable}get domElement(){return this._tiptapEditor.view.dom}clear(){this._tiptapEditor.commands.clearContent(!0),this._tiptapEditor.commands.focus()}};var Et=d;export{Et as default};
2
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/editor.ts","../../src/schema/base.ts","../../src/extensions/ImageInput/ImageInput.ts","../../src/utils/file.ts","../../src/extensions/SuggestionMenu/SuggestionPlugin.ts","../../src/utils/EventEmitter.ts","../../src/blocks/MentionBlock.ts","../../src/index.ts"],"sourcesContent":["import { Editor, Extension } from '@tiptap/core'\nimport { baseExtensions } from './schema/base'\nimport History from '@tiptap/extension-history'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport { ImageInputPayload } from './extensions/ImageInput/types'\nimport { ImageInputExtension } from './extensions/ImageInput/ImageInput'\nimport { Transaction } from 'prosemirror-state'\nimport { SuggestionMenuProseMirrorPlugin } from './extensions/SuggestionMenu/SuggestionPlugin'\nimport { MentionBlock } from './blocks/MentionBlock'\nimport { type MentionItem } from './extensions/SuggestionMenu/types'\n\nexport interface MentionEditorOptions {\n element: HTMLElement\n onChange: (text: string) => void\n placeholder?: string\n onImageInput?: (payload: ImageInputPayload) => void\n onFileReject?: (file: File) => void\n}\n\n\nexport default class MEditor {\n _tiptapEditor: Editor\n public readonly suggestionMenus: SuggestionMenuProseMirrorPlugin\n\n constructor(options: MentionEditorOptions) {\n this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this)\n const MEditorUIExtension = Extension.create({\n name: 'MEditorUIExtension',\n addProseMirrorPlugins: () => {\n return [\n this.suggestionMenus.plugin,\n ]\n },\n })\n\n this._tiptapEditor = new Editor({\n element: options.element,\n extensions: [\n ...baseExtensions,\n History.configure({\n depth: 100,\n newGroupDelay: 500,\n }),\n MentionBlock,\n Placeholder.configure({\n placeholder: options.placeholder || '请输入...',\n emptyEditorClass: 'is-editor-empty'\n }),\n ImageInputExtension.configure({\n onImageInput: options.onImageInput,\n onFileReject: options.onFileReject,\n }),\n MEditorUIExtension,\n ],\n onUpdate: ({ editor }) => {\n options.onChange(editor.getText())\n }\n })\n }\n\n\n dispatch(tr: Transaction) {\n this._tiptapEditor.view.dispatch(tr)\n }\n\n public inserMentionBlock(item: MentionItem) {\n this._tiptapEditor\n .chain()\n .focus()\n .insertContent({\n type: 'mention',\n attrs: item\n })\n .insertContent(' ')\n .run()\n }\n\n public get isEditable(): boolean {\n return this._tiptapEditor.isEditable === undefined ? true : this._tiptapEditor.isEditable\n }\n\n public get domElement() {\n return this._tiptapEditor.view.dom as HTMLDivElement\n }\n\n public clear() {\n this._tiptapEditor.commands.clearContent(true)\n this._tiptapEditor.commands.focus()\n }\n}\n","import Document from '@tiptap/extension-document'\nimport Paragraph from '@tiptap/extension-paragraph'\nimport Text from '@tiptap/extension-text'\n\nexport const baseExtensions = [\n Document,\n Paragraph,\n Text,\n]\n","import { Extension } from '@tiptap/core'\nimport { Plugin } from 'prosemirror-state'\nimport {\n isImageFile,\n fileToBase64,\n getImageSize,\n} from '../../utils/file'\nimport { ImageInputPayload, ImageInputOptions } from './types'\n\n\n\nexport const ImageInputExtension = Extension.create<ImageInputOptions>({\n name: 'imageInput',\n\n addOptions() {\n return {\n onImageInput: undefined,\n onFileReject: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n const handleFile = async (file: File) => {\n if (!isImageFile(file)) {\n this.options.onFileReject?.(file)\n return\n }\n\n const base64 = await fileToBase64(file)\n const size = await getImageSize(base64)\n\n this.options.onImageInput?.({\n file,\n base64,\n ...size,\n })\n }\n\n return [\n new Plugin({\n props: {\n handlePaste: (_, event) => {\n const items = event.clipboardData?.items\n if (!items) return false\n\n for (const item of items) {\n if (item.kind !== 'file') continue\n const file = item.getAsFile()\n if (!file) continue\n\n handleFile(file)\n return true\n }\n\n return false\n },\n\n handleDrop: (_, event) => {\n const files = event.dataTransfer?.files\n if (!files || files.length === 0) return false\n\n handleFile(files[0])\n return true\n },\n },\n }),\n ]\n },\n})\n","export function isImageFile(file: File) {\n return file.type.startsWith('image/')\n}\n\nexport function fileToBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = reject\n reader.readAsDataURL(file)\n })\n}\n\nexport function getImageSize(base64: string): Promise<{ width: number; height: number }> {\n return new Promise(resolve => {\n const img = new Image()\n img.onload = () => resolve({ width: img.width, height: img.height })\n img.src = base64\n })\n}\n","import { findParentNode } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, EditorView } from 'prosemirror-view'\nimport MEditor from '../../editor'\nimport { EventEmitter } from '../../utils/EventEmitter'\nimport { SuggestionMenuState, MentionItem } from './types'\n\ntype SuggestionPluginState =\n | {\n triggerCharacter: string\n deleteTriggerCharacter: boolean\n queryStartPos: number\n query: string\n decorationId: string\n ignoreQueryLength?: boolean\n }\n | undefined\n\nexport const suggestionMenuPluginKey = new PluginKey('SuggestionMenuPlugin')\nconst findBlock = findParentNode(node => node.type.name === 'blockContainer')\n\nclass SuggestionMenuView {\n pluginState: SuggestionPluginState\n public state?: SuggestionMenuState\n public emitUpdate: (triggerCharacter: string) => void\n private rootEl?: Document | ShadowRoot\n\n constructor(\n private readonly editor: MEditor,\n emitUpdate: (menuName: string, state: SuggestionMenuState) => void\n ) {\n this.pluginState = undefined\n\n this.emitUpdate = (menuName: string) => {\n if (!this.state) {\n throw new Error('Attempting to update uninitialized suggestions menu')\n }\n emitUpdate(menuName, {\n ...this.state,\n ignoreQueryLength: this.pluginState?.ignoreQueryLength,\n })\n }\n setTimeout(() => {\n this.rootEl = this.editor._tiptapEditor.view.root\n })\n\n }\n\n update(view: EditorView, prevState: EditorState) {\n\n const prev: SuggestionPluginState = suggestionMenuPluginKey.getState(prevState)\n const next: SuggestionPluginState = suggestionMenuPluginKey.getState(view.state)\n\n // See how the state changed\n const started = prev === undefined && next !== undefined\n const stopped = prev !== undefined && next === undefined\n const changed = prev !== undefined && next !== undefined\n\n // Cancel when suggestion isn't active\n if (!started && !changed && !stopped) {\n return\n }\n\n this.pluginState = stopped ? prev : next\n\n if (stopped || !this.editor.isEditable) {\n\n this.state!.show = false\n this.emitUpdate(this.pluginState!.triggerCharacter)\n\n return\n }\n\n const decorationNode = this.rootEl?.querySelector(`[data-decoration-id=\"${this.pluginState!.decorationId}\"]`)\n\n if (this.editor.isEditable && decorationNode) {\n\n this.state = {\n show: true,\n referencePos: decorationNode.getBoundingClientRect(),\n query: this.pluginState!.query,\n }\n\n this.emitUpdate(this.pluginState!.triggerCharacter!)\n }\n }\n\n destroy() {\n\n }\n\n closeMenu = () => {\n this.editor.dispatch(this.editor._tiptapEditor.view.state.tr.setMeta(suggestionMenuPluginKey, null))\n }\n\n clearQuery = () => {\n if (this.pluginState === undefined) {\n return\n }\n\n this.editor._tiptapEditor\n .chain()\n .focus()\n .deleteRange({\n from:\n this.pluginState.queryStartPos! -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter!.length : 0),\n to: this.editor._tiptapEditor.state.selection.from,\n })\n .run()\n }\n\n insertMention = (item: MentionItem) => {\n this.editor._tiptapEditor\n .chain()\n .focus()\n .insertContent({\n type: 'mention',\n attrs: item\n })\n .insertContent(' ')\n .run()\n }\n\n getReplaceRange() {\n if (!this.pluginState) return null\n\n const from = this.pluginState.queryStartPos -\n (this.pluginState.deleteTriggerCharacter ? this.pluginState.triggerCharacter.length : 0)\n\n const to = this.editor._tiptapEditor.state.selection.from\n\n return { from, to }\n }\n\n}\n\nexport class SuggestionMenuProseMirrorPlugin extends EventEmitter<any> {\n public view: SuggestionMenuView\n public readonly plugin: Plugin\n private triggerCharacters: string[] = []\n\n constructor(editor: MEditor) {\n super()\n const triggerCharacters = this.triggerCharacters\n\n this.plugin = new Plugin({\n key: suggestionMenuPluginKey,\n view: () => {\n this.view = new SuggestionMenuView(editor, (triggerCharacter, state) => {\n this.emit(`update ${triggerCharacter}`, state)\n })\n return this.view\n },\n state: {\n init(): SuggestionPluginState {\n return undefined\n },\n apply(transaction, prev, _oldState, newState): SuggestionPluginState {\n const suggestionPluginTransactionMeta: {\n triggerCharacter: string\n deleteTriggerCharacter?: boolean\n ignoreQueryLength?: boolean\n } | null = transaction.getMeta(suggestionMenuPluginKey)\n\n\n if (\n typeof suggestionPluginTransactionMeta === 'object' &&\n suggestionPluginTransactionMeta !== null &&\n prev === undefined\n ) {\n return {\n triggerCharacter: suggestionPluginTransactionMeta.triggerCharacter,\n deleteTriggerCharacter: suggestionPluginTransactionMeta.deleteTriggerCharacter !== false,\n queryStartPos: transaction.selection?.from || newState.selection.from,\n query: '',\n decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,\n ignoreQueryLength: suggestionPluginTransactionMeta?.ignoreQueryLength,\n }\n }\n\n\n if (prev === undefined) {\n return prev\n }\n\n\n if (\n newState.selection.from !== newState.selection.to ||\n suggestionPluginTransactionMeta === null ||\n transaction.getMeta('focus') ||\n transaction.getMeta('blur') ||\n transaction.getMeta('pointer') ||\n (prev.triggerCharacter !== undefined && newState.selection.from < prev.queryStartPos!)\n ) {\n\n return undefined\n }\n\n const next = { ...prev }\n\n\n next.query = newState.doc.textBetween(prev.queryStartPos!, newState.selection.from)\n\n return next\n }\n },\n\n props: {\n // 当用户输入一个 trigger 字符,且当前没有 suggestion 激活时:\n // 插件拦截这次输入,自己插入字符,并通过 transaction meta 通知 state 打开 suggestion 菜单;\n // 其余情况下,完全不干预编辑器行为。\n handleTextInput(view, _from, _to, text) {\n\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(view.state)\n\n if (triggerCharacters.includes(text) && suggestionPluginState === undefined) {\n view.dispatch(\n view.state.tr.insertText(text).scrollIntoView().setMeta(suggestionMenuPluginKey, {\n triggerCharacter: text,\n })\n )\n\n return true\n }\n return false\n },\n\n // Setup decorator on the currently active suggestion.\n decorations(state) {\n const suggestionPluginState: SuggestionPluginState = (this as Plugin).getState(state)\n\n if (suggestionPluginState === undefined) {\n return null\n }\n\n // If the menu was opened programmatically by another extension, it may not use a trigger character. In this\n // case, the decoration is set on the whole block instead, as the decoration range would otherwise be empty.\n if (!suggestionPluginState.deleteTriggerCharacter) {\n const blockNode = findBlock(state.selection)\n if (blockNode) {\n return DecorationSet.create(state.doc, [\n Decoration.node(blockNode.pos, blockNode.pos + blockNode.node.nodeSize, {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }),\n ])\n }\n }\n // Creates an inline decoration around the trigger character.\n return DecorationSet.create(state.doc, [\n Decoration.inline(\n suggestionPluginState.queryStartPos! - suggestionPluginState.triggerCharacter!.length,\n suggestionPluginState.queryStartPos!,\n {\n nodeName: 'span',\n class: 'bn-suggestion-decorator',\n 'data-decoration-id': suggestionPluginState.decorationId,\n }\n ),\n ])\n },\n }\n })\n }\n\n public onUpdate(triggerCharacter: string, callback: (state: SuggestionMenuState) => void) {\n if (!this.triggerCharacters.includes(triggerCharacter)) {\n this.addTriggerCharacter(triggerCharacter)\n }\n // TODO: be able to remove the triggerCharacter\n return this.on(`update ${triggerCharacter}`, callback)\n }\n\n addTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters.push(triggerCharacter)\n }\n\n // TODO: Should this be called automatically when listeners are removed?\n removeTriggerCharacter = (triggerCharacter: string) => {\n this.triggerCharacters = this.triggerCharacters.filter(c => c !== triggerCharacter)\n }\n\n closeMenu = () => this.view!.closeMenu()\n\n clearQuery = () => this.view!.clearQuery()\n\n insertMention = (item: MentionItem) => this.view!.insertMention(item)\n\n public get shown() {\n return this.view?.state?.show || false\n }\n\n}\n","type StringKeyOf<T> = Extract<keyof T, string>\ntype CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]\n ? T[EventName]\n : [T[EventName]]\ntype CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any\n\nexport class EventEmitter<T extends Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n private callbacks: { [key: string]: Function[] } = {}\n\n public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>) {\n if (!this.callbacks[event]) {\n this.callbacks[event] = []\n }\n\n this.callbacks[event].push(fn)\n\n return () => this.off(event, fn)\n }\n\n protected emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n callbacks.forEach(callback => callback.apply(this, args))\n }\n }\n\n public off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>) {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n if (fn) {\n this.callbacks[event] = callbacks.filter(callback => callback !== fn)\n } else {\n delete this.callbacks[event]\n }\n }\n }\n\n protected removeAllListeners(): void {\n this.callbacks = {}\n }\n}\n","import { Node } from '@tiptap/core'\n\nexport const MentionBlock = Node.create({\n name: 'mention',\n inline: true,\n group: 'inline',\n atom: true,\n selectable: false,\n\n addAttributes() {\n return {\n id: { default: null },\n label: { default: null },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'span[data-type=\"mention\"]',\n },\n ]\n },\n\n renderHTML({ node }) {\n return [\n 'span',\n {\n class: 'mention',\n 'data-type': 'mention',\n 'data-id': node.attrs.id,\n 'data-label': node.attrs.label,\n contenteditable: 'false', // 非编辑\n },\n node.attrs.label,\n ]\n },\n\n // 新增:确保mention节点可以正确序列化/反序列化\n renderText({ node }) {\n return ''\n },\n})\n\n\n","import MEditor from \"./editor\";\n\nexport default MEditor\n\nexport * from \"./extensions/SuggestionMenu/types\"\nexport * from \"./extensions/ImageInput/types\""],"mappings":"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","names":["Editor","Extension","Document","Paragraph","Text","baseExtensions","History","Placeholder","Extension","Plugin","isImageFile","file","fileToBase64","resolve","reject","reader","getImageSize","base64","img","ImageInputExtension","Extension","handleFile","file","isImageFile","base64","fileToBase64","size","getImageSize","Plugin","_","event","items","item","files","findParentNode","Plugin","PluginKey","Decoration","DecorationSet","EventEmitter","__publicField","event","fn","args","callbacks","callback","suggestionMenuPluginKey","PluginKey","findBlock","findParentNode","node","SuggestionMenuView","editor","emitUpdate","__publicField","item","menuName","view","prevState","prev","next","started","stopped","decorationNode","from","to","SuggestionMenuProseMirrorPlugin","EventEmitter","triggerCharacter","c","triggerCharacters","Plugin","state","transaction","_oldState","newState","suggestionPluginTransactionMeta","_from","_to","text","suggestionPluginState","blockNode","DecorationSet","Decoration","callback","Node","MentionBlock","node","MEditor","options","__publicField","SuggestionMenuProseMirrorPlugin","MEditorUIExtension","Extension","Editor","baseExtensions","History","MentionBlock","Placeholder","ImageInputExtension","editor","tr","item","index_default","MEditor"]}
File without changes