modbender / nuxt-tiptap-editor

Essentials to Quickly Integrate TipTap Editor into your Nuxt App
https://nuxt-tiptap-editor.vercel.app/
MIT License
47 stars 4 forks source link

Bubble Menu breaks when navigating away then back #9

Closed IzakJackson closed 4 days ago

IzakJackson commented 1 month ago

When navigation away from my page and then back, the bubble menu stops opening:

<template>
    <Card>
        <CardHeader>
            <CardTitle>Booking Information</CardTitle>
            <CardDescription> Enter the details of your booking. </CardDescription>
        </CardHeader>
        <CardContent>
            <Card class="overflow-hidden">
                <div
                    v-if="editor"
                    class="flex items-center p-1 border-b">
                    <ToggleGroup type="single">
                        <ToggleGroupItem
                            value="bold"
                            aria-label="Toggle bold"
                            :disabled="
                                !editor.can().chain().focus().toggleBold().run() ||
                                businessStore?.userRole === 'Team Member'
                            "
                            :class="{ 'is-active': editor.isActive('bold') }"
                            @click="editor.chain().focus().toggleBold().run()">
                            <Bold class="w-4 h-4" />
                        </ToggleGroupItem>
                        <ToggleGroupItem
                            value="italic"
                            aria-label="Toggle italic"
                            :disabled="
                                !editor.can().chain().focus().toggleItalic().run() ||
                                businessStore?.userRole === 'Team Member'
                            "
                            :class="{ 'is-active': editor.isActive('italic') }"
                            @click="editor.chain().focus().toggleItalic().run()">
                            <Italic class="w-4 h-4" />
                        </ToggleGroupItem>
                        <ToggleGroupItem
                            value="underline"
                            aria-label="Toggle underline"
                            :disabled="
                                !editor.can().chain().focus().toggleUnderline().run() ||
                                businessStore?.userRole === 'Team Member'
                            "
                            :class="{ 'is-active': editor.isActive('underline') }"
                            @click="editor.chain().focus().toggleUnderline().run()">
                            <Underline class="w-4 h-4" />
                        </ToggleGroupItem>
                        <ToggleGroupItem
                            value="bulletList"
                            aria-label="Toggle bullet list"
                            :disabled="businessStore?.userRole === 'Team Member'"
                            :class="{ 'is-active': editor.isActive('bulletList') }"
                            @click="editor.chain().focus().toggleBulletList().run()">
                            <List class="w-4 h-4" />
                        </ToggleGroupItem>
                        <ToggleGroupItem
                            value="orderedList"
                            aria-label="Toggle ordered list"
                            :disabled="businessStore?.userRole === 'Team Member'"
                            :class="{ 'is-active': editor.isActive('orderedList') }"
                            @click="editor.chain().focus().toggleOrderedList().run()">
                            <ListOrdered class="w-4 h-4" />
                        </ToggleGroupItem>
                        <ToggleGroupItem
                            value="blockquote"
                            aria-label="Toggle blockquote"
                            :disabled="businessStore?.userRole === 'Team Member'"
                            :class="{ 'is-active': editor.isActive('blockquote') }"
                            @click="editor.chain().focus().toggleBlockquote().run()">
                            <Quote class="w-4 h-4" />
                        </ToggleGroupItem>
                    </ToggleGroup>
                    <div class="ml-auto"><div class="w-px h-4 mx-2 bg-border" /></div>
                    <Button
                        variant="ghost"
                        size="icon"
                        :disabled="
                            !editor.can().undo() || businessStore?.userRole === 'Team Member'
                        "
                        @click="editor.chain().focus().undo().run()">
                        <Undo2 class="w-4 h-4" />
                    </Button>
                    <Button
                        variant="ghost"
                        size="icon"
                        :disabled="
                            !editor.can().redo() || businessStore?.userRole === 'Team Member'
                        "
                        @click="editor.chain().focus().redo().run()">
                        <Redo2 class="w-4 h-4" />
                    </Button>
                    <Button
                        variant="ghost"
                        size="icon"
                        :disabled="businessStore?.userRole === 'Team Member'"
                        @click="editor.chain().focus().unsetAllMarks().run()">
                        <RemoveFormatting class="w-4 h-4" />
                    </Button>
                </div>
                <CardContent class="px-5 py-4 bg-background">
                    <TiptapBubbleMenu
                        v-if="editor"
                        :editor="editor"
                        :tippy-options="{ duration: 100 }"
                        :disabled="businessStore?.userRole === 'Team Member'">
                        <Card class="p-1 bubble-menu">
                            <span>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    :disabled="!editor.can().chain().focus().toggleBold().run()"
                                    :class="{ 'is-active': editor.isActive('bold') }"
                                    @click="editor.chain().focus().toggleBold().run()">
                                    <Bold class="w-4 h-4" />
                                </Button>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    :disabled="!editor.can().chain().focus().toggleItalic().run()"
                                    :class="{ 'is-active': editor.isActive('italic') }"
                                    @click="editor.chain().focus().toggleItalic().run()">
                                    <Italic class="w-4 h-4" />
                                </Button>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    :disabled="
                                        !editor.can().chain().focus().toggleUnderline().run()
                                    "
                                    :class="{ 'is-active': editor.isActive('underline') }"
                                    @click="editor.chain().focus().toggleUnderline().run()">
                                    <Underline class="w-4 h-4" />
                                </Button>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    :class="{ 'is-active': editor.isActive('bulletList') }"
                                    @click="editor.chain().focus().toggleBulletList().run()">
                                    <List class="w-4 h-4" />
                                </Button>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    :class="{ 'is-active': editor.isActive('orderedList') }"
                                    @click="editor.chain().focus().toggleOrderedList().run()">
                                    <ListOrdered class="w-4 h-4" />
                                </Button>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    :class="{ 'is-active': editor.isActive('blockquote') }"
                                    @click="editor.chain().focus().toggleBlockquote().run()">
                                    <Quote class="w-4 h-4" />
                                </Button>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    @click="setLink">
                                    <Link2 class="w-4 h-4" />
                                </Button>
                                <Button
                                    variant="ghost"
                                    size="icon"
                                    :disabled="!editor.isActive('link')"
                                    @click="editor.chain().focus().unsetLink().run()">
                                    <Link2Off class="w-4 h-4" />
                                </Button>
                            </span>
                        </Card>
                    </TiptapBubbleMenu>
                    <TiptapEditorContent
                        :editor="editor"
                        :disabled="businessStore?.userRole === 'Team Member'" />
                </CardContent>
            </Card>
        </CardContent>
        <CardFooter
            v-if="
                !route.path.includes('/create') &&
                businessStore?.userRole !== 'Team Member'
            "
            class="py-4 border-t">
            <Button
                class="ml-auto"
                :disabled="businessStore?.userRole === 'Team Member'"
                @click.prevent="onSubmit">
                <LoadingIcon
                    v-if="productStore.submitting"
                    class="w-8 h-8" />
                <span v-else>Save Changes</span>
            </Button>
        </CardFooter>
    </Card>
</template>

<script setup lang="ts">
import {
    Bold,
    Italic,
    List,
    ListOrdered,
    RemoveFormatting,
    Underline,
    Quote,
    Link2Off,
    Link2,
    Undo2,
    Redo2,
} from 'lucide-vue-next';
import { useProductStore } from '@/stores/productStore';
import { useBusinessStore } from '@/stores/businessStore';

const businessStore = useBusinessStore();
const productStore = useProductStore();

const route = useRoute();

const editor = useEditor({
    content: productStore.booking_info,
    extensions: [TiptapStarterKit, TiptapUnderline, TiptapLink, TiptapBubbleMenu],
    editorProps: {
        attributes: {
            class:
                'prose max-w-none focus:outline-none prose-neutral dark:prose-invert !p-0 !m-0',
        },
    },
});

const setLink = () => {
    if (!editor.value) {
        return;
    }

    const previousUrl = unref(editor).getAttributes('link').href;
    const url = window.prompt('URL', previousUrl); // Replace with own UI instead of window.prompt

    // cancelled
    if (url === null) {
        return;
    }

    // empty
    if (url === '') {
        unref(editor).chain().focus().extendMarkRange('link').unsetLink().run();
        return;
    }

    // update link
    unref(editor)
        .chain()
        .focus()
        .extendMarkRange('link')
        .setLink({ href: url })
        .run();
};

const onSubmit = async () => {
    const values = {
        booking_info: unref(editor).getJSON(),
    };
    await productStore.updateProduct(route.params.id as string, values);
};

onBeforeUnmount(() => {
    unref(editor).destroy();
});
</script>
modbender commented 3 weeks ago

I haven't used Bubble Menu all that much, so can't really tell. I'll try to look at it soon. But if you find a solution please do post it here and close this issue.

vrijraj commented 2 weeks ago

I relly like this initiative with tiptap and nuxt editor module

I am also working in my project and am unable to use complete features

modbender commented 4 days ago

This should be fixed now. It seems there was overlapping/double import of this component.