Skip to content

Commit 37d562f

Browse files
committed
Disable hamburger while animating
1 parent d5b0c16 commit 37d562f

File tree

1 file changed

+44
-7
lines changed

1 file changed

+44
-7
lines changed

src/components/Footer/Hamburger.component.tsx

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useCallback } from 'react';
1+
import { useState, useEffect, useCallback, useRef } from 'react';
22
import Link from 'next/link';
33

44
import FadeLeftToRight from '@/components/Animations/FadeLeftToRight.component';
@@ -22,34 +22,69 @@ const opacityFull = 'opacity-100 group-hover:opacity-100';
2222
const Hamburger = () => {
2323
const [isExpanded, setisExpanded] = useState(false);
2424
const [hidden, setHidden] = useState('invisible');
25+
const [isAnimating, setIsAnimating] = useState(false);
26+
const animationTimeoutRef = useRef<NodeJS.Timeout | null>(null);
2527

2628
useEffect(() => {
2729
if (isExpanded) {
2830
setHidden('');
31+
setIsAnimating(true);
32+
33+
// Clear any existing timeout
34+
if (animationTimeoutRef.current) {
35+
clearTimeout(animationTimeoutRef.current);
36+
}
37+
38+
// Set a timeout for the animation duration
39+
animationTimeoutRef.current = setTimeout(() => {
40+
setIsAnimating(false);
41+
}, 1000); // Match this with the animation duration
2942
} else {
30-
setTimeout(() => {
43+
setIsAnimating(true);
44+
45+
// Clear any existing timeout
46+
if (animationTimeoutRef.current) {
47+
clearTimeout(animationTimeoutRef.current);
48+
}
49+
50+
// Set a timeout for the animation duration and hiding
51+
animationTimeoutRef.current = setTimeout(() => {
3152
setHidden('invisible');
32-
}, 1000);
53+
setIsAnimating(false);
54+
}, 1000); // Match this with the animation duration
3355
}
56+
57+
// Cleanup function to clear timeout when component unmounts
58+
return () => {
59+
if (animationTimeoutRef.current) {
60+
clearTimeout(animationTimeoutRef.current);
61+
}
62+
};
3463
}, [isExpanded]);
3564

3665
const handleMobileMenuClick = useCallback(() => {
66+
// Prevent clicks during animation
67+
if (isAnimating) {
68+
return;
69+
}
70+
3771
/**
3872
* Anti-pattern: setisExpanded(!isExpanded)
3973
* Even if your state updates are batched and multiple updates to the enabled/disabled state are made together
4074
* each update will rely on the correct previous state so that you always end up with the result you expect.
4175
*/
4276
setisExpanded((prevExpanded) => !prevExpanded);
43-
}, [setisExpanded]);
77+
}, [setisExpanded, isAnimating]);
4478

4579
return (
4680
<div className="z-50 md:hidden lg:hidden xl:hidden bg-blue-800">
4781
<button
48-
className="flex flex-col w-16 rounded justify-center items-center group"
82+
className={`flex flex-col w-16 rounded justify-center items-center group ${isAnimating ? 'cursor-not-allowed' : 'cursor-pointer'}`}
4983
data-cy="hamburger"
5084
data-testid="hamburger"
5185
onClick={handleMobileMenuClick}
5286
aria-expanded={isExpanded}
87+
disabled={isAnimating}
5388
type="button"
5489
>
5590
<span className="sr-only text-white text-2xl">Hamburger</span>
@@ -95,11 +130,13 @@ const Hamburger = () => {
95130
<span
96131
className="text-xl inline-block px-4 py-2 no-underline hover:text-black hover:underline"
97132
onClick={() => {
98-
setisExpanded((prevExpanded) => !prevExpanded);
133+
if (!isAnimating) {
134+
setisExpanded((prevExpanded) => !prevExpanded);
135+
}
99136
}}
100137
onKeyDown={(event) => {
101138
// 'Enter' key or 'Space' key
102-
if (event.key === 'Enter' || event.key === ' ') {
139+
if ((event.key === 'Enter' || event.key === ' ') && !isAnimating) {
103140
setisExpanded((prevExpanded) => !prevExpanded);
104141
}
105142
}}

0 commit comments

Comments
 (0)