[object Object]
Bearbeiten
Die EditorEmojiMenu-Komponente zeigt ein Menü mit Emoji-Vorschlägen an, wenn Sie das :-Zeichen in den Editor eingeben, und fügt das ausgewählte Emoji ein.
useEditorMenu composable, das auf dem Dienstprogramm Suggestion von TipTap aufbaut, um Elemente während der Eingabe zu filtern und die Tastaturnavigation zu unterstützen (Pfeiltasten, zum Auswählen eingeben, zum Schließen entkommen).<script setup lang="ts">
import type { EditorEmojiMenuItem } from '@nuxt/ui'
import { Emoji, gitHubEmojis } from '@tiptap/extension-emoji'
const value = ref(`# Emoji Menu
Type : to insert emojis and select from the list of available emojis.`)
const items: EditorEmojiMenuItem[] = gitHubEmojis.filter(
(emoji) => !emoji.name.startsWith('regional_indicator_')
)
// SSR-safe function to append menus to body (avoids z-index issues in docs)
const appendToBody = false ? () => document.body : undefined
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
:extensions="[Emoji]"
content-type="markdown"
placeholder="Type : to add emojis..."
class="w-full min-h-21"
>
<UEditorEmojiMenu :editor="editor" :items="items" :append-to="appendToBody" />
</UEditor>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { EditorEmojiMenuItem } from '@nuxt/ui'
import { Emoji, gitHubEmojis } from '@tiptap/extension-emoji'
const value = ref(`# Emoji Menu
Type : to insert emojis and select from the list of available emojis.`)
const items: EditorEmojiMenuItem[] = gitHubEmojis.filter(
(emoji) => !emoji.name.startsWith('regional_indicator_')
)
// SSR-safe function to append menus to body (avoids z-index issues in docs)
const appendToBody = false ? () => document.body : undefined
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
:extensions="[Emoji]"
content-type="markdown"
placeholder="Type : to add emojis..."
class="w-full min-h-21"
>
<UEditorEmojiMenu :editor="editor" :items="items" :append-to="appendToBody" />
</UEditor>
</template>@tiptap/extension-emoji ist standardmäßig nicht installiert, Sie müssen es separat installieren.Einträge
Verwenden Sie die items-prop als Array von Objekten mit den folgenden Eigenschaften:
name: string(nicht vorhanden)emoji: string(nicht vorhanden)shortcodes?: string[](nicht vorhanden)tags?: string[](englisch) xph0333xgroup?: string(englisch)fallbackImage?: string(nicht vorhanden)
<script setup lang="ts">
import type { EditorEmojiMenuItem } from '@nuxt/ui'
import { Emoji } from '@tiptap/extension-emoji'
const value = ref(`Type : to see a custom emoji set.
You can also install the \`@tiptap/extension-emoji\` extension to use a comprehensive set with over 1800 emojis.`)
const items: EditorEmojiMenuItem[] = [{
name: 'smile',
emoji: '😄',
shortcodes: ['smile'],
tags: ['happy', 'joy', 'pleased']
}, {
name: 'heart',
emoji: '❤️',
shortcodes: ['heart'],
tags: ['love', 'like']
}, {
name: 'thumbsup',
emoji: '👍',
shortcodes: ['thumbsup', '+1'],
tags: ['approve', 'ok']
}, {
name: 'fire',
emoji: '🔥',
shortcodes: ['fire'],
tags: ['hot', 'burn']
}, {
name: 'rocket',
emoji: '🚀',
shortcodes: ['rocket'],
tags: ['ship', 'launch']
}, {
name: 'eyes',
emoji: '👀',
shortcodes: ['eyes'],
tags: ['look', 'watch']
}, {
name: 'tada',
emoji: '🎉',
shortcodes: ['tada'],
tags: ['party', 'celebration']
}, {
name: 'thinking',
emoji: '🤔',
shortcodes: ['thinking'],
tags: ['hmm', 'think', 'consider']
}]
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
:extensions="[Emoji]"
content-type="markdown"
placeholder="Type : to add emojis..."
class="w-full min-h-26"
>
<UEditorEmojiMenu :editor="editor" :items="items" />
</UEditor>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { EditorEmojiMenuItem } from '@nuxt/ui'
import { Emoji } from '@tiptap/extension-emoji'
const value = ref(`Type : to see a custom emoji set.
You can also install the \`@tiptap/extension-emoji\` extension to use a comprehensive set with over 1800 emojis.`)
const items: EditorEmojiMenuItem[] = [{
name: 'smile',
emoji: '😄',
shortcodes: ['smile'],
tags: ['happy', 'joy', 'pleased']
}, {
name: 'heart',
emoji: '❤️',
shortcodes: ['heart'],
tags: ['love', 'like']
}, {
name: 'thumbsup',
emoji: '👍',
shortcodes: ['thumbsup', '+1'],
tags: ['approve', 'ok']
}, {
name: 'fire',
emoji: '🔥',
shortcodes: ['fire'],
tags: ['hot', 'burn']
}, {
name: 'rocket',
emoji: '🚀',
shortcodes: ['rocket'],
tags: ['ship', 'launch']
}, {
name: 'eyes',
emoji: '👀',
shortcodes: ['eyes'],
tags: ['look', 'watch']
}, {
name: 'tada',
emoji: '🎉',
shortcodes: ['tada'],
tags: ['party', 'celebration']
}, {
name: 'thinking',
emoji: '🤔',
shortcodes: ['thinking'],
tags: ['hmm', 'think', 'consider']
}]
</script>
<template>
<UEditor
v-slot="{ editor }"
v-model="value"
:extensions="[Emoji]"
content-type="markdown"
placeholder="Type : to add emojis..."
class="w-full min-h-26"
>
<UEditorEmojiMenu :editor="editor" :items="items" />
</UEditor>
</template>
items-prop übergeben, um getrennte Gruppen von Elementen zu erstellen.Char (englisch)
Verwenden Sie die char-prop, um das Triggerzeichen zu ändern. Standardmäßig ist :.
<template>
<UEditor v-slot="{ editor }">
<UEditorEmojiMenu :editor="editor" :items="items" char=";" />
</UEditor>
</template>
Suggestion: badge{label="4.7+" class="align-text-top"}
Verwenden Sie die suggestion-Prop, um TipTaps Suggestion-Übereinstimmungsverhalten anzupassen.
Dies ist nützlich, wenn das Triggerzeichen direkt nach anderen Zeichen geöffnet werden soll, anstatt das Standard-Whitespace-Präfix zu benötigen.
<template>
<UEditor v-slot="{ editor }">
<UEditorEmojiMenu
:editor="editor"
:items="items"
:suggestion="{
allowedPrefixes: null
}"
/>
</UEditor>
</template>
Options (Deutsche Ausgabe)
Verwenden Sie die options-Prop, um das Positionierungsverhalten mit Floating UI-Optionen anzupassen.
<template>
<UEditor v-slot="{ editor }">
<UEditorEmojiMenu
:editor="editor"
:items="items"
:options="{
placement: 'bottom-start',
offset: 4
}"
/>
</UEditor>
</template>
API (Englisch)
Props (nicht)
| Prop | Default | Type |
|---|---|---|
size | 'md' | "xs" | "md" | "sm" | "lg" | "xl" |
items | T[] | T[][] | |
editor | Editor | |
char | ':' | stringThe trigger character (e.g., '/', '@', ':') |
pluginKey | 'emojiMenu' | stringPlugin key to identify this menu |
filterFields | ["name", "shortcodes", "tags"] | string[]Fields to filter items by. |
limit | 42 | numberMaximum number of items to display |
options | { strategy: 'absolute', placement: 'bottom-start', offset: 8, shift: { padding: 8 } } | FloatingUIOptionsThe options for positioning the menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, size, autoPlacement, hide, and inline middleware. |
suggestion | Omit<Partial<SuggestionOptions<any, any>>, "editor" | "char" | "pluginKey" | "items" | "command" | "render">Optional TipTap Suggestion matching options. | |
appendTo | HTMLElement | (): HTMLElementThe DOM element to append the 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. | |
ui | { content?: SlotClass; viewport?: SlotClass; group?: SlotClass; label?: SlotClass; separator?: SlotClass; item?: SlotClass; itemLeadingIcon?: SlotClass; itemLeadingAvatar?: SlotClass; itemLeadingAvatarSize?: SlotClass; itemWrapper?: SlotClass; itemLabel?: SlotClass; itemDescription?: SlotClass; itemLabelExternalIcon?: SlotClass; } |
Theme (englisch)
export default defineAppConfig({
ui: {
editorEmojiMenu: {
slots: {
content: 'min-w-48 max-w-60 max-h-96 bg-default shadow-lg rounded-md ring ring-default overflow-hidden data-[state=open]:animate-[scale-in_100ms_var(--ease-out)] data-[state=closed]:animate-[scale-out_100ms_var(--ease-out)] origin-(--reka-dropdown-menu-content-transform-origin) flex flex-col',
viewport: 'relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1',
group: 'p-1 isolate',
label: 'w-full flex items-center font-semibold text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
item: 'group relative w-full flex items-start select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75',
itemLeadingIcon: 'shrink-0 flex items-center justify-center',
itemLeadingAvatar: 'shrink-0',
itemLeadingAvatarSize: '',
itemWrapper: 'flex-1 flex flex-col text-start min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate text-muted',
itemLabelExternalIcon: 'inline-block size-3 align-top text-dimmed'
},
variants: {
size: {
xs: {
label: 'p-1 text-[10px]/3 gap-1',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4 text-sm',
itemLeadingAvatarSize: '3xs'
},
sm: {
label: 'p-1.5 text-[10px]/3 gap-1.5',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4 text-sm',
itemLeadingAvatarSize: '3xs'
},
md: {
label: 'p-1.5 text-xs gap-1.5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5 text-base',
itemLeadingAvatarSize: '2xs'
},
lg: {
label: 'p-2 text-xs gap-2',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5 text-base',
itemLeadingAvatarSize: '2xs'
},
xl: {
label: 'p-2 text-sm gap-2',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6 text-xl',
itemLeadingAvatarSize: 'xs'
}
},
active: {
true: {
item: 'text-highlighted before:bg-elevated/75',
itemLeadingIcon: 'text-default'
},
false: {
item: [
'text-default data-highlighted:not-data-disabled:text-highlighted data-highlighted:not-data-disabled:before:bg-elevated/50',
'transition-colors before:transition-colors'
],
itemLeadingIcon: [
'text-dimmed group-data-highlighted:not-group-data-disabled:text-default',
'transition-colors'
]
}
}
},
defaultVariants: {
size: 'md'
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
editorEmojiMenu: {
slots: {
content: 'min-w-48 max-w-60 max-h-96 bg-default shadow-lg rounded-md ring ring-default overflow-hidden data-[state=open]:animate-[scale-in_100ms_var(--ease-out)] data-[state=closed]:animate-[scale-out_100ms_var(--ease-out)] origin-(--reka-dropdown-menu-content-transform-origin) flex flex-col',
viewport: 'relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1',
group: 'p-1 isolate',
label: 'w-full flex items-center font-semibold text-highlighted',
separator: '-mx-1 my-1 h-px bg-border',
item: 'group relative w-full flex items-start select-none outline-none before:absolute before:z-[-1] before:inset-px before:rounded-md data-disabled:cursor-not-allowed data-disabled:opacity-75',
itemLeadingIcon: 'shrink-0 flex items-center justify-center',
itemLeadingAvatar: 'shrink-0',
itemLeadingAvatarSize: '',
itemWrapper: 'flex-1 flex flex-col text-start min-w-0',
itemLabel: 'truncate',
itemDescription: 'truncate text-muted',
itemLabelExternalIcon: 'inline-block size-3 align-top text-dimmed'
},
variants: {
size: {
xs: {
label: 'p-1 text-[10px]/3 gap-1',
item: 'p-1 text-xs gap-1',
itemLeadingIcon: 'size-4 text-sm',
itemLeadingAvatarSize: '3xs'
},
sm: {
label: 'p-1.5 text-[10px]/3 gap-1.5',
item: 'p-1.5 text-xs gap-1.5',
itemLeadingIcon: 'size-4 text-sm',
itemLeadingAvatarSize: '3xs'
},
md: {
label: 'p-1.5 text-xs gap-1.5',
item: 'p-1.5 text-sm gap-1.5',
itemLeadingIcon: 'size-5 text-base',
itemLeadingAvatarSize: '2xs'
},
lg: {
label: 'p-2 text-xs gap-2',
item: 'p-2 text-sm gap-2',
itemLeadingIcon: 'size-5 text-base',
itemLeadingAvatarSize: '2xs'
},
xl: {
label: 'p-2 text-sm gap-2',
item: 'p-2 text-base gap-2',
itemLeadingIcon: 'size-6 text-xl',
itemLeadingAvatarSize: 'xs'
}
},
active: {
true: {
item: 'text-highlighted before:bg-elevated/75',
itemLeadingIcon: 'text-default'
},
false: {
item: [
'text-default data-highlighted:not-data-disabled:text-highlighted data-highlighted:not-data-disabled:before:bg-elevated/50',
'transition-colors before:transition-colors'
],
itemLeadingIcon: [
'text-dimmed group-data-highlighted:not-group-data-disabled:text-default',
'transition-colors'
]
}
}
},
defaultVariants: {
size: 'md'
}
}
}
})
]
})