1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 00:03:56 +00:00

[CL-707] Migrate CL codebase to signals (#15340)

This commit is contained in:
Vicki League
2025-07-16 08:39:37 -04:00
committed by GitHub
parent 97ec9a6339
commit 6811ea4c0b
124 changed files with 944 additions and 809 deletions

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Directive, EventEmitter, Input, Output } from "@angular/core";
import { Directive, EventEmitter, Output, input } from "@angular/core";
import { RouterLink, RouterLinkActive } from "@angular/router";
/**
@@ -11,17 +11,17 @@ export abstract class NavBaseComponent {
/**
* Text to display in main content
*/
@Input() text: string;
readonly text = input<string>();
/**
* `aria-label` for main content
*/
@Input() ariaLabel: string;
readonly ariaLabel = input<string>();
/**
* Optional icon, e.g. `"bwi-collection-shared"`
*/
@Input() icon: string;
readonly icon = input<string>();
/**
* Optional route to be passed to internal `routerLink`. If not provided, the nav component will render as a button.
@@ -34,31 +34,31 @@ export abstract class NavBaseComponent {
*
* See: {@link https://github.com/angular/angular/issues/24482}
*/
@Input() route?: RouterLink["routerLink"];
readonly route = input<RouterLink["routerLink"]>();
/**
* Passed to internal `routerLink`
*
* See {@link RouterLink.relativeTo}
*/
@Input() relativeTo?: RouterLink["relativeTo"];
readonly relativeTo = input<RouterLink["relativeTo"]>();
/**
* Passed to internal `routerLink`
*
* See {@link RouterLinkActive.routerLinkActiveOptions}
*/
@Input() routerLinkActiveOptions?: RouterLinkActive["routerLinkActiveOptions"] = {
readonly routerLinkActiveOptions = input<RouterLinkActive["routerLinkActiveOptions"]>({
paths: "subset",
queryParams: "ignored",
fragment: "ignored",
matrixParams: "ignored",
};
});
/**
* If `true`, do not change styles when nav item is active.
*/
@Input() hideActiveStyles = false;
readonly hideActiveStyles = input(false);
/**
* Fires when main content is clicked

View File

@@ -1,28 +1,28 @@
<!-- This a higher order component that composes `NavItemComponent` -->
@if (!hideIfEmpty || nestedNavComponents.length > 0) {
@if (!hideIfEmpty() || nestedNavComponents.length > 0) {
<bit-nav-item
[text]="text"
[icon]="icon"
[route]="route"
[relativeTo]="relativeTo"
[routerLinkActiveOptions]="routerLinkActiveOptions"
[text]="text()"
[icon]="icon()"
[route]="route()"
[relativeTo]="relativeTo()"
[routerLinkActiveOptions]="routerLinkActiveOptions()"
(mainContentClicked)="handleMainContentClicked()"
[ariaLabel]="ariaLabel"
[ariaLabel]="ariaLabel()"
[hideActiveStyles]="parentHideActiveStyles"
>
<ng-template #button>
<button
type="button"
class="tw-ms-auto"
[bitIconButton]="open ? 'bwi-angle-up' : 'bwi-angle-down'"
[bitIconButton]="open() ? 'bwi-angle-up' : 'bwi-angle-down'"
[buttonType]="'light'"
(click)="toggle($event)"
size="small"
[title]="'toggleCollapse' | i18n"
aria-haspopup="true"
[attr.aria-expanded]="open.toString()"
[attr.aria-expanded]="open().toString()"
[attr.aria-controls]="contentId"
[attr.aria-label]="['toggleCollapse' | i18n, text].join(' ')"
[attr.aria-label]="['toggleCollapse' | i18n, text()].join(' ')"
></button>
</ng-template>
<ng-container slot="end">
@@ -32,10 +32,10 @@
</bit-nav-item>
<!-- [attr.aria-controls] of the above button expects a unique ID on the controlled element -->
@if (sideNavService.open$ | async) {
@if (open) {
@if (open()) {
<div
[attr.id]="contentId"
[attr.aria-label]="[text, 'submenu' | i18n].join(' ')"
[attr.aria-label]="[text(), 'submenu' | i18n].join(' ')"
role="group"
>
<ng-content></ng-content>

View File

@@ -4,11 +4,12 @@ import {
Component,
ContentChildren,
EventEmitter,
Input,
Optional,
Output,
QueryList,
SkipSelf,
input,
model,
} from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
@@ -36,7 +37,7 @@ export class NavGroupComponent extends NavBaseComponent {
/** When the side nav is open, the parent nav item should not show active styles when open. */
protected get parentHideActiveStyles(): boolean {
return this.hideActiveStyles || (this.open && this.sideNavService.open);
return this.hideActiveStyles() || (this.open() && this.sideNavService.open);
}
/**
@@ -47,14 +48,12 @@ export class NavGroupComponent extends NavBaseComponent {
/**
* Is `true` if the expanded content is visible
*/
@Input()
open = false;
readonly open = model(false);
/**
* Automatically hide the nav group if there are no child buttons
*/
@Input({ transform: booleanAttribute })
hideIfEmpty = false;
readonly hideIfEmpty = input(false, { transform: booleanAttribute });
@Output()
openChange = new EventEmitter<boolean>();
@@ -67,24 +66,24 @@ export class NavGroupComponent extends NavBaseComponent {
}
setOpen(isOpen: boolean) {
this.open = isOpen;
this.openChange.emit(this.open);
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);
this.open() && this.parentNavGroup?.setOpen(this.open());
}
protected toggle(event?: MouseEvent) {
event?.stopPropagation();
this.setOpen(!this.open);
this.setOpen(!this.open());
}
protected handleMainContentClicked() {
if (!this.sideNavService.open) {
if (!this.route) {
if (!this.route()) {
this.sideNavService.setOpen();
}
this.open = true;
this.open.set(true);
} else {
this.toggle();
}

View File

@@ -1,6 +1,6 @@
<div class="tw-ps-2 tw-pe-2">
@let open = sideNavService.open$ | async;
@if (open || icon) {
@if (open || icon()) {
<div
class="tw-relative tw-rounded-md tw-h-10"
[ngClass]="[
@@ -16,17 +16,17 @@
<!-- Main content of `NavItem` -->
<ng-template #anchorAndButtonContent>
<div
[title]="text"
[title]="text()"
class="tw-gap-2 tw-items-center tw-font-bold tw-h-full tw-content-center"
[ngClass]="{ 'tw-text-center': !open, 'tw-flex': open }"
>
<i
class="!tw-m-0 tw-w-4 tw-shrink-0 bwi bwi-fw tw-text-alt2 {{ icon }}"
class="!tw-m-0 tw-w-4 tw-shrink-0 bwi bwi-fw tw-text-alt2 {{ icon() }}"
[attr.aria-hidden]="open"
[attr.aria-label]="text"
[attr.aria-label]="text()"
></i>
@if (open) {
<span class="tw-truncate">{{ text }}</span>
<span class="tw-truncate">{{ text() }}</span>
}
</div>
</ng-template>
@@ -38,11 +38,11 @@
<a
class="tw-size-full tw-px-3 tw-block tw-truncate tw-border-none tw-bg-transparent tw-text-start !tw-text-alt2 hover:tw-text-alt2 hover:tw-no-underline focus:tw-outline-none"
data-fvw
[routerLink]="route"
[relativeTo]="relativeTo"
[attr.aria-label]="ariaLabel || text"
[routerLink]="route()"
[relativeTo]="relativeTo()"
[attr.aria-label]="ariaLabel() || text()"
routerLinkActive
[routerLinkActiveOptions]="routerLinkActiveOptions"
[routerLinkActiveOptions]="routerLinkActiveOptions()"
[ariaCurrentWhenActive]="'page'"
(isActiveChange)="setIsActive($event)"
(click)="mainContentClicked.emit()"

View File

@@ -1,5 +1,5 @@
import { CommonModule } from "@angular/common";
import { Component, HostListener, Input, Optional } from "@angular/core";
import { Component, HostListener, Optional, input } from "@angular/core";
import { RouterModule } from "@angular/router";
import { BehaviorSubject, map } from "rxjs";
@@ -21,7 +21,7 @@ export abstract class NavGroupAbstraction {
})
export class NavItemComponent extends NavBaseComponent {
/** Forces active styles to be shown, regardless of the `routerLinkActiveOptions` */
@Input() forceActiveStyles? = false;
readonly forceActiveStyles = input<boolean>(false);
/**
* Is `true` if `to` matches the current route
@@ -34,7 +34,7 @@ export class NavItemComponent extends NavBaseComponent {
}
}
protected get showActiveStyles() {
return this.forceActiveStyles || (this._isActive && !this.hideActiveStyles);
return this.forceActiveStyles() || (this._isActive && !this.hideActiveStyles());
}
/**

View File

@@ -6,14 +6,14 @@
class="tw-px-2 tw-pt-5"
>
<a
[routerLink]="route"
[routerLink]="route()"
class="tw-p-3 tw-block tw-rounded-md tw-bg-background-alt3 tw-outline-none focus-visible:tw-ring focus-visible:tw-ring-inset focus-visible:tw-ring-text-alt2 hover:tw-bg-hover-contrast [&_path]:tw-fill-text-alt2"
[ngClass]="{ '[&_svg]:tw-w-[1.687rem]': !sideNavService.open }"
[attr.aria-label]="label"
[title]="label"
[attr.aria-label]="label()"
[title]="label()"
routerLinkActive
[ariaCurrentWhenActive]="'page'"
>
<bit-icon [icon]="sideNavService.open ? openIcon : closedIcon"></bit-icon>
<bit-icon [icon]="sideNavService.open ? openIcon() : closedIcon()"></bit-icon>
</a>
</div>

View File

@@ -1,7 +1,8 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { Component, Input } from "@angular/core";
import { Component, input } from "@angular/core";
import { RouterLinkActive, RouterLink } from "@angular/router";
import { Icon } from "../icon";
@@ -17,18 +18,18 @@ import { SideNavService } from "./side-nav.service";
})
export class NavLogoComponent {
/** Icon that is displayed when the side nav is closed */
@Input() closedIcon = BitwardenShield;
readonly closedIcon = input(BitwardenShield);
/** Icon that is displayed when the side nav is open */
@Input({ required: true }) openIcon: Icon;
readonly openIcon = input.required<Icon>();
/**
* Route to be passed to internal `routerLink`
*/
@Input({ required: true }) route: string | any[];
readonly route = input.required<string | any[]>();
/** Passed to `attr.aria-label` and `attr.title` */
@Input({ required: true }) label: string;
readonly label = input.required<string>();
constructor(protected sideNavService: SideNavService) {}
}

View File

@@ -10,7 +10,7 @@
class="tw-fixed md:tw-sticky tw-inset-y-0 tw-left-0 tw-z-30 tw-flex tw-h-screen tw-flex-col tw-overscroll-none tw-overflow-auto tw-bg-background-alt3 tw-outline-none"
[ngClass]="{ 'tw-w-60': data.open }"
[ngStyle]="
variant === 'secondary' && {
variant() === 'secondary' && {
'--color-text-alt2': 'var(--color-text-main)',
'--color-background-alt3': 'var(--color-secondary-100)',
'--color-background-alt4': 'var(--color-secondary-300)',

View File

@@ -2,7 +2,7 @@
// @ts-strict-ignore
import { CdkTrapFocus } from "@angular/cdk/a11y";
import { CommonModule } from "@angular/common";
import { Component, ElementRef, Input, ViewChild } from "@angular/core";
import { Component, ElementRef, ViewChild, input } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
@@ -19,7 +19,7 @@ export type SideNavVariant = "primary" | "secondary";
imports: [CommonModule, CdkTrapFocus, NavDividerComponent, BitIconButtonComponent, I18nPipe],
})
export class SideNavComponent {
@Input() variant: SideNavVariant = "primary";
readonly variant = input<SideNavVariant>("primary");
@ViewChild("toggleButton", { read: ElementRef, static: true })
private toggleButton: ElementRef<HTMLButtonElement>;