エディターツールバー
使用法
EditorToolbarコンポーネントは、アクティブな状態をエディターコンテンツと自動的に同期する書式設定ボタンのツールバーを表示します。@tiptap/vue-3/menusパッケージを使用して、3つのレイアウトモードをサポートします。
fixed常に表示bubble(テキスト選択に表示)floating(空行に表示)
<script setup lang="ts">
import type { EditorToolbarItem } from '@nuxt/ui'
const value = ref(`# Toolbar
Select some text to see the formatting toolbar appear above your selection.`)
const items: EditorToolbarItem[][] = [
[
{
icon: 'i-lucide-heading',
tooltip: { text: 'Headings' },
content: {
align: 'start'
},
items: [
{
kind: 'heading',
level: 1,
icon: 'i-lucide-heading-1',
label: 'Heading 1'
},
{
kind: 'heading',
level: 2,
icon: 'i-lucide-heading-2',
label: 'Heading 2'
},
{
kind: 'heading',
level: 3,
icon: 'i-lucide-heading-3',
label: 'Heading 3'
},
{
kind: 'heading',
level: 4,
icon: 'i-lucide-heading-4',
label: 'Heading 4'
}
]
}
],
[
{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold',
tooltip: { text: 'Bold' }
},
{
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic',
tooltip: { text: 'Italic' }
},
{
kind: 'mark',
mark: 'underline',
icon: 'i-lucide-underline',
tooltip: { text: 'Underline' }
},
{
kind: 'mark',
mark: 'strike',
icon: 'i-lucide-strikethrough',
tooltip: { text: 'Strikethrough' }
},
{
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code',
tooltip: { text: 'Code' }
}
]
]
</script>
<template>
<UEditor v-slot="{ editor }" v-model="value" content-type="markdown" class="w-full min-h-21">
<UEditorToolbar :editor="editor" :items="items" layout="bubble" />
</UEditor>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { EditorToolbarItem } from '@nuxt/ui'
const value = ref(`# Toolbar
Select some text to see the formatting toolbar appear above your selection.`)
const items: EditorToolbarItem[][] = [
[
{
icon: 'i-lucide-heading',
tooltip: { text: 'Headings' },
content: {
align: 'start'
},
items: [
{
kind: 'heading',
level: 1,
icon: 'i-lucide-heading-1',
label: 'Heading 1'
},
{
kind: 'heading',
level: 2,
icon: 'i-lucide-heading-2',
label: 'Heading 2'
},
{
kind: 'heading',
level: 3,
icon: 'i-lucide-heading-3',
label: 'Heading 3'
},
{
kind: 'heading',
level: 4,
icon: 'i-lucide-heading-4',
label: 'Heading 4'
}
]
}
],
[
{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold',
tooltip: { text: 'Bold' }
},
{
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic',
tooltip: { text: 'Italic' }
},
{
kind: 'mark',
mark: 'underline',
icon: 'i-lucide-underline',
tooltip: { text: 'Underline' }
},
{
kind: 'mark',
mark: 'strike',
icon: 'i-lucide-strikethrough',
tooltip: { text: 'Strikethrough' }
},
{
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code',
tooltip: { text: 'Code' }
}
]
]
</script>
<template>
<UEditor v-slot="{ editor }" v-model="value" content-type="markdown" class="w-full min-h-21">
<UEditorToolbar :editor="editor" :items="items" layout="bubble" />
</UEditor>
</template>アイテム
itemsプロパティを次のプロパティを持つオブジェクトの配列として使用します。
label?: stringicon?: stringcolor?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"activeColor?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"variant?: "solid" | "outline" | "soft" | "ghost" | "link" | "subtle"activeVariant?: "solid" | "outline" | "soft" | "ghost" | "link" | "subtle"size?: "xs" | "sm" | "md" | "lg" | "xl"kind?: "mark" | "textAlign" | "heading" | "link" | "image" | "blockquote" | "bulletList" | "orderedList" | "taskList" | "codeBlock" | "horizontalRule" | "paragraph" | "undo" | "redo" | "clearFormatting" | "duplicate" | "delete" | "moveUp" | "moveDown" | "suggestion" | "mention" | "emoji"disabled?: booleanloading?: booleanactive?: booleantooltip?: TooltipPropsslot?: stringonClick?: (e: MouseEvent) => voiditems?: EditorToolbarItem[] | EditorToolbarItem[][]class?: any
Buttonコンポーネントから、color、variant、sizeなどの任意のプロパティを渡すことができます。
<script setup lang="ts">
import type { EditorToolbarItem } from '@nuxt/ui'
import { TextAlign } from '@tiptap/extension-text-align'
const value = ref(`This toolbar showcases **all available formatting options** using built-in handlers. Try the different controls to see them in action!
You can apply **bold**, *italic*, <u>underline</u>, ~~strikethrough~~, and \`inline code\` formatting to your text.
`)
const items: EditorToolbarItem[][] = [
// History controls
[{
kind: 'undo',
icon: 'i-lucide-undo',
tooltip: { text: 'Undo' }
}, {
kind: 'redo',
icon: 'i-lucide-redo',
tooltip: { text: 'Redo' }
}],
// Block types
[{
icon: 'i-lucide-heading',
tooltip: { text: 'Headings' },
content: {
align: 'start'
},
items: [{
kind: 'heading',
level: 1,
icon: 'i-lucide-heading-1',
label: 'Heading 1'
}, {
kind: 'heading',
level: 2,
icon: 'i-lucide-heading-2',
label: 'Heading 2'
}, {
kind: 'heading',
level: 3,
icon: 'i-lucide-heading-3',
label: 'Heading 3'
}, {
kind: 'heading',
level: 4,
icon: 'i-lucide-heading-4',
label: 'Heading 4'
}]
}, {
icon: 'i-lucide-list',
tooltip: { text: 'Lists' },
content: {
align: 'start'
},
items: [{
kind: 'bulletList',
icon: 'i-lucide-list',
label: 'Bullet List'
}, {
kind: 'orderedList',
icon: 'i-lucide-list-ordered',
label: 'Ordered List'
}]
}, {
kind: 'blockquote',
icon: 'i-lucide-text-quote',
tooltip: { text: 'Blockquote' }
}, {
kind: 'codeBlock',
icon: 'i-lucide-square-code',
tooltip: { text: 'Code Block' }
}, {
kind: 'horizontalRule',
icon: 'i-lucide-separator-horizontal',
tooltip: { text: 'Horizontal Rule' }
}],
// Text formatting
[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold',
tooltip: { text: 'Bold' }
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic',
tooltip: { text: 'Italic' }
}, {
kind: 'mark',
mark: 'underline',
icon: 'i-lucide-underline',
tooltip: { text: 'Underline' }
}, {
kind: 'mark',
mark: 'strike',
icon: 'i-lucide-strikethrough',
tooltip: { text: 'Strikethrough' }
}, {
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code',
tooltip: { text: 'Code' }
}],
// Link
[{
kind: 'link',
icon: 'i-lucide-link',
tooltip: { text: 'Link' }
}],
// Text alignment
[{
icon: 'i-lucide-align-justify',
tooltip: { text: 'Text Align' },
content: {
align: 'end'
},
items: [{
kind: 'textAlign',
align: 'left',
icon: 'i-lucide-align-left',
label: 'Align Left'
}, {
kind: 'textAlign',
align: 'center',
icon: 'i-lucide-align-center',
label: 'Align Center'
}, {
kind: 'textAlign',
align: 'right',
icon: 'i-lucide-align-right',
label: 'Align Right'
}, {
kind: 'textAlign',
align: 'justify',
icon: 'i-lucide-align-justify',
label: 'Align Justify'
}]
}]
]
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
content-type="markdown"
:extensions="[TextAlign.configure({ types: ['heading', 'paragraph'] })]"
class="w-full min-h-37 flex flex-col gap-4"
>
<UEditorToolbar :editor="editor" :items="items" class="sm:px-8 overflow-x-auto" />
</UEditor>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { EditorToolbarItem } from '@nuxt/ui'
import { TextAlign } from '@tiptap/extension-text-align'
const value = ref(`This toolbar showcases **all available formatting options** using built-in handlers. Try the different controls to see them in action!
You can apply **bold**, *italic*, <u>underline</u>, ~~strikethrough~~, and \`inline code\` formatting to your text.
`)
const items: EditorToolbarItem[][] = [
// History controls
[{
kind: 'undo',
icon: 'i-lucide-undo',
tooltip: { text: 'Undo' }
}, {
kind: 'redo',
icon: 'i-lucide-redo',
tooltip: { text: 'Redo' }
}],
// Block types
[{
icon: 'i-lucide-heading',
tooltip: { text: 'Headings' },
content: {
align: 'start'
},
items: [{
kind: 'heading',
level: 1,
icon: 'i-lucide-heading-1',
label: 'Heading 1'
}, {
kind: 'heading',
level: 2,
icon: 'i-lucide-heading-2',
label: 'Heading 2'
}, {
kind: 'heading',
level: 3,
icon: 'i-lucide-heading-3',
label: 'Heading 3'
}, {
kind: 'heading',
level: 4,
icon: 'i-lucide-heading-4',
label: 'Heading 4'
}]
}, {
icon: 'i-lucide-list',
tooltip: { text: 'Lists' },
content: {
align: 'start'
},
items: [{
kind: 'bulletList',
icon: 'i-lucide-list',
label: 'Bullet List'
}, {
kind: 'orderedList',
icon: 'i-lucide-list-ordered',
label: 'Ordered List'
}]
}, {
kind: 'blockquote',
icon: 'i-lucide-text-quote',
tooltip: { text: 'Blockquote' }
}, {
kind: 'codeBlock',
icon: 'i-lucide-square-code',
tooltip: { text: 'Code Block' }
}, {
kind: 'horizontalRule',
icon: 'i-lucide-separator-horizontal',
tooltip: { text: 'Horizontal Rule' }
}],
// Text formatting
[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold',
tooltip: { text: 'Bold' }
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic',
tooltip: { text: 'Italic' }
}, {
kind: 'mark',
mark: 'underline',
icon: 'i-lucide-underline',
tooltip: { text: 'Underline' }
}, {
kind: 'mark',
mark: 'strike',
icon: 'i-lucide-strikethrough',
tooltip: { text: 'Strikethrough' }
}, {
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code',
tooltip: { text: 'Code' }
}],
// Link
[{
kind: 'link',
icon: 'i-lucide-link',
tooltip: { text: 'Link' }
}],
// Text alignment
[{
icon: 'i-lucide-align-justify',
tooltip: { text: 'Text Align' },
content: {
align: 'end'
},
items: [{
kind: 'textAlign',
align: 'left',
icon: 'i-lucide-align-left',
label: 'Align Left'
}, {
kind: 'textAlign',
align: 'center',
icon: 'i-lucide-align-center',
label: 'Align Center'
}, {
kind: 'textAlign',
align: 'right',
icon: 'i-lucide-align-right',
label: 'Align Right'
}, {
kind: 'textAlign',
align: 'justify',
icon: 'i-lucide-align-justify',
label: 'Align Justify'
}]
}]
]
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
content-type="markdown"
:extensions="[TextAlign.configure({ types: ['heading', 'paragraph'] })]"
class="w-full min-h-37 flex flex-col gap-4"
>
<UEditorToolbar :editor="editor" :items="items" class="sm:px-8 overflow-x-auto" />
</UEditor>
</template>
itemsプロパティに配列の配列を渡して、項目の分離グループを作成することもできます。Layout
layoutプロパティを使用して、ツールバーの表示方法を変更します。デフォルトはfixedです。
<script setup lang="ts">
import type { EditorToolbarItem } from '@nuxt/ui'
defineProps<{
layout: 'fixed' | 'bubble' | 'floating'
}>()
const value = ref(`Switch between layouts to see the different toolbar modes.
The **fixed** layout displays the toolbar above the editor. The **bubble** layout shows the toolbar when you select text. The **floating** layout appears on empty lines.`)
const items: EditorToolbarItem[][] = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}, {
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code'
}]]
</script>
<template>
<UEditor v-slot="{ editor }" v-model="value" content-type="markdown" class="w-full min-h-26 flex flex-col gap-4">
<UEditorToolbar
:key="layout"
:editor="editor"
:items="items"
:layout="layout"
:data-layout="layout"
class="data-[layout=fixed]:sm:px-8"
/>
</UEditor>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { EditorToolbarItem } from '@nuxt/ui'
defineProps<{
layout: 'fixed' | 'bubble' | 'floating'
}>()
const value = ref(`Switch between layouts to see the different toolbar modes.
The **fixed** layout displays the toolbar above the editor. The **bubble** layout shows the toolbar when you select text. The **floating** layout appears on empty lines.`)
const items: EditorToolbarItem[][] = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}, {
kind: 'mark',
mark: 'code',
icon: 'i-lucide-code'
}]]
</script>
<template>
<UEditor v-slot="{ editor }" v-model="value" content-type="markdown" class="w-full min-h-26 flex flex-col gap-4">
<UEditorToolbar
:key="layout"
:editor="editor"
:items="items"
:layout="layout"
:data-layout="layout"
class="data-[layout=fixed]:sm:px-8"
/>
</UEditor>
</template>
Options
bubbleまたはfloatingレイアウトを使用する場合は、optionsプロパティを使用して、Floating UIオプションを使用して位置決めの動作をカスタマイズします。
<template>
<UEditor v-slot="{ editor }">
<UEditorToolbar
:editor="editor"
:items="items"
layout="bubble"
:options="{
placement: 'top',
offset: 8,
flip: { padding: 8 },
shift: { padding: 8 }
}"
/>
</UEditor>
</template>
表示するべき
bubbleまたはfloatingレイアウトを使用する場合は、should-showプロパティを使用してツールバーの表示を制御します。この関数はエディタの状態に関するコンテキストを受け取り、真偽値を返します。
<template>
<UEditor v-slot="{ editor }">
<UEditorToolbar
:editor="editor"
:items="items"
layout="bubble"
:should-show="({ view, state }) => {
const { selection } = state
const { from, to } = selection
const text = state.doc.textBetween(from, to)
return view.hasFocus() && !selection.empty && text.length > 10
}"
/>
</UEditor>
</template>
例
画像ツールバー付き
should-showプロパティを使用して、特定のノードタイプにのみ表示されるコンテキスト固有のツールバーを作成します。この例では、画像が選択されたときにのみ表示されるダウンロードと削除アクションを持つbubbleツールバーを示しています。
<script setup lang="ts">
import type { Editor } from '@tiptap/vue-3'
import type { EditorToolbarItem } from '@nuxt/ui'
const value = ref(`Click on the image below to see the image-specific toolbar:
`)
const items = (editor: Editor): EditorToolbarItem[][] => {
const node = editor.state.doc.nodeAt(editor.state.selection.from)
return [[{
icon: 'i-lucide-download',
to: node?.attrs?.src,
download: true,
tooltip: { text: 'Download' }
}], [{
icon: 'i-lucide-trash',
tooltip: { text: 'Delete' },
onClick: () => {
const { state } = editor
const { selection } = state
const pos = selection.from
const node = state.doc.nodeAt(pos)
if (node && node.type.name === 'image') {
editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run()
}
}
}]]
}
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
content-type="markdown"
class="w-full min-h-113"
>
<UEditorToolbar
:editor="editor"
:items="items(editor)"
layout="bubble"
:should-show="({ editor, view }) => {
return editor.isActive('image') && view.hasFocus()
}"
/>
</UEditor>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { Editor } from '@tiptap/vue-3'
import type { EditorToolbarItem } from '@nuxt/ui'
const value = ref(`Click on the image below to see the image-specific toolbar:
`)
const items = (editor: Editor): EditorToolbarItem[][] => {
const node = editor.state.doc.nodeAt(editor.state.selection.from)
return [[{
icon: 'i-lucide-download',
to: node?.attrs?.src,
download: true,
tooltip: { text: 'Download' }
}], [{
icon: 'i-lucide-trash',
tooltip: { text: 'Delete' },
onClick: () => {
const { state } = editor
const { selection } = state
const pos = selection.from
const node = state.doc.nodeAt(pos)
if (node && node.type.name === 'image') {
editor.chain().focus().deleteRange({ from: pos, to: pos + node.nodeSize }).run()
}
}
}]]
}
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
content-type="markdown"
class="w-full min-h-113"
>
<UEditorToolbar
:editor="editor"
:items="items(editor)"
layout="bubble"
:should-show="({ editor, view }) => {
return editor.isActive('image') && view.hasFocus()
}"
/>
</UEditor>
</template>
Withリンクポップオーバー
この例では、ツールバーアイテムのslotプロパティとPopoverコンポーネントを使用してカスタムリンクポップオーバーを作成する方法を示します。
- リンク編集機能付きのPopoverをラップするVueコンポーネントを作成します。
<script setup lang="ts">
import type { Editor } from '@tiptap/vue-3'
const props = defineProps<{
editor: Editor
autoOpen?: boolean
}>()
const open = ref(false)
const url = ref('')
const active = computed(() => props.editor.isActive('link'))
const disabled = computed(() => {
if (!props.editor.isEditable) return true
const { selection } = props.editor.state
return selection.empty && !props.editor.isActive('link')
})
watch(() => props.editor, (editor, _, onCleanup) => {
if (!editor) return
const updateUrl = () => {
const { href } = editor.getAttributes('link')
url.value = href || ''
}
updateUrl()
editor.on('selectionUpdate', updateUrl)
onCleanup(() => {
editor.off('selectionUpdate', updateUrl)
})
}, { immediate: true })
watch(active, (isActive) => {
if (isActive && props.autoOpen) {
open.value = true
}
})
function setLink() {
if (!url.value) return
const { selection } = props.editor.state
const isEmpty = selection.empty
const hasCode = props.editor.isActive('code')
let chain = props.editor.chain().focus()
// When linking code, extend the code mark range first to select the full code
if (hasCode && !isEmpty) {
chain = chain.extendMarkRange('code').setLink({ href: url.value })
} else {
chain = chain.extendMarkRange('link').setLink({ href: url.value })
if (isEmpty) {
chain = chain.insertContent({ type: 'text', text: url.value })
}
}
chain.run()
open.value = false
}
function removeLink() {
props.editor
.chain()
.focus()
.extendMarkRange('link')
.unsetLink()
.setMeta('preventAutolink', true)
.run()
url.value = ''
open.value = false
}
function openLink() {
if (!url.value) return
window.open(url.value, '_blank', 'noopener,noreferrer')
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault()
setLink()
}
}
</script>
<template>
<UPopover v-model:open="open" :ui="{ content: 'p-0.5' }">
<UTooltip text="Link">
<UButton
icon="i-lucide-link"
color="neutral"
active-color="primary"
variant="ghost"
active-variant="soft"
size="sm"
:active="active"
:disabled="disabled"
/>
</UTooltip>
<template #content>
<UInput
v-model="url"
autofocus
name="url"
type="url"
variant="none"
placeholder="Paste a link..."
@keydown="handleKeyDown"
>
<div class="flex items-center mr-0.5">
<UButton
icon="i-lucide-corner-down-left"
variant="ghost"
size="sm"
:disabled="!url && !active"
title="Apply link"
@click="setLink"
/>
<USeparator orientation="vertical" class="h-6 mx-1" />
<UButton
icon="i-lucide-external-link"
color="neutral"
variant="ghost"
size="sm"
:disabled="!url && !active"
title="Open in new window"
@click="openLink"
/>
<UButton
icon="i-lucide-trash"
color="neutral"
variant="ghost"
size="sm"
:disabled="!url && !active"
title="Remove link"
@click="removeLink"
/>
</div>
</UInput>
</template>
</UPopover>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import type { Editor } from '@tiptap/vue-3'
const props = defineProps<{
editor: Editor
autoOpen?: boolean
}>()
const open = ref(false)
const url = ref('')
const active = computed(() => props.editor.isActive('link'))
const disabled = computed(() => {
if (!props.editor.isEditable) return true
const { selection } = props.editor.state
return selection.empty && !props.editor.isActive('link')
})
watch(() => props.editor, (editor, _, onCleanup) => {
if (!editor) return
const updateUrl = () => {
const { href } = editor.getAttributes('link')
url.value = href || ''
}
updateUrl()
editor.on('selectionUpdate', updateUrl)
onCleanup(() => {
editor.off('selectionUpdate', updateUrl)
})
}, { immediate: true })
watch(active, (isActive) => {
if (isActive && props.autoOpen) {
open.value = true
}
})
function setLink() {
if (!url.value) return
const { selection } = props.editor.state
const isEmpty = selection.empty
const hasCode = props.editor.isActive('code')
let chain = props.editor.chain().focus()
// When linking code, extend the code mark range first to select the full code
if (hasCode && !isEmpty) {
chain = chain.extendMarkRange('code').setLink({ href: url.value })
} else {
chain = chain.extendMarkRange('link').setLink({ href: url.value })
if (isEmpty) {
chain = chain.insertContent({ type: 'text', text: url.value })
}
}
chain.run()
open.value = false
}
function removeLink() {
props.editor
.chain()
.focus()
.extendMarkRange('link')
.unsetLink()
.setMeta('preventAutolink', true)
.run()
url.value = ''
open.value = false
}
function openLink() {
if (!url.value) return
window.open(url.value, '_blank', 'noopener,noreferrer')
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault()
setLink()
}
}
</script>
<template>
<UPopover v-model:open="open" :ui="{ content: 'p-0.5' }">
<UTooltip text="Link">
<UButton
icon="i-lucide-link"
color="neutral"
active-color="primary"
variant="ghost"
active-variant="soft"
size="sm"
:active="active"
:disabled="disabled"
/>
</UTooltip>
<template #content>
<UInput
v-model="url"
autofocus
name="url"
type="url"
variant="none"
placeholder="Paste a link..."
@keydown="handleKeyDown"
>
<div class="flex items-center mr-0.5">
<UButton
icon="i-lucide-corner-down-left"
variant="ghost"
size="sm"
:disabled="!url && !active"
title="Apply link"
@click="setLink"
/>
<USeparator orientation="vertical" class="h-6 mx-1" />
<UButton
icon="i-lucide-external-link"
color="neutral"
variant="ghost"
size="sm"
:disabled="!url && !active"
title="Open in new window"
@click="openLink"
/>
<UButton
icon="i-lucide-trash"
color="neutral"
variant="ghost"
size="sm"
:disabled="!url && !active"
title="Remove link"
@click="removeLink"
/>
</div>
</UInput>
</template>
</UPopover>
</template>
- ツールバーのカスタムコンポーネントを名前付きスロットで使用します。
<script setup lang="ts">
import type { EditorToolbarItem } from '@nuxt/ui'
import EditorLinkPopover from './EditorLinkPopover.vue'
const value = ref(`Select text and click the link button to add a link with the custom popover.
You can also edit existing links like [this one](https://ui.nuxt.com).`)
const toolbarItems = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}, {
slot: 'link' as const
}]] satisfies EditorToolbarItem[][]
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
content-type="markdown"
class="w-full min-h-30 flex flex-col gap-4"
>
<UEditorToolbar :editor="editor" :items="toolbarItems" class="sm:px-8">
<template #link>
<EditorLinkPopover :editor="editor" auto-open />
</template>
</UEditorToolbar>
</UEditor>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { EditorToolbarItem } from '@nuxt/ui'
import EditorLinkPopover from './EditorLinkPopover.vue'
const value = ref(`Select text and click the link button to add a link with the custom popover.
You can also edit existing links like [this one](https://ui.nuxt.com).`)
const toolbarItems = [[{
kind: 'mark',
mark: 'bold',
icon: 'i-lucide-bold'
}, {
kind: 'mark',
mark: 'italic',
icon: 'i-lucide-italic'
}, {
slot: 'link' as const
}]] satisfies EditorToolbarItem[][]
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
content-type="markdown"
class="w-full min-h-30 flex flex-col gap-4"
>
<UEditorToolbar :editor="editor" :items="toolbarItems" class="sm:px-8">
<template #link>
<EditorLinkPopover :editor="editor" auto-open />
</template>
</UEditorToolbar>
</UEditor>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
editor | Editor | |
color | 'neutral' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"The color of the toolbar controls. |
variant | 'ghost' | "solid" | "outline" | "soft" | "subtle" | "ghost" | "link"The variant of the toolbar controls. |
activeColor | 'primary' | "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral"The color of the active toolbar control. |
activeVariant | 'soft' | "solid" | "outline" | "soft" | "subtle" | "ghost" | "link"The variant of the active toolbar control. |
size | 'sm' | "xs" | "sm" | "md" | "lg" | "xl"The size of the toolbar controls. |
items | T | |
layout | 'fixed' | "fixed" | "floating" | "bubble" |
pluginKey | unknownThe plugin key. The plugin key for the floating menu. | |
updateDelay | unknownThe delay in milliseconds before the menu should be updated. This can be useful to prevent performance issues. | |
resizeDelay | unknownThe delay in milliseconds before the menu position should be updated on window resize. This can be useful to prevent performance issues. | |
shouldShow | unknownA function that determines whether the menu should be shown or not.
If this function returns | |
appendTo | unknownThe DOM element to append your menu to. Default is the editor's parent element. Sometimes the menu needs to be appended to a different DOM context due to accessibility, clipping, or z-index issues. | |
getReferencedVirtualElement | unknownA function that returns the virtual element for the menu. This is useful when the menu needs to be positioned relative to a specific DOM element. | |
options | unknownThe options for the bubble menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, arrow, size, autoPlacement, hide, and inline middlewares. The options for the floating menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, arrow, size, autoPlacement, hide, and inline middlewares. | |
ui | { root?: SlotClass; base?: SlotClass; group?: SlotClass; separator?: SlotClass; } |
スロット
| Slot | Type |
|---|---|
item | { item: NestedItem<T>; } & SlotPropsProps |
Theme
export default defineAppConfig({
ui: {
editorToolbar: {
slots: {
root: 'focus:outline-none',
base: 'flex items-stretch gap-1.5',
group: 'flex items-center gap-0.5',
separator: 'w-px self-stretch bg-border'
},
variants: {
layout: {
bubble: {
base: 'bg-default border border-default rounded-lg p-1'
},
floating: {
base: 'bg-default border border-default rounded-lg p-1'
},
fixed: {
base: ''
}
}
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
editorToolbar: {
slots: {
root: 'focus:outline-none',
base: 'flex items-stretch gap-1.5',
group: 'flex items-center gap-0.5',
separator: 'w-px self-stretch bg-border'
},
variants: {
layout: {
bubble: {
base: 'bg-default border border-default rounded-lg p-1'
},
floating: {
base: 'bg-default border border-default rounded-lg p-1'
},
fixed: {
base: ''
}
}
}
}
}
})
]
})