veza/apps/web/src/hooks/useCopyToClipboard.ts

62 lines
1.8 KiB
TypeScript
Raw Normal View History

import { useState, useCallback } from 'react';
/**
* Reusable hook for copy-to-clipboard with visual feedback.
*
* Returns a `copied` boolean (true for `resetDelay` ms after a successful
* copy) and a `copy` async function.
*
* @example
* ```tsx
* const { copied, copy } = useCopyToClipboard();
*
* <Button onClick={() => copy(someText)}>
* {copied ? <Check className="h-4 w-4 text-success" /> : <Copy className="h-4 w-4" />}
* {copied ? 'Copied!' : 'Copy'}
* </Button>
* ```
*/
export function useCopyToClipboard(resetDelay = 2000) {
const [copied, setCopied] = useState(false);
const copy = useCallback(
async (text: string) => {
if (!text || typeof text !== 'string') return false;
try {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), resetDelay);
return true;
}
} catch {
// clipboard API failed (e.g. non-secure context, permission)
}
// Fallback for http or when clipboard API is unavailable
try {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.top = '0';
document.body.appendChild(textarea);
textarea.select();
const ok = document.execCommand('copy');
document.body.removeChild(textarea);
if (ok) {
setCopied(true);
setTimeout(() => setCopied(false), resetDelay);
return true;
}
} catch {
// ignore
}
return false;
},
[resetDelay],
);
return { copied, copy };
}