編輯器
Editor Emoji Menu
輸入 : 觸發浮動選單;挑選並插入 emoji。
安裝
Terminal
pnpm dlx shadcn-vue add @admin/editor-emoji-menuimport type { EmojiMenuItem } from '@/registry/admin/ui/editor-emoji-menu'import { AdminEditorEmojiMenu } from '@/registry/admin/ui/editor-emoji-menu'admin-editor-emoji-menu 依賴 @admin/rich-editor 與 @tiptap/extension-emoji。
裝完必須在 :extensions 注入 Emoji
@tiptap/extension-emoji 跟 mention 一樣是 Node extension,會動到 schema,而 schema 在編輯器建立時就固定、無法後補。<AdminEditorEmojiMenu> mount 時會註冊 Suggestion plugin,popup 照常浮現,但 schema 沒有 emoji node 時,選定後只能退回插入純 Unicode 字元,失去 emoji node 的三個好處:
- 多 codepoint 安全:
👨👩👧這類 ZWJ 組合 emoji,純 Unicode 模式下游標和 backspace 會跳到字元中間 - attrs 持久化:node 模式存的是
data-name="smile",之後可以用 shortcode 反查、切換 emoji set - 不吃 mark:atomic node 不會被 bold / italic / color 等 mark 套到
import Emoji from '@tiptap/extension-emoji'const extensions = [Emoji.configure({ enableEmoticons: false })]// <AdminRichEditor :extensions="extensions">用法
<script setup lang="ts">import type { JSONContent } from '@tiptap/core'import type { EmojiMenuItem } from '@/components/admin/editor-emoji-menu'import Emoji, { gitHubEmojis } from '@tiptap/extension-emoji'import { ref } from 'vue'import { cn } from '@/lib/utils'import { baseProseClass } from '@/components/admin/editor'import { AdminEditorEmojiMenu } from '@/components/admin/editor-emoji-menu'import { AdminEditorToolbar, defaultEditorItems } from '@/components/admin/editor-toolbar'import { AdminRichEditor, starterKitProseClass } from '@/components/admin/rich-editor'const value = ref<JSONContent | string>('<p>輸入 : 觸發 emoji menu(例如 :smile)</p>')// 用 @tiptap/extension-emoji 內附的 gitHubEmojis 資料集(免自帶 JSON)。// 過濾掉 regional_indicator(國旗用字母)與顯而易見不適合 demo 的群,取前 60 個常用。const emojis: EmojiMenuItem[] = gitHubEmojis .filter( (e) => !e.name.startsWith('regional_indicator_') && !e.name.endsWith('_button') && !e.name.endsWith('_sign') ) .slice(0, 60)// 把 Emoji extension 傳給 admin-rich-editor,讓 schema 有 emoji node// 選定後插入的是 atomic node(多 codepoint 安全 / attrs 持久化 / 不吃 mark)// 不傳的話 SFC 會 fallback 為純 Unicode 插入(有缺點)const extensions = [Emoji.configure({ enableEmoticons: false })]</script><template> <admin-rich-editor v-slot="{ editor, handlers }" v-model="value" :extensions="extensions" :ui="{ base: cn(baseProseClass, starterKitProseClass) }" > <admin-editor-toolbar :editor :handlers="handlers" :items="defaultEditorItems" /> <admin-editor-emoji-menu :editor :items="emojis" /> </admin-rich-editor></template><script setup lang="ts">import Emoji, { gitHubEmojis } from '@tiptap/extension-emoji'import { ref } from 'vue'import { AdminEditorEmojiMenu } from '@/registry/admin/ui/editor-emoji-menu'import { AdminRichEditor } from '@/registry/admin/ui/rich-editor'const value = ref('')// @tiptap/extension-emoji 內附 gitHubEmojis 資料集(約 1500 個),免自帶 JSONconst emojis = gitHubEmojis.slice(0, 50)// schema 需要 emoji node,顯式傳入 Emoji extensionconst extensions = [Emoji.configure({ enableEmoticons: false })]</script><template> <admin-rich-editor v-slot="{ editor }" v-model="value" :extensions="extensions"> <admin-editor-emoji-menu :editor :items="emojis" /> </admin-rich-editor></template>EmojiMenuItem 介面
本 block 的 EmojiMenuItem 跟 @tiptap/extension-emoji 的 EmojiItem shape 一致:
interface EmojiMenuItem { name: string // 唯一名稱,當作 emoji node attr 存 emoji?: string // Unicode 字元(fallback 模式時用) shortcodes: string[] // 簡碼 filter 欄位(:smile / :+1) tags: string[] // 標籤 filter 欄位(happy / love) group?: string // 分組(供 UI 分類) fallbackImage?: string // emoji 為 undefined 時的圖片 fallback(自訂貼圖) emoticons?: string[] // 顏文字(`:)` 對應 :smile),配 enableEmoticons 使用 [key: string]: unknown}filter 多欄位行為
預設 filterFields = ['name', 'shortcodes', 'tags'],輸入的字串會同時比對這三個欄位。例如輸入 :happy 時,{ name: 'grinning', shortcodes: ['grin'], tags: ['happy', 'joy'] } 會因 tags 命中而列入結果。
emoji 資料集
@tiptap/extension-emoji 內附三個資料集,免自帶 JSON:
import { emojis, gitHubCustomEmojis, gitHubEmojis } from '@tiptap/extension-emoji'// emojis:通用 Unicode emoji 全集// gitHubEmojis:GitHub 風格(約 1500 個,含 shortcode)// gitHubCustomEmojis:GitHub 自訂(如 :octocat:)要自訂資料集,直接傳 EmojiMenuItem[] 給 :items 即可。後台情境通常 50 到 100 個常用就夠:
const emojis = gitHubEmojis.slice(0, 50)資料量上千時,改用 v-model:search-term 加 ignore-filter 接外部 fuzzy library(如 fuse.js),避免內建 fuzzy filter 卡頓。
鍵盤導航
API 參考
admin-editor-emoji-menu
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
editor* | Editor | — | — |
items* | EmojiMenuItem[] | EmojiMenuItem[][] | — | — |
char | string | undefined | ":" | 觸發字元,預設 `:` |
pluginKey | string | undefined | "editorEmojiMenu" | PM plugin key,預設 `editorEmojiMenu` |
limit | number | undefined | 10 | 顯示筆數上限,預設 10 |
filterFields | string[] | undefined | ["name", "shortcodes", "tags"] | filter 比對欄位,預設 `['name', 'shortcodes', 'tags']` |
ignoreFilter | boolean | undefined | false | true 時跳過內建 fuzzy filter |
class | ClassValue | — | — |
* 代表必填
更新紀錄
- feat: 新增 tiptap editor 元件系列273a0d0