Skip to content

Custom popper component ​

WARNING

This is an advanced API! You are advised to get familiar with the library before trying this.

You can create an entirely custom component to use as a popper. v-tooltip exposes the following building blocks:

  • Popper (component): main logic component and integration with popperjs
  • PopperContent (component): standard minimal popper content with all the needed containers and CSS classes
  • getThemeClasses (function): computes the final root CSS classes depending on themes

WARNING

PopperMethods and ThemeClass (Options API mixins) are deprecated. Use the Composition API example below instead.

Popper ​

The <Popper> component should receive all attributes and event listeners (don't forget to set inheritAttrs to true).

It also expects the following props:

  • theme: currently applied theme
  • targetNodes: a function that returns the target HTML elements that should trigger the popper
  • referenceNode: a function that returns the reference HTML element that should be used as the Popper JS reference for positionning
  • popperNode: a function that returns the popper HTML element that will be displayed inside the popper (recommended to target the <PopperContent> root element)

It exposes the following to the default slot:

  • popperId: a unique ID for the popper, usefull for accessibility (should be passed to <PopperContent>)
  • isShown: boolean to know if the popper is open or not (should be passed to <PopperContent>)
  • shouldMountContent: boolean to know if the popper content should be mounted or not (should be passed to <PopperContent>)
  • skipTransition: boolean to disable animations and transitions (should be passed to <PopperContent>)
  • autoHide: if auto hidding is enabled (should be passed to <PopperContent>)
  • hide: method to hide the popper (should be handled with <PopperContent>'s hide event)
  • handleResize: is resizing detection enabled (should be passed to <PopperContent>)
  • onResize: method to handle a change of the size of the popper content (should be handled with <PopperContent>'s resize event)
  • classes: object with boolean flags to toggle CSS classes (should be passed to <PopperContent>)
  • result: object with positioning data computed for the popper (should be passed to <PopperContent>)

PopperContent ​

It expects the following props:

  • popperId
  • theme
  • shown
  • mounted
  • skipTransition
  • autoHide
  • handleResize
  • classes
  • result

Events:

  • hide
  • resize

Refs:

  • $refs.arrow (arrow element)
  • $refs.inner (div where most styling of the popper should be applied)

Full example ​

html
<template>
  <Popper
    ref="popper"
    v-slot="{
      popperId,
      isShown,
      shouldMountContent,
      skipTransition,
      autoHide,
      hide,
      handleResize,
      onResize,
      classes,
      result,
    }"
    v-bind="$attrs"
    :theme="theme"
    :target-nodes="getTargetNodes"
    :reference-node="() => reference"
    :popper-node="() => popperContent.$el"
  >
    <div
      ref="reference"
      class="v-popper"
      :class="[
        themeClass,
        {
          'v-popper--shown': isShown,
        },
      ]"
    >
      <slot />

      <PopperContent
        ref="popperContent"
        :popper-id="popperId"
        :theme="theme"
        :shown="isShown"
        :mounted="shouldMountContent"
        :skip-transition="skipTransition"
        :auto-hide="autoHide"
        :handle-resize="handleResize"
        :classes="classes"
        :result="result"
        @hide="hide"
        @resize="onResize"
      >
        <slot
          name="popper"
          :shown="isShown"
        />
      </PopperContent>
    </div>
  </Popper>
</template>

<script setup>
import { computed, ref } from 'vue'
import {
  Popper,
  PopperContent,
  getThemeClasses,
} from 'floating-vue'

defineOptions({
  name: 'VPopperWrapper',
  inheritAttrs: false,
})

const props = defineProps({
  theme: {
    type: String,
    default: 'my-popper-theme',
  },
})

const reference = ref()
const popperContent = ref()
const popper = ref()

const themeClass = computed(() => getThemeClasses(props.theme))

function getTargetNodes () {
  return Array.from(reference.value.children)
}

defineExpose({
  show: (...args) => popper.value.show(...args),
  hide: (...args) => popper.value.hide(...args),
  dispose: (...args) => popper.value.dispose(...args),
  onResize: (...args) => popper.value.onResize(...args),
})
</script>

Made by Guillaume Chau with ❤️