"use client" import * as React from "react" import { useFloating, autoUpdate, offset, flip, shift, size, useDismiss, useRole, useInteractions, FloatingPortal, useHover, safePolygon, arrow, } from '@floating-ui/react' import { cn } from "../../utils/cn" interface FloatingTooltipProps { content: React.ReactNode children: React.ReactNode side?: "top" | "right" | "bottom" | "left" className?: string delayDuration?: number /** Disable the tooltip without unmounting the trigger wrapper. */ disabled?: boolean /** * Classes for the trigger wrapper — the element this component puts around * `children` to anchor the tooltip. That element becomes the flex/grid item in * the caller's layout, so anything the child needed there (`min-w-0`, `flex-1`) * must move onto it. Omit it and the wrapper carries no class, exactly as before. */ triggerClassName?: string /** * Trigger wrapper element. Default `'div'`. Use `'span'` where a block element * is invalid HTML — inside a `
`, a heading, or another ``.
*/
as?: 'div' | 'span'
}
// Parse colored text markup like [YELLOW]text[/YELLOW] into JSX
function parseColoredText(text: string): React.ReactNode {
if (typeof text !== 'string') return text;
const parts: React.ReactNode[] = []
let lastIndex = 0
// Regex to match [COLOR]text[/COLOR] patterns
const colorRegex = /\[([A-Z]+)\](.*?)\[\/\1\]/g
let match
let keyIndex = 0
while ((match = colorRegex.exec(text)) !== null) {
// Add text before the colored part
if (match.index > lastIndex) {
const beforeText = text.slice(lastIndex, match.index)
parts.push({beforeText})
}
// Add colored text
const color = match[1].toLowerCase()
const coloredText = match[2]
// Map colors to ODS CSS classes using correct Tailwind class names
const colorClass = color === 'yellow' ? 'text-ods-accent' :
color === 'green' ? 'text-ods-success' :
color === 'red' ? 'text-ods-error' :
color === 'blue' ? 'text-ods-info' :
color === 'pink' ? 'text-ods-accent' :
color === 'cyan' ? 'text-ods-info' :
'text-ods-accent' // Default to ODS accent
parts.push(
{coloredText}
)
lastIndex = match.index + match[0].length
}
// Add remaining text
if (lastIndex < text.length) {
const remainingText = text.slice(lastIndex)
parts.push({remainingText})
}
return parts.length > 0 ? <>{parts}> : text
}
export function FloatingTooltip({
content,
children,
side = "right",
className,
delayDuration = 0,
disabled = false,
triggerClassName,
as: TriggerTag = 'div',
}: FloatingTooltipProps) {
const [isOpen, setIsOpen] = React.useState(false)
const arrowRef = React.useRef