mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 23:47:12 +02:00
feat: add rich emoji preview in editor
Emoji inserted via the autocomplete now display as actual images/characters instead of :shortcode: text: - Custom emoji: renders as inline <img> with proper sizing - Unicode emoji: renders as text with emoji font sizing - Both show :shortcode: on hover via title attribute CSS styles ensure proper vertical alignment with surrounding text.
This commit is contained in:
@@ -5,8 +5,7 @@ import {
|
||||
useMemo,
|
||||
useCallback,
|
||||
} from "react";
|
||||
import { useEditor, EditorContent } from "@tiptap/react";
|
||||
import { ReactRenderer } from "@tiptap/react";
|
||||
import { useEditor, EditorContent, ReactRenderer } from "@tiptap/react";
|
||||
import StarterKit from "@tiptap/starter-kit";
|
||||
import Mention from "@tiptap/extension-mention";
|
||||
import Placeholder from "@tiptap/extension-placeholder";
|
||||
@@ -62,9 +61,47 @@ export interface MentionEditorHandle {
|
||||
submit: () => void;
|
||||
}
|
||||
|
||||
// Create emoji extension by extending Mention with a different name
|
||||
// Create emoji extension by extending Mention with a different name and custom node view
|
||||
const EmojiMention = Mention.extend({
|
||||
name: "emoji",
|
||||
|
||||
addNodeView() {
|
||||
return ({ node, HTMLAttributes }) => {
|
||||
// Create wrapper span
|
||||
const dom = document.createElement("span");
|
||||
dom.className = "emoji-node";
|
||||
Object.entries(HTMLAttributes).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
dom.setAttribute(key, String(value));
|
||||
}
|
||||
});
|
||||
|
||||
const { url, source, id } = node.attrs;
|
||||
const isUnicode = source === "unicode";
|
||||
|
||||
if (isUnicode) {
|
||||
// Unicode emoji - render as text span
|
||||
const span = document.createElement("span");
|
||||
span.className = "emoji-unicode";
|
||||
span.textContent = url;
|
||||
span.title = `:${id}:`;
|
||||
dom.appendChild(span);
|
||||
} else {
|
||||
// Custom emoji - render as image
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = `:${id}:`;
|
||||
img.title = `:${id}:`;
|
||||
img.className = "emoji-image";
|
||||
img.draggable = false;
|
||||
dom.appendChild(img);
|
||||
}
|
||||
|
||||
return {
|
||||
dom,
|
||||
};
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const MentionEditor = forwardRef<
|
||||
|
||||
@@ -314,3 +314,23 @@ body.animating-layout
|
||||
.ProseMirror .mention:hover {
|
||||
background-color: hsl(var(--primary) / 0.2);
|
||||
}
|
||||
|
||||
/* Emoji styles */
|
||||
.ProseMirror .emoji-node {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ProseMirror .emoji-image {
|
||||
height: 1.2em;
|
||||
width: auto;
|
||||
vertical-align: middle;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.ProseMirror .emoji-unicode {
|
||||
font-size: 1.1em;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user