UNPKG

react-antd-admin-panel

Version:

Modern TypeScript-first React admin panel builder with Ant Design 6

173 lines 5.81 kB
import React from 'react'; import { Layout, Menu } from 'antd'; import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; import { useMain, useUser } from './MainContext'; import { ProfileMenu } from './ProfileMenu'; const { Header, Sider, Content } = Layout; /** * AppLayout - Main application layout with sidebar navigation * Renders a collapsible sidebar with menu items generated from sections */ export function AppLayout({ sections, currentPath, sidebarConfig, children }) { const main = useMain(); const user = useUser(); const config = sidebarConfig || main.config.sidebar || {}; const { logo, collapsedLogo, title, defaultCollapsed = false, theme = 'dark', width = 256, collapsedWidth = 80, footer, } = config; const [collapsed, setCollapsed] = React.useState(defaultCollapsed); // Build menu items from sections const menuItems = React.useMemo(() => { const items = []; for (const [path, route] of Object.entries(sections)) { // Skip hidden routes if (route.hidden) continue; // Check access if (!main.canAccess(route)) continue; // Handle nested routes if (route.children) { const childItems = []; for (const [childPath, childRoute] of Object.entries(route.children)) { if (childRoute.hidden) continue; if (!main.canAccess(childRoute)) continue; childItems.push({ key: `${path}${childPath}`, label: childRoute.title, icon: childRoute.icon, }); } if (childItems.length > 0) { items.push({ key: path, label: route.title, icon: route.icon, children: childItems, }); } } else { items.push({ key: path, label: route.title, icon: route.icon, }); } } return items; }, [sections, main]); // Handle menu click const handleMenuClick = ({ key }) => { main.navigate(key); }; // Determine selected keys const selectedKeys = [currentPath]; const openKeys = Object.keys(sections).filter(path => { const section = sections[path]; return currentPath.startsWith(path) && section && section.children; }); // Render logo const renderLogo = () => { const logoContent = collapsed ? collapsedLogo || logo : logo; if (!logoContent) return null; const logoElement = typeof logoContent === 'string' ? React.createElement('img', { src: logoContent, alt: 'Logo', style: { height: 32, objectFit: 'contain' }, }) : logoContent; return React.createElement('div', { style: { height: 64, display: 'flex', alignItems: 'center', justifyContent: collapsed ? 'center' : 'flex-start', padding: collapsed ? 0 : '0 16px', gap: 12, }, }, logoElement, !collapsed && title && React.createElement('span', { style: { color: theme === 'dark' ? '#fff' : '#000', fontSize: 18, fontWeight: 600, whiteSpace: 'nowrap', }, }, title)); }; return React.createElement(Layout, { style: { minHeight: '100vh' } }, // Sidebar React.createElement(Sider, { collapsible: true, collapsed, onCollapse: setCollapsed, theme, width, collapsedWidth, trigger: null, style: { overflow: 'auto', height: '100vh', position: 'fixed', left: 0, top: 0, bottom: 0, }, }, renderLogo(), React.createElement(Menu, { theme, mode: 'inline', selectedKeys, defaultOpenKeys: openKeys, items: menuItems, onClick: handleMenuClick, }), footer && React.createElement('div', { style: { position: 'absolute', bottom: 48, left: 0, right: 0, padding: '0 16px', }, }, footer)), // Main content area React.createElement(Layout, { style: { marginLeft: collapsed ? collapsedWidth : width, transition: 'margin-left 0.2s', }, }, // Header React.createElement(Header, { style: { padding: '0 24px', background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'space-between', boxShadow: '0 1px 4px rgba(0, 21, 41, 0.08)', position: 'sticky', top: 0, zIndex: 1, }, }, // Collapse trigger React.createElement('div', { onClick: () => setCollapsed(!collapsed), style: { cursor: 'pointer', fontSize: 18 }, }, React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined)), // Profile menu user && React.createElement(ProfileMenu, {})), // Content React.createElement(Content, { style: { margin: 24, padding: 24, background: '#fff', minHeight: 280, borderRadius: 8, }, }, children))); } //# sourceMappingURL=AppLayout.js.map