11"use client" ;
22
3- import { useCallback , useState } from "react" ;
4- import { Check , Copy } from "lucide-react" ;
3+ import { useCallback , useEffect , useRef , useState } from "react" ;
4+ import { Check , Copy , X } from "lucide-react" ;
55import { colorizeSnippet } from "@/components/icons/shared/syntax-highlight" ;
66import type { DocsSnippet } from "@/lib/docs-content" ;
77
8+ type CopyStatus = "idle" | "copied" | "failed" ;
9+
810export function DocsCodeBlock ( { snippet } : { snippet : DocsSnippet } ) {
9- const [ copied , setCopied ] = useState ( false ) ;
11+ const [ status , setStatus ] = useState < CopyStatus > ( "idle" ) ;
12+ const resetTimer = useRef < ReturnType < typeof setTimeout > | null > ( null ) ;
13+
14+ useEffect ( ( ) => ( ) => {
15+ if ( resetTimer . current ) clearTimeout ( resetTimer . current ) ;
16+ } , [ ] ) ;
1017
1118 const handleCopy = useCallback ( async ( ) => {
12- await navigator . clipboard . writeText ( snippet . code ) ;
13- setCopied ( true ) ;
14- setTimeout ( ( ) => setCopied ( false ) , 1500 ) ;
19+ if ( resetTimer . current ) clearTimeout ( resetTimer . current ) ;
20+ try {
21+ await navigator . clipboard . writeText ( snippet . code ) ;
22+ setStatus ( "copied" ) ;
23+ } catch {
24+ setStatus ( "failed" ) ;
25+ }
26+ resetTimer . current = setTimeout ( ( ) => setStatus ( "idle" ) , 1500 ) ;
1527 } , [ snippet . code ] ) ;
1628
1729 return (
@@ -22,13 +34,21 @@ export function DocsCodeBlock({ snippet }: { snippet: DocsSnippet }) {
2234 type = "button"
2335 onClick = { handleCopy }
2436 className = { `flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium transition-colors ${
25- copied
37+ status === " copied"
2638 ? "bg-green-500/10 text-green-600 dark:text-green-400"
27- : "bg-orange-500/15 text-orange-500 hover:bg-orange-500/25 hover:text-orange-400"
39+ : status === "failed"
40+ ? "bg-red-500/10 text-red-600 dark:text-red-400"
41+ : "bg-orange-500/15 text-orange-500 hover:bg-orange-500/25 hover:text-orange-400"
2842 } `}
2943 >
30- { copied ? < Check className = "h-3 w-3" /> : < Copy className = "h-3 w-3" /> }
31- { copied ? "Copied" : "Copy" }
44+ { status === "copied" ? (
45+ < Check className = "h-3 w-3" />
46+ ) : status === "failed" ? (
47+ < X className = "h-3 w-3" />
48+ ) : (
49+ < Copy className = "h-3 w-3" />
50+ ) }
51+ { status === "copied" ? "Copied" : status === "failed" ? "Couldn't copy" : "Copy" }
3252 </ button >
3353 </ div >
3454 < div className = "bg-zinc-950" >
0 commit comments