update sidebar
All checks were successful
Build and Deploy Next.js + Nginx Docker Image / build-and-deploy (push) Successful in 50s
Build and Deploy Next.js + Nginx Docker Image / deploy (push) Successful in 7s

This commit is contained in:
Mohamadzadeh 2026-08-19 17:59:03 +03:30
parent c3d9a696f6
commit be4a6ed120
5 changed files with 264 additions and 209 deletions

View File

@ -814,3 +814,26 @@ function UsersTable({ data, columns }) {
);
}
```
## Sidebar.Exit
`Sidebar` به‌صورت پیش‌فرض گزینه خروج را نمایش نمی‌دهد. برای فعال‌کردن خروج Keycloak داخلی UI Kit، آن را به‌صورت compound component اضافه کنید:
```jsx
<Sidebar
items={items}
currentPath={router.asPath}
linkComponent={NextLink}
>
<Sidebar.Exit />
</Sidebar>
```
عنوان و آیکون خروج قابل تغییر هستند:
```jsx
<Sidebar.Exit title="خروج از سامانه" icon={MyExitIcon} />
```
`Sidebar.Exit` از `logoutKeycloak` داخلی UI Kit استفاده می‌کند و callback خروج از سامانه مقصد نیاز ندارد.

12
dist/layout.d.ts vendored
View File

@ -13,8 +13,7 @@ export type SidebarProps = {
currentPath?: string;
linkComponent?: any;
onNavigate?: (item: SidebarItem) => void;
logoutTitle?: string;
logoutIcon?: any;
children?: any;
primaryMobileCount?: number;
moreTitle?: string;
moreDrawerTitle?: string;
@ -22,7 +21,14 @@ export type SidebarProps = {
mobileProps?: Record<string, any>;
};
export function Sidebar(props: SidebarProps): any;
export type SidebarExitProps = {
title?: string;
icon?: any;
};
export const Sidebar: ((props: SidebarProps) => any) & {
Exit: (props: SidebarExitProps) => any;
};
export function isSidebarPathActive(
currentPath: string | undefined,
item: SidebarItem,

322
dist/layout.js vendored

File diff suppressed because one or more lines are too long

View File

@ -19,9 +19,9 @@ import {
VStack,
useDisclosure,
} from "@chakra-ui/react";
import { forwardRef, useEffect, useRef } from "react";
import { FiMoreHorizontal } from "react-icons/fi";
import { IoExit } from "react-icons/io5";
import { Children, forwardRef, isValidElement, useEffect, 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";
@ -165,13 +165,14 @@ const MobileNavigationButton = ({
);
};
export const Sidebar = ({
const SidebarExit = () => null;
const SidebarRoot = ({
items = [],
currentPath,
linkComponent,
onNavigate,
logoutTitle = "خروج",
logoutIcon = IoExit,
children,
primaryMobileCount = 4,
moreTitle = "بیشتر",
moreDrawerTitle = "سایر بخش‌های سامانه",
@ -182,6 +183,13 @@ export const Sidebar = ({
const moreButtonRef = useRef();
const { cannot, can, isLoading } = 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;
useEffect(() => {
if (isLoading) return;
@ -209,7 +217,7 @@ export const Sidebar = ({
const active = (item) => isSidebarPathActive(resolvedCurrentPath, item);
const isSecondaryItemActive = secondaryItems.some(active);
const hasMoreContent = true;
const hasMoreContent = secondaryItems.length > 0 || hasExit;
const handleNavigate = (item) => {
onNavigate?.(item);
@ -252,25 +260,27 @@ export const Sidebar = ({
/>
))}
<Tooltip
label={logoutTitle}
placement="left-start"
hasArrow
rounded="md"
py={2}
px={4}
fontWeight="bold"
closeDelay={150}
>
<IconButton
variant="ghost"
icon={<Icon as={logoutIcon} />}
fontSize="xl"
size={{ base: "sm", lg: "lg" }}
aria-label={logoutTitle}
onClick={handleLogout}
/>
</Tooltip>
{hasExit && (
<Tooltip
label={logoutTitle}
placement="left-start"
hasArrow
rounded="md"
py={2}
px={4}
fontWeight="bold"
closeDelay={150}
>
<IconButton
variant="ghost"
icon={<Icon as={logoutIcon} />}
fontSize="xl"
size={{ base: "sm", lg: "lg" }}
aria-label={logoutTitle}
onClick={handleLogout}
/>
</Tooltip>
)}
</VStack>
</Stack>
@ -414,24 +424,26 @@ export const Sidebar = ({
})}
</SimpleGrid>
<Box mt={4} pt={4} borderTopWidth="1px" borderColor="gray.200">
<HStack
as="button"
type="button"
w="full"
justify="center"
py={3}
borderRadius="xl"
border={0}
color="red.600"
bg="red.50"
cursor="pointer"
onClick={handleLogout}
>
<Icon as={logoutIcon} boxSize={5} />
<Text fontWeight="bold">خروج از سامانه</Text>
</HStack>
</Box>
{hasExit && (
<Box mt={4} pt={4} borderTopWidth="1px" borderColor="gray.200">
<HStack
as="button"
type="button"
w="full"
justify="center"
py={3}
borderRadius="xl"
border={0}
color="red.600"
bg="red.50"
cursor="pointer"
onClick={handleLogout}
>
<Icon as={logoutIcon} boxSize={5} />
<Text fontWeight="bold">{logoutTitle}</Text>
</HStack>
</Box>
)}
</DrawerBody>
</DrawerContent>
</Drawer>
@ -439,3 +451,7 @@ export const Sidebar = ({
</>
);
};
export const Sidebar = Object.assign(SidebarRoot, {
Exit: SidebarExit,
});

12
src/layout/index.d.ts vendored
View File

@ -13,8 +13,7 @@ export type SidebarProps = {
currentPath?: string;
linkComponent?: any;
onNavigate?: (item: SidebarItem) => void;
logoutTitle?: string;
logoutIcon?: any;
children?: any;
primaryMobileCount?: number;
moreTitle?: string;
moreDrawerTitle?: string;
@ -22,7 +21,14 @@ export type SidebarProps = {
mobileProps?: Record<string, any>;
};
export function Sidebar(props: SidebarProps): any;
export type SidebarExitProps = {
title?: string;
icon?: any;
};
export const Sidebar: ((props: SidebarProps) => any) & {
Exit: (props: SidebarExitProps) => any;
};
export function isSidebarPathActive(
currentPath: string | undefined,
item: SidebarItem,