feat: responsive app navbar
This commit is contained in:
parent
c6dcd75b84
commit
ecaa606342
|
@ -0,0 +1,144 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import Link, { LinkProps } from "next/link";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Icons } from "@/components/icons";
|
||||||
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
|
||||||
|
import { appNavItems, navItems } from "@/constants/data";
|
||||||
|
import { Button } from "./ui/button";
|
||||||
|
import { ScrollArea } from "./ui/scroll-area";
|
||||||
|
import { NavItem } from "@/types";
|
||||||
|
import { AlignRight } from "lucide-react";
|
||||||
|
|
||||||
|
const mobileNavMenu: NavItem[] = [
|
||||||
|
{
|
||||||
|
title: "Courses",
|
||||||
|
href: "#",
|
||||||
|
icon: "dashboard",
|
||||||
|
label: "Courses",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Career Paths",
|
||||||
|
href: "#",
|
||||||
|
icon: "shieldQuestion",
|
||||||
|
label: "Career Paths",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Community",
|
||||||
|
href: "#",
|
||||||
|
icon: "shieldQuestion",
|
||||||
|
label: "Community",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: "Pricing",
|
||||||
|
href: "/pricing",
|
||||||
|
icon: "profile",
|
||||||
|
label: "Pricing",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function MobileNav() {
|
||||||
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sheet open={open} onOpenChange={setOpen}>
|
||||||
|
<SheetTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="mr-2 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
|
||||||
|
>
|
||||||
|
<AlignRight />
|
||||||
|
<span className="sr-only">Toggle Menu</span>
|
||||||
|
</Button>
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent side="right" className="pr-0">
|
||||||
|
<MobileLink
|
||||||
|
href="/"
|
||||||
|
className="flex items-center"
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
>
|
||||||
|
<Icons.logo className="mr-2 h-4 w-4" />
|
||||||
|
<span className="font-bold">Skilld</span>
|
||||||
|
</MobileLink>
|
||||||
|
<ScrollArea className="my-4 h-[calc(100vh-8rem)] pb-10 pl-6">
|
||||||
|
<div className="flex flex-col space-y-3">
|
||||||
|
{appNavItems.mainNav.map(
|
||||||
|
(item) =>
|
||||||
|
item.href && (
|
||||||
|
<MobileLink
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</MobileLink>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
{appNavItems.sidebarNav.map((item, index) => (
|
||||||
|
<div key={index} className="flex flex-col space-y-3 pt-6">
|
||||||
|
<h4 className="font-medium">{item.title}</h4>
|
||||||
|
{item?.items?.length &&
|
||||||
|
item.items.map((item) => (
|
||||||
|
<React.Fragment key={item.href}>
|
||||||
|
{!item.disabled &&
|
||||||
|
(item.href ? (
|
||||||
|
<MobileLink
|
||||||
|
href={item.href}
|
||||||
|
onOpenChange={setOpen}
|
||||||
|
className="text-muted-foreground"
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
{item.label && (
|
||||||
|
<span className="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs leading-none text-[#000000] no-underline group-hover:no-underline">
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</MobileLink>
|
||||||
|
) : (
|
||||||
|
item.title
|
||||||
|
))}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MobileLinkProps extends LinkProps {
|
||||||
|
onOpenChange?: (open: boolean) => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MobileLink({
|
||||||
|
href,
|
||||||
|
onOpenChange,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: MobileLinkProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={href}
|
||||||
|
onClick={() => {
|
||||||
|
router.push(href.toString());
|
||||||
|
onOpenChange?.(false);
|
||||||
|
}}
|
||||||
|
className={cn(className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
|
@ -16,6 +16,17 @@ import {
|
||||||
} from "@/components/ui/navigation-menu";
|
} from "@/components/ui/navigation-menu";
|
||||||
import ThemeToggle from "./layout/ThemeToggle/theme-toggle";
|
import ThemeToggle from "./layout/ThemeToggle/theme-toggle";
|
||||||
import { buttonVariants } from "./ui/button";
|
import { buttonVariants } from "./ui/button";
|
||||||
|
import {
|
||||||
|
Sheet,
|
||||||
|
SheetContent,
|
||||||
|
SheetDescription,
|
||||||
|
SheetHeader,
|
||||||
|
SheetTitle,
|
||||||
|
SheetTrigger,
|
||||||
|
} from "@/components/ui/sheet";
|
||||||
|
import { AlignRight } from "lucide-react";
|
||||||
|
import { NavItem } from "@/types";
|
||||||
|
import { MobileNav } from "./mobile-nav";
|
||||||
|
|
||||||
const careerPaths: { title: string; href: string; description: string }[] = [
|
const careerPaths: { title: string; href: string; description: string }[] = [
|
||||||
{
|
{
|
||||||
|
@ -56,6 +67,8 @@ const careerPaths: { title: string; href: string; description: string }[] = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
return (
|
return (
|
||||||
<header className="border-b py-3">
|
<header className="border-b py-3">
|
||||||
|
@ -64,7 +77,7 @@ export default function Navbar() {
|
||||||
<h2 className="text-xl font-bold">Skilld</h2>
|
<h2 className="text-xl font-bold">Skilld</h2>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex gap-8 items-center">
|
<div className="hidden md:flex gap-8 items-center">
|
||||||
<NavigationMenu>
|
<NavigationMenu>
|
||||||
<NavigationMenuList>
|
<NavigationMenuList>
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
|
@ -145,6 +158,23 @@ export default function Navbar() {
|
||||||
</Link>
|
</Link>
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="md:hidden">
|
||||||
|
<MobileNav/>
|
||||||
|
{/* <Sheet>
|
||||||
|
<SheetTrigger>
|
||||||
|
<AlignRight />
|
||||||
|
</SheetTrigger>
|
||||||
|
<SheetContent>
|
||||||
|
<SheetHeader>
|
||||||
|
<SheetTitle>Skilld</SheetTitle>
|
||||||
|
</SheetHeader>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</SheetContent>
|
||||||
|
</Sheet> */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { NavItem } from "@/types";
|
import { NavItem } from "@/types";
|
||||||
|
import { MainNavItem, SidebarNavItem } from "@/types/nav";
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -131,3 +132,78 @@ export const questions = [
|
||||||
isNiche: false,
|
isNiche: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
interface AppNavItem {
|
||||||
|
mainNav: MainNavItem[];
|
||||||
|
sidebarNav: SidebarNavItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const appNavItems: AppNavItem = {
|
||||||
|
mainNav: [
|
||||||
|
{
|
||||||
|
title: "Pricing",
|
||||||
|
href: "/pricing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Community",
|
||||||
|
href: "#",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sidebarNav: [
|
||||||
|
{
|
||||||
|
title: "Courses",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: "Python Programming",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Web Dev With ReactJS",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Machine Learning Fundamentals",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Career Paths",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: "Frontend Developer",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Backend Developer",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Full Stack Developer",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Mobile App Developer",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Game Developer",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Machine Learning Developer",
|
||||||
|
href: "#",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Icons } from "@/components/icons"
|
||||||
|
|
||||||
|
export interface NavItem {
|
||||||
|
title: string
|
||||||
|
href?: string
|
||||||
|
disabled?: boolean
|
||||||
|
external?: boolean
|
||||||
|
icon?: keyof typeof Icons
|
||||||
|
label?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NavItemWithChildren extends NavItem {
|
||||||
|
items: NavItemWithChildren[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MainNavItem extends NavItem {}
|
||||||
|
|
||||||
|
export interface SidebarNavItem extends NavItemWithChildren {}
|
Loading…
Reference in New Issue