1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +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,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { FocusableOption } from "@angular/cdk/a11y";
import { Directive, ElementRef, HostBinding, Input } from "@angular/core";
import { Directive, ElementRef, HostBinding, Input, input } from "@angular/core";
/**
* Directive used for styling tab header items for both nav links (anchor tags)
@@ -11,7 +11,10 @@ import { Directive, ElementRef, HostBinding, Input } from "@angular/core";
selector: "[bitTabListItem]",
})
export class TabListItemDirective implements FocusableOption {
@Input() active: boolean;
readonly active = input<boolean>();
// TODO: Skipped for signal migration because:
// This input overrides a field from a superclass, while the superclass field
// is not migrated.
@Input() disabled: boolean;
@HostBinding("attr.disabled")
@@ -32,7 +35,7 @@ export class TabListItemDirective implements FocusableOption {
@HostBinding("class")
get classList(): string[] {
return this.baseClassList
.concat(this.active ? this.activeClassList : [])
.concat(this.active() ? this.activeClassList : [])
.concat(this.disabled ? this.disabledClassList : [])
.concat(this.textColorClassList);
}
@@ -45,7 +48,7 @@ export class TabListItemDirective implements FocusableOption {
if (this.disabled) {
return ["!tw-text-secondary-300", "hover:!tw-text-secondary-300"];
}
if (this.active) {
if (this.active()) {
return ["!tw-text-primary-600", "hover:!tw-text-primary-700"];
}
return ["!tw-text-main", "hover:!tw-text-main"];

View File

@@ -1,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { TemplatePortal, CdkPortalOutlet } from "@angular/cdk/portal";
import { Component, HostBinding, Input } from "@angular/core";
import { Component, effect, HostBinding, input } from "@angular/core";
@Component({
selector: "bit-tab-body",
@@ -11,24 +11,22 @@ import { Component, HostBinding, Input } from "@angular/core";
export class TabBodyComponent {
private _firstRender: boolean;
@Input() content: TemplatePortal;
@Input() preserveContent = false;
readonly content = input<TemplatePortal>();
readonly preserveContent = input(false);
@HostBinding("attr.hidden") get hidden() {
return !this.active || null;
return !this.active() || null;
}
@Input()
get active() {
return this._active;
readonly active = input<boolean>();
constructor() {
effect(() => {
if (this.active()) {
this._firstRender = true;
}
});
}
set active(value: boolean) {
this._active = value;
if (this._active) {
this._firstRender = true;
}
}
private _active: boolean;
/**
* The tab content to render.
@@ -37,11 +35,11 @@ export class TabBodyComponent {
* then the content persists after the first time content is rendered.
*/
get tabContent() {
if (this.active) {
return this.content;
if (this.active()) {
return this.content();
}
if (this.preserveContent && this._firstRender) {
return this.content;
if (this.preserveContent() && this._firstRender) {
return this.content();
}
return null;
}

View File

@@ -2,7 +2,7 @@
<div
bitTabListContainer
role="tablist"
[attr.aria-label]="label"
[attr.aria-label]="label()"
(keydown)="keyManager.onKeydown($event)"
>
@for (tab of tabs; track tab; let i = $index) {
@@ -12,7 +12,7 @@
role="tab"
[id]="getTabLabelId(i)"
[active]="tab.isActive"
[disabled]="tab.disabled"
[disabled]="tab.disabled()"
[attr.aria-selected]="selectedIndex === i"
[attr.tabindex]="selectedIndex === i ? 0 : -1"
(click)="selectTab(i)"
@@ -22,7 +22,7 @@
@if (tab.templateLabel) {
<ng-container [ngTemplateOutlet]="tab.templateLabel.templateRef"></ng-container>
} @else {
{{ tab.textLabel }}
{{ tab.textLabel() }}
}
</ng-template>
</button>
@@ -34,11 +34,11 @@
<bit-tab-body
role="tabpanel"
[id]="getTabContentId(i)"
[attr.tabindex]="tab.contentTabIndex"
[attr.tabindex]="tab.contentTabIndex()"
[attr.labeledby]="getTabLabelId(i)"
[active]="tab.isActive"
[content]="tab.content"
[preserveContent]="preserveContent"
[preserveContent]="preserveContent()"
>
</bit-tab-body>
}

View File

@@ -15,6 +15,7 @@ import {
Output,
QueryList,
ViewChildren,
input,
} from "@angular/core";
import { Subject, takeUntil } from "rxjs";
@@ -49,19 +50,21 @@ export class TabGroupComponent
/**
* Aria label for the tab list menu
*/
@Input() label = "";
readonly label = input("");
/**
* Keep the content of off-screen tabs in the DOM.
* Useful for keeping <audio> or <video> elements from re-initializing
* after navigating between tabs.
*/
@Input() preserveContent = false;
readonly preserveContent = input(false);
@ContentChildren(TabComponent) tabs: QueryList<TabComponent>;
@ViewChildren(TabListItemDirective) tabLabels: QueryList<TabListItemDirective>;
/** The index of the active tab. */
// TODO: Skipped for signal migration because:
// Accessor inputs cannot be migrated as they are too complex.
@Input()
get selectedIndex(): number | null {
return this._selectedIndex;

View File

@@ -4,11 +4,11 @@ import { TemplatePortal } from "@angular/cdk/portal";
import {
Component,
ContentChild,
Input,
OnInit,
TemplateRef,
ViewChild,
ViewContainerRef,
input,
} from "@angular/core";
import { TabLabelDirective } from "./tab-label.directive";
@@ -21,8 +21,8 @@ import { TabLabelDirective } from "./tab-label.directive";
},
})
export class TabComponent implements OnInit {
@Input() disabled = false;
@Input("label") textLabel = "";
readonly disabled = input(false);
readonly textLabel = input("", { alias: "label" });
/**
* Optional tabIndex for the tabPanel that contains this tab's content.
@@ -32,7 +32,7 @@ export class TabComponent implements OnInit {
*
* @remarks See note 4 of https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/
*/
@Input() contentTabIndex: number | undefined;
readonly contentTabIndex = input<number | undefined>();
@ViewChild(TemplateRef, { static: true }) implicitContent: TemplateRef<unknown>;
@ContentChild(TabLabelDirective) templateLabel: TabLabelDirective;

View File

@@ -1,6 +1,6 @@
<a
bitTabListItem
[routerLink]="disabled ? null : route"
[routerLink]="disabled ? null : route()"
routerLinkActive
[routerLinkActiveOptions]="routerLinkMatchOptions"
#rla="routerLinkActive"

View File

@@ -1,7 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { FocusableOption } from "@angular/cdk/a11y";
import { AfterViewInit, Component, HostListener, Input, OnDestroy, ViewChild } from "@angular/core";
import {
AfterViewInit,
Component,
HostListener,
Input,
OnDestroy,
ViewChild,
input,
} from "@angular/core";
import { IsActiveMatchOptions, RouterLinkActive, RouterModule } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
@@ -27,7 +35,10 @@ export class TabLinkComponent implements FocusableOption, AfterViewInit, OnDestr
fragment: "ignored",
};
@Input() route: string | any[];
readonly route = input<string | any[]>();
// TODO: Skipped for signal migration because:
// This input overrides a field from a superclass, while the superclass field
// is not migrated.
@Input() disabled = false;
@HostListener("keydown", ["$event"]) onKeyDown(event: KeyboardEvent) {

View File

@@ -1,5 +1,5 @@
<bit-tab-header>
<nav bitTabListContainer [attr.aria-label]="label" (keydown)="keyManager.onKeydown($event)">
<nav bitTabListContainer [attr.aria-label]="label()" (keydown)="keyManager.onKeydown($event)">
<ng-content></ng-content>
</nav>
</bit-tab-header>

View File

@@ -6,8 +6,8 @@ import {
Component,
ContentChildren,
forwardRef,
Input,
QueryList,
input,
} from "@angular/core";
import { TabHeaderComponent } from "../shared/tab-header.component";
@@ -25,7 +25,7 @@ import { TabLinkComponent } from "./tab-link.component";
})
export class TabNavBarComponent implements AfterContentInit {
@ContentChildren(forwardRef(() => TabLinkComponent)) tabLabels: QueryList<TabLinkComponent>;
@Input() label = "";
readonly label = input("");
/**
* Focus key manager for keeping tab controls accessible.

View File

@@ -44,14 +44,18 @@ export default {
component: TabGroupComponent,
decorators: [
moduleMetadata({
declarations: [
imports: [
CommonModule,
TabsModule,
ButtonModule,
FormFieldModule,
RouterModule,
ActiveDummyComponent,
ItemTwoDummyComponent,
ItemThreeDummyComponent,
ItemWithChildCounterDummyComponent,
DisabledDummyComponent,
],
imports: [CommonModule, TabsModule, ButtonModule, FormFieldModule, RouterModule],
}),
applicationConfig({
providers: [