Skip to content

Commit

Permalink
feat: nav
Browse files Browse the repository at this point in the history
  • Loading branch information
johnshift committed May 14, 2024
1 parent d5eab43 commit 717d42e
Show file tree
Hide file tree
Showing 22 changed files with 780 additions and 32 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"dependencies": {
"@next/bundle-analyzer": "^14.2.3",
"@nextui-org/button": "^2.0.31",
"@nextui-org/system": "^2.1.2",
"@nextui-org/link": "^2.0.29",
"@nextui-org/system": "0.0.0-canary-20240504162810",
"@nextui-org/theme": "^2.2.3",
"@tanstack/react-query": "^5.36.0",
"@tanstack/react-virtual": "^3.5.0",
Expand All @@ -28,6 +29,7 @@
"react-error-boundary": "^4.0.13",
"react-intersection-observer": "^9.10.2",
"react-use-draggable-scroll": "^0.4.7",
"sharp": "^0.33.3",
"tailwind-merge": "^2.3.0",
"zod": "^3.23.8"
},
Expand Down
401 changes: 381 additions & 20 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Metadata } from 'next';
import { Inter } from 'next/font/google';

import { lato, roboto } from '@/shared/core/fonts';
import { Nav } from '@/shared/components/nav';
import { NextUIProvider } from '@/shared/providers/next-ui-provider';
import { ReactQueryProvider } from '@/shared/providers/react-query-provider';

Expand All @@ -22,7 +23,10 @@ const RootLayout: React.FC<RootLayoutProps> = ({ children }) => (
<html lang="en" className={`${lato.variable} ${roboto.variable}`}>
<body className={inter.className}>
<NextUIProvider>
<ReactQueryProvider>{children}</ReactQueryProvider>
<ReactQueryProvider>
<Nav />
{children}
</ReactQueryProvider>
</NextUIProvider>
</body>
</html>
Expand Down
3 changes: 1 addition & 2 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ const classNames = {
};

const NotFound = () => (
// TODO: Adjust left-padding after nav implementation
<div className="flex min-h-screen w-full items-center justify-center">
<div className="flex min-h-screen w-full items-center justify-center md:pl-[212px]">
<ErrorAction
isTransparent
textContent={textContent}
Expand Down
2 changes: 2 additions & 0 deletions src/app/organizations/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const OrganizationsPage = () => null;
export default OrganizationsPage;
8 changes: 1 addition & 7 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
const Home = () => {
return (
<main>
<p>Henlo</p>
</main>
);
};
const Home = () => null;

export default Home;
2 changes: 2 additions & 0 deletions src/app/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const ProjectsPage = () => null;
export default ProjectsPage;
3 changes: 3 additions & 0 deletions src/shared/atoms/show-fullscreen-nav-atom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { atom } from 'jotai';

export const showFullscreenNavAtom = atom<boolean>(false);
27 changes: 27 additions & 0 deletions src/shared/components/brand.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Image from 'next/image';
import Link from 'next/link';

const IMAGE_SRC = '/jobstash-nav-brand.png';
const IMAGE_WIDTH = 120;
const IMAGE_HEIGHT = 32;
const MIN_HEIGHT_STYLE = { minHeight: 53 };

export const Brand = () => {
return (
<Link href="/">
<div
className="flex items-center justify-center"
style={MIN_HEIGHT_STYLE}
>
<Image
src={IMAGE_SRC}
alt="JobStash Logo"
width={IMAGE_WIDTH}
height={IMAGE_HEIGHT}
priority
quality={100}
/>
</div>
</Link>
);
};
26 changes: 26 additions & 0 deletions src/shared/components/icons/code-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { memo } from 'react';

export const CodeIcon = memo(() => (
<svg
width="16"
height="16"
viewBox="0 1 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.38 5.18121C4.3389 5.13005 4.2879 5.08771 4.23004 5.05674C4.17218 5.02576 4.10867 5.00679 4.04331 5.00096C3.97794 4.99512 3.91207 5.00254 3.84964 5.02277C3.78722 5.04301 3.72952 5.07564 3.68 5.11871L0.680002 7.61871C0.623716 7.66503 0.578385 7.72324 0.547263 7.78915C0.516141 7.85507 0.5 7.92706 0.5 7.99996C0.5 8.07285 0.516141 8.14484 0.547263 8.21076C0.578385 8.27668 0.623716 8.33489 0.680002 8.38121L3.68 10.8812C3.72952 10.9243 3.78722 10.9569 3.84964 10.9771C3.91207 10.9974 3.97794 11.0048 4.04331 10.999C4.10867 10.9931 4.17218 10.9741 4.23004 10.9432C4.2879 10.9122 4.3389 10.8699 4.38 10.8187C4.42307 10.7692 4.4557 10.7115 4.47594 10.6491C4.49617 10.5866 4.50359 10.5208 4.49776 10.4554C4.49192 10.39 4.47295 10.3265 4.44197 10.2687C4.411 10.2108 4.36866 10.1598 4.3175 10.1187L1.78 7.99996L4.3175 5.88121C4.36866 5.84011 4.411 5.7891 4.44197 5.73125C4.47295 5.67339 4.49192 5.60988 4.49776 5.54451C4.50359 5.47915 4.49617 5.41328 4.47594 5.35085C4.4557 5.28842 4.42307 5.23072 4.38 5.18121Z"
fill="#F9FAFB"
/>
<path
d="M15.3175 7.61872L12.3175 5.11872C12.215 5.05069 12.0909 5.02287 11.9692 5.04057C11.8474 5.05827 11.7364 5.12026 11.6575 5.21466C11.5786 5.30906 11.5372 5.42926 11.5414 5.55224C11.5455 5.67522 11.5949 5.79235 11.68 5.88122L14.2175 7.99997L11.68 10.1187C11.6022 10.1847 11.5465 10.2729 11.5203 10.3714C11.4941 10.47 11.4986 10.5742 11.5333 10.6701C11.568 10.766 11.6311 10.8489 11.7143 10.9079C11.7975 10.9669 11.8968 10.999 11.9987 11C12.1159 11.0005 12.2292 10.9583 12.3175 10.8812L15.3175 8.38122C15.3738 8.3349 15.4191 8.27669 15.4502 8.21077C15.4814 8.14485 15.4975 8.07286 15.4975 7.99997C15.4975 7.92707 15.4814 7.85508 15.4502 7.78916C15.4191 7.72324 15.3738 7.66504 15.3175 7.61872Z"
fill="#F9FAFB"
/>
<path
d="M10.1675 2.03121C10.106 2.00843 10.0406 1.99814 9.97503 2.00094C9.90949 2.00373 9.84517 2.01956 9.78582 2.04748C9.72647 2.07541 9.67328 2.11489 9.62936 2.1636C9.58544 2.21232 9.55166 2.26929 9.53 2.33121L5.53 13.3312C5.50723 13.3927 5.49694 13.4582 5.49973 13.5237C5.50253 13.5892 5.51835 13.6535 5.54628 13.7129C5.57421 13.7722 5.61368 13.8254 5.6624 13.8693C5.71111 13.9133 5.76809 13.9471 5.83 13.9687C5.88422 13.9881 5.94119 13.9986 5.99875 14C6.10166 14.0005 6.20216 13.9688 6.28621 13.9094C6.37025 13.8501 6.43363 13.7659 6.4675 13.6687L10.4675 2.66871C10.4903 2.6072 10.5006 2.54176 10.4978 2.47623C10.495 2.4107 10.4792 2.34638 10.4512 2.28703C10.4233 2.22768 10.3838 2.17449 10.3351 2.13057C10.2864 2.08664 10.2294 2.05287 10.1675 2.03121Z"
fill="#F9FAFB"
/>
</svg>
));

CodeIcon.displayName = 'CodeIcon';
20 changes: 20 additions & 0 deletions src/shared/components/icons/handbag-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { memo } from 'react';

export const HandbagIcon = memo(() => {
return (
<svg
width="16"
height="17"
viewBox="0 1 16 17"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.9375 13.6993L14.0437 5.69929C14.0174 5.45455 13.9012 5.22829 13.7176 5.06433C13.534 4.90037 13.2961 4.81038 13.05 4.81179H10.9562C10.8359 4.11497 10.4733 3.48305 9.9324 3.02755C9.39151 2.57206 8.7071 2.32227 7.99997 2.32227C7.29284 2.32227 6.60843 2.57206 6.06754 3.02755C5.52665 3.48305 5.16404 4.11497 5.04372 4.81179H2.94997C2.70383 4.81038 2.46592 4.90037 2.28233 5.06433C2.09874 5.22829 1.98253 5.45455 1.95622 5.69929L1.06247 13.6993C1.04733 13.8397 1.06176 13.9818 1.10481 14.1164C1.14787 14.2509 1.2186 14.375 1.31247 14.4805C1.40602 14.5851 1.52067 14.6687 1.64886 14.7258C1.77706 14.7829 1.91589 14.8122 2.05622 14.8118H13.9437C14.0841 14.8122 14.2229 14.7829 14.3511 14.7258C14.4793 14.6687 14.5939 14.5851 14.6875 14.4805C14.7813 14.375 14.8521 14.2509 14.8951 14.1164C14.9382 13.9818 14.9526 13.8397 14.9375 13.6993ZM5.99997 7.31179C5.99997 7.4444 5.94729 7.57157 5.85352 7.66534C5.75975 7.75911 5.63258 7.81179 5.49997 7.81179C5.36736 7.81179 5.24018 7.75911 5.14642 7.66534C5.05265 7.57157 4.99997 7.4444 4.99997 7.31179V6.31179C4.99997 6.17918 5.05265 6.052 5.14642 5.95824C5.24018 5.86447 5.36736 5.81179 5.49997 5.81179C5.63258 5.81179 5.75975 5.86447 5.85352 5.95824C5.94729 6.052 5.99997 6.17918 5.99997 6.31179V7.31179ZM6.06247 4.81179C6.17262 4.38158 6.42282 4.00027 6.77362 3.72797C7.12443 3.45567 7.55588 3.30787 7.99997 3.30787C8.44405 3.30787 8.87551 3.45567 9.22631 3.72797C9.57712 4.00027 9.82732 4.38158 9.93747 4.81179H6.06247ZM11 7.31179C11 7.4444 10.9473 7.57157 10.8535 7.66534C10.7598 7.75911 10.6326 7.81179 10.5 7.81179C10.3674 7.81179 10.2402 7.75911 10.1464 7.66534C10.0526 7.57157 9.99997 7.4444 9.99997 7.31179V6.31179C9.99997 6.17918 10.0526 6.052 10.1464 5.95824C10.2402 5.86447 10.3674 5.81179 10.5 5.81179C10.6326 5.81179 10.7598 5.86447 10.8535 5.95824C10.9473 6.052 11 6.17918 11 6.31179V7.31179Z"
fill="#F9FAFB"
/>
</svg>
);
});

HandbagIcon.displayName = 'HandbagIcon';
20 changes: 20 additions & 0 deletions src/shared/components/icons/users-three-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { memo } from 'react';

export const UsersThreeIcon = memo(() => {
return (
<svg
width="16"
height="17"
viewBox="0 1 16 17"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.00002 9.56281C4.00086 9.6287 3.9885 9.6941 3.96367 9.75514C3.93884 9.81618 3.90204 9.87164 3.85544 9.91823C3.80885 9.96483 3.75339 10.0016 3.69235 10.0265C3.63131 10.0513 3.56591 10.0636 3.50002 10.0628H0.75002C0.657132 10.0621 0.566195 10.0361 0.486933 9.98763C0.407671 9.9392 0.343064 9.87013 0.30002 9.78781C0.259993 9.70399 0.243502 9.61086 0.252309 9.51838C0.261116 9.42591 0.29489 9.33757 0.35002 9.26281C0.792512 8.66883 1.38297 8.20121 2.06252 7.90656C1.77052 7.63926 1.54616 7.30644 1.40793 6.93549C1.26969 6.56454 1.22152 6.16606 1.26738 5.77286C1.31325 5.37965 1.45183 5.00296 1.67174 4.67378C1.89164 4.34461 2.18657 4.07236 2.53225 3.87944C2.87793 3.68652 3.26449 3.57846 3.6601 3.56414C4.05571 3.54981 4.44907 3.62965 4.8078 3.79706C5.16653 3.96447 5.48037 4.21469 5.7235 4.5271C5.96663 4.83951 6.13209 5.2052 6.20627 5.59406C6.22476 5.69534 6.21193 5.79985 6.1695 5.89365C6.12707 5.98745 6.05704 6.06608 5.96877 6.11906C5.16041 6.59841 4.54561 7.34589 4.23127 8.23156C4.07631 8.65821 3.99802 9.1089 4.00002 9.56281ZM15.65 9.26281C15.2075 8.66883 14.6171 8.20121 13.9375 7.90656C14.2295 7.63926 14.4539 7.30644 14.5921 6.93549C14.7304 6.56454 14.7785 6.16606 14.7327 5.77286C14.6868 5.37965 14.5482 5.00296 14.3283 4.67378C14.1084 4.34461 13.8135 4.07236 13.4678 3.87944C13.1221 3.68652 12.7355 3.57846 12.3399 3.56414C11.9443 3.54981 11.551 3.62965 11.1922 3.79706C10.8335 3.96447 10.5197 4.21469 10.2765 4.5271C10.0334 4.83951 9.86795 5.2052 9.79377 5.59406C9.77528 5.69534 9.78811 5.79985 9.83054 5.89365C9.87297 5.98745 9.943 6.06608 10.0313 6.11906C10.8407 6.59718 11.4558 7.3451 11.7688 8.23156C11.9237 8.65821 12.002 9.1089 12 9.56281C11.9992 9.6287 12.0115 9.6941 12.0364 9.75514C12.0612 9.81618 12.098 9.87164 12.1446 9.91823C12.1912 9.96483 12.2466 10.0016 12.3077 10.0265C12.3687 10.0513 12.4341 10.0636 12.5 10.0628H15.25C15.3429 10.0628 15.4339 10.037 15.5129 9.98814C15.5919 9.93932 15.6557 9.86947 15.6972 9.78642C15.7388 9.70336 15.7563 9.61039 15.748 9.51791C15.7397 9.42543 15.7057 9.33709 15.65 9.26281ZM9.82502 11.9441C10.3226 11.5627 10.6882 11.0349 10.8705 10.435C11.0527 9.83513 11.0424 9.19321 10.8411 8.59946C10.6397 8.00571 10.2574 7.48997 9.74778 7.12471C9.2382 6.75945 8.62699 6.56301 8.00002 6.56301C7.37305 6.56301 6.76184 6.75945 6.25226 7.12471C5.74268 7.48997 5.36034 8.00571 5.15897 8.59946C4.9576 9.19321 4.94731 9.83513 5.12955 10.435C5.31179 11.0349 5.67741 11.5627 6.17502 11.9441C5.20285 12.3757 4.4147 13.1373 3.95002 14.0941C3.91305 14.1701 3.89649 14.2544 3.90197 14.3388C3.90745 14.4232 3.93477 14.5047 3.98127 14.5753C4.02454 14.6478 4.08588 14.7077 4.15928 14.7494C4.23268 14.791 4.31564 14.8128 4.40002 14.8128H11.6C11.6844 14.8128 11.7674 14.791 11.8408 14.7494C11.9142 14.7077 11.9755 14.6478 12.0188 14.5753C12.0653 14.5047 12.0926 14.4232 12.0981 14.3388C12.1036 14.2544 12.087 14.1701 12.05 14.0941C11.5853 13.1373 10.7972 12.3757 9.82502 11.9441Z"
fill="#F9FAFB"
/>
</svg>
);
});

UsersThreeIcon.displayName = 'UsersThreeIcon';
35 changes: 35 additions & 0 deletions src/shared/components/menu-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import { Button } from '@nextui-org/button';
import { useSetAtom } from 'jotai';

import { showFullscreenNavAtom } from '@/shared/atoms/show-fullscreen-nav-atom';

export const MenuButton = () => {
const setShowNav = useSetAtom(showFullscreenNavAtom);

const openNav = () => {
setShowNav(true);
};

return (
<Button isIconOnly className="md:hidden" onClick={openNav}>
<svg
width="32"
height="33"
viewBox="0 0 32 33"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 21.4062H27C27.5523 21.4062 28 20.9585 28 20.4062C28 19.854 27.5523 19.4062 27 19.4062H5C4.44772 19.4062 4 19.854 4 20.4062C4 20.9585 4.44772 21.4062 5 21.4062Z"
fill="#F9FAFB"
/>
<path
d="M5 13.4062H27C27.5523 13.4062 28 12.9585 28 12.4062C28 11.854 27.5523 11.4062 27 11.4062H5C4.44772 11.4062 4 11.854 4 12.4062C4 12.9585 4.44772 13.4062 5 13.4062Z"
fill="#F9FAFB"
/>
</svg>
</Button>
);
};
24 changes: 24 additions & 0 deletions src/shared/components/mobile-header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ClassValue } from 'clsx';

import { cn } from '@/shared/utils/cn';

import { MenuButton } from './menu-button';

interface Props {
left: React.ReactNode;
className?: ClassValue;
}

export const MobileHeader = ({ left, className }: Props) => {
return (
<div
className={cn(
'fixed left-0 top-0 z-10 flex h-[70px] w-full items-center justify-between border-b border-white/10 bg-base-dark p-4 py-2 md:hidden md:h-20',
className,
)}
>
{left}
<MenuButton />
</div>
);
};
52 changes: 52 additions & 0 deletions src/shared/components/nav/bartab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use client';

import { usePathname } from 'next/navigation';

import { Button } from '@nextui-org/button';
import { Link } from '@nextui-org/link';

import { cn } from '@/shared/utils/cn';

import { useCloseNav } from './use-close-nav';

interface BartabProps {
icon: React.ReactNode;
text: string;
href: string;
isMobile?: boolean;
}

export const Bartab = (props: BartabProps) => {
const { icon, text, href, isMobile = false } = props;

const pathname = usePathname();
const isActive = href === pathname.slice(0, href.length);

const { closeNav } = useCloseNav();

const handleClick: React.MouseEventHandler = (event) => {
event.stopPropagation();
closeNav();
};

const buttonClassName = cn('justify-start bg-none', {
'bg-gradient-to-l from-[#8743FF] to-[#4136F1]': isActive || isMobile,
});

return (
<Button
fullWidth
as={Link}
href={href}
radius="sm"
startContent={icon}
className={buttonClassName}
onClick={handleClick}
data-active={isActive}
>
<span className="text-2xl text-white md:text-sm md:font-semibold">
{text}
</span>
</Button>
);
};
41 changes: 41 additions & 0 deletions src/shared/components/nav/close-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { MouseEventHandler } from 'react';

import { Button } from '@nextui-org/button';

import { cn } from '@/shared/utils/cn';

import { useCloseNav } from './use-close-nav';

export const CloseButton = () => {
const { showNav, closeNav } = useCloseNav();

const handleClick: MouseEventHandler = (event) => {
event.stopPropagation();
closeNav();
};

return (
<Button
isIconOnly
aria-label="Close Nav"
className={cn('md:hidden', { hidden: !showNav })}
onClick={handleClick}
variant="light"
>
<svg
width="32"
height="33"
viewBox="0 0 32 33"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.3431 9.33586C9.95257 8.94534 9.3194 8.94534 8.92888 9.33586C8.53836 9.72639 8.53836 10.3596 8.92888 10.7501L14.5858 16.407L8.92895 22.0638C8.53843 22.4543 8.53843 23.0875 8.92895 23.478C9.31948 23.8685 9.95264 23.8685 10.3432 23.478L16 17.8212L21.6568 23.478C22.0473 23.8685 22.6805 23.8685 23.071 23.478C23.4615 23.0875 23.4615 22.4543 23.071 22.0638L17.4142 16.407L23.0711 10.7501C23.4616 10.3596 23.4616 9.72639 23.0711 9.33586C22.6806 8.94534 22.0474 8.94534 21.6569 9.33586L16 14.9928L10.3431 9.33586Z"
fill="#F9FAFB"
/>
</svg>
</Button>
);
};
14 changes: 14 additions & 0 deletions src/shared/components/nav/fullscreen-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client';

import { useAtomValue } from 'jotai';

import { showFullscreenNavAtom } from '@/shared/atoms/show-fullscreen-nav-atom';

interface Props {
children: React.ReactNode;
}

export const FullscreenWrapper = ({ children }: Props) => {
const isDisplayed = useAtomValue(showFullscreenNavAtom);
return isDisplayed ? children : null;
};
26 changes: 26 additions & 0 deletions src/shared/components/nav/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Brand } from '@/shared/components/brand';
import { MobileHeader } from '@/shared/components/mobile-header';

import { FullscreenWrapper } from './fullscreen-wrapper';
import { NavSections } from './sections';

export const Nav = () => {
return (
<nav>
{/* Mobile Top Header */}
<MobileHeader left={<Brand />} />

{/* Mobile Fullscreen Nav */}
<FullscreenWrapper>
<div className="fixed left-0 top-0 z-50 flex size-full shrink-0 flex-col bg-gradient-to-l from-[#8743FF] to-[#4136F1] p-4 py-2 md:hidden">
<NavSections isMobile />
</div>
</FullscreenWrapper>

{/* Desktop Sidebar Nav */}
<div className="fixed left-0 top-0 hidden size-full max-w-fit shrink-0 flex-col border-r-2 border-white/5 p-4 md:flex">
<NavSections />
</div>
</nav>
);
};
Loading

0 comments on commit 717d42e

Please sign in to comment.