import Mention from '@tiptap/extension-mention' import Image from '@tiptap/extension-image' import { DOMParser } from 'prosemirror-model' import { PluginKey } from 'prosemirror-state' import { mergeAttributes, textblockTypeInputRule } from '@tiptap/core' import { EmojiIndex } from 'emoji-mart-vue-fast' import emojiData from 'emoji-mart-vue-fast/data/all.json' import { klausmojis } from './emoji' export const EmojiSearch = Mention.extend({ name: 'emoji-search', addOptions() { return { ...this.parent?.(), suggestion: { pluginKey: new PluginKey('emojisearch'), command: ({ editor, range, props }) => { editor .chain() .focus() .insertContentAt(range, [ { type: 'emoji-search', attrs: props }, { type: 'text', text: ' ' }, ]) .run() }, }, } }, addAttributes() { return { label: { default: null }, } }, renderHTML({ node, HTMLAttributes }) { return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), `${node.attrs.label}`] }, renderText({ node }) { return `${node.attrs.label}` }, addKeyboardShortcuts() { return {} }, }) declare module '@tiptap/core' { interface Commands { klausmoji: { /** * Set emoji with url */ setKlausmoji: (options: { src: string }) => ReturnType } } } export const Klausmoji = Image.extend({ name: 'klausmoji', inline: true, group: 'inline', draggable: false, addAttributes() { return { src: {}, alt: { default: null }, title: { default: null }, height: { default: 18 }, width: { default: 18 }, } }, parseHTML() { return [{ tag: 'img.klausmoji[src]' }] }, addCommands() { return { setKlausmoji: (options) => ({ tr, dispatch }) => { const { selection } = tr const node = this.type.create(options) if (dispatch) tr.replaceRangeWith(selection.from, selection.to, node) return true }, } }, }) const elementFromString = (value: string) => { const element = document.createElement('span') element.innerHTML = value.trim() return element } export const insertHTML = ({ state, view }, value: string) => { const { selection } = state const element = elementFromString(value) const slice = DOMParser.fromSchema(state.schema).parseSlice(element) const transaction = state.tr.insert(selection.anchor, slice.content) view.dispatch(transaction) } let emojiIndex export const getEmojis = () => { if (!emojiIndex) emojiIndex = new EmojiIndex(emojiData, { custom: klausmojis }) return emojiIndex }