Vue 3 custom directive

I have tooltip custom directive. Is working as I expected, only when I use beforeUpdate hooks, not working on update hooks. The problem is that is creating multiple element when I hovering on h1 where I'm using mouseover and mouseleve.

There is some way that it will not create multiple elements?

As I'm beginner on custom directive I'll appreciate any of your help.

<div class="tooltip"><h1>Hello world from html</h1></div>
<div class="tooltip"><h1>Hello world from html</h1></div>
<div class="tooltip"><h1>Hello world from html</h1></div>
<div class="tooltip"><h1>Hello world from html</h1></div>

tooltip.ts

const updateTooltip = (el: HTMLElement, value: DirectiveBinding["value"], modifiers: DirectiveBinding["modifiers"], arg: DirectiveBinding["arg"], vnode: VNode): void => {
if (!value.text) {
    el.classList.remove("tooltip-parent")
  }

  if (value.text) {
    tooltipDiv.classList.add("tooltip")
    tooltipDiv.innerHTML = value.text
    el.appendChild(tooltipDiv)
  }

  if (value.displayArrow) {
    el.style.setProperty(Tooltip.arrowDisplay, "inline")
    argTooltip?.style.setProperty(Tooltip.arrowDisplay, "inline")
  }

  tooltipPlacement(value, el, argTooltip, argTooltipDiv)

}

export const tooltip = {
  mounted: (el: HTMLElement, { value, modifiers, arg }: DirectiveBinding): void => {
    if (typeof value === "object" && value.text) {
      el.classList.add("tooltip-parent")
    }

    if (typeof value === "string" && value) {
      el.classList.add("tooltip-parent")
    }

    updateTooltip(el, value, modifiers, arg)
  },

  beforeUpdate: (el: HTMLElement, { value, modifiers, arg }: DirectiveBinding): void => {
    updateTooltip(el, value, modifiers, arg)
  },
}

tooltipPlacement.ts

export const tooltipPlacement = (value: DirectiveBinding["value"], el: HTMLElement, argTooltip: HTMLElement, argTooltipDiv: HTMLDivElement) => {
 if (value.theme) {
     for (const [key, val] of Object.entries(value.theme) as [string, any][]) {
       switch (key) {
            case "hide":
            watch(
              () => val,
              () => {
                if (val) {
                  argTooltip?.style.setProperty(Tooltip.opacity, "0")
                  argTooltip?.style.setProperty(Tooltip.visibility, "hidden")
                  /****  arrow ****/
                  argTooltip?.style.setProperty(Tooltip.arrowOpacity, "0")
                  argTooltip?.style.setProperty(Tooltip.arrowVisibility, "hidden")
                }

                if (!val) {
                  console.log("show")
                  argTooltip?.style.setProperty(Tooltip.opacity, "1")
                  argTooltip?.style.setProperty(Tooltip.visibility, "visible")
                  /****  arrow ****/
                  argTooltip?.style.setProperty(Tooltip.arrowOpacity, "1")
                  argTooltip?.style.setProperty(Tooltip.arrowVisibility, "visible")
                }
              },
              { immediate: true },
            )
            break
          default:
            break
      }
    }
  }
}

Template

<template>
  <div>
    <div class="p-6 centered flex-col space-y-6">
      {{ !!hideTooltip }}
      <h1>Tooltip</h1>
      <br />
      <p v-tooltip:actions.arrow="tooltipContent(html, tooltipTheme, true)" class="border w-40 h-20 text-center">hello world</p>
    </div>
    <div class="p-6 centered flex-col space-y-6">
      <h1 class="h-20" @mouseover="hideTooltip = false" @mouseleave="hideTooltip = true">Actions</h1>
      <br />
      <p class="h-60 border px-4 actions">Test of args tooltip</p>
      <button class="mt-10 p-6" @click="hideTooltip = !hideTooltip">{{ hideTooltip ? "Show Tooltip" : "Hide" }}</button>
    </div>
  </div>
</template>

Script

<script lang="ts" setup>
import { tooltipContent, ThemeOptions } from "../directives/tooltip"
import { ref, computed } from "vue"

const hideTooltip = ref(true)

const html = ref("<h1>Hello world from html</h1>")

const tooltipTheme = computed((): ThemeOptions => {
  return {
    placement: "leftTop",
    argTxt: html.value,
    hide: !!hideTooltip.value,
    maxWidth: "300px",
  }
})
</script>

Upvotes: 0

Views: 228

Answers (1)

By adding the following code, it fixed the issue

const hasTooltipClass = el.querySelector(".tooltip") as HTMLDivElement

   if (!hasTooltipClass) {
      tooltipDiv.classList.add("tooltip")
      tooltipDiv.innerHTML = value
      el.appendChild(tooltipDiv)
    }

Upvotes: 0

Related Questions