import { Node as t, mergeAttributes as e, nodeInputRule as o } from "@tiptap/core"
import { Suggestion as s } from "@tiptap/suggestion"
const n = (t) => (t ? t.charAt(t.length - 1) : ""),
i = (t) => t?.match(/([^\s]+)\s*$/)?.[1] || ""
export const Emoji = t.create({
name: "emoji",
content: "text*",
group: "inline",
inline: !0,
selectable: !0,
atom: !0,
defining: !0,
addStorage: () => ({ suggestionOpen: !1 }),
addOptions() {
return {
HTMLAttributes: { class: "emoji" },
suggestion: {
char: ":",
command: ({ editor: t, range: e, props: o }) => {
const { nodeAfter: s } = t.view.state.selection.$to,
n = s?.text?.startsWith(" ")
n && (e.to += 1)
const { emoji: i, shortCodes: a } = o
;(t
.chain()
.focus()
.insertContentAt(e, [
{
type: this.name,
tag: "span",
attrs: { "data-shortcode": a[0], "data-emoji": i },
text: i,
},
{ type: "text", text: " " },
])
.run(),
window.getSelection()?.collapseToEnd())
},
allow: ({ state: t, range: e }) => {
const o = t.doc.resolve(e.from),
s = t.schema.nodes[this.name]
return !!o.parent.type.contentMatch.matchType(s)
},
},
}
},
addCommands() {
return {
setEmoji:
({ key: t, emoji: e }) =>
({ commands: o }) => {
const s = this.options.emojiModule.getEmojiData(t)
if (!s) return !1
o.insertContent(" ")
const { url: n, shortCodes: i } = s
return (
n
? o.insertContent(
`<span class="emoji" data-shortcode="${i?.[0]}" data-emoji=${e}>:${i?.[0]}:</span>`,
)
: o.insertContent(
`<span class="emoji" data-shortcode="${t}" data-emoji="${e}">${e}</span>`,
),
!0
)
},
}
},
addAttributes: () => ({
"data-shortcode": { parseHTML: (t) => t.getAttribute("data-shortcode"), default: "" },
"data-emoji": { parseHTML: (t) => t.getAttribute("data-emoji"), default: "" },
}),
addInputRules() {
const { getEmojiData: t } = this.options.emojiModule,
{ emojisWithEmoticons: e } = this.options.suggestion,
s = (t, e, o) => -1 !== t && e.slice(t) === o && (/\s/.test(e.slice(t - 1, t)) || 0 === t)
return [
o({
find: (o) => {
const a = ((t) => {
const e = []
return (
t.descendants((t) => {
"mention" === t.type.name && e.push(`@${t.attrs.label}`)
}),
e
)
})(this.editor.state.doc)
for (const t of a) o = o.replaceAll(t, "")
const r = n(o)
if (" " !== r && "\n" !== r) return
const c = ((o) => {
for (const t of e)
if (Array.isArray(t.emoticon))
for (const e of t.emoticon) {
const n = o.indexOf(e)
if (s(n, o, e)) return { text: o.slice(n), emoji: t }
}
else {
const e = o.indexOf(t.emoticon)
if (s(e, o, t.emoticon)) return { text: o.slice(e), emoji: t }
}
const n = o.match(/:(.*):/)
if (n) {
const e = t(n[1])
if (e) return { text: o.slice(n.index), emoji: e }
}
return null
})(i(o))
return c
? { data: c.emoji, text: o, replaceWith: "\n" === r ? `${c.text}\n` : c.text }
: void 0
},
type: this.type,
getAttributes: (t) => {
if (t.data) {
const { shortCodes: e, emoji: o } = t.data
return { "data-shortcode": e?.[0], "data-emoji": o }
}
},
}),
]
},
addProseMirrorPlugins() {
return [s({ editor: this.editor, ...this.options.suggestion })]
},
parseHTML: () => [
{
tag: "span",
getAttrs: (t) =>
!(!t.hasAttribute("class") || !t.classList.contains("emoji")) && {
"data-shortcode": t.getAttribute("data-shortcode"),
"data-emoji": t.getAttribute("data-emoji"),
},
},
],
renderHTML({ node: t, HTMLAttributes: o }) {
const { getEmojiData: s } = this.options.emojiModule,
n = o["data-shortcode"],
{ url: i } = s(n) || {}
if (i)
return [
"span",
e(this.options.HTMLAttributes, o, { contenteditable: "false" }),
["img", { class: "emoji-img", src: i }, 0],
]
const { emoji: a } = s(n) || {}
return [
"span",
e(this.options.HTMLAttributes, o, { contenteditable: "false" }),
a || n || t.textContent || "",
]
},
})