1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-29 22:53:44 +00:00

[CL-934] Migrate breadcrumbs to OnPush (#17390)

Migrates BreadcrumbComponent and BreadcrumbsComponent to OnPush and changes any property to computed signals.
This commit is contained in:
Oscar Hinton
2025-12-16 16:15:34 +01:00
committed by GitHub
parent ede027a43e
commit ac23878847
5 changed files with 71 additions and 43 deletions

View File

@@ -1,6 +1,6 @@
<ng-template>
@if (icon(); as icon) {
<i class="bwi {{ icon }} !tw-me-2" aria-hidden="true"></i>
<i class="bwi !tw-me-2" [class]="icon" aria-hidden="true"></i>
}
<ng-content></ng-content>
</ng-template>

View File

@@ -1,29 +1,55 @@
import { Component, EventEmitter, Output, TemplateRef, input, viewChild } from "@angular/core";
import {
ChangeDetectionStrategy,
Component,
TemplateRef,
input,
output,
viewChild,
} from "@angular/core";
import { QueryParamsHandling } from "@angular/router";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
/**
* Individual breadcrumb item used within the `bit-breadcrumbs` component.
* Represents a single navigation step in the breadcrumb trail.
*
* This component should be used as a child of `bit-breadcrumbs` and supports both
* router navigation and custom click handlers.
*/
@Component({
selector: "bit-breadcrumb",
templateUrl: "./breadcrumb.component.html",
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BreadcrumbComponent {
/**
* Optional icon to display before the breadcrumb text.
*/
readonly icon = input<string>();
/**
* Router link for the breadcrumb. Can be a string or an array of route segments.
*/
readonly route = input<string | any[]>();
/**
* Query parameters to include in the router link.
*/
readonly queryParams = input<Record<string, string>>({});
/**
* How to handle query parameters when navigating. Options include 'merge' or 'preserve'.
*/
readonly queryParamsHandling = input<QueryParamsHandling>();
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref
@Output()
click = new EventEmitter();
/**
* Emitted when the breadcrumb is clicked.
*/
readonly click = output<unknown>();
/** Used by the BreadcrumbsComponent to access the breadcrumb content */
readonly content = viewChild(TemplateRef);
onClick(args: unknown) {
this.click.next(args);
this.click.emit(args);
}
}

View File

@@ -1,4 +1,4 @@
@for (breadcrumb of beforeOverflow; track breadcrumb; let last = $last) {
@for (breadcrumb of beforeOverflow(); track breadcrumb; let last = $last) {
@if (breadcrumb.route(); as route) {
<a
bitLink
@@ -26,8 +26,8 @@
}
}
@if (hasOverflow) {
@if (beforeOverflow.length > 0) {
@if (hasOverflow()) {
@if (beforeOverflow().length > 0) {
<i class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i>
}
<button
@@ -38,7 +38,7 @@
[label]="'moreBreadcrumbs' | i18n"
></button>
<bit-menu #overflowMenu>
@for (breadcrumb of overflow; track breadcrumb) {
@for (breadcrumb of overflow(); track breadcrumb) {
@if (breadcrumb.route(); as route) {
<a
bitMenuItem
@@ -57,7 +57,7 @@
}
</bit-menu>
<i class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i>
@for (breadcrumb of afterOverflow; track breadcrumb; let last = $last) {
@for (breadcrumb of afterOverflow(); track breadcrumb; let last = $last) {
@if (breadcrumb.route(); as route) {
<a
bitLink

View File

@@ -1,5 +1,11 @@
import { CommonModule } from "@angular/common";
import { Component, ContentChildren, QueryList, input } from "@angular/core";
import {
ChangeDetectionStrategy,
Component,
computed,
contentChildren,
input,
} from "@angular/core";
import { RouterModule } from "@angular/router";
import { I18nPipe } from "@bitwarden/ui-common";
@@ -15,42 +21,39 @@ import { BreadcrumbComponent } from "./breadcrumb.component";
* Bitwarden uses this component to indicate the user's current location in a set of data organized in
* containers (Collections, Folders, or Projects).
*/
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "bit-breadcrumbs",
templateUrl: "./breadcrumbs.component.html",
imports: [I18nPipe, CommonModule, LinkModule, RouterModule, IconButtonModule, MenuModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BreadcrumbsComponent {
/**
* The maximum number of breadcrumbs to show before overflow.
*/
readonly show = input(3);
private breadcrumbs: BreadcrumbComponent[] = [];
protected readonly breadcrumbs = contentChildren(BreadcrumbComponent);
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@ContentChildren(BreadcrumbComponent)
protected set breadcrumbList(value: QueryList<BreadcrumbComponent>) {
this.breadcrumbs = value.toArray();
}
/** Whether the breadcrumbs exceed the show limit and require an overflow menu */
protected readonly hasOverflow = computed(() => this.breadcrumbs().length > this.show());
protected get beforeOverflow() {
if (this.hasOverflow) {
return this.breadcrumbs.slice(0, this.show() - 1);
/** Breadcrumbs shown before the overflow menu */
protected readonly beforeOverflow = computed(() => {
const items = this.breadcrumbs();
const showCount = this.show();
if (items.length > showCount) {
return items.slice(0, showCount - 1);
}
return items;
});
return this.breadcrumbs;
}
/** Breadcrumbs hidden in the overflow menu */
protected readonly overflow = computed(() => {
return this.breadcrumbs().slice(this.show() - 1, -1);
});
protected get overflow() {
return this.breadcrumbs.slice(this.show() - 1, -1);
}
protected get afterOverflow() {
return this.breadcrumbs.slice(-1);
}
protected get hasOverflow() {
return this.breadcrumbs.length > this.show();
}
/** The last breadcrumb, shown after the overflow menu */
protected readonly afterOverflow = computed(() => this.breadcrumbs().slice(-1));
}

View File

@@ -1,4 +1,4 @@
import { Component, importProvidersFrom } from "@angular/core";
import { ChangeDetectionStrategy, Component, importProvidersFrom } from "@angular/core";
import { RouterModule } from "@angular/router";
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
@@ -18,10 +18,9 @@ interface Breadcrumb {
route: string;
}
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
template: "",
changeDetection: ChangeDetectionStrategy.OnPush,
})
class EmptyComponent {}