shadimage

Tooltip

A tooltip component with arrow and multiple styling options

registry/preskok/ui/preskok-ui/tooltip.tsx

"use client"

import { twJoin } from "cn"
import { Button } from "react-aria-components/Button"
import { composeRenderProps } from "react-aria-components/composeRenderProps"
import type { TooltipProps as TooltipPrimitiveProps } from "react-aria-components/Tooltip"
import {
  OverlayArrow,
  Tooltip as TooltipPrimitive,
  TooltipTrigger as TooltipTriggerPrimitive,
} from "react-aria-components/Tooltip"
import type { VariantProps } from "tailwind-variants"
import { tv } from "tailwind-variants"

const tooltipStyles = tv({
  base: [
    "group origin-(--trigger-anchor-point) rounded-lg border border-(--tooltip-border) px-2.5 py-1.5 text-sm/6 will-change-transform [--tooltip-border:var(--color-muted-foreground)]/30 dark:shadow-none *:[strong]:font-medium",
  ],
  variants: {
    inverse: {
      true: [
        "border-transparent bg-foreground text-background",
        "**:[.text-muted-foreground]:text-background/60",
      ],
      false: "bg-overlay text-overlay-foreground",
    },
    isEntering: {
      true: [
        "animate-in fade-in",
        "placement-left:slide-in-from-right-1 placement-right:slide-in-from-left-1 placement-top:slide-in-from-bottom-1 placement-bottom:slide-in-from-top-1",
      ],
    },
    isExiting: {
      true: [
        "animate-in direction-reverse fade-in",
        "placement-left:slide-out-to-right-1 placement-right:slide-out-to-left-1 placement-top:slide-out-to-bottom-1 placement-bottom:slide-out-to-top-1",
      ],
    },
  },
  defaultVariants: {
    inverse: false,
  },
})

type TooltipProps = React.ComponentProps<typeof TooltipTriggerPrimitive>
const Tooltip = (props: TooltipProps) => <TooltipTriggerPrimitive {...props} />

interface TooltipContentProps
  extends
    Omit<TooltipPrimitiveProps, "children">,
    VariantProps<typeof tooltipStyles> {
  arrow?: boolean
  children?: React.ReactNode
}

const TooltipContent = ({
  offset = 10,
  arrow = true,
  inverse,
  children,
  ...props
}: TooltipContentProps) => {
  return (
    <TooltipPrimitive
      {...props}
      offset={offset}
      className={composeRenderProps(props.className, (className, renderProps) =>
        tooltipStyles({
          ...renderProps,
          inverse,
          className,
        })
      )}
    >
      {arrow && (
        <OverlayArrow className="group">
          <svg
            width={12}
            height={12}
            viewBox="0 0 12 12"
            // inverse
            className={twJoin(
              "group-placement-left:-rotate-90 group-placement-bottom:rotate-180 group-placement-right:rotate-90 block forced-colors:fill-[Canvas] forced-colors:stroke-[ButtonBorder]",
              inverse
                ? "fill-foreground stroke-transparent"
                : "fill-overlay stroke-(--tooltip-border)"
            )}
          >
            <path d="M0 0 L6 6 L12 0" />
          </svg>
        </OverlayArrow>
      )}
      {children}
    </TooltipPrimitive>
  )
}

const TooltipTrigger = Button

export { Tooltip, TooltipContent, TooltipTrigger }
export type { TooltipContentProps, TooltipProps }
Interaction
tooltip

DependenciesBundle weight · light

react-aria-componentscn@^0.2.4tailwind-variants