Files
Kam 5ec0b1668b fix(docs-infra): disable nav-list tooltip on mobile
On mobile, focusing a long nav item shows a matTooltip that escapes the
drawer, ignores inner scroll, and blocks scrolling while open. Suppress
it on mobile using the existing isMobile helper.
2026-07-07 09:54:47 -07:00

124 lines
3.8 KiB
TypeScript

/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {NgTemplateOutlet} from '@angular/common';
import {Component, inject, input, output} from '@angular/core';
import {MatTooltip} from '@angular/material/tooltip';
import {RouterLink, RouterLinkActive} from '@angular/router';
import {NavigationItem} from '../../interfaces/index';
import {IsActiveNavigationItem} from '../../pipes';
import {NavigationState} from '../../services/index';
import {isMobile} from '../../utils';
import {IconComponent} from '../icon/icon.component';
@Component({
selector: 'docs-navigation-list',
imports: [
RouterLink,
RouterLinkActive,
IconComponent,
IsActiveNavigationItem,
NgTemplateOutlet,
MatTooltip,
],
templateUrl: './navigation-list.component.html',
styleUrls: ['./navigation-list.component.scss'],
})
export class NavigationList {
readonly navigationItems = input.required<NavigationItem[]>();
readonly preserveOtherCategoryOrder = input.required<boolean>();
readonly displayItemsToLevel = input(2);
readonly collapsableLevel = input<number | undefined>();
readonly expandableLevel = input(2);
readonly isDropdownView = input(false);
readonly linkClicked = output<void>();
protected readonly labelTruncationThreshold = 27;
private readonly navigationState = inject(NavigationState);
private readonly crossCategoryOrigin = this.navigationState.crossCategoryOrigin;
readonly activeItem = this.navigationState.activeNavigationItem;
protected readonly isMobile = isMobile;
toggle(item: NavigationItem): void {
if (
item.level === 1 &&
item.level !== this.expandableLevel() &&
item.level !== this.collapsableLevel()
) {
return;
}
const prevParentItem = this.crossCategoryOrigin();
if (prevParentItem) {
this.crossCategoryOrigin.set(undefined);
this.navigationState.toggleItem(prevParentItem);
return;
}
this.navigationState.toggleItem(item);
}
emitClickOnLink(item: NavigationItem): void {
if (item.isCrossReferenced) {
this.crossCategoryOrigin.set(item.parent);
}
this.linkClicked.emit();
}
private hasCategories(items: NavigationItem[]): boolean {
return items.some((item) => !!item.category);
}
private getCategoryStatus(item: NavigationItem, category: string): 'new' | 'updated' | undefined {
const categoriesStatus = item.parent?.categoriesStatus;
if (!categoriesStatus) {
return undefined;
}
return categoriesStatus.find((status) => status[category])?.[category];
}
protected groupItems(
items: NavigationItem[],
preserveOtherCategoryOrder: boolean,
): Map<string, {items: NavigationItem[]; status: 'new' | 'updated' | undefined}> {
const hasCategories = this.hasCategories(items);
if (hasCategories) {
const others: NavigationItem[] = [];
const categorizedItems = new Map<
string,
{items: NavigationItem[]; status: 'new' | 'updated' | undefined}
>();
for (const item of items) {
const category = item.category || 'Other';
if (!preserveOtherCategoryOrder && category === 'Other') {
others.push(item);
continue;
}
if (!categorizedItems.has(category)) {
categorizedItems.set(category, {
items: [],
status: this.getCategoryStatus(item, category),
});
}
categorizedItems.get(category)!.items.push(item);
}
if (others.length) {
categorizedItems.set('Other', {items: others, status: undefined});
}
return categorizedItems;
} else {
return new Map([['', {items, status: undefined}]]);
}
}
}