veza/apps/web/src/hooks/useCopyToClipboard.ts
senke 95e31646cb feat(ui): Sidebar refactor, premium skeletons, ContentFadeIn transitions
- Sidebar: useSidebarNavigation hook, ARIA, token-based layout
- Layout: lg:ml-main-expanded/collapsed (replace arbitrary ml-64)
- TrackCardSkeleton + PlaylistCardSkeleton: KŌDŌ tokens, min-heights for CLS
- ContentFadeIn: 200ms fade-in with --ease-out
- TrackGrid, PlaylistList, LibraryPage: integrate skeletons + fade-in
- Player: player-bar subcomponents, useAudioAnalyser
- Tests: TrackGrid wrapper (QueryClient, ToastProvider)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 22:51:51 +01:00

61 lines
1.8 KiB
TypeScript

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 };
}