KABBOUCHI / vue-tippy

VueJS Tooltip powered by Tippy.js
https://vue-tippy.netlify.app
MIT License
740 stars 89 forks source link

How to use composition hook with ref to the component? #174

Closed negezor closed 3 years ago

negezor commented 3 years ago

I get an error when I try to use a hook with a component

tippy() was passed a plain object which is not supported as an argument for virtual positioning. Use props.getReferenceClientRect instead.

And

DOMException: Failed to execute 'querySelectorAll' on 'Document': '[object Object]' is not a valid selector.

TheNavigation.vue

<template>
    <div>
        <BaseButton
            label="Open dropdown"
            ref="triggerElement"
        />

        <div ref="contentElement">
            Content
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';
import { useTippy } from 'vue-tippy/composition';

import { BaseButton } from '@/components';

export default defineComponent({
    components: {
        BaseButton
    },
    setup() {
        const triggerElement = ref(null);
        const contentElement = ref(null);

        const { tippy } = useTippy(triggerElement, {
            content: contentElement,

            placement: 'bottom',
            animation: 'shift-away',
            role: 'dropdown',
            arrow: false,
            trigger: 'click',
            appendTo: 'parent',
            interactive: true
        });

        return {
            tippy,
            triggerElement,
            contentElement
        };
    }
});
</script>

BaseButton.vue

<template>
    <button>
        <slot>
            {{ label }}
        </slot>
    </button>
</template>

<script lang="ts">
import { defineComponent } from '@vue/composition-api';

export default defineComponent({
    props: {
        label: {
            type: String,
            default: null
        }
    },
    setup() {
        return {};
    }
});
</script>
KABBOUCHI commented 3 years ago

@negezor plz update to v4.9.0 and retry

https://kabbouchi.github.io/vue-tippy/4.0/features/composition-api.html#example-5

negezor commented 3 years ago

It works, thanks! Taking this opportunity, how can useTippy() be initialized asynchronously? Since by default it always requires an element to trigger.

KABBOUCHI commented 3 years ago

I didn't fully understand the question, can u provide a basic example (a broken example is fine)?

for vue-tippy@next you can disable automount and trigger it manually:

import { useTippy } from 'vue-tippy';

const element = ref();

const { mount, unmount } =  useTippy(element, { arrow : true } , { mount : false })

const mountTippy = (el : Element) {
      umount() // safely unmount previous element/instance if exist
      element.value = el
      mount()
}

mountTippy(...) // call it any time you want

is that what you're looking for?

negezor commented 3 years ago

I modify the first example for clarity. This will throw an error before the data is loaded.

<template>
    <div>
        <div v-if="!loading">
            <BaseButton
                label="Open dropdown"
                ref="triggerElement"
            />

            <div ref="contentElement">
                Content
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';
import { useTippy } from 'vue-tippy/composition';

import { BaseButton } from '@/components';

export default defineComponent({
    components: {
        BaseButton
    },
    setup() {
        const loading = ref(false);

        setTimeout(() => {
            // Load data from server...
            loading.value = false;
        }, 1e3);

        const triggerElement = ref(null);
        const contentElement = ref(null);

        const { tippy } = useTippy(triggerElement, {
            content: contentElement,

            placement: 'bottom',
            animation: 'shift-away',
            role: 'dropdown',
            arrow: false,
            trigger: 'click',
            appendTo: 'parent',
            interactive: true
        });

        return {
            loading,

            tippy,
            triggerElement,
            contentElement
        };
    }
});
</script>

The vue-tippy@next looks like what I need. Unfortunately I cannot use vue-tippy@next as it is for Vue 3. I am using Nuxt.js with Composition API.

KABBOUCHI commented 3 years ago

I've released a new version v4.10.0 now you can mount/unmount manually

https://kabbouchi.github.io/vue-tippy/4.0/features/composition-api.html?v4.10#example-6

https://github.com/KABBOUCHI/vue-tippy/blob/b803f1e9722342ca0a2948504a757d2ca054a8d2/docs/.vuepress/components/CompDemo6.vue#L1-L58

negezor commented 3 years ago

That's very helpful, thank you.