112 lines
3.0 KiB
TypeScript
112 lines
3.0 KiB
TypeScript
import {
|
|
Component,
|
|
computed,
|
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
EventEmitter,
|
|
inject,
|
|
Input,
|
|
OnInit,
|
|
Output,
|
|
signal
|
|
} from '@angular/core';
|
|
import {Route, Router, RouterLink, RouterLinkActive} from '@angular/router';
|
|
import {MatListItem, MatListItemIcon, MatListItemTitle, MatNavList} from '@angular/material/list';
|
|
import {NgForOf} from '@angular/common';
|
|
import {MatIconButton} from '@angular/material/button';
|
|
import {MatTooltip} from '@angular/material/tooltip';
|
|
import {MatIcon} from '@angular/material/icon';
|
|
import {adminRouting} from '../../modules/admin-module/admin.routing';
|
|
|
|
export interface NavItem {
|
|
path: string; // absolute path like /dashboard
|
|
title: string; // label to show
|
|
icon?: string; // material icon name
|
|
exact?: boolean; // whether to match exactly
|
|
}
|
|
|
|
|
|
@Component({
|
|
selector: 'app-vertical-nav',
|
|
imports: [
|
|
MatTooltip,
|
|
MatIcon,
|
|
MatListItemIcon,
|
|
MatListItemTitle,
|
|
RouterLinkActive,
|
|
RouterLink,
|
|
MatListItem,
|
|
MatNavList,
|
|
NgForOf,
|
|
MatIconButton,
|
|
|
|
],
|
|
templateUrl: './vertical-nav.component.html',
|
|
styleUrl: './vertical-nav.component.scss',
|
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
|
})
|
|
export class VerticalNavComponent implements OnInit{
|
|
private router = inject(Router);
|
|
|
|
// Optional: if you want to pass items directly instead of deriving from routes
|
|
@Input() items: NavItem[] | null = null;
|
|
|
|
// Optional: pass a specific Route[] (e.g., ADMIN_ROUTES). If not provided, uses router.config.
|
|
@Input() routes: Route[] = [];
|
|
|
|
// Optional: prepend a base path (e.g., '/admin') when using feature routes.
|
|
@Input() basePath = '';
|
|
|
|
// Sidebar state (expanded/collapsed)
|
|
opened = signal(true);
|
|
|
|
navItems = signal<NavItem[]>([]);
|
|
readonly hasItems = computed(() => (this.items?.length ?? this.navItems().length) > 0);
|
|
|
|
@Output() collapsedChange = new EventEmitter<boolean>();
|
|
|
|
|
|
constructor() {
|
|
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
|
|
this.navItems.set(this.deriveFromRoutes(this.routes, this.basePath));
|
|
}
|
|
|
|
toggle(): void {
|
|
this.opened.update(v => !v);
|
|
this.collapsedChange.emit(!this.opened());
|
|
}
|
|
|
|
private deriveFromRoutes(routes: Route[], base: string): NavItem[] {
|
|
// Flatten one level; you can make this recursive if needed
|
|
const items: NavItem[] = [];
|
|
for (const r of routes) {
|
|
if (!r || r.path === '**') continue;
|
|
|
|
const data = (r as any).data || {};
|
|
const seg = r.path || '';
|
|
const nextBase = [base, seg].filter(Boolean).join('/');
|
|
|
|
/* // Recurse into children if present (common in feature modules)
|
|
if (r.children && r.children.length) {
|
|
items.push(...this.deriveFromRoutes(r.children, nextBase));
|
|
}
|
|
*/
|
|
if (data.nav === false) continue;
|
|
|
|
const title = data.title as string | undefined;
|
|
if (!title) continue;
|
|
|
|
const icon = data.icon as string | undefined;
|
|
const full = '/' + nextBase.replace(/\/+/g, '/');
|
|
items.push({ path: full, title, icon, exact: true });
|
|
}
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
}
|