編輯器

Editor Emoji Menu

參考
輸入 : 觸發浮動選單;挑選並插入 emoji。

安裝

Terminal
pnpm dlx shadcn-vue add @admin/editor-emoji-menu
import 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 卡頓。

鍵盤導航

同 Editor Suggestion Menu。

API 參考

admin-editor-emoji-menu

Props

名稱型別預設值說明
editor*Editor——
items*EmojiMenuItem[] | EmojiMenuItem[][]——
charstring | undefined":"觸發字元,預設 `:`
pluginKeystring | undefined"editorEmojiMenu"PM plugin key,預設 `editorEmojiMenu`
limitnumber | undefined10顯示筆數上限,預設 10
filterFieldsstring[] | undefined["name", "shortcodes", "tags"]filter 比對欄位,預設 `['name', 'shortcodes', 'tags']`
ignoreFilterboolean | undefinedfalsetrue 時跳過內建 fuzzy filter
classClassValue——

* 代表必填

更新紀錄

  • feat: 新增 tiptap editor 元件系列273a0d0