import {
Refine,
useTranslate,
useLogout,
useTitle,
CanAccess,
ITreeMenu,
useIsExistAuthentication,
useRouterContext,
useMenu,
useRefineContext,
} from '@pankod/refine-core'
import {
Layout,
AntdLayout,
Grid,
Icons,
Menu,
Title as DefaultTitle,
} from '@pankod/refine-antd'
import routerProvider from '@pankod/refine-react-router-v6'
import dataProvider from '@pankod/refine-simple-rest'
import { PostList } from './pages/posts'
const API_URL = 'https://api.fake-rest.refine.dev'
export type SiderRenderProps = {
items: JSX.Element[]
logout: React.ReactNode
dashboard: React.ReactNode
}
export type RefineLayoutSiderProps = {
render?: (props: SiderRenderProps) => React.ReactNode
}
const { DashboardOutlined, LogoutOutlined, UnorderedListOutlined } = Icons
const CustomSider: React.FC<RefineLayoutSiderProps> = ({ render }) => {
const [collapsed, setCollapsed] = useState<boolean>(false)
const isExistAuthentication = useIsExistAuthentication()
const { Link } = useRouterContext()
const { mutate: mutateLogout } = useLogout()
const Title = useTitle()
const translate = useTranslate()
const { menuItems, selectedKey, defaultOpenKeys } = useMenu()
const breakpoint = Grid.useBreakpoint()
const { hasDashboard } = useRefineContext()
const isMobile = typeof breakpoint.lg === 'undefined' ? false : !breakpoint.lg
const RenderToTitle = Title ?? DefaultTitle
const renderTreeView = (tree: ITreeMenu[], selectedKey: string) => {
return tree.map((item: ITreeMenu) => {
const { icon, label, route, name, children, parentName } = item
if (children.length > 0) {
return (
<CanAccess
key={route}
resource={name.toLowerCase()}
action="list"
params={{
resource: item,
}}
>
<SubMenu
key={route}
icon={icon ?? <UnorderedListOutlined />}
title={label}
>
{renderTreeView(children, selectedKey)}
</SubMenu>
</CanAccess>
)
}
const isSelected = route === selectedKey
const isRoute = !(parentName !== undefined && children.length === 0)
return (
<CanAccess
key={route}
resource={name.toLowerCase()}
action="list"
params={{
resource: item,
}}
>
<Menu.Item
key={route}
style={{
fontWeight: isSelected ? 'bold' : 'normal',
}}
icon={icon ?? (isRoute && <UnorderedListOutlined />)}
>
<Link to={route}>{label}</Link>
{!collapsed && isSelected && (
<div className="ant-menu-tree-arrow" />
)}
</Menu.Item>
</CanAccess>
)
})
}
const logout = isExistAuthentication ? (
<Menu.Item
key="logout"
onClick={() => mutateLogout()}
icon={<LogoutOutlined />}
>
{translate('buttons.logout', 'Logout')}
</Menu.Item>
) : null
const dashboard = hasDashboard ? (
<Menu.Item
key="dashboard"
style={{
fontWeight: selectedKey === '/' ? 'bold' : 'normal',
}}
icon={<DashboardOutlined />}
>
<Link to="/">{translate('dashboard.title', 'Dashboard')}</Link>
{!collapsed && selectedKey === '/' && (
<div className="ant-menu-tree-arrow" />
)}
</Menu.Item>
) : null
const items = renderTreeView(menuItems, selectedKey)
const renderSider = () => {
if (render) {
return render({
dashboard,
items,
logout,
})
}
return (
<>
{dashboard}
{items}
{logout}
</>
)
}
const antLayoutSider: CSSProperties = {
position: 'relative',
}
const antLayoutSiderMobile: CSSProperties = {
position: 'fixed',
height: '100vh',
zIndex: 999,
}
return (
<AntdLayout.Sider
collapsible
collapsed={collapsed}
onCollapse={(collapsed: boolean): void => setCollapsed(collapsed)}
collapsedWidth={isMobile ? 0 : 80}
breakpoint="lg"
style={isMobile ? antLayoutSiderMobile : antLayoutSider}
>
<RenderToTitle collapsed={collapsed} />
<Menu
selectedKeys={[selectedKey]}
defaultOpenKeys={defaultOpenKeys}
mode="inline"
onClick={() => {
if (!breakpoint.lg) {
setCollapsed(true)
}
}}
>
{renderSider()}
</Menu>
</AntdLayout.Sider>
)
}
const App: React.FC = () => {
return (
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(API_URL)}
Layout={Layout}
Sider={CustomSider}
resources={[
{
name: 'posts',
list: PostList,
},
]}
/>
)
}