# All files / src/components/ui tooltip.tsx

0% Statements 0/99
0% Branches 0/1
0% Functions 0/1
0% Lines 0/99

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100

     "use client"

    import React, { ComponentProps, useState, useRef, useEffect } from "react"
    import { createPortal } from "react-dom"
    import { cn } from "@/lib/utils"

    interface TooltipContextValue {
      open: boolean
      setOpen: (open: boolean) => void
    }

    const TooltipContext = React.createContext<TooltipContextValue | null>(null)

    function TooltipProvider({ children }: { children: React.ReactNode; delayDuration?: number }) {
      return <>{children}</>
    }

    function Tooltip({ children }: { children: React.ReactNode }) {
      const [open, setOpen] = useState(false)
      return (
        <TooltipContext.Provider value={{ open, setOpen }}>
          {children}
        </TooltipContext.Provider>
      )
    }

    function TooltipTrigger({ children, asChild, ...props }: ComponentProps<"button"> & { asChild?: boolean }) {
      const context = React.useContext(TooltipContext)
      const timeoutRef = useRef<NodeJS.Timeout | null>(null)

      const handleMouseEnter = () => {
        timeoutRef.current = setTimeout(() => {
          context?.setOpen(true)
        }, 700)
      }

      const handleMouseLeave = () => {
        if (timeoutRef.current) {
          clearTimeout(timeoutRef.current)
        }
        context?.setOpen(false)
      }

      if (asChild && React.isValidElement(children)) {
        return React.cloneElement(children as React.ReactElement, {
          onMouseEnter: handleMouseEnter,
          onMouseLeave: handleMouseLeave,
          ...props,
        })
      }

      return (
        <button
          type="button"
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
          {...props}
        >
          {children}
        </button>
      )
    }

    function TooltipContent({
      className,
      children,
      ...props
    }: ComponentProps<"div"> & { sideOffset?: number }) {
      const context = React.useContext(TooltipContext)
      const [mounted, setMounted] = useState(false)

      useEffect(() => {
        setMounted(true)
      }, [])

      if (!context?.open || !mounted) return null

      return createPortal(
        <div className="cdk-overlay-container">
          <div className="cdk-overlay-pane">
            <div
              className={cn(
                "mat-mdc-tooltip mdc-tooltip mat-mdc-tooltip-show",
                className
              )}
              role="tooltip"
              {...props}
            >
              <div className="mdc-tooltip__surface mdc-tooltip__surface-animation">
                {children}
              </div>
            </div>
          </div>
        </div>,
        document.body
      )
    }

    export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }