| 1 | import { useEffect, useId, useRef, useState } from "react"; |
| 2 | import { Info } from "lucide-react"; |
| 3 | import { AnchoredPopover } from "./AnchoredPopover"; |
| 4 | import { useProviderT } from "../lib/providerSettingsLocale"; |
| 5 | |
| 6 | export function ModelSettingHelp({ label, text }: { label: string; text: string }) { |
| 7 | const t = useProviderT(); |
| 8 | const id = useId(); |
| 9 | const anchor = useRef<HTMLButtonElement>(null); |
| 10 | const timer = useRef<ReturnType<typeof setTimeout> | null>(null); |
| 11 | const pinned = useRef(false); |
| 12 | const [open, setOpen] = useState(false); |
| 13 | const clear = () => { |
| 14 | if (timer.current !== null) clearTimeout(timer.current); |
| 15 | timer.current = null; |
| 16 | }; |
| 17 | const show = () => { clear(); setOpen(true); }; |
| 18 | const close = () => { clear(); pinned.current = false; setOpen(false); }; |
| 19 | const leave = () => { |
| 20 | clear(); |
| 21 | if (!pinned.current) timer.current = setTimeout(() => setOpen(false), 150); |
| 22 | }; |
| 23 | useEffect(() => () => { if (timer.current !== null) clearTimeout(timer.current); }, []); |
| 24 | return <span className="model-setting-label"> |
| 25 | <span>{label}</span> |
| 26 | <button ref={anchor} type="button" className="model-setting-help" |
| 27 | aria-label={t("providerUI.modelHelpLabel", { name: label })} |
| 28 | aria-expanded={open} aria-controls={open ? id : undefined} aria-describedby={open ? id : undefined} |
| 29 | onMouseEnter={show} onMouseLeave={leave} onFocus={show} onBlur={leave} |
| 30 | onClick={() => { if (pinned.current) close(); else { pinned.current = true; show(); } }}> |
| 31 | <Info size={14} aria-hidden="true" /> |
| 32 | </button> |
| 33 | <AnchoredPopover open={open} anchorRef={anchor} onClose={close} className="model-setting-help-popover" placement="bottom"> |
| 34 | <div id={id} role="tooltip" onMouseEnter={show} onMouseLeave={leave}> |
| 35 | <strong>{label}</strong> |
| 36 | {text.split("\n\n").map((paragraph, index) => <p key={index}>{paragraph}</p>)} |
| 37 | </div> |
| 38 | </AnchoredPopover> |
| 39 | </span>; |
| 40 | } |
| 41 |