Skip to content

Commit ed6098d

Browse files
committed
refactor(frontend): improve navbar animations
1 parent 22b7245 commit ed6098d

File tree

13 files changed

+237
-106
lines changed

13 files changed

+237
-106
lines changed

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"clsx": "^2.1.1",
1616
"graphql": "^16.9.0",
1717
"graphql-request": "^7.1.0",
18+
"motion": "^12.0.11",
1819
"next": "^15.1.4",
1920
"overlayscrollbars": "^2.10.1",
2021
"overlayscrollbars-react": "^0.5.6",

frontend/src/app/home/components/TrustedBy.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ const PartnersCarousel: React.FC<IPartnersCarousel> = ({ partners }) => (
6060
<div
6161
key={name}
6262
className={clsx(
63-
"relative mx-2 inline-block h-16 w-16 rounded-full bg-white lg:mx-10",
64-
"hover:cursor-pointer",
63+
"relative mx-2 inline-block h-16 w-16 rounded-full bg-white",
64+
"hover:cursor-pointer lg:mx-10",
6565
)}
6666
>
6767
<Image

frontend/src/components/Footer.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ const Footer: React.FC = async () => {
2121
}));
2222
return (
2323
<div>
24-
<div className={"bg-primary-purple py-16"}>
25-
<div className="flex flex-col gap-12 px-6">
24+
<div className={"bg-background-dark py-16 md:px-16 xl:px-32"}>
25+
<div
26+
className={clsx(
27+
"grid grid-flow-col grid-cols-2 grid-rows-2 gap-12 px-6",
28+
"lg:grid-cols-4 lg:grid-rows-1",
29+
)}
30+
>
2631
{sections.map(({ title, links }) => (
2732
<div key={title} className="flex flex-col gap-4">
28-
<h2 className="text-background-2">{title}</h2>
33+
<h2 className="text-primary-purple">{title}</h2>
2934
{links.map(({ name, url }) => (
3035
<CustomLink
3136
className={clsx(hoverScaleUp, "w-max")}
@@ -38,7 +43,7 @@ const Footer: React.FC = async () => {
3843
</div>
3944
))}
4045
</div>
41-
<hr className="mx-6 mb-6 mt-16 h-0.5 border-t-0 bg-secondary-purple" />
46+
<hr className="mx-6 mb-6 mt-16 h-0.5 border-t-0 bg-primary-purple" />
4247
<div className="flex items-center justify-center gap-8">
4348
{socials.map(({ name, icon_white: icon, url }) => (
4449
<CustomLink className={hoverScaleUp} key={name} href={url}>

frontend/src/components/Navbar/AppsDropdownContent/Card.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { Solution } from "@/queries/navbar";
77
interface ICard {
88
solution: Solution;
99
variant: "large" | "medium" | "small";
10+
onClick?: () => void;
1011
className?: string;
1112
}
1213

13-
const Card: React.FC<ICard> = ({ solution, variant, className }) => {
14+
const Card: React.FC<ICard> = ({ solution, variant, onClick, className }) => {
1415
return (
1516
<CustomLink
1617
href={solution?.url}
@@ -24,6 +25,7 @@ const Card: React.FC<ICard> = ({ solution, variant, className }) => {
2425
"lg:flex-row": variant === "medium" || variant === "small",
2526
},
2627
)}
28+
{...{ onClick }}
2729
>
2830
<Image
2931
src={solution?.logo_svg?.url}

frontend/src/components/Navbar/AppsDropdownContent/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import Card from "./Card";
77

88
interface AppsDropdownContentProps {
99
appsSection: AppsSection;
10+
closeFn?: () => void;
1011
}
1112

1213
const AppsDropdownContent: React.FC<AppsDropdownContentProps> = ({
1314
appsSection,
15+
closeFn,
1416
}) => (
1517
<div className="mx-auto py-6 lg:max-w-[1172px] lg:py-12">
1618
<div
@@ -25,6 +27,7 @@ const AppsDropdownContent: React.FC<AppsDropdownContentProps> = ({
2527
key={solution.solution_name}
2628
{...{ solution }}
2729
variant={getVariant(i)}
30+
onClick={closeFn}
2831
/>
2932
))}
3033
</div>

frontend/src/components/Navbar/DesktopNavigation.tsx

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from "react";
44

55
import clsx from "clsx";
6+
import { motion, AnimatePresence } from "motion/react";
67
import Image from "next/image";
78
import Link from "next/link";
89
import { useLockBodyScroll } from "react-use";
@@ -77,28 +78,40 @@ const DesktopNavigation: React.FC<DesktopNavigationProps> = ({
7778
/>
7879
</button>
7980

80-
{openDropdownIndex === index && navLink.is_dropdown ? (
81-
<div
82-
className="fixed inset-0 z-40 h-dvh bg-black bg-opacity-50"
83-
onClick={() => setOpenDropdownIndex(null)}
84-
>
85-
<div
86-
className={
87-
"animate-slideInFromTop relative mt-20 bg-background-2"
88-
}
89-
onClick={(e) => e.stopPropagation()}
81+
<AnimatePresence>
82+
{openDropdownIndex === index && navLink.is_dropdown ? (
83+
<motion.div
84+
className={clsx(
85+
"fixed inset-0 top-20 z-40 h-dvh bg-black/50",
86+
)}
87+
initial={{ opacity: 0 }}
88+
animate={{ opacity: 1 }}
89+
exit={{ opacity: 0 }}
90+
onClick={() => setOpenDropdownIndex(null)}
9091
>
91-
{navLink?.title === "Apps" ? (
92-
<AppsDropdownContent {...{ appsSection }} />
93-
) : null}
94-
{navLink?.title === "Resources" ? (
95-
<ResourcesDropdownContent
96-
{...{ resourceSections, socials }}
97-
/>
98-
) : null}
99-
</div>
100-
</div>
101-
) : null}
92+
<motion.div
93+
className="absolute top-0 w-full bg-background-2"
94+
initial={{ translateY: "-5%" }}
95+
animate={{ translateY: 0 }}
96+
exit={{ translateY: "-5%" }}
97+
onClick={(e) => e.stopPropagation()}
98+
>
99+
{navLink?.title === "Apps" ? (
100+
<AppsDropdownContent
101+
{...{ appsSection }}
102+
closeFn={() => setOpenDropdownIndex(null)}
103+
/>
104+
) : null}
105+
{navLink?.title === "Resources" ? (
106+
<ResourcesDropdownContent
107+
{...{ resourceSections, socials }}
108+
closeFn={() => setOpenDropdownIndex(null)}
109+
/>
110+
) : null}
111+
</motion.div>
112+
</motion.div>
113+
) : null}
114+
</AnimatePresence>
102115
</>
103116
)}
104117
</div>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"use client";
2+
3+
import React from "react";
4+
5+
import clsx from "clsx";
6+
import { motion, Variants } from "motion/react";
7+
8+
interface IHamburgerButton {
9+
className?: string;
10+
isOpen: boolean;
11+
}
12+
13+
const variants: Variants = {
14+
topClose: {
15+
top: [null, "50%", "50%"],
16+
translateY: [null, "-50%", "-50%"],
17+
rotate: [null, 0, "45deg"],
18+
transition: { duration: 0.3 },
19+
},
20+
botClose: {
21+
bottom: [null, "50%", "50%"],
22+
translateY: [null, "50%", "50%"],
23+
rotate: [null, 0, "-45deg"],
24+
transition: { duration: 0.3 },
25+
},
26+
topOpen: {
27+
top: ["50%", "50%", 0],
28+
translateY: ["-50%", 0, 0],
29+
rotate: ["45deg", "0deg", "0deg"],
30+
transition: { duration: 0.3 },
31+
},
32+
midOpen: {
33+
bottom: ["50%", "50%", "50%"],
34+
translateY: ["50%", "50%", "50%"],
35+
rotate: ["-45deg", "0deg", "0deg"],
36+
transition: { duration: 0.3 },
37+
},
38+
botOpen: {
39+
bottom: ["50%", "50%", 0],
40+
translateY: ["50%", 0, 0],
41+
rotate: ["-45deg", "0deg", "0deg"],
42+
transition: { duration: 0.3 },
43+
},
44+
};
45+
46+
const HamburgerButton: React.FC<IHamburgerButton> = ({ className, isOpen }) => {
47+
return (
48+
<div className={clsx("relative", className)}>
49+
<motion.div
50+
{...{ variants }}
51+
animate={isOpen ? "topClose" : "topOpen"}
52+
initial={false}
53+
className={clsx("absolute h-1 w-full bg-white transition")}
54+
/>
55+
<motion.div
56+
{...{ variants }}
57+
animate={isOpen ? "botClose" : "midOpen"}
58+
initial={false}
59+
className={clsx("absolute h-1 w-full bg-white transition")}
60+
/>
61+
<motion.div
62+
{...{ variants }}
63+
animate={isOpen ? "botClose" : "botOpen"}
64+
initial={false}
65+
className={clsx("absolute h-1 w-full bg-white transition")}
66+
/>
67+
</div>
68+
);
69+
};
70+
71+
export default HamburgerButton;

frontend/src/components/Navbar/MobileMenu.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface IMobileMenu {
2626
resourceSections: ResourceSection[];
2727
socials: Social[];
2828
navbarButton: NavbarButton;
29+
closeFn: () => void;
2930
className?: string;
3031
}
3132

@@ -36,6 +37,7 @@ const MobileMenu: React.FC<IMobileMenu> = ({
3637
resourceSections,
3738
socials,
3839
navbarButton,
40+
closeFn,
3941
className,
4042
}) => {
4143
const [openDropdownIndex, setOpenDropdownIndex] = useState<number | null>(
@@ -50,7 +52,7 @@ const MobileMenu: React.FC<IMobileMenu> = ({
5052
<div
5153
className={clsx(
5254
className,
53-
"fixed right-0 top-20 z-50 w-screen overflow-y-auto rounded-b-lg",
55+
"z-50 w-screen overflow-y-auto rounded-b-lg",
5456
"bg-background-2 p-6 shadow-lg",
5557
)}
5658
>
@@ -92,10 +94,10 @@ const MobileMenu: React.FC<IMobileMenu> = ({
9294
)}
9395
>
9496
{navLink?.title === "Apps" ? (
95-
<AppsDropdownContent {...{ appsSection }} />
97+
<AppsDropdownContent {...{ appsSection, closeFn }} />
9698
) : navLink?.title === "Resources" ? (
9799
<ResourcesDropdownContent
98-
{...{ resourceSections, socials }}
100+
{...{ resourceSections, socials, closeFn }}
99101
/>
100102
) : null}
101103
</div>

frontend/src/components/Navbar/ResourcesDropdownContent/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { ResourceSection, Social } from "@/queries/navbar";
77
interface ResourcesDropdownContentProps {
88
resourceSections: ResourceSection[];
99
socials: Social[];
10+
closeFn: () => void;
1011
}
1112

1213
const ResourcesDropdownContent: React.FC<ResourcesDropdownContentProps> = ({
1314
resourceSections,
1415
socials,
16+
closeFn,
1517
}) => {
1618
return (
1719
<div className="flex flex-col items-center bg-background-2">
@@ -40,7 +42,9 @@ const ResourcesDropdownContent: React.FC<ResourcesDropdownContentProps> = ({
4042
"w-max transform transition duration-75 hover:scale-[1.01]"
4143
}
4244
>
43-
<CustomLink href={link.url}>{link.name}</CustomLink>
45+
<CustomLink href={link.url} onClick={closeFn}>
46+
{link.name}
47+
</CustomLink>
4448
</li>
4549
))}
4650
</ul>
@@ -58,6 +62,7 @@ const ResourcesDropdownContent: React.FC<ResourcesDropdownContentProps> = ({
5862
key={social.name}
5963
href={social.url}
6064
className={"transform transition duration-75 hover:scale-[1.10]"}
65+
onClick={closeFn}
6166
>
6267
<Image
6368
src={social.icon.url}

0 commit comments

Comments
 (0)