Add initial project files
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import { BarChart2, Settings, ShoppingCart, User, Users } from 'lucide-react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { cn } from '@crux-kit/utils';
|
||||
import { MenuConfig } from '#app/config/types';
|
||||
import { useMenuConfig } from '#app/menu';
|
||||
import { useMenu } from '#shared/hooks/useMenu';
|
||||
import { createMenuPathMatcher } from '#shared/lib/menuPath';
|
||||
import { Button } from '#shared/ui/Button';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from '#shared/ui/DropdownMenu';
|
||||
|
||||
export function SidebarMenu() {
|
||||
const { pathname } = useLocation();
|
||||
const { hasActiveChild } = useMenu(pathname);
|
||||
const config = useMenuConfig();
|
||||
|
||||
const menuConfig: MenuConfig = [
|
||||
{
|
||||
title: 'Boards',
|
||||
icon: BarChart2,
|
||||
path: '/'
|
||||
},
|
||||
{
|
||||
title: 'Profiles',
|
||||
icon: User,
|
||||
children: config?.[2].children
|
||||
},
|
||||
{
|
||||
title: 'Account',
|
||||
icon: Settings,
|
||||
children: config?.[3].children
|
||||
},
|
||||
{
|
||||
title: 'Store',
|
||||
icon: ShoppingCart,
|
||||
children: config?.[7].children
|
||||
},
|
||||
{
|
||||
title: 'Network',
|
||||
icon: Users,
|
||||
children: config?.[4].children
|
||||
}
|
||||
];
|
||||
|
||||
// A leaf highlights only when nothing deeper in the menu matches the route more closely.
|
||||
const isActive = createMenuPathMatcher(menuConfig, pathname);
|
||||
|
||||
const buildMenu = (items: MenuConfig) => {
|
||||
return items.map((item, index) => (
|
||||
<div key={index} className="flex flex-col items-center">
|
||||
{item.children ? (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
data-here={hasActiveChild(item.children) || undefined}
|
||||
className={cn(
|
||||
'flex h-15 w-15 flex-col items-center justify-center gap-1 rounded-lg shadow-none',
|
||||
'text-secondary-foreground bg-transparent text-xs font-medium',
|
||||
'hover:text-primary hover:bg-background hover:border-border',
|
||||
'data-[state=open]:text-primary data-[state=open]:bg-background data-[state=open]:border-border',
|
||||
'data-[here=true]:text-primary data-[here=true]:bg-background data-[here=true]:border-border'
|
||||
)}
|
||||
>
|
||||
{item.icon && <item.icon className="size-5!" />}
|
||||
{item.title}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" side="right" className="w-50">
|
||||
<DropdownMenuLabel>{item.title}</DropdownMenuLabel>
|
||||
{buildMenuChildren(item.children)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : (
|
||||
<Link
|
||||
data-active={isActive(item.path) || undefined}
|
||||
to={item.path || '#'}
|
||||
className={cn(
|
||||
'flex h-15 w-15.5 flex-col items-center justify-center gap-1 rounded-lg p-2',
|
||||
'text-muted-foreground bg-transparent text-xs font-medium',
|
||||
'hover:text-primary hover:bg-background hover:border-border',
|
||||
'data-[active=true]:text-primary data-[active=true]:bg-background data-[active=true]:border-border'
|
||||
)}
|
||||
>
|
||||
{item.icon && <item.icon className="size-5!" />}
|
||||
{item.title}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
));
|
||||
};
|
||||
|
||||
const buildMenuChildren = (items: MenuConfig) => {
|
||||
return items.map((item, index) => {
|
||||
if (item.disabled) return null;
|
||||
if (item.separator) return <DropdownMenuSeparator key={index} />;
|
||||
if (item.children) {
|
||||
return (
|
||||
<DropdownMenuSub key={index}>
|
||||
<DropdownMenuSubTrigger data-here={hasActiveChild(item.children) || undefined}>
|
||||
{item.title}
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent className="w-50">
|
||||
<DropdownMenuLabel>{item.title}</DropdownMenuLabel>
|
||||
{buildMenuChildren(item.children)}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<DropdownMenuItem key={index} asChild>
|
||||
<Link to={item.path || '#'}>{item.title}</Link>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="kt-scrollable-y-auto flex max-h-[calc(100vh-5rem)] grow flex-col gap-2.5 lg:max-h-[calc(100vh-6rem)]">
|
||||
{buildMenu(menuConfig)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user