Skip to content

Commit ab9d27f

Browse files
feat:navigation bar
1 parent ebc4068 commit ab9d27f

File tree

2 files changed

+145
-26
lines changed

2 files changed

+145
-26
lines changed

app/[locale]/header.tsx

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import MaybeLink from '~/components/maybe-link';
1313
import {
1414
NavigationMenu,
1515
NavigationMenuContent,
16+
NavigationMenuCustomListItem,
1617
NavigationMenuItem,
1718
NavigationMenuLink,
1819
NavigationMenuList,
1920
NavigationMenuTrigger,
21+
navigationMenuTriggerStyle,
2022
} from '~/components/ui';
2123
import { getTranslations } from '~/i18n/translations';
2224
import { cn } from '~/lib/utils';
@@ -37,7 +39,45 @@ export default async function Header({ locale }: { locale: string }) {
3739
{ label: text.alumni, href: 'alumni' },
3840
{ label: text.activities, href: 'student-activities' },
3941
];
40-
42+
const components: { title: string; href: string; description: string }[] = [
43+
{
44+
title: 'Alert Dialog',
45+
href: '/docs/primitives/alert-dialog',
46+
description:
47+
'A modal dialog that interrupts the user with important content and expects a response.',
48+
},
49+
{
50+
title: 'Hover Card',
51+
href: '/docs/primitives/hover-card',
52+
description:
53+
'For sighted users to preview content available behind a link.',
54+
},
55+
{
56+
title: 'Progress',
57+
href: '/docs/primitives/progress',
58+
description:
59+
'Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.',
60+
},
61+
{
62+
title: 'Scroll-area',
63+
href: '/docs/primitives/scroll-area',
64+
description: 'Visually or semantically separates content.',
65+
},
66+
{
67+
title: 'Tabs',
68+
href: '/docs/primitives/tabs',
69+
description:
70+
'A set of layered sections of content—known as tab panels—that are displayed one at a time.',
71+
},
72+
{
73+
title: 'Tooltip',
74+
href: '/docs/primitives/tooltip',
75+
description:
76+
'A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.',
77+
},
78+
];
79+
const itemss = 3;
80+
const componentsCount = itemss >= 4 ? 4 : itemss;
4181
return (
4282
<header className="header-sticky-ness sticky top-0 z-nav min-w-full bg-background">
4383
<nav
@@ -56,28 +96,28 @@ export default async function Header({ locale }: { locale: string }) {
5696
src="assets/nitlogo.png"
5797
/>
5898
</Link>
59-
60-
<ol className={cn('hidden grow lg:flex', 'gap-4 xl:gap-5 2xl:gap-6')}>
61-
{items.map(({ label, href }, index) => (
62-
<li className="my-auto min-h-fit" key={index}>
63-
<Link href={`/${locale}/${href}`} prefetch>
64-
{label}
99+
<NavigationMenu>
100+
<NavigationMenuList
101+
className={cn('hidden grow lg:flex', 'gap-4 xl:gap-5 2xl:gap-6')}
102+
>
103+
<NavigationMenuCustomListItem
104+
triggerName={items[0].label}
105+
imageDetails={{
106+
src: 'https://s3-alpha-sig.figma.com/img/054e/19b7/43c945f2ee30e43f797f944b1c02fe2e?Expires=1728259200&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=TjH6LvDVUoTwEvUvc02-8DAwccrJ9YRqDqTy5h-0O0cYSVWG03it8-zr5OSBcjGVuu5TMnV7ZlnEiM3CIHQ3mQAy7Z3~Bv2sbCL8plMbE0GDxzmjpaVkKPAfgbMpuyofWABnyQjH4cda6qWEzeBuGEw~KQfFxVuAA-wHYTA6GL~B776fRbfdfzNxtSqucrIEqfGG1nUMFEdxvTLMPCqXTjErPikIs2rDXtAZ3K3U4suPFFqLRyBQ9H0B3DAGDzxZ64CIVLkAaE~ALCRy1BBUDyXrU24E1~BeTobiCoR0q1WcnBOPMKpnUb0c2qTyaDz8BxMe9hMMI9vGnv4fxdqGBA__',
107+
alt: items[0].label,
108+
href: items[0].href,
109+
}}
110+
listItems={components}
111+
/>
112+
<NavigationMenuItem>
113+
<Link href="/docs" legacyBehavior passHref>
114+
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
115+
Documentation
116+
</NavigationMenuLink>
65117
</Link>
66-
</li>
67-
))}
68-
<li>
69-
<NavigationMenu>
70-
<NavigationMenuList>
71-
<NavigationMenuItem>
72-
<NavigationMenuTrigger>Item One</NavigationMenuTrigger>
73-
<NavigationMenuContent>
74-
<NavigationMenuLink>Link</NavigationMenuLink>
75-
</NavigationMenuContent>
76-
</NavigationMenuItem>
77-
</NavigationMenuList>
78-
</NavigationMenu>
79-
</li>
80-
</ol>
118+
</NavigationMenuItem>
119+
</NavigationMenuList>
120+
</NavigationMenu>
81121

82122
<ol className="inline-flex h-10 gap-2">
83123
<li className="flex h-full rounded-xl border border-neutral-500 bg-neutral-50">

components/ui/navigation-menu.tsx

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import * as React from 'react';
22
import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu';
33
import { cva } from 'class-variance-authority';
44
import { RxChevronDown } from 'react-icons/rx';
5+
import Link from 'next/link';
6+
import Image from 'next/image';
57

68
import { cn } from '~/lib/utils';
79

@@ -41,13 +43,13 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
4143
const NavigationMenuItem = NavigationMenuPrimitive.Item;
4244

4345
const navigationMenuTriggerStyle = cva(
44-
'hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900 group flex max-w-fit rounded-md bg-background transition-colors hover:bg-neutral-100 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-neutral-100/50 data-[state=open]:bg-neutral-100/50'
46+
'hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900 group flex max-w-fit rounded-md bg-background transition-colors hover:bg-neutral-100 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-neutral-100/50 data-[state=open]:bg-neutral-100/50 lg:text-lg'
4547
);
4648

4749
const NavigationMenuTrigger = React.forwardRef<
4850
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
4951
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
50-
>(({ className, children, title, ...props }, ref) => (
52+
>(({ className, children, ...props }, ref) => (
5153
<NavigationMenuPrimitive.Trigger
5254
ref={ref}
5355
className={cn(navigationMenuTriggerStyle(), 'group', className)}
@@ -86,7 +88,7 @@ const NavigationMenuViewport = React.forwardRef<
8688
<div className={cn('absolute left-0 top-full flex justify-center')}>
8789
<NavigationMenuPrimitive.Viewport
8890
className={cn(
89-
'origin-top-center border-gray-200 bg-white text-gray-950 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]',
91+
'origin-top-center text-neutral-950 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-xl border border-neutral-200 bg-shade-light shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]',
9092
className
9193
)}
9294
ref={ref}
@@ -109,17 +111,94 @@ const NavigationMenuIndicator = React.forwardRef<
109111
)}
110112
{...props}
111113
>
112-
<div className="bg-gray-200 relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
114+
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-neutral-200 shadow-md" />
113115
</NavigationMenuPrimitive.Indicator>
114116
));
115117
NavigationMenuIndicator.displayName =
116118
NavigationMenuPrimitive.Indicator.displayName;
117119

120+
const NavigationMenuCustomListItem = React.forwardRef<
121+
React.ElementRef<typeof NavigationMenuPrimitive.Item>,
122+
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Item> & {
123+
imageDetails?: {
124+
src: string;
125+
alt: string;
126+
href: string;
127+
};
128+
listItems: {
129+
title: string;
130+
description: string;
131+
href: string;
132+
}[];
133+
triggerName: string;
134+
}
135+
>(({ imageDetails, listItems, triggerName, ...props }, ref) => {
136+
const imageHeight = listItems.length > 4 ? 4 : listItems.length;
137+
return (
138+
<NavigationMenuItem {...props} ref={ref}>
139+
<NavigationMenuTrigger>{triggerName}</NavigationMenuTrigger>
140+
<NavigationMenuContent className="flex gap-4 p-6 xl:gap-6 2xl:gap-8">
141+
{imageDetails && (
142+
<Link href={imageDetails.href} passHref legacyBehavior>
143+
<NavigationMenuLink
144+
className="group relative flex select-none flex-col justify-end overflow-hidden rounded-xl no-underline outline-none"
145+
style={{ minWidth: `${70 * imageHeight}px` }}
146+
>
147+
<Image
148+
className="absolute inset-0 z-0 h-full w-full object-cover transition-transform duration-500 ease-in-out group-hover:scale-125"
149+
alt=""
150+
src={imageDetails.src}
151+
width={0}
152+
height={0}
153+
/>
154+
<section className="relative z-30 flex h-3/4 w-full flex-col justify-end bg-gradient-to-b from-primary-500/25 to-primary-500 p-2 focus:shadow-md">
155+
<h5 className="!mb-0 origin-bottom-left text-shade-light transition-transform duration-500 ease-in-out group-hover:scale-150">
156+
{imageDetails.alt + '→'}
157+
</h5>
158+
</section>
159+
</NavigationMenuLink>
160+
</Link>
161+
)}
162+
<ul
163+
className={cn(
164+
'grid grid-flow-col auto-rows-max gap-4 xl:gap-6 2xl:gap-8'
165+
)}
166+
style={{
167+
gridTemplateRows: `repeat(${imageHeight}, minmax(0, 1fr))`,
168+
}}
169+
>
170+
{listItems.map(({ title, description, href }, index) => (
171+
<li key={index}>
172+
<NavigationMenuLink asChild>
173+
<Link
174+
className={cn(
175+
'group block min-w-[12rem] select-none space-y-1 rounded-xl p-3 leading-none no-underline outline-none transition-colors transition-transform duration-500 ease-in-out hover:scale-110 hover:bg-neutral-50 focus:bg-neutral-50'
176+
)}
177+
href={href}
178+
>
179+
<h6 className="font-sans font-semibold leading-none text-shade-dark group-hover:text-primary-500 group-focus:text-primary-500">
180+
{title}
181+
</h6>
182+
<p className="line-clamp-3 text-sm leading-snug text-neutral-700 group-hover:text-primary-500 group-focus:text-primary-500">
183+
{description}
184+
</p>
185+
</Link>
186+
</NavigationMenuLink>
187+
</li>
188+
))}
189+
</ul>
190+
</NavigationMenuContent>
191+
</NavigationMenuItem>
192+
);
193+
});
194+
NavigationMenuCustomListItem.displayName = 'NavigationMenuCustomListItem';
195+
118196
export {
119197
navigationMenuTriggerStyle,
120198
NavigationMenu,
121199
NavigationMenuList,
122200
NavigationMenuItem,
201+
NavigationMenuCustomListItem,
123202
NavigationMenuContent,
124203
NavigationMenuTrigger,
125204
NavigationMenuLink,

0 commit comments

Comments
 (0)