{"version":3,"sources":["../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["import {\n  ReactNode,\n  useCallback,\n  useEffect,\n  useLayoutEffect,\n  useRef,\n  useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Tooltip position options\n */\ntype TooltipPosition = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Tooltip component props interface\n */\nexport interface TooltipProps {\n  /** Content to display in the tooltip */\n  content: ReactNode;\n  /** Element that triggers the tooltip */\n  children: ReactNode;\n  /** Position of the tooltip relative to the trigger */\n  position?: TooltipPosition;\n  /** Additional className for the tooltip container */\n  className?: string;\n  /** Additional className for the tooltip content */\n  contentClassName?: string;\n  /** Whether the tooltip is disabled */\n  disabled?: boolean;\n  /**\n   * Render the tooltip inside a React Portal attached to document.body.\n   * Use this when the trigger lives inside an ancestor with `overflow:hidden`\n   * (e.g. scroll containers) that would otherwise clip the tooltip.\n   */\n  usePortal?: boolean;\n}\n\n/**\n * Position classes for tooltip placement (non-portal mode, CSS-only)\n */\nconst POSITION_CLASSES: Record<TooltipPosition, string> = {\n  top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',\n  bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',\n  left: 'right-full top-1/2 -translate-y-1/2 mr-2',\n  right: 'left-full top-1/2 -translate-y-1/2 ml-2',\n};\n\nconst TOOLTIP_GAP_PX = 8;\n\n/**\n * Compute fixed-position coordinates relative to the viewport for a given\n * trigger element and desired tooltip position.\n */\nconst computePortalCoords = (\n  triggerRect: DOMRect,\n  position: TooltipPosition,\n  tooltipRect: { width: number; height: number }\n): { top: number; left: number } => {\n  switch (position) {\n    case 'bottom':\n      return {\n        top: triggerRect.bottom + TOOLTIP_GAP_PX,\n        left: triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2,\n      };\n    case 'left':\n      return {\n        top: triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2,\n        left: triggerRect.left - tooltipRect.width - TOOLTIP_GAP_PX,\n      };\n    case 'right':\n      return {\n        top: triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2,\n        left: triggerRect.right + TOOLTIP_GAP_PX,\n      };\n    case 'top':\n    default:\n      return {\n        top: triggerRect.top - tooltipRect.height - TOOLTIP_GAP_PX,\n        left: triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2,\n      };\n  }\n};\n\nconst TOOLTIP_CONTENT_CLASSES = cn(\n  'whitespace-nowrap',\n  'px-4 py-2 rounded-lg',\n  'bg-background-dark text-white',\n  'text-sm font-medium',\n  'shadow-[0px_3px_10px_0px_rgba(38,38,38,0.2)]',\n  'transition-opacity duration-150'\n);\n\n/**\n * Tooltip component - Displays contextual information on hover/focus\n *\n * By default uses a CSS-only approach with `group-hover` for performance.\n * When `usePortal` is true, the tooltip is rendered in a React Portal so it\n * escapes ancestors with `overflow:hidden`.\n *\n * @example\n * ```tsx\n * <Tooltip content=\"Desempenho baseado nas atividades\">\n *   <Info size={18} weight=\"bold\" className=\"text-text-950 cursor-pointer\" />\n * </Tooltip>\n * ```\n */\nexport function Tooltip({\n  content,\n  children,\n  position = 'top',\n  className,\n  contentClassName,\n  disabled = false,\n  usePortal = false,\n}: Readonly<TooltipProps>) {\n  const triggerRef = useRef<HTMLSpanElement>(null);\n  const tooltipRef = useRef<HTMLDivElement>(null);\n  const [open, setOpen] = useState(false);\n  const [coords, setCoords] = useState<{ top: number; left: number }>({\n    top: 0,\n    left: 0,\n  });\n\n  const updatePosition = useCallback(() => {\n    if (!triggerRef.current || !tooltipRef.current) return;\n    const triggerRect = triggerRef.current.getBoundingClientRect();\n    const tooltipRect = {\n      width: tooltipRef.current.offsetWidth,\n      height: tooltipRef.current.offsetHeight,\n    };\n    setCoords(computePortalCoords(triggerRect, position, tooltipRect));\n  }, [position]);\n\n  useLayoutEffect(() => {\n    if (!usePortal || !open) return;\n    updatePosition();\n    const onViewportChange = () => updatePosition();\n    window.addEventListener('resize', onViewportChange);\n    window.addEventListener('scroll', onViewportChange, true);\n    return () => {\n      window.removeEventListener('resize', onViewportChange);\n      window.removeEventListener('scroll', onViewportChange, true);\n    };\n  }, [usePortal, open, updatePosition, content]);\n\n  /**\n   * Attach hover/focus listeners via the DOM API (not JSX props) so the\n   * wrapper element stays semantically non-interactive. This satisfies the\n   * SonarQube `S6848`/`jsx-a11y/no-static-element-interactions` rule which\n   * fires on static elements (`<span>`/`<div>`) that carry interactive JSX\n   * handlers (`onMouseEnter`/`onFocus`/etc.).\n   */\n  useEffect(() => {\n    if (!usePortal) return;\n    const node = triggerRef.current;\n    if (!node) return;\n\n    const handleOpen = () => setOpen(true);\n    const handleClose = () => setOpen(false);\n\n    node.addEventListener('mouseenter', handleOpen);\n    node.addEventListener('mouseleave', handleClose);\n    node.addEventListener('focusin', handleOpen);\n    node.addEventListener('focusout', handleClose);\n\n    return () => {\n      node.removeEventListener('mouseenter', handleOpen);\n      node.removeEventListener('mouseleave', handleClose);\n      node.removeEventListener('focusin', handleOpen);\n      node.removeEventListener('focusout', handleClose);\n    };\n  }, [usePortal]);\n\n  if (disabled) {\n    return <>{children}</>;\n  }\n\n  if (usePortal) {\n    return (\n      <Text\n        as=\"span\"\n        ref={triggerRef}\n        className={cn('relative inline-flex', className)}\n        aria-describedby={open ? 'tooltip-portal' : undefined}\n      >\n        {children}\n        {open &&\n          typeof document !== 'undefined' &&\n          createPortal(\n            <div\n              ref={tooltipRef}\n              id=\"tooltip-portal\"\n              role=\"tooltip\"\n              style={{\n                position: 'fixed',\n                top: coords.top,\n                left: coords.left,\n                zIndex: 9999,\n              }}\n              className={cn(TOOLTIP_CONTENT_CLASSES, contentClassName)}\n            >\n              {content}\n            </div>,\n            document.body\n          )}\n      </Text>\n    );\n  }\n\n  return (\n    <div className={cn('relative inline-flex group', className)}>\n      {children}\n\n      {/* Tooltip content - shown on hover/focus via CSS */}\n      <div\n        role=\"tooltip\"\n        className={cn(\n          'absolute z-50',\n          TOOLTIP_CONTENT_CLASSES,\n          'opacity-0 invisible',\n          'group-hover:opacity-100 group-hover:visible',\n          'group-focus-within:opacity-100 group-focus-within:visible',\n          POSITION_CLASSES[position],\n          contentClassName\n        )}\n      >\n        {content}\n      </div>\n    </div>\n  );\n}\n\nexport default Tooltip;\n"],"mappings":"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","names":[]}