mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
[CL-707] Migrate CL codebase to signals (#15340)
This commit is contained in:
@@ -39,6 +39,9 @@ export class MenuItemDirective implements FocusableOption {
|
||||
return this.disabled || null; // native disabled attr must be null when false
|
||||
}
|
||||
|
||||
// TODO: Skipped for signal migration because:
|
||||
// This input overrides a field from a superclass, while the superclass field
|
||||
// is not migrated.
|
||||
@Input({ transform: coerceBooleanProperty }) disabled?: boolean = false;
|
||||
|
||||
constructor(public elementRef: ElementRef<HTMLButtonElement>) {}
|
||||
|
||||
@@ -8,26 +8,30 @@ import {
|
||||
ElementRef,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Input,
|
||||
OnDestroy,
|
||||
ViewContainerRef,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { Observable, Subscription } from "rxjs";
|
||||
import { filter, mergeWith } from "rxjs/operators";
|
||||
|
||||
import { MenuComponent } from "./menu.component";
|
||||
|
||||
@Directive({ selector: "[bitMenuTriggerFor]", exportAs: "menuTrigger", standalone: true })
|
||||
@Directive({
|
||||
selector: "[bitMenuTriggerFor]",
|
||||
exportAs: "menuTrigger",
|
||||
standalone: true,
|
||||
host: { "[attr.role]": "this.role()" },
|
||||
})
|
||||
export class MenuTriggerForDirective implements OnDestroy {
|
||||
@HostBinding("attr.aria-expanded") isOpen = false;
|
||||
@HostBinding("attr.aria-haspopup") get hasPopup(): "menu" | "dialog" {
|
||||
return this.menu?.ariaRole || "menu";
|
||||
return this.menu()?.ariaRole() || "menu";
|
||||
}
|
||||
@HostBinding("attr.role")
|
||||
@Input()
|
||||
role = "button";
|
||||
|
||||
@Input("bitMenuTriggerFor") menu: MenuComponent;
|
||||
readonly role = input("button");
|
||||
|
||||
readonly menu = input<MenuComponent>(undefined, { alias: "bitMenuTriggerFor" });
|
||||
|
||||
private overlayRef: OverlayRef;
|
||||
private defaultMenuConfig: OverlayConfig = {
|
||||
@@ -66,14 +70,15 @@ export class MenuTriggerForDirective implements OnDestroy {
|
||||
}
|
||||
|
||||
private openMenu() {
|
||||
if (this.menu == null) {
|
||||
const menu = this.menu();
|
||||
if (menu == null) {
|
||||
throw new Error("Cannot find bit-menu element");
|
||||
}
|
||||
|
||||
this.isOpen = true;
|
||||
this.overlayRef = this.overlay.create(this.defaultMenuConfig);
|
||||
|
||||
const templatePortal = new TemplatePortal(this.menu.templateRef, this.viewContainerRef);
|
||||
const templatePortal = new TemplatePortal(menu.templateRef, this.viewContainerRef);
|
||||
this.overlayRef.attach(templatePortal);
|
||||
|
||||
this.closedEventsSub = this.getClosedEvents().subscribe((event: KeyboardEvent | undefined) => {
|
||||
@@ -90,11 +95,11 @@ export class MenuTriggerForDirective implements OnDestroy {
|
||||
}
|
||||
this.destroyMenu();
|
||||
});
|
||||
if (this.menu.keyManager) {
|
||||
this.menu.keyManager.setFirstItemActive();
|
||||
if (menu.keyManager) {
|
||||
menu.keyManager.setFirstItemActive();
|
||||
this.keyDownEventsSub = this.overlayRef
|
||||
.keydownEvents()
|
||||
.subscribe((event: KeyboardEvent) => this.menu.keyManager.onKeydown(event));
|
||||
.subscribe((event: KeyboardEvent) => this.menu().keyManager.onKeydown(event));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,19 +110,19 @@ export class MenuTriggerForDirective implements OnDestroy {
|
||||
|
||||
this.isOpen = false;
|
||||
this.disposeAll();
|
||||
this.menu.closed.emit();
|
||||
this.menu().closed.emit();
|
||||
}
|
||||
|
||||
private getClosedEvents(): Observable<any> {
|
||||
const detachments = this.overlayRef.detachments();
|
||||
const escKey = this.overlayRef.keydownEvents().pipe(
|
||||
filter((event: KeyboardEvent) => {
|
||||
const keys = this.menu.ariaRole === "menu" ? ["Escape", "Tab"] : ["Escape"];
|
||||
const keys = this.menu().ariaRole() === "menu" ? ["Escape", "Tab"] : ["Escape"];
|
||||
return keys.includes(event.key);
|
||||
}),
|
||||
);
|
||||
const backdrop = this.overlayRef.backdropClick();
|
||||
const menuClosed = this.menu.closed;
|
||||
const menuClosed = this.menu().closed;
|
||||
|
||||
return detachments.pipe(mergeWith(escKey, backdrop, menuClosed));
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<div
|
||||
(click)="closed.emit()"
|
||||
class="tw-flex tw-shrink-0 tw-flex-col tw-rounded-lg tw-border tw-border-solid tw-border-secondary-500 tw-bg-background tw-bg-clip-padding tw-py-1 tw-overflow-y-auto"
|
||||
[attr.role]="ariaRole"
|
||||
[attr.aria-label]="ariaLabel"
|
||||
[attr.role]="ariaRole()"
|
||||
[attr.aria-label]="ariaLabel()"
|
||||
cdkTrapFocus
|
||||
[cdkTrapFocusAutoCapture]="ariaRole === 'dialog'"
|
||||
[cdkTrapFocusAutoCapture]="ariaRole() === 'dialog'"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
ContentChildren,
|
||||
QueryList,
|
||||
AfterContentInit,
|
||||
Input,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
import { MenuItemDirective } from "./menu-item.directive";
|
||||
@@ -28,12 +28,12 @@ export class MenuComponent implements AfterContentInit {
|
||||
menuItems: QueryList<MenuItemDirective>;
|
||||
keyManager?: FocusKeyManager<MenuItemDirective>;
|
||||
|
||||
@Input() ariaRole: "menu" | "dialog" = "menu";
|
||||
readonly ariaRole = input<"menu" | "dialog">("menu");
|
||||
|
||||
@Input() ariaLabel: string;
|
||||
readonly ariaLabel = input<string>();
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.ariaRole === "menu") {
|
||||
if (this.ariaRole() === "menu") {
|
||||
this.keyManager = new FocusKeyManager(this.menuItems)
|
||||
.withWrap()
|
||||
.skipPredicate((item) => item.disabled);
|
||||
|
||||
Reference in New Issue
Block a user