import * as React from 'react'; import { toast } from '@/components/toast'; import { useMessages } from '~/i18n'; /** * Copy text, and say so twice. * * The `toast()` is the feedback that carries: the matrix's per-cell button is * 24px, revealed on hover, and often outside where you are looking by the time * it fires. The returned flag drives the icon flip, which stays because it is * the affordance directly under the cursor — and it is per hook call rather * than per page, so the icon gallery can put four buttons side by side without * one press lighting all four. * * Its own file rather than `copy.tsx` because it has two consumers: the buttons * there, and `chrome/ui/share.tsx`. */ const FLIP_MS = 1200; export function useCopy( /** * What the toast says. Defaults to the plain "Copied" every code button wants; * `chrome/ui/share.tsx` passes its own, because a link landing on the clipboard * under the word "Copied" says nothing about *what* was copied. */ message?: string ): [boolean, (text: string) => void] { const [copied, setCopied] = React.useState(false); const timer = React.useRef(undefined); const m = useMessages(); React.useEffect(() => () => window.clearTimeout(timer.current), []); const copy = React.useCallback( (text: string) => { void navigator.clipboard.writeText(text); toast.success(message ?? m.common.copied); setCopied(true); window.clearTimeout(timer.current); timer.current = window.setTimeout(() => setCopied(false), FLIP_MS); }, [m, message] ); return [copied, copy]; }