2026-03-28 14:59:40 +00:00
|
|
|
'use client';
|
|
|
|
|
|
2026-07-02 21:34:07 +01:00
|
|
|
import { useEffect, useRef, useState } from 'react';
|
2026-03-28 14:59:40 +00:00
|
|
|
import { METRIC_EXPLANATIONS } from '@/lib/metrics';
|
|
|
|
|
import styles from './MetricTooltip.module.css';
|
|
|
|
|
|
|
|
|
|
interface MetricTooltipProps {
|
|
|
|
|
metricKey?: string;
|
|
|
|
|
label?: string;
|
|
|
|
|
plain?: string;
|
|
|
|
|
detail?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function MetricTooltip({ metricKey, label, plain, detail }: MetricTooltipProps) {
|
|
|
|
|
const explanation = metricKey ? METRIC_EXPLANATIONS[metricKey] : undefined;
|
|
|
|
|
const tooltipLabel = label ?? explanation?.label;
|
|
|
|
|
const tooltipPlain = plain ?? explanation?.plain;
|
|
|
|
|
const tooltipDetail = detail ?? explanation?.detail;
|
|
|
|
|
|
2026-07-02 21:34:07 +01:00
|
|
|
// Tap/click/keyboard toggle so the definition is reachable on touch devices
|
|
|
|
|
// and by keyboard, not just mouse hover (hover still works on desktop).
|
|
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const wrapperRef = useRef<HTMLSpanElement>(null);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!open) return;
|
|
|
|
|
const dismiss = (e: Event) => {
|
|
|
|
|
if (wrapperRef.current && e.target instanceof Node && !wrapperRef.current.contains(e.target)) {
|
|
|
|
|
setOpen(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
const onKey = (e: KeyboardEvent) => {
|
|
|
|
|
if (e.key === 'Escape') setOpen(false);
|
|
|
|
|
};
|
|
|
|
|
document.addEventListener('click', dismiss);
|
|
|
|
|
document.addEventListener('keydown', onKey);
|
|
|
|
|
return () => {
|
|
|
|
|
document.removeEventListener('click', dismiss);
|
|
|
|
|
document.removeEventListener('keydown', onKey);
|
|
|
|
|
};
|
|
|
|
|
}, [open]);
|
|
|
|
|
|
2026-03-28 14:59:40 +00:00
|
|
|
if (!tooltipPlain) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
2026-07-02 21:34:07 +01:00
|
|
|
<span className={styles.wrapper} ref={wrapperRef}>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className={styles.icon}
|
|
|
|
|
aria-expanded={open}
|
|
|
|
|
aria-label={`What does ${tooltipLabel ?? 'this metric'} mean?`}
|
|
|
|
|
onClick={() => setOpen((o) => !o)}
|
|
|
|
|
>
|
|
|
|
|
ⓘ
|
|
|
|
|
</button>
|
|
|
|
|
<span className={`${styles.tooltip}${open ? ` ${styles.tooltipOpen}` : ''}`} role="tooltip">
|
2026-03-28 14:59:40 +00:00
|
|
|
{tooltipLabel && <span className={styles.tooltipLabel}>{tooltipLabel}</span>}
|
|
|
|
|
<span className={styles.tooltipPlain}>{tooltipPlain}</span>
|
|
|
|
|
{tooltipDetail && <span className={styles.tooltipDetail}>{tooltipDetail}</span>}
|
|
|
|
|
</span>
|
|
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
|
}
|