Enhance sidebar menu experience
This commit is contained in:
parent
b9214dfe88
commit
f4ddd8461d
|
|
@ -4,9 +4,11 @@ import { Text } from "@mantine/core";
|
|||
|
||||
import classNames from "./styles/sidebarChildMenu.module.css";
|
||||
import SidebarMenu from "../types/SidebarMenu";
|
||||
import dashboardConfig from "../dashboard.config";
|
||||
|
||||
interface Props {
|
||||
item: NonNullable<SidebarMenu["children"]>[number];
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -26,7 +28,8 @@ export default function ChildMenu(props: Props) {
|
|||
<Text<"a">
|
||||
component="a"
|
||||
className={classNames.link}
|
||||
href={`/dashboard${linkPath}`}
|
||||
href={`${dashboardConfig.baseRoute}${linkPath}`}
|
||||
fw={props.active ? "bold" : "normal"}
|
||||
>
|
||||
{props.item.label}
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { ReactNode, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import {
|
||||
Box,
|
||||
|
|
@ -6,7 +6,9 @@ import {
|
|||
Group,
|
||||
ThemeIcon,
|
||||
UnstyledButton,
|
||||
alpha,
|
||||
rem,
|
||||
useMantineTheme,
|
||||
} from "@mantine/core";
|
||||
import { TbChevronRight } from "react-icons/tb";
|
||||
import * as TbIcons from "react-icons/tb";
|
||||
|
|
@ -14,6 +16,9 @@ import * as TbIcons from "react-icons/tb";
|
|||
import ChildMenu from "./SidebarChildMenu";
|
||||
import classNames from "./styles/sidebarMenuItem.module.css";
|
||||
import SidebarMenu from "../types/SidebarMenu";
|
||||
import dashboardConfig from "../dashboard.config";
|
||||
import { usePathname } from "next/navigation";
|
||||
import areURLsSame from "@/utils/areUrlSame";
|
||||
|
||||
interface Props {
|
||||
menu: SidebarMenu;
|
||||
|
|
@ -30,7 +35,11 @@ interface Props {
|
|||
export default function MenuItem({ menu }: Props) {
|
||||
const hasChildren = Array.isArray(menu.children);
|
||||
|
||||
const [opened, setOpened] = useState(false);
|
||||
const pathname = usePathname()
|
||||
|
||||
const theme = useMantineTheme();
|
||||
|
||||
const [opened, setOpened] = useState(menu.children?.some(child => areURLsSame(`${dashboardConfig.baseRoute}${child.link}`, pathname)) ?? false);
|
||||
|
||||
const toggleOpenMenu = () => {
|
||||
setOpened((prev) => !prev);
|
||||
|
|
@ -38,20 +47,23 @@ export default function MenuItem({ menu }: Props) {
|
|||
|
||||
// Mapping children menu items if available
|
||||
const subItems = (hasChildren ? menu.children! : []).map((child, index) => (
|
||||
<ChildMenu key={index} item={child} />
|
||||
<ChildMenu key={index} item={child} active={areURLsSame(`${dashboardConfig.baseRoute}${child.link}`, pathname)} />
|
||||
));
|
||||
|
||||
const Icons = TbIcons as any;
|
||||
|
||||
const Icon = typeof menu.icon === "string" ? Icons[menu.icon] : menu.icon;
|
||||
// const a = typeof menu.icon === "string"
|
||||
|
||||
const isActive = areURLsSame(`${dashboardConfig.baseRoute}${menu.link}`, pathname)
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Main Menu Item */}
|
||||
<UnstyledButton
|
||||
<UnstyledButton<"a" | "button">
|
||||
onClick={toggleOpenMenu}
|
||||
className={classNames.control}
|
||||
href={menu.link ? dashboardConfig.baseRoute + menu.link : ""}
|
||||
component={menu.link ? "a" : "button"}
|
||||
>
|
||||
<Group justify="space-between" gap={0}>
|
||||
{/* Icon and Label */}
|
||||
|
|
@ -60,7 +72,7 @@ export default function MenuItem({ menu }: Props) {
|
|||
<Icon style={{ width: rem(18), height: rem(18) }} />
|
||||
</ThemeIcon>
|
||||
|
||||
<Box ml="md">{menu.label}</Box>
|
||||
<Box ml="md" fw={isActive ? 700 : 500}>{menu.label}</Box>
|
||||
</Box>
|
||||
|
||||
{/* Chevron Icon for collapsible items */}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const sidebarMenus: SidebarMenu[] = [
|
|||
label: "Dashboard",
|
||||
icon: "TbLayoutDashboard",
|
||||
allowedPermissions: ["*"],
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
label: "Users",
|
||||
|
|
|
|||
11
src/modules/dashboard/types/SidebarMenu.d.ts
vendored
11
src/modules/dashboard/types/SidebarMenu.d.ts
vendored
|
|
@ -7,10 +7,11 @@ export default interface SidebarMenu {
|
|||
children?: {
|
||||
label: string;
|
||||
link: string;
|
||||
allowedPermissions?: PermissionCode[],
|
||||
allowedRoles?: RoleCode[],
|
||||
allowedPermissions?: PermissionCode[];
|
||||
allowedRoles?: RoleCode[];
|
||||
}[];
|
||||
link?: string;
|
||||
color?: ThemeIconProps["color"];
|
||||
allowedPermissions?: PermissionCode[],
|
||||
allowedRoles?: RoleCode[]
|
||||
}
|
||||
allowedPermissions?: PermissionCode[];
|
||||
allowedRoles?: RoleCode[];
|
||||
}
|
||||
|
|
|
|||
13
src/utils/areUrlSame.ts
Normal file
13
src/utils/areUrlSame.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
function areURLsSame(url1: string, url2: string): boolean {
|
||||
const normalizeUrl = (url: string) => {
|
||||
// Remove query parameters
|
||||
url = url.split("?")[0];
|
||||
// Ensure there is no trailing slash
|
||||
url = url.endsWith("/") ? url.slice(0, -1) : url;
|
||||
return url.toLowerCase();
|
||||
};
|
||||
|
||||
return normalizeUrl(url1) === normalizeUrl(url2);
|
||||
}
|
||||
|
||||
export default areURLsSame;
|
||||
Loading…
Reference in New Issue
Block a user