import { useEffect, useRef, useState } from "react";
import { getHovercard } from "~/fns/hovercards";
const SHOW_DELAY_MS = 350;
const HIDE_DELAY_MS = 200;
const KINDS = ["issue", "user", "team", "commit"] as const;
type Kind = (typeof KINDS)[number];
type Card = NonNullable<Awaited<ReturnType<typeof getHovercard>>>;
export type HovercardState = { card: Card; top: number; left: number } | null;
function isKind(value: string | undefined): value is Kind {
return KINDS.some((kind) => kind === value);
}
/** Watches the document for hovers over `a[data-hovercard]` links and loads preview cards (memoized per ref). */
export function useHovercards() {
const [state, setState] = useState<HovercardState>(null);
const cache = useRef(new Map<string, Promise<Card | null>>());
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
useEffect(() => {
const target = (event: Event) =>
event.target instanceof Element ? event.target.closest<HTMLAnchorElement>("a[data-hovercard]") : null;
const inPopup = (event: Event) =>
event.target instanceof Element && event.target.closest("[data-hovercard-popup]") !== null;
const over = (event: MouseEvent) => {
if (inPopup(event)) {
clearTimeout(timer.current);
return;
}
const link = target(event);
if (!link) return;
const kind = link.dataset.hovercard;
const ref = link.dataset.ref;
if (!isKind(kind) || !ref) return;
clearTimeout(timer.current);
timer.current = setTimeout(async () => {
const key = `${kind}:${ref}`;
let pending = cache.current.get(key);