Skip to content

Commit 63f5629

Browse files
authored
Merge pull request #1465 from w3bdesign/develop
Disable hamburger while animating
2 parents d5b0c16 + 6d410f1 commit 63f5629

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

src/components/Footer/Hamburger.component.tsx

+49-7
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,71 @@ 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<ReturnType<typeof setTimeout> | null>(
27+
null,
28+
);
2529

2630
useEffect(() => {
2731
if (isExpanded) {
2832
setHidden('');
33+
setIsAnimating(true);
34+
35+
// Clear any existing timeout
36+
if (animationTimeoutRef.current) {
37+
clearTimeout(animationTimeoutRef.current);
38+
}
39+
40+
// Set a timeout for the animation duration
41+
animationTimeoutRef.current = setTimeout(() => {
42+
setIsAnimating(false);
43+
}, 1000); // Match this with the animation duration
2944
} else {
30-
setTimeout(() => {
45+
setIsAnimating(true);
46+
47+
// Clear any existing timeout
48+
if (animationTimeoutRef.current) {
49+
clearTimeout(animationTimeoutRef.current);
50+
}
51+
52+
// Set a timeout for the animation duration and hiding
53+
animationTimeoutRef.current = setTimeout(() => {
3154
setHidden('invisible');
32-
}, 1000);
55+
setIsAnimating(false);
56+
}, 1000); // Match this with the animation duration
3357
}
58+
59+
// Cleanup function to clear timeout when component unmounts
60+
return () => {
61+
if (animationTimeoutRef.current) {
62+
clearTimeout(animationTimeoutRef.current);
63+
}
64+
};
3465
}, [isExpanded]);
3566

3667
const handleMobileMenuClick = useCallback(() => {
68+
// Prevent clicks during animation
69+
if (isAnimating) {
70+
return;
71+
}
72+
3773
/**
3874
* Anti-pattern: setisExpanded(!isExpanded)
3975
* Even if your state updates are batched and multiple updates to the enabled/disabled state are made together
4076
* each update will rely on the correct previous state so that you always end up with the result you expect.
4177
*/
4278
setisExpanded((prevExpanded) => !prevExpanded);
43-
}, [setisExpanded]);
79+
}, [setisExpanded, isAnimating]);
4480

4581
return (
4682
<div className="z-50 md:hidden lg:hidden xl:hidden bg-blue-800">
4783
<button
48-
className="flex flex-col w-16 rounded justify-center items-center group"
84+
className={`flex flex-col w-16 rounded justify-center items-center group ${isAnimating ? 'cursor-not-allowed' : 'cursor-pointer'}`}
4985
data-cy="hamburger"
5086
data-testid="hamburger"
5187
onClick={handleMobileMenuClick}
5288
aria-expanded={isExpanded}
89+
disabled={isAnimating}
5390
type="button"
5491
>
5592
<span className="sr-only text-white text-2xl">Hamburger</span>
@@ -95,11 +132,16 @@ const Hamburger = () => {
95132
<span
96133
className="text-xl inline-block px-4 py-2 no-underline hover:text-black hover:underline"
97134
onClick={() => {
98-
setisExpanded((prevExpanded) => !prevExpanded);
135+
if (!isAnimating) {
136+
setisExpanded((prevExpanded) => !prevExpanded);
137+
}
99138
}}
100139
onKeyDown={(event) => {
101140
// 'Enter' key or 'Space' key
102-
if (event.key === 'Enter' || event.key === ' ') {
141+
if (
142+
(event.key === 'Enter' || event.key === ' ') &&
143+
!isAnimating
144+
) {
103145
setisExpanded((prevExpanded) => !prevExpanded);
104146
}
105147
}}

0 commit comments

Comments
 (0)