import { NgTemplateOutlet } from '@angular/common'; import { booleanAttribute, ChangeDetectionStrategy, Component, computed, effect, inject, input, model, signal, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { type IsActiveMatchOptions, NavigationEnd, Router, RouterLink, } from '@angular/router'; import { filter, map, startWith } from 'rxjs'; import { AfricaniesIconComponent } from '@africanies/africanies-icons'; import { ModeColorService } from '@africanies/africanies-theme'; import { AFRICANIES_BRAND_LOGO_MINI_URL, AFRICANIES_BRAND_LOGO_URL } from '../../brand'; import { resolveActiveSideNavItem } from '../header-back.util'; import { navItemUrlTree } from '../nav-router.util'; import type { AfricaniesSideNavItem } from './side-nav-item'; const DEFAULT_LINK_ACTIVE: IsActiveMatchOptions = { paths: 'subset', queryParams: 'subset', fragment: 'ignored', matrixParams: 'ignored', }; /** * App shell side navigation with icons, nested items, and a collapsible rail. * * **Design - ink spine:** a continuous mode-accent edge; active leaves use * primary fill + white text (portal parity); expanded parents of an active * child use primary text on white. Collapsed mode keeps icons; hover opens a * floating label blade (name + children) instead of a plain tooltip. * * ## Parent items with children * **Expanded rail:** branches start open by default (`expandBranchesByDefault`). * Clicking a parent only opens/closes the branch (no route change). When any * branch is open, a sticky collapse-all control sits above the list (top right). * The rail expand/collapse control stays pinned in the logo header. Project * chrome above the scrollable list with `pre-nav` (e.g. shipping modes) or * below with `footer`. * **Collapsed rail:** clicking a parent expands the rail, opens the * branch, and activates the first enabled child (router or `activeId`). * * ## Router mode * Set `routerLink` on items. Active state follows the consumer's Router * (including cold loads). * * ## Local mode * Omit `routerLink` and bind `[(activeId)]`. * * @example * ```html * * ``` */ @Component({ selector: 'africanies-side-nav', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AfricaniesIconComponent, RouterLink, NgTemplateOutlet], host: { class: 'relative z-20 block h-full min-h-0', }, styleUrl: './africanies-side-nav.component.css', templateUrl: './africanies-side-nav.component.html', }) /** * App shell side navigation with icons, nested items, and a collapsible rail. */ export class SideNavComponent { protected readonly modeColor = inject(ModeColorService); private readonly defaultLogoUrl = inject(AFRICANIES_BRAND_LOGO_URL); private readonly defaultLogoMiniUrl = inject(AFRICANIES_BRAND_LOGO_MINI_URL); private readonly router = inject(Router); /** Navigation tree. */ readonly items = input.required(); /** When true, the rail collapses to icons only. Two-way bindable. */ readonly collapsed = model(false); /** * Active item id for local (non-routed) trees. Synced from the router when * items use `routerLink`. */ readonly activeId = model(null); /** Accessible label for the `