1
- import React , { useCallback , useEffect , useRef , useState } from "react" ;
1
+ import React , { useCallback , useEffect , useLayoutEffect , useRef , useState } from "react" ;
2
2
3
3
import { AnimatePresence , motion } from "motion/react" ;
4
4
import { useTranslation } from "react-i18next" ;
@@ -20,16 +20,15 @@ export const Toast = () => {
20
20
const [ hoveredToasts , setHoveredToasts ] = useState < { [ id : string ] : boolean } > ( { } ) ;
21
21
22
22
const timerRefs = useRef < { [ key : string ] : NodeJS . Timeout } > ( { } ) ;
23
+ const toastRefs = useRef < { [ key : string ] : HTMLDivElement | null } > ( { } ) ;
23
24
24
- const startTimer = useCallback (
25
- ( id : string ) => {
26
- if ( timerRefs . current [ id ] ) {
27
- clearTimeout ( timerRefs . current [ id ] ) ;
28
- }
29
- timerRefs . current [ id ] = setTimeout ( ( ) => removeToast ( id ) , 3000 ) ;
30
- } ,
31
- [ removeToast ]
32
- ) ;
25
+ const startTimer = useCallback ( ( id : string ) => {
26
+ if ( timerRefs . current [ id ] ) {
27
+ clearTimeout ( timerRefs . current [ id ] ) ;
28
+ }
29
+ timerRefs . current [ id ] = setTimeout ( ( ) => removeToast ( id ) , 3000 ) ;
30
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31
+ } , [ ] ) ;
33
32
34
33
const handleMouseEnter = useCallback ( ( id : string ) => {
35
34
setHoveredToasts ( ( prev ) => ( { ...prev , [ id ] : true } ) ) ;
@@ -38,30 +37,30 @@ export const Toast = () => {
38
37
}
39
38
} , [ ] ) ;
40
39
41
- const handleMouseLeave = useCallback (
42
- ( id : string ) => {
43
- setHoveredToasts ( ( prev ) => ( { ...prev , [ id ] : false } ) ) ;
44
- startTimer ( id ) ;
45
- } ,
46
- [ startTimer ]
47
- ) ;
40
+ const handleMouseLeave = useCallback ( ( id : string ) => {
41
+ setHoveredToasts ( ( prev ) => ( { ...prev , [ id ] : false } ) ) ;
42
+ startTimer ( id ) ;
43
+ // eslint-disable-next-line react-hooks/exhaustive-deps
44
+ } , [ ] ) ;
48
45
49
- useEffect ( ( ) => {
46
+ useLayoutEffect ( ( ) => {
50
47
const topToasts = toasts . filter ( ( t ) => t . position === "top-right" ) ;
51
- const bottomToasts = toasts . filter ( ( t ) => t . position !== "top-right ") ;
48
+ const bottomToasts = toasts . filter ( ( t ) => t . position === "default ") ;
52
49
53
50
const newPositions : { [ key : string ] : { bottom ?: number ; top ?: number } } = { } ;
54
- let topOffset = topToasts [ 0 ] ?. offset || 80 ;
55
- let bottomOffset = bottomToasts [ 0 ] ?. offset || 15 ;
56
51
52
+ let topOffset = topToasts [ 0 ] ?. offset || 15 ;
57
53
topToasts . forEach ( ( toast ) => {
58
54
newPositions [ toast . id ] = { top : topOffset } ;
59
- topOffset += 80 ;
55
+ const height = toastRefs . current [ toast . id ] ?. offsetHeight || 80 ;
56
+ topOffset += height + 10 ;
60
57
} ) ;
61
58
59
+ let bottomOffset = bottomToasts [ 0 ] ?. offset || 15 ;
62
60
bottomToasts . forEach ( ( toast ) => {
63
61
newPositions [ toast . id ] = { bottom : bottomOffset } ;
64
- bottomOffset += 95 ;
62
+ const height = toastRefs . current [ toast . id ] ?. offsetHeight || 95 ;
63
+ bottomOffset += height + 10 ;
65
64
} ) ;
66
65
67
66
setPositions ( newPositions ) ;
@@ -75,6 +74,7 @@ export const Toast = () => {
75
74
} ) ;
76
75
77
76
return ( ) => {
77
+ // eslint-disable-next-line react-hooks/exhaustive-deps
78
78
const timers = { ...timerRefs . current } ;
79
79
Object . values ( timers ) . forEach ( clearTimeout ) ;
80
80
} ;
@@ -127,6 +127,7 @@ export const Toast = () => {
127
127
key = { id }
128
128
onMouseEnter = { ( ) => handleMouseEnter ( id ) }
129
129
onMouseLeave = { ( ) => handleMouseLeave ( id ) }
130
+ ref = { ( el ) => ( toastRefs . current [ id ] = el ) }
130
131
style = { {
131
132
...positions [ id ] ,
132
133
transition : "all 0.2s ease-out" ,
0 commit comments