feat: dynamic menu item support
parent
5a5e3c00f3
commit
a425c78bdf
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue