# Navbar rendering in module panel Technical documentation for building and rendering the navbar in the admin panel. ## Purpose The navbar is built declaratively from a `NavbarItem` tree, not from hardcoded HTML menus. Tree sources: 1. Core nodes from `CoreNavbarProvider::register()`. 2. Module nodes from `ModuleInterface::registerNavbar()`. ## Lifecycle 1. `ModuleLoader::bootAll()` calls `CoreNavbarProvider::register()`. 2. Then `registerNavbar()` is called for each loaded module. 3. In `Public/Views/admin/header.php`, the tree is rendered from `NavbarRegistry`. ## Rendering in header Rendering is done by helper functions: 1. `_xc_nav_visible()` - visibility filtering for a node. 2. `_xc_nav_label()` - text label resolution. 3. `_xc_nav_children()` - recursive rendering of child items. Top-level nodes come from `NavbarRegistry::getTopLevel()`, child nodes come from `NavbarRegistry::getChildren($key)`. ## Visibility rules Checks are performed in `_xc_nav_visible()`: 1. `desktopOnly`: hides node on mobile. 2. `settingDisabled`: hides node when a setting flag is enabled. 3. `permissions`: OR-check via `Authorization::check('adv', $permission)`. 4. A group with `url='#'` is shown only if at least one child is visible. 5. `divider` is always passed through and rendered as separator. ## Rendering specifics 1. `divider` renders as separator without a link. 2. `submenuClass('megamenu')` enables two-column rendering for long lists. 3. `noMobileSubmenu` disables child submenu expansion on mobile. ## How a module adds a menu item A module adds items only through `registerNavbar()`: ```php public function registerNavbar(NavbarRegistry $registry): void { NavbarRegistry::add((new NavbarItem('management.service_setup.my_module')) ->parent('management.service_setup') ->url('my_module') ->label('my_module') ->permissions(['my_module']) ->order(60)); NavbarRegistry::add((new NavbarItem('management.logs.my_module_log')) ->parent('management.logs') ->url('my_module_logs') ->label('', 'My Module Logs') ->permissions(['my_module']) ->order(170)); } ``` ## NavbarItem builder API `NavbarItem` is a fluent value object (`src/Core/Module/NavbarItem.php`) — chain setters off `new NavbarItem($key)`: | Method | Purpose | | --- | --- | | `new NavbarItem($key)` | create a node; `$key` is its unique `section.group.item` id | | `->parent($parentKey)` | attach under an existing node (omit for a top-level node) | | `->url($url)` | target path; `'#'` makes it a non-navigating **group** header | | `->label($key, $fallback = '')` | translation key, or `('', 'Literal')` for fixed text | | `->icon($icon)` | icon CSS class for the item | | `->permissions([...])` | OR-list of permission keys; node hidden unless the viewer has one | | `->order($n)` | sort position within the parent | | `->desktopOnly()` | hide on mobile | | `->noMobileSubmenu()` | don't expand this node's submenu on mobile | | `->submenuClass('megamenu')` | two-column rendering for long child lists | | `->settingDisabled($settingKey)` | hide the node when that panel setting flag is truthy | | `->makeDivider()` | render this node as a separator (no link) | ### Group node and divider ```php public function registerNavbar(NavbarRegistry $registry): void { // A group header (url('#')) — shown only if at least one child is visible NavbarRegistry::add((new NavbarItem('management.my_group')) ->parent('management') ->url('#') ->label('my_group') ->order(50)); // A divider inside that group NavbarRegistry::add((new NavbarItem('management.my_group.sep1')) ->parent('management.my_group') ->makeDivider() ->order(55)); } ``` > `settingDisabled('some_setting')` hides the node whenever that setting is truthy (gate a feature behind a toggle). Visibility is also **viewer-scoped**: the `permissions` OR-check runs against the current user via `Authorization::check('adv', …)`, so an admin and a reseller can see different subsets of the same tree. ## Practical rules for modules 1. Use unique `key` values in `section.group.item` format. 2. Set `parent` to an existing core tree node or your own already-added node. 3. Position items using `order` inside one parent. 4. Use `label('translation_key')` for translatable text. 5. Use `label('', 'Literal Text')` for fixed literal text. 6. If the module has no menu items, keep `registerNavbar()` empty. ## Related files | File | Role | | --- | --- | | `src/Core/Module/NavbarRegistry.php` | Collects navbar items from providers | | `src/Core/Module/NavbarItem.php` | Navbar item value object | | `src/Core/Module/CoreNavbarProvider.php` | Built-in core menu items | | `src/Public/Views/admin/header.php` | Renders the navbar tree |