編輯器

Editor Toolbar

參考
admin-rich-editor 的工具列。layout:fixed/bubble/floating;可放按鈕、下拉選單與自訂元件。

安裝

Terminal
# 只依賴 @admin/editor-core;可搭 rich-editor 或 base editor 使用pnpm dlx shadcn-vue add @admin/editor-toolbar
import { AdminEditorToolbar, defaultEditorItems } from '@/registry/admin/ui/editor-toolbar'

用法

最小可用組合:<AdminRichEditor> 容器內擺 <AdminEditorToolbar>(預設 layout 'fixed'),items 給 defaultEditorItems:

<script setup lang="ts">import type { JSONContent } from '@tiptap/core'import { ref } from 'vue'import { cn } from '@/lib/utils'import { baseProseClass } from '@/components/admin/editor'import { AdminEditorToolbar, defaultEditorItems } from '@/components/admin/editor-toolbar'import { AdminRichEditor, starterKitProseClass } from '@/components/admin/rich-editor'​const value = ref<JSONContent | string>(  '<h2>歡迎使用 admin-rich-editor</h2><p>這是一個 <strong>富文本編輯器</strong>,基於 <a href="https://tiptap.dev">tiptap</a> 並對齊 admin design tokens。</p><ul><li>項目一</li><li>項目二</li></ul>')​// 預設只 bundle StarterKit;預設 toolbar 僅含 StarterKit 範圍能力,所有按鈕開箱即用</script>​<template>  <admin-rich-editor    v-slot="{ editor, handlers }"    v-model="value"    :ui="{ base: cn(baseProseClass, starterKitProseClass) }"  >    <admin-editor-toolbar :editor :handlers :items="defaultEditorItems" />  </admin-rich-editor></template>

場景

Bubble layout

選取文字後浮現 toolbar(Notion / Medium 風)。設定 layout="bubble":

<script setup lang="ts">import type { JSONContent } from '@tiptap/core'import { ref } from 'vue'import { cn } from '@/lib/utils'import { baseProseClass } from '@/components/admin/editor'import { AdminEditorToolbar, defaultEditorItems } from '@/components/admin/editor-toolbar'import { AdminRichEditor, starterKitProseClass } from '@/components/admin/rich-editor'​const value = ref<JSONContent | string>(  '<p>選取一段文字試試看 — toolbar 會浮現在選取範圍上方(bubble 模式)。</p>')</script>​<template>  <admin-rich-editor    v-slot="{ editor, handlers }"    v-model="value"    :ui="{ base: cn(baseProseClass, starterKitProseClass) }"  >    <admin-editor-toolbar      layout="bubble"      :editor      :handlers="handlers"      :items="defaultEditorItems"    />  </admin-rich-editor></template>

Floating layout

游標停在空行時浮現(Notion 的 block 插入選單風格)。設定 layout="floating",items 只放區塊操作:

<script setup lang="ts">import type { JSONContent } from '@tiptap/core'import type { EditorToolbarItem } from '@/components/admin/editor-toolbar'import { IconBlockquote, IconList, IconListNumbers, IconSourceCode } from '@tabler/icons-vue'import { ref } from 'vue'import { cn } from '@/lib/utils'import { baseProseClass } from '@/components/admin/editor'import { AdminEditorToolbar } from '@/components/admin/editor-toolbar'import { AdminRichEditor, starterKitProseClass } from '@/components/admin/rich-editor'​const value = ref<JSONContent | string>(  '<p>在段尾按 Enter 產生空行,block menu 會浮現在該行(floating 模式)。</p>')​// floating 的語意是「在空行插入區塊」,items 只放 block 操作const blockItems: EditorToolbarItem[][] = [  [    {      label: '段落樣式',      dropdownVariant: 'select',      items: [        { kind: 'paragraph', label: '內文' },        { kind: 'heading', level: 1, label: '標題 1' },        { kind: 'heading', level: 2, label: '標題 2' }      ]    }  ],  [    { kind: 'blockquote', icon: IconBlockquote, label: '引言', tooltip: { text: '引言' } },    { kind: 'bulletList', icon: IconList, label: '項目清單', tooltip: { text: '項目清單' } },    {      kind: 'orderedList',      icon: IconListNumbers,      label: '數字清單',      tooltip: { text: '數字清單' }    },    {      kind: 'codeBlock',      icon: IconSourceCode,      label: '程式碼區塊',      tooltip: { text: '程式碼區塊' }    }  ]]</script>​<template>  <admin-rich-editor    v-slot="{ editor, handlers }"    v-model="value"    :ui="{ base: cn(baseProseClass, starterKitProseClass) }"  >    <admin-editor-toolbar layout="floating" :editor :handlers="handlers" :items="blockItems" />  </admin-rich-editor></template>

客製 items(精簡版)

:items 接受 EditorToolbarItem[](單列)或 EditorToolbarItem[][](分組,每組之間自動加 separator)。自寫 items 收緊功能:

<script setup lang="ts">import type { JSONContent } from '@tiptap/core'import type { EditorToolbarItem } from '@/components/admin/editor-toolbar'import { ref } from 'vue'import { cn } from '@/lib/utils'import { baseProseClass } from '@/components/admin/editor'import { AdminEditorToolbar } from '@/components/admin/editor-toolbar'import { AdminRichEditor, starterKitProseClass } from '@/components/admin/rich-editor'​const value = ref<JSONContent | string>(  '<p>只啟用 <strong>bold</strong> / <em>italic</em> / link 的精簡版,適合留言情境。</p>')​// 客製 items:只保留留言情境會用到的功能const leanItems: EditorToolbarItem[][] = [  [    { kind: 'mark', mark: 'bold', tooltip: { text: '粗體' } },    { kind: 'mark', mark: 'italic', tooltip: { text: '斜體' } }  ],  [{ kind: 'link', tooltip: { text: '連結' } }]]</script>​<template>  <admin-rich-editor    v-slot="{ editor, handlers }"    v-model="value"    :starter-kit="{      heading: false,      codeBlock: false,      blockquote: false,      horizontalRule: false    }"    :ui="{ base: cn(baseProseClass, starterKitProseClass) }"  >    <admin-editor-toolbar :editor :handlers="handlers" :items="leanItems" />  </admin-rich-editor></template>

具名 slot 與 #item slot

item 可以整顆換成自訂元件:指定 slot: '<name>' 就交給同名具名 slot 渲染,沒指定的落到通用 #item slot。slot props 為 { item, index, isActive, isDisabled, onClick };不含 editor,需要就從外層 <AdminRichEditor v-slot="{ editor, handlers }"> 取。@admin/editor-text-color / @admin/editor-highlight 就是這樣接入的:

<script setup lang="ts">import type { JSONContent } from '@tiptap/core'import type { EditorToolbarItem } from '@/components/admin/editor-toolbar'import { IconLetterA } from '@tabler/icons-vue'import { Color } from '@tiptap/extension-color'import { TextStyle } from '@tiptap/extension-text-style'import { ref } from 'vue'import { cn } from '@/lib/utils'import { baseProseClass } from '@/components/admin/editor'import {  AdminEditorTextColorButton,  defaultColorSwatches} from '@/components/admin/editor-text-color'import { AdminEditorToolbar, defaultEditorItems } from '@/components/admin/editor-toolbar'import { AdminRichEditor, starterKitProseClass } from '@/components/admin/rich-editor'​const value = ref<JSONContent | string>('<p>選取文字後,用 toolbar 最右側的 A 鈕設定文字顏色。</p>')​// 文字顏色需自行注入官方 extension:Color 依賴 TextStyle,兩者都要const extensions = [TextStyle, Color]​// color picker 為 kindless slot item:靠具名 slot 自畫,不綁 handlerconst items: EditorToolbarItem[][] = [  ...defaultEditorItems,  [{ slot: 'color', icon: IconLetterA, label: '文字顏色', tooltip: { text: '文字顏色' } }]]</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="items">      <!-- slot props 不含 editor;picker 從外層 v-slot 的 editor 取 -->      <template #color>        <admin-editor-text-color-button :editor="editor" :swatches="defaultColorSwatches" />      </template>    </admin-editor-toolbar>  </admin-rich-editor></template>

defaultEditorItems

defaultEditorItems: EditorToolbarItem[][] 是預設工具列分組,只涵蓋 StarterKit 能力(復原、文字裝飾、段落樣式、區塊、Link),每個 item 都帶好 icon、繁中 label 與 tooltip。textAlign / image / indent 等非 StarterKit 能力由對應的 opt-in 包提供,spread 後追加即可:

const items = [  ...defaultEditorItems,  [{ kind: 'insertVariable', name: 'username', label: '使用者名稱' }],]

API 參考

組成

<template>  <admin-rich-editor v-slot="{ editor, handlers }" v-model="value">    <admin-editor-toolbar :editor :handlers :items="defaultEditorItems" />  </admin-rich-editor></template>

admin-editor-toolbar

Props

名稱型別預設值說明
classClassValue——
editor*Editor—一律由外部顯式傳入的 tiptap editor 實例
handlers*EditorHandlers—merged handler map(必填):由呼叫端顯式傳入,決定各 kind 的 active / disabled / 執行
items*A——
layoutEditorToolbarLayout | undefined"fixed"—

* 代表必填

Slots

名稱Bindings說明
item{ item: NestedItem<A>; } & SlotExtraProps—

注意事項

  • 不支援 SSR:bubble / floating layout 依賴瀏覽器 DOM 定位;元件殼不含 <ClientOnly>。Nuxt 請自行以 <ClientOnly> 包覆,其他 SSR 框架用等價機制;純 CSR 不需處理。見 安裝 · SSR。

更新紀錄

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