Skip to content

Commit 2af8b25

Browse files
authored
Merge pull request #75 from kleros/refactor(frontend)/navbar
Refactor(frontend): Navbar and animations
2 parents edda820 + 712320b commit 2af8b25

File tree

16 files changed

+373
-249
lines changed

16 files changed

+373
-249
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/app/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from "react";
22

3+
import clsx from "clsx";
34
import { Urbanist } from "next/font/google";
45

56
import Footer from "@/components/Footer";
@@ -25,7 +26,7 @@ export default async function RootLayout({
2526
return (
2627
<html lang="en" data-overlayscrollbars-initialize>
2728
<OverlayScrollbarBody className="bg-background-1 antialiased">
28-
<main className={urbanist.className}>
29+
<main className={clsx(urbanist.className)}>
2930
<Navbar {...{ navbarData }} />
3031
{children}
3132
<Footer />

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/IntegrateSection/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ const IntegrateSection: React.FC = async () => {
2323
<p className="text-base text-secondary-text lg:mb-4 lg:text-lg">
2424
{integrateData.description}
2525
</p>
26-
<AppsDropdownContent appsSection={integrateData.appsSection} />
26+
<AppsDropdownContent
27+
appsSection={integrateData.appsSection}
28+
className="pt-8 lg:pt-16"
29+
/>
2730
<LearnMore
2831
background={integrateData.getInTouchSection.background}
2932
title={integrateData.header}
3033
button={integrateData.arrowLink}
31-
className="!mt-16 lg:!mt-4"
34+
className="!mt-12 lg:!mt-16"
3235
/>
3336
<ExternalLink
3437
className="mt-12 flex-wrap justify-center text-center lg:mt-16"

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

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,70 +3,56 @@ import Image from "next/image";
33

44
import CustomLink from "@/components/CustomLink";
55
import { Solution } from "@/queries/navbar";
6-
import { hoverEffect } from "@/styles";
76

8-
const cardBaseStyle = clsx(
9-
"bg-background-2 rounded-2xl border border-stroke text-wrap",
10-
"p-4 flex gap-4 h-full lg:items-start",
11-
);
12-
const headerTextStyle = clsx("text-primary-purple font-bold text-base");
13-
const descriptionTextStyle = clsx(
14-
"text-secondary-text text-base leading-tight",
15-
);
16-
17-
interface CardProps {
7+
interface ICard {
188
solution: Solution;
199
variant: "large" | "medium" | "small";
10+
onClick?: () => void;
2011
className?: string;
2112
}
2213

23-
const Card: React.FC<CardProps> = ({ solution, variant, className }) => {
14+
const Card: React.FC<ICard> = ({ solution, variant, onClick, className }) => {
2415
return (
2516
<CustomLink
26-
key={solution?.solution_name}
2717
href={solution?.url}
2818
className={clsx(
29-
cardBaseStyle,
30-
hoverEffect,
31-
"flex-row",
32-
"w-full",
19+
className,
20+
"flex h-full w-full transform flex-row gap-4 text-wrap rounded-2xl",
21+
"border border-stroke bg-background-2 p-4 transition duration-100",
22+
"hover:scale-[1.01] lg:items-start",
3323
{
34-
"xl:flex-col xl:pb-8": variant === "large",
35-
"xl:flex-row": variant === "medium" || variant === "small",
36-
"xl:w-[380px]": true,
24+
"lg:flex-col": variant === "large",
25+
"lg:flex-row": variant === "medium" || variant === "small",
3726
},
38-
className,
3927
)}
28+
{...{ onClick }}
4029
>
4130
<Image
4231
src={solution?.logo_svg?.url}
4332
alt={solution?.solution_name}
4433
width={64}
4534
height={64}
4635
/>
47-
<div>
48-
<h2 className={headerTextStyle}>{solution?.solution_name}</h2>
36+
<div className="space-y-2">
37+
<h2 className="text-base font-medium text-primary-purple">
38+
{solution?.solution_name}
39+
</h2>
4940

5041
<h3
51-
className={clsx(
52-
headerTextStyle,
53-
"mt-1 leading-tight text-primary-text",
54-
{
55-
"font-normal": variant === "small",
56-
"xl:text-xl": variant === "large",
57-
"xl:text-lg": variant === "medium",
58-
"xl:text-base": variant === "small",
59-
},
60-
)}
42+
className={clsx("text-base font-medium text-primary-text", {
43+
"md:text-lg lg:text-xl": variant === "large",
44+
"md:text-lg": variant === "medium",
45+
"md:text-lg lg:text-base": variant === "small",
46+
})}
6147
>
6248
{solution?.header_title}
6349
</h3>
6450

6551
{solution?.header_description && (
6652
<p
67-
className={clsx(descriptionTextStyle, "mt-2", {
53+
className={clsx("mt-2 text-base text-secondary-text", {
6854
hidden: true,
69-
"lg:block": variant !== "small",
55+
"md:block": variant !== "small",
7056
})}
7157
>
7258
{solution?.header_description}

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

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,56 +7,51 @@ import Card from "./Card";
77

88
interface AppsDropdownContentProps {
99
appsSection: AppsSection;
10+
closeFn?: () => void;
11+
className?: string;
1012
}
1113

1214
const AppsDropdownContent: React.FC<AppsDropdownContentProps> = ({
1315
appsSection,
14-
}) => {
15-
const columnOne = appsSection?.solutions.slice(0, 1);
16-
const columnTwo = appsSection?.solutions.slice(1, 3);
17-
const columnThree = appsSection?.solutions.slice(3, 7);
18-
19-
return (
16+
closeFn,
17+
className,
18+
}) => (
19+
<div className={clsx(className, "mx-auto lg:max-w-[1172px]")}>
2020
<div
2121
className={clsx(
22-
"mt-4 grid w-full gap-3 py-2 xl:mt-0 xl:w-[1172px] xl:gap-4 xl:py-12",
23-
"mx-auto grid-cols-1 bg-background-2 xl:grid-cols-3",
22+
"grid w-full grid-cols-1 gap-3 bg-background-2 lg:grid-flow-col",
23+
"lg:grid-cols-3 lg:grid-rows-4 lg:gap-4",
2424
)}
2525
>
26-
<div className="flex flex-col gap-3 xl:gap-4">
27-
{columnOne?.map((solution) => (
28-
<Card
29-
key={solution.solution_name}
30-
solution={solution}
31-
variant="large"
32-
/>
33-
))}
34-
</div>
35-
<div className="flex flex-col gap-3 xl:gap-4">
36-
{columnTwo?.map((solution) => (
37-
<Card
38-
key={solution.solution_name}
39-
solution={solution}
40-
variant="medium"
41-
/>
42-
))}
43-
</div>
44-
<div className="flex flex-col gap-3 xl:gap-4">
45-
{columnThree?.map((solution) => (
46-
<Card
47-
key={solution.solution_name}
48-
solution={solution}
49-
variant="small"
50-
/>
51-
))}
52-
<ExternalLink
53-
url={appsSection?.arrowLink.link.url}
54-
text={appsSection?.arrowLink.text}
55-
className="self-start xl:self-end"
26+
{appsSection?.solutions.map((solution, i) => (
27+
<Card
28+
className={getRowSpan(i)}
29+
key={solution.solution_name}
30+
{...{ solution }}
31+
variant={getVariant(i)}
32+
onClick={closeFn}
5633
/>
57-
</div>
34+
))}
5835
</div>
59-
);
36+
<ExternalLink
37+
className="ml-auto mt-2 w-max"
38+
url={appsSection?.arrowLink.link.url}
39+
text={appsSection?.arrowLink.text}
40+
/>
41+
</div>
42+
);
43+
44+
const getVariant = (index: number): "large" | "medium" | "small" => {
45+
if (index === 0) return "large";
46+
if (index < 3) return "medium";
47+
return "small";
48+
};
49+
50+
const getRowSpan = (
51+
index: number,
52+
): "lg:row-span-4" | "lg:row-span-2" | undefined => {
53+
if (index === 0) return "lg:row-span-4";
54+
if (index < 3) return "lg:row-span-2";
6055
};
6156

6257
export default AppsDropdownContent;

frontend/src/components/Navbar/DesktopNavigation.tsx

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import { useState } from "react";
44

5+
import clsx from "clsx";
6+
import { motion, AnimatePresence } from "motion/react";
57
import Image from "next/image";
68
import Link from "next/link";
79
import { useLockBodyScroll } from "react-use";
@@ -51,6 +53,7 @@ const DesktopNavigation: React.FC<DesktopNavigationProps> = ({
5153
className={`${
5254
pathname === `/${navLink.path_name}` ? "font-bold" : ""
5355
}`}
56+
onClick={() => setOpenDropdownIndex(null)}
5457
>
5558
{navLink.title}
5659
</Link>
@@ -70,30 +73,51 @@ const DesktopNavigation: React.FC<DesktopNavigationProps> = ({
7073
alt="Down Arrow"
7174
width={12}
7275
height={12}
73-
className="ml-2"
76+
className={clsx("ml-2 transition", {
77+
"rotate-180": openDropdownIndex === index,
78+
})}
7479
/>
7580
</button>
7681

77-
{openDropdownIndex === index && navLink.is_dropdown ? (
78-
<div
79-
className="animate-slideInFromTop fixed inset-0 z-40 bg-black bg-opacity-50"
80-
onClick={() => setOpenDropdownIndex(null)}
81-
>
82-
<div
83-
className="relative mt-20 bg-background-2"
84-
onClick={(e) => e.stopPropagation()}
82+
<AnimatePresence>
83+
{openDropdownIndex === index && navLink.is_dropdown ? (
84+
<motion.div
85+
className={clsx(
86+
"fixed inset-0 top-20 z-40 h-[calc(100dvh-5rem)]",
87+
"bg-black/50",
88+
)}
89+
initial={{ opacity: 0 }}
90+
animate={{ opacity: 1 }}
91+
exit={{ opacity: 0 }}
92+
onClick={() => setOpenDropdownIndex(null)}
8593
>
86-
{navLink?.title === "Apps" ? (
87-
<AppsDropdownContent {...{ appsSection }} />
88-
) : null}
89-
{navLink?.title === "Resources" ? (
90-
<ResourcesDropdownContent
91-
{...{ resourceSections, socials }}
92-
/>
93-
) : null}
94-
</div>
95-
</div>
96-
) : null}
94+
<motion.div
95+
className={clsx(
96+
"absolute top-0 max-h-full w-full overflow-y-auto",
97+
"bg-background-2",
98+
)}
99+
initial={{ translateY: "-5%" }}
100+
animate={{ translateY: 0 }}
101+
exit={{ translateY: "-5%" }}
102+
onClick={(e) => e.stopPropagation()}
103+
>
104+
{navLink?.title === "Apps" ? (
105+
<AppsDropdownContent
106+
className="px-6 py-12"
107+
{...{ appsSection }}
108+
closeFn={() => setOpenDropdownIndex(null)}
109+
/>
110+
) : null}
111+
{navLink?.title === "Resources" ? (
112+
<ResourcesDropdownContent
113+
{...{ resourceSections, socials }}
114+
closeFn={() => setOpenDropdownIndex(null)}
115+
/>
116+
) : null}
117+
</motion.div>
118+
</motion.div>
119+
) : null}
120+
</AnimatePresence>
97121
</>
98122
)}
99123
</div>

0 commit comments

Comments
 (0)