|
activeLink
|
Type : string
|
|
|
The menu item link that is currently displayed in the app
in order to highlight the active menu.
|
|
item
|
Type : MenuItem
|
|
Required : true
|
|
|
The menu item to be displayed.
|
Methods
|
toggleSubMenu
|
toggleSubMenu()
|
|
|
|
|
|
isExpanded
|
Type : boolean
|
Default value : false
|
|
|
|
linkPath
|
Type : unknown
|
Default value : computed(() => this.item().link?.split("?")[0])
|
|
|
The path portion of item.link, without query parameters.
e.g. "/user?type=X" → "/user"
|
|
linkQueryParams
|
Type : unknown
|
Default value : computed((): Record<string, string> | undefined => {
const qs = this.item().link?.split("?")[1];
if (!qs) return undefined;
const params: Record<string, string> = {};
new URLSearchParams(qs).forEach((value, key) => (params[key] = value));
return params;
})
|
|
|
The query parameters parsed from item.link, if any.
e.g. "/user?type=X" → { type: "X" }
|
import {
ChangeDetectionStrategy,
Component,
computed,
input,
} from "@angular/core";
import { MatListModule } from "@angular/material/list";
import { FaIconComponent } from "@fortawesome/angular-fontawesome";
import { FaDynamicIconComponent } from "../../../common-components/fa-dynamic-icon/fa-dynamic-icon.component";
import { RouterLink } from "@angular/router";
import { Angulartics2Module } from "angulartics2";
import { MenuItem } from "../menu-item";
import { MatMenuModule } from "@angular/material/menu";
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "app-menu-item",
templateUrl: "./menu-item.component.html",
styleUrls: ["./menu-item.component.scss"],
imports: [
MatListModule,
FaIconComponent,
FaDynamicIconComponent,
RouterLink,
Angulartics2Module,
MatMenuModule,
],
standalone: true,
})
export class MenuItemComponent {
/**
* The menu item to be displayed.
*/
item = input.required<MenuItem>();
/**
* The menu item link that is currently displayed in the app
* in order to highlight the active menu.
*/
activeLink = input<string>();
/**
* The path portion of item.link, without query parameters.
* e.g. "/user?type=X" → "/user"
*/
linkPath = computed(() => this.item().link?.split("?")[0]);
/**
* The query parameters parsed from item.link, if any.
* e.g. "/user?type=X" → { type: "X" }
*/
linkQueryParams = computed((): Record<string, string> | undefined => {
const qs = this.item().link?.split("?")[1];
if (!qs) return undefined;
const params: Record<string, string> = {};
new URLSearchParams(qs).forEach((value, key) => (params[key] = value));
return params;
});
isExpanded: boolean = false;
toggleSubMenu(): void {
this.isExpanded = !this.isExpanded;
}
hasSubMenu(item: MenuItem): boolean {
return !!item.subMenu && item.subMenu.length > 0;
}
}
<mat-list-item
angulartics2On="click"
angularticsCategory="Navigation"
angularticsAction="app_navigation_link_click"
[angularticsLabel]="item().label"
[routerLink]="item().link ? linkPath() : undefined"
[queryParams]="linkQueryParams()"
class="indent-item"
[class.matched-background]="item().link === activeLink()"
(click)="toggleSubMenu()"
>
<a class="flex-row gap-small">
<app-fa-dynamic-icon
class="nav-icon"
[icon]="item().icon"
></app-fa-dynamic-icon>
<div class="menu-label" [title]="item().label">{{ item().label }}</div>
@if (hasSubMenu(item())) {
<fa-icon [icon]="isExpanded ? 'chevron-down' : 'chevron-right'"></fa-icon>
}
</a>
</mat-list-item>
<!-- Render submenus if they exist and are expanded -->
@if (isExpanded) {
<div class="submenu">
@for (subItem of item().subMenu; track subItem.label) {
<!-- Pass activeLink to each submenu item to highlight the active submenu -->
<app-menu-item
[item]="subItem"
[activeLink]="activeLink()"
></app-menu-item>
}
</div>
}
@use "../../../../../styles/variables/sizes";
@use "../../../../../styles/variables/colors";
/* ensures that all icons have the same width */
.nav-icon {
min-width: sizes.$max-icon-width;
}
.matched-background {
background-color: colors.$background !important;
}
.submenu {
padding-left: 20px;
}
.indent-item {
margin-left: 8px;
padding-left: 8px;
width: auto;
border-end-end-radius: 0;
border-start-end-radius: 0;
}
.menu-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
}
Legend
Html element with directive