diff --git a/libs/components/src/navigation/nav-base.component.ts b/libs/components/src/navigation/nav-base.component.ts index 9092ac4447c..706df2b25ad 100644 --- a/libs/components/src/navigation/nav-base.component.ts +++ b/libs/components/src/navigation/nav-base.component.ts @@ -1,4 +1,4 @@ -import { Directive, EventEmitter, Output, input } from "@angular/core"; +import { Directive, EventEmitter, Output, input, model } from "@angular/core"; import { RouterLink, RouterLinkActive } from "@angular/router"; /** @@ -21,6 +21,16 @@ export abstract class NavBaseComponent { */ readonly icon = input(); + /** + * If this item is used within a tree, set `variant` to `"tree"` + */ + readonly variant = input<"default" | "tree">("default"); + + /** + * Depth level when nested inside of a `'tree'` variant + */ + readonly treeDepth = model(0); + /** * Optional route to be passed to internal `routerLink`. If not provided, the nav component will render as a button. * diff --git a/libs/components/src/navigation/nav-group.component.html b/libs/components/src/navigation/nav-group.component.html index bcf6ae2b5b7..5a66eeafbf7 100644 --- a/libs/components/src/navigation/nav-group.component.html +++ b/libs/components/src/navigation/nav-group.component.html @@ -1,9 +1,13 @@ +@let variantValue = variant(); + @if (!hideIfEmpty() || nestedNavComponents().length > 0) { + @if (variantValue === "tree") { + + + + } - + @if (variantValue !== "tree") { + + } diff --git a/libs/components/src/navigation/nav-group.component.ts b/libs/components/src/navigation/nav-group.component.ts index 3408af3d734..5797d34da5d 100644 --- a/libs/components/src/navigation/nav-group.component.ts +++ b/libs/components/src/navigation/nav-group.component.ts @@ -34,7 +34,8 @@ import { SideNavService } from "./side-nav.service"; imports: [CommonModule, NavItemComponent, IconButtonModule, I18nPipe], }) export class NavGroupComponent extends NavBaseComponent { - readonly nestedNavComponents = contentChildren(NavBaseComponent, { descendants: true }); + // Query direct children for hideIfEmpty functionality + readonly nestedNavComponents = contentChildren(NavBaseComponent, { descendants: false }); readonly sideNavOpen = toSignal(this.sideNavService.open$); @@ -47,6 +48,18 @@ export class NavGroupComponent extends NavBaseComponent { return this.hideActiveStyles() || this.sideNavAndGroupOpen(); }); + /** + * Determines the appropriate icon for the toggle button based on variant and open state. + * - Tree variant: Always uses 'bwi-up-solid' + * - Default variant: Uses 'bwi-angle-up' when open, 'bwi-angle-down' when closed + */ + readonly toggleButtonIcon = computed(() => { + if (this.variant() === "tree") { + return "bwi-up-solid"; + } + return this.open() ? "bwi-angle-up" : "bwi-angle-down"; + }); + /** * Allow overriding of the RouterLink['ariaCurrentWhenActive'] property. * @@ -89,14 +102,20 @@ export class NavGroupComponent extends NavBaseComponent { @Optional() @SkipSelf() private parentNavGroup: NavGroupComponent, ) { super(); + + // Set tree depth based on parent's depth + // Both NavGroups and NavItems use constructor-based depth initialization + if (this.parentNavGroup) { + this.treeDepth.set(this.parentNavGroup.treeDepth() + 1); + } } setOpen(isOpen: boolean) { this.open.set(isOpen); this.openChange.emit(this.open()); - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.open() && this.parentNavGroup?.setOpen(this.open()); + if (this.open()) { + this.parentNavGroup?.setOpen(this.open()); + } } protected toggle(event?: MouseEvent) { diff --git a/libs/components/src/navigation/nav-group.stories.ts b/libs/components/src/navigation/nav-group.stories.ts index 9910c99fab4..fc9df19a3e2 100644 --- a/libs/components/src/navigation/nav-group.stories.ts +++ b/libs/components/src/navigation/nav-group.stories.ts @@ -132,3 +132,24 @@ export const Secondary: StoryObj = { `, }), }; + +export const Tree: StoryObj = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + + + + + + + + + + + + + `, + }), +}; diff --git a/libs/components/src/navigation/nav-item.component.html b/libs/components/src/navigation/nav-item.component.html index 10f68145a4d..9f6ea3373b7 100644 --- a/libs/components/src/navigation/nav-item.component.html +++ b/libs/components/src/navigation/nav-item.component.html @@ -2,6 +2,12 @@ @let open = sideNavService.open$ | async; @if (open || icon()) {