import { Component, Show, createEffect, createSignal, onCleanup } from 'solid-js'; interface SearchTip { code: string; description: string; } interface SearchTipsPopoverProps { buttonLabel?: string; title?: string; intro?: string; tips: SearchTip[]; footerText?: string; footerHighlight?: string; popoverId?: string; align?: 'left' | 'right'; class?: string; triggerVariant?: 'button' | 'link' | 'icon'; openOnHover?: boolean; } export const SearchTipsPopover: Component = (props) => { const [open, setOpen] = createSignal(false); let popoverRef: HTMLDivElement | undefined; let triggerRef: HTMLButtonElement | undefined; let pointerInside = false; const close = () => setOpen(false); createEffect(() => { if (!open()) { return; } const handlePointerDown = (event: PointerEvent) => { const target = event.target as Node; const insidePopover = popoverRef?.contains(target) ?? false; const onTrigger = triggerRef?.contains(target) ?? false; if (!insidePopover && !onTrigger) { close(); } }; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { close(); } }; window.addEventListener('pointerdown', handlePointerDown); window.addEventListener('keydown', handleKeyDown); onCleanup(() => { window.removeEventListener('pointerdown', handlePointerDown); window.removeEventListener('keydown', handleKeyDown); }); }); const popoverPositionClass = props.align === 'left' ? 'left-0' : 'right-0'; const popoverId = props.popoverId ?? 'search-tips-popover'; const triggerVariant = props.triggerVariant ?? 'button'; const openOnHover = props.openOnHover ?? false; const buttonLabel = props.buttonLabel ?? 'Search tips'; const triggerBaseClasses = 'text-xs font-medium focus:outline-none focus:ring-2 focus:ring-blue-500/40 focus:ring-offset-1 focus:ring-offset-white dark:focus:ring-blue-400/40 dark:focus:ring-offset-gray-900'; const triggerClasses = triggerVariant === 'button' ? `rounded-md border border-gray-200 px-2.5 py-1 text-gray-600 transition-colors hover:bg-gray-100 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700 ${triggerBaseClasses}` : triggerVariant === 'link' ? `rounded px-1 py-0.5 text-gray-500 underline decoration-dotted underline-offset-4 transition-colors hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 ${triggerBaseClasses}` : `flex h-5 w-5 items-center justify-center rounded-full text-gray-400 transition-colors hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-100 ${triggerBaseClasses}`; const handleMouseEnter = () => { pointerInside = true; setOpen(true); }; const handleMouseLeave = () => { pointerInside = false; setOpen(false); }; return (
(popoverRef = el)} id={popoverId} role="dialog" aria-label={props.title ?? 'Search tips'} class={`absolute ${popoverPositionClass} z-50 mt-2 w-72 overflow-hidden rounded-lg border border-gray-200 bg-white text-left shadow-xl dark:border-gray-600 dark:bg-gray-800`} >
{props.title ?? 'Search tips'}

{props.intro}

{props.tips.map((tip) => (
{tip.code} {tip.description}
))}
{props.footerHighlight} {props.footerText}
); }; export default SearchTipsPopover;