"use client";
import {
Box,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerHeader,
DrawerOverlay,
HStack,
Icon,
IconButton,
Link,
SimpleGrid,
Stack,
Text,
Tooltip,
VStack,
useDisclosure,
} from "@chakra-ui/react";
import { Children, forwardRef, isValidElement, useRef } from "react";
import { FiMoreHorizontal } from "react-icons/fi/index.js";
import { IoExit } from "react-icons/io5/index.js";
import { logoutKeycloak } from "../core/keycloak.jsx";
import { usePermission } from "../core/permission.jsx";
const normalizeSidebarPath = (value = "") => {
const path = String(value).split(/[?#]/)[0] || "";
if (!path || path === "/") return path || "/";
return path.replace(/\/+$/, "");
};
export const isSidebarPathActive = (currentPath, item) => {
const normalizedPath = normalizeSidebarPath(currentPath);
const candidatePaths = [item?.path, ...(item?.aliases || [])]
.filter(Boolean)
.map(normalizeSidebarPath);
return candidatePaths.some(
(path) => normalizedPath === path || normalizedPath.startsWith(`${path}/`),
);
};
export const getActiveSidebarItem = (items = [], currentPath) =>
items.find((item) => isSidebarPathActive(currentPath, item));
const SidebarLink = forwardRef(
({ linkComponent, href, children, ...props }, ref) => {
const linkProps = linkComponent ? { as: linkComponent } : {};
return (
{children}
);
},
);
SidebarLink.displayName = "SidebarLink";
const DesktopNavItem = ({
item,
isActive,
isDisabled,
linkComponent,
onNavigate,
}) => {
const commonProps = {
variant: "ghost",
position: "relative",
bg: "transparent",
_before: isActive
? {
content: '""',
position: "absolute",
top: 0,
bottom: 0,
insetInlineStart: 0,
insetInlineEnd: 0,
bg: "gray.200",
borderRadius: "md",
zIndex: 0,
}
: undefined,
sx: {
"> svg": {
position: "relative",
zIndex: 1,
},
},
icon: ,
fontSize: "xl",
size: { base: "sm", lg: "md" },
"aria-label": item.title,
"aria-current": isActive ? "page" : undefined,
};
let content;
if (isDisabled || isActive) {
content = (
);
} else {
const linkProps = linkComponent
? { as: linkComponent, href: item.path }
: { as: "a", href: item.path };
content = (
);
}
return (
{content}
);
};
const MobileNavigationButton = ({
item,
isActive,
isDisabled,
linkComponent,
onNavigate,
}) => {
const content = (
{item.mobileTitle || item.title}
);
if (isDisabled || isActive) {
return (
{content}
);
}
return (
{content}
);
};
const SidebarExit = () => null;
const SidebarRoot = ({
items = [],
currentPath,
linkComponent,
onNavigate,
children,
primaryMobileCount = 4,
moreTitle = "بیشتر",
moreDrawerTitle = "سایر بخشهای سامانه",
desktopProps,
mobileProps,
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const moreButtonRef = useRef();
const { cannot } = usePermission();
const exitElement = Children.toArray(children).find(
(child) => isValidElement(child) && child.type === SidebarExit,
);
const hasExit = Boolean(exitElement);
const logoutTitle = exitElement?.props?.title || "خروج";
const logoutIcon = exitElement?.props?.icon || IoExit;
const resolvedCurrentPath =
currentPath ??
(typeof window !== "undefined"
? `${window.location.pathname}${window.location.search}`
: "");
const primaryItems = items.slice(0, primaryMobileCount);
const secondaryItems = items.slice(primaryMobileCount);
const disabled = (item) => {
const permissions = Array.isArray(item?.perm) ? item.perm : [item?.perm];
return permissions.filter(Boolean).some((permission) => cannot(permission));
};
const active = (item) => isSidebarPathActive(resolvedCurrentPath, item);
const isSecondaryItemActive = secondaryItems.some(active);
const hasMoreContent = secondaryItems.length > 0 || hasExit;
const handleNavigate = (item) => {
onNavigate?.(item);
onClose();
};
const handleLogout = () => {
onClose();
logoutKeycloak();
};
return (
<>
{items.map((item) => (
onNavigate?.(item)}
/>
))}
{hasExit && (
}
fontSize="xl"
size={{ base: "sm", lg: "lg" }}
aria-label={logoutTitle}
onClick={handleLogout}
/>
)}
{primaryItems.map((item) => (
handleNavigate(item)}
/>
))}
{hasMoreContent && (
{moreTitle}
)}
{hasMoreContent && (
{moreDrawerTitle}
{secondaryItems.map((item) => {
const isDisabled = disabled(item);
const isActive = active(item);
const content = (
{item.mobileTitle || item.title}
);
if (isDisabled || isActive) {
return (
{content}
);
}
return (
handleNavigate(item)}
aria-current={isActive ? "page" : undefined}
>
{content}
);
})}
{hasExit && (
{logoutTitle}
)}
)}
>
);
};
export const Sidebar = Object.assign(SidebarRoot, {
Exit: SidebarExit,
});