1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 10:13:31 +00:00

[CL-946] Migrate ToggleGroup to OnPush (#17718)

Migrates the ToggleGroup and Toggle components to use OnPush.
This commit is contained in:
Oscar Hinton
2025-12-05 13:06:03 +01:00
committed by GitHub
parent 110c955cff
commit ff7b625851
5 changed files with 178 additions and 122 deletions

View File

@@ -1,113 +1,109 @@
import { NgClass } from "@angular/common";
import {
AfterContentChecked,
AfterViewInit,
afterNextRender,
ChangeDetectionStrategy,
Component,
computed,
contentChild,
ElementRef,
HostBinding,
signal,
inject,
input,
signal,
viewChild,
} from "@angular/core";
import { BadgeComponent } from "../badge";
import { ToggleGroupComponent } from "./toggle-group.component";
let nextId = 0;
// 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-toggle",
templateUrl: "./toggle.component.html",
imports: [NgClass],
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
tabindex: "-1",
"[class]": "hostClasses",
},
})
export class ToggleComponent<TValue> implements AfterContentChecked, AfterViewInit {
id = nextId++;
export class ToggleComponent<TValue> {
protected readonly id = "bit-toggle-" + nextId++;
private readonly groupComponent = inject(ToggleGroupComponent<TValue>);
readonly value = input.required<TValue>();
readonly labelContent = viewChild<ElementRef<HTMLSpanElement>>("labelContent");
readonly bitBadgeContainer = viewChild<ElementRef<HTMLSpanElement>>("bitBadgeContainer");
protected readonly labelContent = viewChild<ElementRef<HTMLSpanElement>>("labelContent");
protected readonly badgeElement = contentChild(BadgeComponent);
protected readonly hasBadge = computed(() => !!this.badgeElement());
constructor(private groupComponent: ToggleGroupComponent<TValue>) {}
@HostBinding("tabIndex") tabIndex = "-1";
@HostBinding("class") classList = ["tw-group/toggle", "tw-flex", "tw-min-w-16"];
protected readonly bitBadgeContainerHasChidlren = signal(false);
protected readonly labelTitle = signal<string | null>(null);
get name() {
return this.groupComponent.name;
constructor() {
// Set label title after view is initialized
afterNextRender(() => {
const labelText = this.labelContent()?.nativeElement.innerText;
if (labelText) {
this.labelTitle.set(labelText);
}
});
}
get selected() {
return this.groupComponent.selected() === this.value();
}
protected readonly name = this.groupComponent.name;
readonly selected = computed(() => this.groupComponent.selected() === this.value());
get inputClasses() {
return ["tw-peer/toggle-input", "tw-appearance-none", "tw-outline-none"];
}
get labelClasses() {
return [
"tw-h-full",
"tw-w-full",
"tw-flex",
"tw-items-center",
"tw-justify-center",
"tw-gap-1.5",
"!tw-font-medium",
"tw-leading-5",
"tw-transition",
"tw-text-center",
"tw-text-sm",
"tw-border-primary-600",
"!tw-text-primary-600",
"tw-border-solid",
"tw-border-y",
"tw-border-r",
"tw-border-l-0",
"tw-cursor-pointer",
"hover:tw-bg-hover-default",
"group-first-of-type/toggle:tw-border-l",
"group-first-of-type/toggle:tw-rounded-s-full",
"group-last-of-type/toggle:tw-rounded-e-full",
"peer-focus-visible/toggle-input:tw-outline-none",
"peer-focus-visible/toggle-input:tw-ring",
"peer-focus-visible/toggle-input:tw-ring-offset-2",
"peer-focus-visible/toggle-input:tw-ring-primary-600",
"peer-focus-visible/toggle-input:tw-z-10",
"peer-focus-visible/toggle-input:tw-bg-primary-600",
"peer-focus-visible/toggle-input:tw-border-primary-600",
"peer-focus-visible/toggle-input:!tw-text-contrast",
"peer-checked/toggle-input:tw-bg-primary-600",
"peer-checked/toggle-input:tw-border-primary-600",
"peer-checked/toggle-input:!tw-text-contrast",
"tw-py-1.5",
"tw-px-3",
// Fix for bootstrap styles that add bottom margin
"!tw-mb-0",
];
}
onInputInteraction() {
protected handleInputChange() {
this.groupComponent.onInputInteraction(this.value());
}
ngAfterContentChecked() {
this.bitBadgeContainerHasChidlren.set(
(this.bitBadgeContainer()?.nativeElement.childElementCount ?? 0) > 0,
);
}
protected readonly hostClasses = ["tw-group/toggle", "tw-flex", "tw-min-w-16"];
ngAfterViewInit() {
const labelText = this.labelContent()?.nativeElement.innerText;
if (labelText) {
this.labelTitle.set(labelText);
}
}
protected readonly inputClasses = [
"tw-peer/toggle-input",
"tw-appearance-none",
"tw-outline-none",
];
protected readonly labelClasses = [
"tw-h-full",
"tw-w-full",
"tw-flex",
"tw-items-center",
"tw-justify-center",
"tw-gap-1.5",
"!tw-font-medium",
"tw-leading-5",
"tw-transition",
"tw-text-center",
"tw-text-sm",
"tw-border-primary-600",
"!tw-text-primary-600",
"tw-border-solid",
"tw-border-y",
"tw-border-r",
"tw-border-l-0",
"tw-cursor-pointer",
"hover:tw-bg-hover-default",
"group-first-of-type/toggle:tw-border-l",
"group-first-of-type/toggle:tw-rounded-s-full",
"group-last-of-type/toggle:tw-rounded-e-full",
"peer-focus-visible/toggle-input:tw-outline-none",
"peer-focus-visible/toggle-input:tw-ring",
"peer-focus-visible/toggle-input:tw-ring-offset-2",
"peer-focus-visible/toggle-input:tw-ring-primary-600",
"peer-focus-visible/toggle-input:tw-z-10",
"peer-focus-visible/toggle-input:tw-bg-primary-600",
"peer-focus-visible/toggle-input:tw-border-primary-600",
"peer-focus-visible/toggle-input:!tw-text-contrast",
"peer-checked/toggle-input:tw-bg-primary-600",
"peer-checked/toggle-input:tw-border-primary-600",
"peer-checked/toggle-input:!tw-text-contrast",
"tw-py-1.5",
"tw-px-3",
// Fix for bootstrap styles that add bottom margin
"!tw-mb-0",
];
}