1
- import type { ComponentProps , FC } from 'react' ;
1
+ import { useRef , type ComponentProps , type FC } from 'react' ;
2
2
import cn from 'clsx' ;
3
3
import { useHover } from '@/lib/hooks/use-hover' ;
4
4
import { useTimed } from '@/lib/hooks/use-timed' ;
@@ -7,28 +7,46 @@ import { CheckIcon, CopyIcon } from './icon';
7
7
export const Code : FC < ComponentProps < 'code' > > = ( { children, className, ...props } ) => {
8
8
const [ copied , startCopyTimer ] = useTimed ( 1500 ) ;
9
9
const [ ref , hovering ] = useHover ( ) ;
10
+ const codeRef = useRef < HTMLElement | null > ( null )
11
+ // in case this browser does not support this newer API...
12
+ const navigatorClipboardSupport = typeof navigator . clipboard ?. writeText === 'function' ;
10
13
return (
11
14
< span
12
15
ref = { ref }
13
- className = "relative flex items-center gap-2 break-all rounded-md border border-gray-600 bg-black p-4 pr-14 font-mono text-sm"
16
+ className = "relative flex items-center gap-2 break-all rounded-md border border-gray-600 bg-black p-4 pr-14 font-mono text-sm cursor-text"
17
+ // Make this element able to be focused by setting tabIndex.
18
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
19
+ tabIndex = { 0 }
20
+ onFocus = { ( ) => {
21
+ if ( codeRef . current ) {
22
+ const selection = window . getSelection ( ) ;
23
+ const range = document . createRange ( ) ;
24
+ range . setStart ( codeRef . current , 0 ) ;
25
+ range . setEnd ( codeRef . current , codeRef . current . childNodes . length ) ;
26
+ selection ?. removeAllRanges ( ) ;
27
+ selection ?. addRange ( range ) ;
28
+ }
29
+ } }
14
30
>
15
31
< code
16
- className = { cn ( 'whitespace-pre-line' , 'cursor-text' , className ) }
32
+ ref = { codeRef }
33
+ className = { cn ( 'whitespace-pre-line' , className ) }
17
34
// always show code blocks in ltr
18
35
dir = "ltr"
19
36
{ ...props }
20
37
>
21
38
{ children }
22
39
</ code >
23
40
< button
41
+ hidden = { ! navigatorClipboardSupport }
24
42
data-hovering = { hovering || copied }
25
43
className = "absolute right-3 top-2 cursor-pointer rounded-md border border-gray-600 p-2 opacity-0 hover:text-orange-600 data-[hovering=true]:opacity-100 data-[hovering=true]:transition-opacity"
26
44
onClick = { async ev => {
27
45
const value = children ?. valueOf ( ) . toString ( ) ;
28
46
if ( value ) {
29
47
ev . preventDefault ( ) ;
30
- startCopyTimer ( ) ;
31
48
await navigator . clipboard . writeText ( value ) ;
49
+ startCopyTimer ( ) ;
32
50
}
33
51
} }
34
52
title = "Copy to clipboard"
0 commit comments