feat: dynamic menu item support

main
Mahmudul Hasan 2024-07-15 16:28:12 +06:00
parent 5a5e3c00f3
commit a425c78bdf
4 changed files with 45 additions and 20 deletions

View File

@ -1,9 +1,10 @@
import { Sheet, SheetContent, SheetTrigger } from "@/Components/ui/sheet"; import { Sheet, SheetContent, SheetTrigger } from "@/Components/ui/sheet";
import { Button } from "@/Components/ui/button"; import { Button } from "@/Components/ui/button";
import { Home, Menu } from "lucide-react"; import { AlignJustifyIcon, Menu } from "lucide-react";
import { Link } from "@inertiajs/react"; import { Link } from "@inertiajs/react";
import { MenuItemProp } from "@/types"; import { MenuItemProp } from "@/types";
import ApplicationLogo from "@/Components/ApplicationLogo"; import ApplicationLogo from "@/Components/ApplicationLogo";
import { DropdownMenuSeparator } from "./ui/dropdown-menu";
const MobileMenu = ({ links }: { links: MenuItemProp[] }) => { const MobileMenu = ({ links }: { links: MenuItemProp[] }) => {
return ( return (
@ -19,31 +20,41 @@ const MobileMenu = ({ links }: { links: MenuItemProp[] }) => {
</Button> </Button>
</SheetTrigger> </SheetTrigger>
<SheetContent side="left" className="flex flex-col"> <SheetContent side="left" className="flex flex-col">
<nav className="grid gap-2 text-lg font-medium"> <nav className="text-lg font-medium">
<Link <Link
href="#" href="#"
className="flex items-center gap-2 text-lg font-semibold" className="flex items-center gap-2 text-lg font-semibold mb-4"
> >
<ApplicationLogo className="h-8 w-8 fill-current text-gray-500" /> <ApplicationLogo className="h-8 w-8 fill-current text-gray-500" />
<span>Acme Inc</span> <span>Acme Inc</span>
</Link> </Link>
{links.map((link, index) => {
return ( <DropdownMenuSeparator />
<Link
key={index} <div className="space-y-2">
href={link.href} {links.map((link, index) => (
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground" <MobileMenuItem key={index} link={link} />
> ))}
<Home className="h-5 w-5" /> </div>
{link.title}
</Link>
);
})}
</nav> </nav>
</SheetContent> </SheetContent>
</Sheet> </Sheet>
); );
}; };
const MobileMenuItem = ({ link }: { link: MenuItemProp }) => {
const Icon = (link.icon ?? AlignJustifyIcon) as React.ElementType;
return (
<Link
href={link.href}
className="mx-[-0.65rem] flex items-center gap-4 rounded-xl px-3 py-2 text-muted-foreground hover:text-foreground"
>
<Icon className="h-5 w-5" />
{link.title}
</Link>
);
};
export default MobileMenu; export default MobileMenu;

View File

@ -1,5 +1,5 @@
import { Link, usePage } from "@inertiajs/react"; import { Link, usePage } from "@inertiajs/react";
import { Home } from "lucide-react"; import { AlignJustifyIcon, Home, Icon } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { buttonVariants } from "@/Components/ui/button"; import { buttonVariants } from "@/Components/ui/button";
import { import {
@ -10,6 +10,7 @@ import {
} from "@/Components/ui/tooltip"; } from "@/Components/ui/tooltip";
import { MenuItemProp } from "@/types"; import { MenuItemProp } from "@/types";
import ApplicationLogo from "@/Components/ApplicationLogo"; import ApplicationLogo from "@/Components/ApplicationLogo";
import { ReactNode } from "react";
type Props = { type Props = {
links: MenuItemProp[]; links: MenuItemProp[];
@ -22,6 +23,8 @@ type MenuItemProps = {
}; };
const CollapsedMenuItem = ({ link, isActive }: MenuItemProps) => { const CollapsedMenuItem = ({ link, isActive }: MenuItemProps) => {
const Icon = (link.icon ?? AlignJustifyIcon) as React.ElementType;
return ( return (
<Tooltip delayDuration={0}> <Tooltip delayDuration={0}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@ -37,7 +40,7 @@ const CollapsedMenuItem = ({ link, isActive }: MenuItemProps) => {
"dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white" "dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white"
)} )}
> >
<Home className="h-4 w-4" /> {<Icon className="h-4 w-4" />}
<span className="sr-only">{link.title}</span> <span className="sr-only">{link.title}</span>
</Link> </Link>
</TooltipTrigger> </TooltipTrigger>
@ -49,6 +52,8 @@ const CollapsedMenuItem = ({ link, isActive }: MenuItemProps) => {
}; };
const ExpandedMenuItem = ({ link, isActive }: MenuItemProps) => { const ExpandedMenuItem = ({ link, isActive }: MenuItemProps) => {
const Icon = (link.icon ?? AlignJustifyIcon) as React.ElementType;
return ( return (
<Link <Link
href={link.href} href={link.href}
@ -57,14 +62,14 @@ const ExpandedMenuItem = ({ link, isActive }: MenuItemProps) => {
"justify-start" "justify-start"
)} )}
> >
<Home className="mr-2 h-4 w-4" /> {<Icon className="mr-2 h-4 w-4" />}
{link.title} {link.title}
</Link> </Link>
); );
}; };
const Sidebar = ({ links, isCollapsed }: Props) => { const Sidebar = ({ links, isCollapsed }: Props) => {
const { url, component } = usePage(); const { url } = usePage();
return ( return (
<TooltipProvider> <TooltipProvider>

View File

@ -1,7 +1,7 @@
import { PropsWithChildren, ReactNode, useState } from "react"; import { PropsWithChildren, ReactNode, useState } from "react";
import { Link } from "@inertiajs/react"; import { Link } from "@inertiajs/react";
import { MenuItemProp, User } from "@/types"; import { MenuItemProp, User } from "@/types";
import { CircleUser, Search } from "lucide-react"; import { CircleUser, Search, UserIcon } from "lucide-react";
import { Button } from "@/Components/ui/button"; import { Button } from "@/Components/ui/button";
import { import {
DropdownMenu, DropdownMenu,
@ -33,6 +33,7 @@ const links: MenuItemProp[] = [
title: "Profile", title: "Profile",
href: route("profile.edit"), href: route("profile.edit"),
variant: "ghost", variant: "ghost",
icon: UserIcon,
}, },
]; ];

View File

@ -1,3 +1,6 @@
import { LucideProps } from "lucide-react";
import { ForwardRefExoticComponent, ReactNode, RefAttributes } from "react";
export interface User { export interface User {
id: number; id: number;
name: string; name: string;
@ -16,6 +19,11 @@ export type PageProps<
export type MenuItemProp = { export type MenuItemProp = {
title: string; title: string;
href: string; href: string;
icon?:
| ForwardRefExoticComponent<
Omit<LucideProps, "ref"> & RefAttributes<SVGSVGElement>
>
| ReactNode;
variant: variant:
| "link" | "link"
| "default" | "default"