@@ -4,9 +4,12 @@ import {
4
4
useEffect ,
5
5
useImperativeHandle ,
6
6
useMemo ,
7
+ useRef ,
7
8
useState ,
8
9
} from 'react'
9
10
import clsx from 'clsx'
11
+ import { AnimatePresence , m } from 'framer-motion'
12
+ import type { Variants } from 'framer-motion'
10
13
import type { PropsWithChildren } from 'react'
11
14
12
15
import { getViewport } from '~/atoms/hooks'
@@ -28,6 +31,20 @@ interface Props {
28
31
renderedHTML ?: string
29
32
}
30
33
34
+ const copyIconVariants : Variants = {
35
+ initial : {
36
+ opacity : 1 ,
37
+ scale : 1 ,
38
+ } ,
39
+ animate : {
40
+ opacity : 1 ,
41
+ scale : 1 ,
42
+ } ,
43
+ exit : {
44
+ opacity : 0 ,
45
+ scale : 0 ,
46
+ } ,
47
+ }
31
48
export const ShikiHighLighterWrapper = forwardRef <
32
49
HTMLDivElement ,
33
50
PropsWithChildren <
@@ -43,9 +60,16 @@ export const ShikiHighLighterWrapper = forwardRef<
43
60
attrs,
44
61
} = props
45
62
63
+ const [ copied , setCopied ] = useState ( false )
64
+ const copiedTimerRef = useRef < any > ( )
46
65
const handleCopy = useCallback ( ( ) => {
47
66
navigator . clipboard . writeText ( value )
48
- toast . success ( '已复制到剪贴板' )
67
+ setCopied ( true )
68
+
69
+ clearTimeout ( copiedTimerRef . current )
70
+ copiedTimerRef . current = setTimeout ( ( ) => {
71
+ setCopied ( false )
72
+ } , 2000 )
49
73
} , [ value ] )
50
74
51
75
const [ codeBlockRef , setCodeBlockRef ] = useState < HTMLDivElement | null > ( null )
@@ -122,16 +146,31 @@ export const ShikiHighLighterWrapper = forwardRef<
122
146
< MotionButtonBase
123
147
onClick = { handleCopy }
124
148
className = { clsx (
125
- 'center absolute right-2 top-2 z-[1] flex text-xs' ,
149
+ 'absolute right-2 top-2 z-[1] flex text-xs center ' ,
126
150
'rounded-md border border-accent/5 bg-accent/80 p-1.5 text-white backdrop-blur duration-200' ,
127
151
'opacity-0 group-hover:opacity-100' ,
128
152
filename && '!top-12' ,
129
153
) }
130
154
>
131
- < i className = "icon-[mingcute--copy-2-fill] size-4" />
155
+ < AnimatePresence mode = "wait" >
156
+ { copied ? (
157
+ < m . i
158
+ key = { 'copied' }
159
+ className = "icon-[mingcute--check-line] size-4"
160
+ { ...copyIconVariants }
161
+ />
162
+ ) : (
163
+ < m . i
164
+ key = { 'copy' }
165
+ className = "icon-[mingcute--copy-2-fill] size-4"
166
+ { ...copyIconVariants }
167
+ />
168
+ ) }
169
+ </ AnimatePresence >
132
170
</ MotionButtonBase >
133
171
< AutoResizeHeight spring className = "relative" >
134
172
< div
173
+ onCopy = { stopPropagation }
135
174
ref = { setCodeBlockRef }
136
175
className = { clsxm (
137
176
'relative max-h-[50vh] w-full overflow-auto' ,
0 commit comments