Files
tabler/shared/components/navbar/NavbarMenu.astro

105 lines
4.0 KiB
Plaintext

---
import Icon from '@ui/Icon.astro'
import NavbarMenuItem, { type Level2 } from './NavbarMenuItem.astro'
import menu from '@data/menu.json'
import menuSample from '@data/menu-sample.json'
interface Level1 {
'title': string
'title-long'?: string
'icon'?: string
'url'?: string
'badge'?: string
'active'?: boolean
'disabled'?: boolean
'right'?: boolean
'columns'?: number
/** section label rendered before this item (sidebar only, needs the `sections` prop) */
'section'?: string
'children'?: Record<string, Level2>
}
interface Props {
/** active menu entry, e.g. "dashboards.default" */
pageMenu?: string | undefined
sample?: boolean
hideIcons?: boolean
longTitles?: boolean
keepOpen?: boolean
autoOpen?: boolean
/** renders `section` fields from the menu data as group labels */
sections?: boolean
class?: string
}
const { pageMenu = '', sample = false, hideIcons = false, longTitles = false, keepOpen = false, autoOpen = false, sections = false, class: className } = Astro.props
const currentPage = pageMenu.split('.')
const items = Object.entries((sample ? menuSample : menu) as Record<string, Level1>)
// per-column = ceil(size / columns), break every per-column.
const chunk = <T,>(arr: T[], size: number): T[][] => {
const out: T[][] = []
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size))
return out
}
---
<!-- BEGIN NAVBAR MENU -->
<ul class:list={['navbar-nav', className]}>
{
items.map(([key, item]) => {
const active = key === currentPage[0] || item.active
const hasChildren = !!item.children
const children = Object.entries(item.children ?? {})
const columns = item.columns ? chunk(children, Math.ceil(children.length / item.columns)) : null
const title = longTitles ? (item['title-long'] ?? item.title) : item.title
return (
<Fragment>
{sections && item.section && <li class="nav-section-title">{item.section}</li>}
<Fragment set:html={'<!-- BEGIN NAVBAR ITEM -->'} />
<li class:list={['nav-item', active && 'active', hasChildren && 'dropdown']}>
<a
class:list={['nav-link', hasChildren && 'dropdown-toggle', item.disabled && 'disabled']}
href={hasChildren ? '#' : `./${item.url}`}
data-bs-toggle={hasChildren ? 'dropdown' : undefined}
data-bs-auto-close={hasChildren ? (keepOpen ? 'false' : 'outside') : undefined}
role={hasChildren ? 'button' : undefined}
aria-haspopup={hasChildren ? 'true' : undefined}
aria-expanded={hasChildren ? (autoOpen && key === currentPage[0] ? 'true' : 'false') : undefined}
aria-current={active && !hasChildren ? 'page' : undefined}
>
{!hideIcons && <span class="nav-link-icon">{item.icon && <Icon name={item.icon} />}</span>}
<span class="nav-link-title">{title}</span>
{item.badge && <span class="badge badge-sm bg-red text-red-fg">{item.badge}</span>}
</a>
{hasChildren && (
<div class:list={['dropdown-menu', item.right && 'dropdown-menu-end', autoOpen && key === currentPage[0] && 'show']}>
{columns ? (
<div class="dropdown-menu-columns">
{columns.map((column) => (
<div class="dropdown-menu-column">
{column.map(([childKey, child]) => (
<NavbarMenuItem level1Key={key} itemKey={childKey} item={child} currentPage={currentPage} keepOpen={keepOpen} />
))}
</div>
))}
</div>
) : (
children.map(([childKey, child]) => <NavbarMenuItem level1Key={key} itemKey={childKey} item={child} currentPage={currentPage} keepOpen={keepOpen} />)
)}
</div>
)}
</li>
<Fragment set:html={'<!-- END NAVBAR ITEM -->'} />
</Fragment>
)
})
}
</ul>
<!-- END NAVBAR MENU -->