📄 Tooltip.tsx
/home/palash/git/site/node_modules/react-activity-calendar/src/components/Tooltip.tsx
Language: tsx • Lines: 98
'use client'

import { cloneElement, useRef, useState, type ReactElement, type Ref } from 'react'
import {
  arrow,
  autoUpdate,
  flip,
  FloatingArrow,
  FloatingPortal,
  offset,
  shift,
  useDismiss,
  useFloating,
  useHover,
  useInteractions,
  useRole,
  useTransitionStyles,
  type OffsetOptions,
  type Placement,
  type UseHoverProps,
  type UseTransitionStylesProps,
} from '@floating-ui/react'
import { getClassName } from '../lib/calendar'
import type { ColorScheme } from '../types'

export type TooltipConfig = {
  placement?: Placement
  offset?: OffsetOptions
  transitionStyles?: UseTransitionStylesProps
  hoverRestMs?: UseHoverProps['restMs']
  withArrow?: boolean
}

export type TooltipProps = TooltipConfig & {
  children: ReactElement<{ ref: Ref<unknown> }>
  text: string
  colorScheme: ColorScheme
}

export function Tooltip({
  children,
  text,
  colorScheme,
  placement,
  offset: offsetProp = 4,
  transitionStyles: transitionStylesProp,
  hoverRestMs = 150,
  withArrow = false,
}: TooltipProps) {
  const [isOpen, setIsOpen] = useState(false)
  const arrowRef = useRef(null)

  const { context, refs, floatingStyles } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    placement,
    middleware: [
      flip(),
      offset(offsetProp),
      shift({ padding: 8 }),
      withArrow ? arrow({ element: arrowRef }) : null, // eslint-disable-line react-hooks/refs
    ],
    whileElementsMounted: autoUpdate,
  })

  const hover = useHover(context, { restMs: hoverRestMs })
  const dismiss = useDismiss(context)
  const role = useRole(context, { role: 'tooltip' })

  const { getReferenceProps, getFloatingProps } = useInteractions([hover, dismiss, role])
  const { isMounted, styles: transitionStyles } = useTransitionStyles(context, transitionStylesProp)

  return (
    <>
      {cloneElement(children, { ref: refs.setReference, ...getReferenceProps() })}
      {isMounted && (
        <FloatingPortal>
          <div
            ref={refs.setFloating}
            className={getClassName('tooltip')}
            style={{ ...floatingStyles, ...transitionStyles }}
            data-color-scheme={colorScheme}
            {...getFloatingProps()}
          >
            {text}
            {withArrow && (
              <FloatingArrow
                ref={arrowRef}
                context={context}
                className={getClassName('tooltip-arrow')}
              />
            )}
          </div>
        </FloatingPortal>
      )}
    </>
  )
}