mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 19:53:59 +00:00
committed by
William Martin
parent
172623e050
commit
b42ecccb39
@@ -6,6 +6,8 @@ import { Directive, ElementRef, Input, OnInit, Renderer2 } from "@angular/core";
|
||||
selector: "[appA11yTitle]",
|
||||
})
|
||||
export class A11yTitleDirective implements OnInit {
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() set appA11yTitle(title: string) {
|
||||
this.title = title;
|
||||
this.setAttributes();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}"
|
||||
>
|
||||
<a
|
||||
*ngIf="!hideLogo"
|
||||
*ngIf="!hideLogo()"
|
||||
[routerLink]="['/']"
|
||||
class="tw-w-[128px] tw-block tw-mb-12 [&>*]:tw-align-top"
|
||||
>
|
||||
@@ -14,8 +14,8 @@
|
||||
</a>
|
||||
|
||||
<div class="tw-text-center tw-mb-4 sm:tw-mb-6 tw-mx-auto" [ngClass]="maxWidthClass">
|
||||
<div *ngIf="!hideIcon" class="tw-w-24 sm:tw-w-28 md:tw-w-32 tw-mx-auto">
|
||||
<bit-icon [icon]="icon"></bit-icon>
|
||||
<div *ngIf="!hideIcon()" class="tw-w-24 sm:tw-w-28 md:tw-w-32 tw-mx-auto">
|
||||
<bit-icon [icon]="icon()"></bit-icon>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="title">
|
||||
@@ -50,11 +50,11 @@
|
||||
<ng-content select="[slot=secondary]"></ng-content>
|
||||
</div>
|
||||
|
||||
<footer *ngIf="!hideFooter" class="tw-text-center tw-mt-4 sm:tw-mt-6">
|
||||
<div *ngIf="showReadonlyHostname" bitTypography="body2">
|
||||
<footer *ngIf="!hideFooter()" class="tw-text-center tw-mt-4 sm:tw-mt-6">
|
||||
<div *ngIf="showReadonlyHostname()" bitTypography="body2">
|
||||
{{ "accessing" | i18n }} {{ hostname }}
|
||||
</div>
|
||||
<ng-container *ngIf="!showReadonlyHostname">
|
||||
<ng-container *ngIf="!showReadonlyHostname()">
|
||||
<ng-content select="[slot=environment-selector]"></ng-content>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!hideYearAndVersion">
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, HostBinding, Input, OnChanges, OnInit, SimpleChanges } from "@angular/core";
|
||||
import {
|
||||
Component,
|
||||
HostBinding,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
@@ -28,13 +36,17 @@ export class AnonLayoutComponent implements OnInit, OnChanges {
|
||||
return ["tw-h-full"];
|
||||
}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input() title: string;
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input() subtitle: string;
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() icon: Icon;
|
||||
@Input() showReadonlyHostname: boolean;
|
||||
@Input() hideLogo: boolean = false;
|
||||
@Input() hideFooter: boolean = false;
|
||||
@Input() hideIcon: boolean = false;
|
||||
@Input() hideCardWrapper: boolean = false;
|
||||
|
||||
/**
|
||||
@@ -42,7 +54,22 @@ export class AnonLayoutComponent implements OnInit, OnChanges {
|
||||
*
|
||||
* @default 'md'
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() maxWidth: AnonLayoutMaxWidth = "md";
|
||||
readonly showReadonlyHostname = input<boolean>(undefined);
|
||||
readonly hideLogo = input<boolean>(false);
|
||||
readonly hideFooter = input<boolean>(false);
|
||||
readonly hideIcon = input<boolean>(false);
|
||||
|
||||
/**
|
||||
* Max width of the title area content
|
||||
*
|
||||
* @default null
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() titleAreaMaxWidth?: "md";
|
||||
|
||||
protected logo = BitwardenLogo;
|
||||
protected year: string;
|
||||
|
||||
@@ -38,6 +38,8 @@ export class BitActionDirective implements OnDestroy {
|
||||
|
||||
disabled = false;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input("bitAction") handler: FunctionReturningAwaitable;
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Directive, Input, OnDestroy, OnInit, Optional } from "@angular/core";
|
||||
import { Directive, OnDestroy, OnInit, Optional, input } from "@angular/core";
|
||||
import { FormGroupDirective } from "@angular/forms";
|
||||
import { BehaviorSubject, catchError, filter, of, Subject, switchMap, takeUntil } from "rxjs";
|
||||
|
||||
@@ -20,9 +20,9 @@ export class BitSubmitDirective implements OnInit, OnDestroy {
|
||||
private _loading$ = new BehaviorSubject<boolean>(false);
|
||||
private _disabled$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
@Input("bitSubmit") handler: FunctionReturningAwaitable;
|
||||
readonly handler = input<FunctionReturningAwaitable>(undefined, { alias: "bitSubmit" });
|
||||
|
||||
@Input() allowDisabledFormSubmit?: boolean = false;
|
||||
readonly allowDisabledFormSubmit = input<boolean>(false);
|
||||
|
||||
readonly loading$ = this._loading$.asObservable();
|
||||
readonly disabled$ = this._disabled$.asObservable();
|
||||
@@ -38,7 +38,7 @@ export class BitSubmitDirective implements OnInit, OnDestroy {
|
||||
switchMap(() => {
|
||||
// Calling functionToObservable executes the sync part of the handler
|
||||
// allowing the function to check form validity before it gets disabled.
|
||||
const awaitable = functionToObservable(this.handler);
|
||||
const awaitable = functionToObservable(this.handler());
|
||||
|
||||
// Disable form
|
||||
this.loading = true;
|
||||
@@ -61,7 +61,7 @@ export class BitSubmitDirective implements OnInit, OnDestroy {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.formGroupDirective.statusChanges.pipe(takeUntil(this.destroy$)).subscribe((c) => {
|
||||
if (this.allowDisabledFormSubmit) {
|
||||
if (this.allowDisabledFormSubmit()) {
|
||||
this._disabled$.next(false);
|
||||
} else {
|
||||
this._disabled$.next(c === "DISABLED");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Directive, Input, OnDestroy, Optional } from "@angular/core";
|
||||
import { Directive, OnDestroy, Optional, input } from "@angular/core";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { ButtonLikeAbstraction } from "../shared/button-like.abstraction";
|
||||
@@ -29,8 +29,8 @@ import { BitSubmitDirective } from "./bit-submit.directive";
|
||||
export class BitFormButtonDirective implements OnDestroy {
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
@Input() type: string;
|
||||
@Input() disabled?: boolean;
|
||||
readonly type = input<string>(undefined);
|
||||
readonly disabled = input<boolean>(undefined);
|
||||
|
||||
constructor(
|
||||
buttonComponent: ButtonLikeAbstraction,
|
||||
@@ -39,16 +39,17 @@ export class BitFormButtonDirective implements OnDestroy {
|
||||
) {
|
||||
if (submitDirective && buttonComponent) {
|
||||
submitDirective.loading$.pipe(takeUntil(this.destroy$)).subscribe((loading) => {
|
||||
if (this.type === "submit") {
|
||||
if (this.type() === "submit") {
|
||||
buttonComponent.loading.set(loading);
|
||||
} else {
|
||||
buttonComponent.disabled.set(this.disabled || loading);
|
||||
buttonComponent.disabled.set(this.disabled() || loading);
|
||||
}
|
||||
});
|
||||
|
||||
submitDirective.disabled$.pipe(takeUntil(this.destroy$)).subscribe((disabled) => {
|
||||
if (this.disabled !== false) {
|
||||
buttonComponent.disabled.set(this.disabled || disabled);
|
||||
const disabledValue = this.disabled();
|
||||
if (disabledValue !== false) {
|
||||
buttonComponent.disabled.set(disabledValue || disabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { NgClass } from "@angular/common";
|
||||
import { Component, Input, OnChanges } from "@angular/core";
|
||||
import { Component, OnChanges, input } from "@angular/core";
|
||||
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
|
||||
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@@ -25,17 +25,17 @@ const SizeClasses: Record<SizeTypes, string[]> = {
|
||||
@Component({
|
||||
selector: "bit-avatar",
|
||||
template: `@if (src) {
|
||||
<img [src]="src" title="{{ title || text }}" [ngClass]="classList" />
|
||||
<img [src]="src" title="{{ title() || text() }}" [ngClass]="classList" />
|
||||
}`,
|
||||
imports: [NgClass],
|
||||
})
|
||||
export class AvatarComponent implements OnChanges {
|
||||
@Input() border = false;
|
||||
@Input() color?: string;
|
||||
@Input() id?: string;
|
||||
@Input() text?: string;
|
||||
@Input() title: string;
|
||||
@Input() size: SizeTypes = "default";
|
||||
readonly border = input(false);
|
||||
readonly color = input<string>(undefined);
|
||||
readonly id = input<string>(undefined);
|
||||
readonly text = input<string>(undefined);
|
||||
readonly title = input<string>(undefined);
|
||||
readonly size = input<SizeTypes>("default");
|
||||
|
||||
private svgCharCount = 2;
|
||||
private svgFontSize = 20;
|
||||
@@ -51,13 +51,13 @@ export class AvatarComponent implements OnChanges {
|
||||
|
||||
get classList() {
|
||||
return ["tw-rounded-full"]
|
||||
.concat(SizeClasses[this.size] ?? [])
|
||||
.concat(this.border ? ["tw-border", "tw-border-solid", "tw-border-secondary-600"] : []);
|
||||
.concat(SizeClasses[this.size()] ?? [])
|
||||
.concat(this.border() ? ["tw-border", "tw-border-solid", "tw-border-secondary-600"] : []);
|
||||
}
|
||||
|
||||
private generate() {
|
||||
let chars: string = null;
|
||||
const upperCaseText = this.text?.toUpperCase() ?? "";
|
||||
const upperCaseText = this.text()?.toUpperCase() ?? "";
|
||||
|
||||
chars = this.getFirstLetters(upperCaseText, this.svgCharCount);
|
||||
|
||||
@@ -71,12 +71,13 @@ export class AvatarComponent implements OnChanges {
|
||||
}
|
||||
|
||||
let svg: HTMLElement;
|
||||
let hexColor = this.color;
|
||||
let hexColor = this.color();
|
||||
|
||||
if (!Utils.isNullOrWhitespace(this.color)) {
|
||||
const id = this.id();
|
||||
if (!Utils.isNullOrWhitespace(this.color())) {
|
||||
svg = this.createSvgElement(this.svgSize, hexColor);
|
||||
} else if (!Utils.isNullOrWhitespace(this.id)) {
|
||||
hexColor = Utils.stringToColor(this.id.toString());
|
||||
} else if (!Utils.isNullOrWhitespace(id)) {
|
||||
hexColor = Utils.stringToColor(id.toString());
|
||||
svg = this.createSvgElement(this.svgSize, hexColor);
|
||||
} else {
|
||||
hexColor = Utils.stringToColor(upperCaseText);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="tw-inline-flex tw-flex-wrap tw-gap-2">
|
||||
@for (item of filteredItems; track item; let last = $last) {
|
||||
<span bitBadge [variant]="variant" [truncate]="truncate">
|
||||
<span bitBadge [variant]="variant()" [truncate]="truncate()">
|
||||
{{ item }}
|
||||
</span>
|
||||
@if (!last || isFiltered) {
|
||||
@@ -8,8 +8,8 @@
|
||||
}
|
||||
}
|
||||
@if (isFiltered) {
|
||||
<span bitBadge [variant]="variant">
|
||||
{{ "plusNMore" | i18n: (items.length - filteredItems.length).toString() }}
|
||||
<span bitBadge [variant]="variant()">
|
||||
{{ "plusNMore" | i18n: (items().length - filteredItems.length).toString() }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { Component, Input, OnChanges } from "@angular/core";
|
||||
import { Component, Input, OnChanges, input } from "@angular/core";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
@@ -18,10 +18,12 @@ export class BadgeListComponent implements OnChanges {
|
||||
protected filteredItems: string[] = [];
|
||||
protected isFiltered = false;
|
||||
|
||||
@Input() variant: BadgeVariant = "primary";
|
||||
@Input() items: string[] = [];
|
||||
@Input() truncate = true;
|
||||
readonly variant = input<BadgeVariant>("primary");
|
||||
readonly items = input<string[]>([]);
|
||||
readonly truncate = input(true);
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get maxItems(): number | undefined {
|
||||
return this._maxItems;
|
||||
@@ -32,11 +34,11 @@ export class BadgeListComponent implements OnChanges {
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.maxItems == undefined || this.items.length <= this.maxItems) {
|
||||
this.filteredItems = this.items;
|
||||
if (this.maxItems == undefined || this.items().length <= this.maxItems) {
|
||||
this.filteredItems = this.items();
|
||||
} else {
|
||||
this.filteredItems = this.items.slice(0, this.maxItems - 1);
|
||||
this.filteredItems = this.items().slice(0, this.maxItems - 1);
|
||||
}
|
||||
this.isFiltered = this.items.length > this.filteredItems.length;
|
||||
this.isFiltered = this.items().length > this.filteredItems.length;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<span [ngClass]="{ 'tw-truncate tw-block': truncate }">
|
||||
<span [ngClass]="{ 'tw-truncate tw-block': truncate() }">
|
||||
<ng-content></ng-content>
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, ElementRef, HostBinding, Input } from "@angular/core";
|
||||
import { Component, ElementRef, HostBinding, input } from "@angular/core";
|
||||
|
||||
import { FocusableElement } from "../shared/focusable-element";
|
||||
|
||||
@@ -89,33 +89,34 @@ export class BadgeComponent implements FocusableElement {
|
||||
"disabled:hover:!tw-text-muted",
|
||||
"disabled:tw-cursor-not-allowed",
|
||||
]
|
||||
.concat(styles[this.variant])
|
||||
.concat(this.hasHoverEffects ? [...hoverStyles[this.variant], "tw-min-w-10"] : [])
|
||||
.concat(this.truncate ? this.maxWidthClass : []);
|
||||
.concat(styles[this.variant()])
|
||||
.concat(this.hasHoverEffects ? [...hoverStyles[this.variant()], "tw-min-w-10"] : [])
|
||||
.concat(this.truncate() ? this.maxWidthClass() : []);
|
||||
}
|
||||
@HostBinding("attr.title") get titleAttr() {
|
||||
if (this.title !== undefined) {
|
||||
return this.title;
|
||||
const title = this.title();
|
||||
if (title !== undefined) {
|
||||
return title;
|
||||
}
|
||||
return this.truncate ? this?.el?.nativeElement?.textContent?.trim() : null;
|
||||
return this.truncate() ? this?.el?.nativeElement?.textContent?.trim() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional override for the automatic badge title when truncating.
|
||||
*/
|
||||
@Input() title?: string;
|
||||
readonly title = input<string>(undefined);
|
||||
|
||||
/**
|
||||
* Variant, sets the background color of the badge.
|
||||
*/
|
||||
@Input() variant: BadgeVariant = "primary";
|
||||
readonly variant = input<BadgeVariant>("primary");
|
||||
|
||||
/**
|
||||
* Truncate long text
|
||||
*/
|
||||
@Input() truncate = true;
|
||||
readonly truncate = input(true);
|
||||
|
||||
@Input() maxWidthClass: `tw-max-w-${string}` = "tw-max-w-40";
|
||||
readonly maxWidthClass = input<`tw-max-w-${string}`>("tw-max-w-40");
|
||||
|
||||
getFocusTarget() {
|
||||
return this.el.nativeElement;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div
|
||||
class="tw-flex tw-items-center tw-gap-2 tw-p-2 tw-ps-4 tw-text-main tw-border-transparent tw-bg-clip-padding tw-border-solid tw-border-b tw-border-0"
|
||||
[ngClass]="bannerClass"
|
||||
[attr.role]="useAlertRole ? 'status' : null"
|
||||
[attr.aria-live]="useAlertRole ? 'polite' : null"
|
||||
[attr.role]="useAlertRole() ? 'status' : null"
|
||||
[attr.aria-live]="useAlertRole() ? 'polite' : null"
|
||||
>
|
||||
@if (icon) {
|
||||
<i class="bwi tw-align-middle tw-text-base" [ngClass]="icon" aria-hidden="true"></i>
|
||||
@@ -12,7 +12,7 @@
|
||||
<ng-content></ng-content>
|
||||
</span>
|
||||
<!-- Overriding hover and focus-visible colors for a11y against colored background -->
|
||||
@if (showClose) {
|
||||
@if (showClose()) {
|
||||
<button
|
||||
class="hover:tw-border-text-main focus-visible:before:tw-ring-text-main"
|
||||
type="button"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 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, OnInit, Output, EventEmitter } from "@angular/core";
|
||||
import { Component, Input, OnInit, Output, EventEmitter, input } from "@angular/core";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
@@ -31,19 +31,22 @@ const defaultIcon: Record<BannerType, string> = {
|
||||
imports: [CommonModule, IconButtonModule, I18nPipe],
|
||||
})
|
||||
export class BannerComponent implements OnInit {
|
||||
@Input("bannerType") bannerType: BannerType = "info";
|
||||
readonly bannerType = input<BannerType>("info");
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input() icon: string;
|
||||
@Input() useAlertRole = true;
|
||||
@Input() showClose = true;
|
||||
readonly useAlertRole = input(true);
|
||||
readonly showClose = input(true);
|
||||
|
||||
@Output() onClose = new EventEmitter<void>();
|
||||
|
||||
ngOnInit(): void {
|
||||
this.icon ??= defaultIcon[this.bannerType];
|
||||
this.icon ??= defaultIcon[this.bannerType()];
|
||||
}
|
||||
|
||||
get bannerClass() {
|
||||
switch (this.bannerType) {
|
||||
switch (this.bannerType()) {
|
||||
case "danger":
|
||||
return "tw-bg-danger-100 tw-border-b-danger-700";
|
||||
case "info":
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core";
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { QueryParamsHandling } from "@angular/router";
|
||||
|
||||
@Component({
|
||||
@@ -9,17 +17,21 @@ import { QueryParamsHandling } from "@angular/router";
|
||||
templateUrl: "./breadcrumb.component.html",
|
||||
})
|
||||
export class BreadcrumbComponent {
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input()
|
||||
icon?: string;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input()
|
||||
route?: string | any[] = undefined;
|
||||
|
||||
@Input()
|
||||
queryParams?: Record<string, string> = {};
|
||||
readonly queryParams = input<Record<string, string>>({});
|
||||
|
||||
@Input()
|
||||
queryParamsHandling?: QueryParamsHandling;
|
||||
readonly queryParamsHandling = input<QueryParamsHandling>(undefined);
|
||||
|
||||
@Output()
|
||||
click = new EventEmitter();
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
linkType="primary"
|
||||
class="tw-my-2 tw-inline-block"
|
||||
[routerLink]="breadcrumb.route"
|
||||
[queryParams]="breadcrumb.queryParams"
|
||||
[queryParamsHandling]="breadcrumb.queryParamsHandling"
|
||||
[queryParams]="breadcrumb.queryParams()"
|
||||
[queryParamsHandling]="breadcrumb.queryParamsHandling()"
|
||||
>
|
||||
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
|
||||
</a>
|
||||
@@ -44,8 +44,8 @@
|
||||
bitMenuItem
|
||||
linkType="primary"
|
||||
[routerLink]="breadcrumb.route"
|
||||
[queryParams]="breadcrumb.queryParams"
|
||||
[queryParamsHandling]="breadcrumb.queryParamsHandling"
|
||||
[queryParams]="breadcrumb.queryParams()"
|
||||
[queryParamsHandling]="breadcrumb.queryParamsHandling()"
|
||||
>
|
||||
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
|
||||
</a>
|
||||
@@ -65,8 +65,8 @@
|
||||
linkType="primary"
|
||||
class="tw-my-2 tw-inline-block"
|
||||
[routerLink]="breadcrumb.route"
|
||||
[queryParams]="breadcrumb.queryParams"
|
||||
[queryParamsHandling]="breadcrumb.queryParamsHandling"
|
||||
[queryParams]="breadcrumb.queryParams()"
|
||||
[queryParamsHandling]="breadcrumb.queryParamsHandling()"
|
||||
>
|
||||
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
|
||||
</a>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, ContentChildren, Input, QueryList } from "@angular/core";
|
||||
import { Component, ContentChildren, QueryList, input } from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
|
||||
import { IconButtonModule } from "../icon-button";
|
||||
@@ -19,8 +19,7 @@ import { BreadcrumbComponent } from "./breadcrumb.component";
|
||||
imports: [CommonModule, LinkModule, RouterModule, IconButtonModule, MenuModule],
|
||||
})
|
||||
export class BreadcrumbsComponent {
|
||||
@Input()
|
||||
show = 3;
|
||||
readonly show = input(3);
|
||||
|
||||
private breadcrumbs: BreadcrumbComponent[] = [];
|
||||
|
||||
@@ -31,14 +30,14 @@ export class BreadcrumbsComponent {
|
||||
|
||||
protected get beforeOverflow() {
|
||||
if (this.hasOverflow) {
|
||||
return this.breadcrumbs.slice(0, this.show - 1);
|
||||
return this.breadcrumbs.slice(0, this.show() - 1);
|
||||
}
|
||||
|
||||
return this.breadcrumbs;
|
||||
}
|
||||
|
||||
protected get overflow() {
|
||||
return this.breadcrumbs.slice(this.show - 1, -1);
|
||||
return this.breadcrumbs.slice(this.show() - 1, -1);
|
||||
}
|
||||
|
||||
protected get afterOverflow() {
|
||||
@@ -46,6 +45,6 @@ export class BreadcrumbsComponent {
|
||||
}
|
||||
|
||||
protected get hasOverflow() {
|
||||
return this.breadcrumbs.length > this.show;
|
||||
return this.breadcrumbs.length > this.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class ButtonComponent implements ButtonLikeAbstraction {
|
||||
"focus:tw-outline-none",
|
||||
]
|
||||
.concat(this.block ? ["tw-w-full", "tw-block"] : ["tw-inline-block"])
|
||||
.concat(buttonStyles[this.buttonType ?? "secondary"])
|
||||
.concat(buttonStyles[this.buttonType() ?? "secondary"])
|
||||
.concat(
|
||||
this.showDisabledStyles() || this.disabled()
|
||||
? [
|
||||
@@ -106,12 +106,14 @@ export class ButtonComponent implements ButtonLikeAbstraction {
|
||||
return this.showLoadingStyle() || (this.disabledAttr() && this.loading() === false);
|
||||
});
|
||||
|
||||
@Input() buttonType: ButtonType = "secondary";
|
||||
readonly buttonType = input<ButtonType>("secondary");
|
||||
|
||||
size = input<ButtonSize>("default");
|
||||
|
||||
private _block = false;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get block(): boolean {
|
||||
return this._block;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { Component, Input, OnInit, input } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
@@ -35,23 +35,29 @@ let nextId = 0;
|
||||
imports: [SharedModule, TypographyModule],
|
||||
})
|
||||
export class CalloutComponent implements OnInit {
|
||||
@Input() type: CalloutTypes = "info";
|
||||
readonly type = input<CalloutTypes>("info");
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input() icon: string;
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() title: string;
|
||||
@Input() useAlertRole = false;
|
||||
readonly useAlertRole = input(false);
|
||||
protected titleId = `bit-callout-title-${nextId++}`;
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.icon ??= defaultIcon[this.type];
|
||||
if (this.title == null && defaultI18n[this.type] != null) {
|
||||
this.title = this.i18nService.t(defaultI18n[this.type]);
|
||||
const type = this.type();
|
||||
this.icon ??= defaultIcon[type];
|
||||
if (this.title == null && defaultI18n[type] != null) {
|
||||
this.title = this.i18nService.t(defaultI18n[type]);
|
||||
}
|
||||
}
|
||||
|
||||
get calloutClass() {
|
||||
switch (this.type) {
|
||||
switch (this.type()) {
|
||||
case "danger":
|
||||
return "tw-bg-danger-100";
|
||||
case "info":
|
||||
|
||||
@@ -90,6 +90,8 @@ export class CheckboxComponent implements BitFormControlAbstraction {
|
||||
protected indeterminateImage =
|
||||
`url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 13 13"%3E%3Cpath stroke="%23fff" stroke-width="2" d="M2.5 6.5h8"/%3E%3C/svg%3E%0A')`;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding()
|
||||
@Input()
|
||||
get disabled() {
|
||||
@@ -100,6 +102,8 @@ export class CheckboxComponent implements BitFormControlAbstraction {
|
||||
}
|
||||
private _disabled: boolean;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get required() {
|
||||
return (
|
||||
|
||||
@@ -69,10 +69,10 @@
|
||||
bitMenuItem
|
||||
(click)="viewOption(parent, $event)"
|
||||
class="tw-text-[length:inherit]"
|
||||
[title]="'backTo' | i18n: parent.label ?? placeholderText"
|
||||
[title]="'backTo' | i18n: parent.label ?? placeholderText()"
|
||||
>
|
||||
<i slot="start" class="bwi bwi-angle-left" aria-hidden="true"></i>
|
||||
{{ "backTo" | i18n: parent.label ?? placeholderText }}
|
||||
{{ "backTo" | i18n: parent.label ?? placeholderText() }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
booleanAttribute,
|
||||
inject,
|
||||
signal,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
@@ -54,13 +55,15 @@ export class ChipSelectComponent<T = unknown> implements ControlValueAccessor, A
|
||||
@ViewChild("chipSelectButton") chipSelectButton: ElementRef<HTMLButtonElement>;
|
||||
|
||||
/** Text to show when there is no selected option */
|
||||
@Input({ required: true }) placeholderText: string;
|
||||
readonly placeholderText = input.required<string>();
|
||||
|
||||
/** Icon to show when there is no selected option or the selected option does not have an icon */
|
||||
@Input() placeholderIcon: string;
|
||||
readonly placeholderIcon = input<string>(undefined);
|
||||
|
||||
private _options: ChipSelectOption<T>[];
|
||||
/** The select options to render */
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input({ required: true })
|
||||
get options(): ChipSelectOption<T>[] {
|
||||
return this._options;
|
||||
@@ -71,10 +74,12 @@ export class ChipSelectComponent<T = unknown> implements ControlValueAccessor, A
|
||||
}
|
||||
|
||||
/** Disables the entire chip */
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input({ transform: booleanAttribute }) disabled = false;
|
||||
|
||||
/** Chip will stretch to full width of its container */
|
||||
@Input({ transform: booleanAttribute }) fullWidth?: boolean;
|
||||
readonly fullWidth = input<boolean, unknown>(undefined, { transform: booleanAttribute });
|
||||
|
||||
/**
|
||||
* We have `:focus-within` and `:focus-visible` but no `:focus-visible-within`
|
||||
@@ -91,7 +96,7 @@ export class ChipSelectComponent<T = unknown> implements ControlValueAccessor, A
|
||||
|
||||
@HostBinding("class")
|
||||
get classList() {
|
||||
return ["tw-inline-block", this.fullWidth ? "tw-w-full" : "tw-max-w-52"];
|
||||
return ["tw-inline-block", this.fullWidth() ? "tw-w-full" : "tw-max-w-52"];
|
||||
}
|
||||
|
||||
private destroyRef = inject(DestroyRef);
|
||||
@@ -113,12 +118,12 @@ export class ChipSelectComponent<T = unknown> implements ControlValueAccessor, A
|
||||
|
||||
/** The label to show in the chip button */
|
||||
protected get label(): string {
|
||||
return this.selectedOption?.label || this.placeholderText;
|
||||
return this.selectedOption?.label || this.placeholderText();
|
||||
}
|
||||
|
||||
/** The icon to show in the chip button */
|
||||
protected get icon(): string {
|
||||
return this.selectedOption?.icon || this.placeholderIcon;
|
||||
return this.selectedOption?.icon || this.placeholderIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Directive, HostListener, Input, InjectionToken, Inject, Optional } from "@angular/core";
|
||||
import {
|
||||
Directive,
|
||||
HostListener,
|
||||
Input,
|
||||
InjectionToken,
|
||||
Inject,
|
||||
Optional,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@@ -28,13 +36,13 @@ export class CopyClickDirective {
|
||||
@Optional() @Inject(COPY_CLICK_LISTENER) private copyListener?: CopyClickListener,
|
||||
) {}
|
||||
|
||||
@Input("appCopyClick") valueToCopy = "";
|
||||
readonly valueToCopy = input("", { alias: "appCopyClick" });
|
||||
|
||||
/**
|
||||
* When set, the toast displayed will show `<valueLabel> copied`
|
||||
* instead of the default messaging.
|
||||
*/
|
||||
@Input() valueLabel?: string;
|
||||
readonly valueLabel = input<string>(undefined);
|
||||
|
||||
/**
|
||||
* When set without a value, a success toast will be shown when the value is copied
|
||||
@@ -49,6 +57,8 @@ export class CopyClickDirective {
|
||||
* <app-component [appCopyClick]="value to copy" showToast="info"/></app-component>
|
||||
* ```
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() set showToast(value: ToastVariant | "") {
|
||||
// When the `showToast` is set without a value, an empty string will be passed
|
||||
if (value === "") {
|
||||
@@ -60,15 +70,17 @@ export class CopyClickDirective {
|
||||
}
|
||||
|
||||
@HostListener("click") onClick() {
|
||||
this.platformUtilsService.copyToClipboard(this.valueToCopy);
|
||||
const valueToCopy = this.valueToCopy();
|
||||
this.platformUtilsService.copyToClipboard(valueToCopy);
|
||||
|
||||
if (this.copyListener) {
|
||||
this.copyListener.onCopy(this.valueToCopy);
|
||||
this.copyListener.onCopy(valueToCopy);
|
||||
}
|
||||
|
||||
if (this._showToast) {
|
||||
const message = this.valueLabel
|
||||
? this.i18nService.t("valueCopied", this.valueLabel)
|
||||
const valueLabel = this.valueLabel();
|
||||
const message = valueLabel
|
||||
? this.i18nService.t("valueCopied", valueLabel)
|
||||
: this.i18nService.t("copySuccessful");
|
||||
|
||||
this.toastService.showToast({
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
noMargin
|
||||
class="tw-text-main tw-mb-0 tw-truncate"
|
||||
>
|
||||
{{ title }}
|
||||
{{ title() }}
|
||||
@if (subtitle) {
|
||||
<span class="tw-text-muted tw-font-normal tw-text-sm">
|
||||
{{ subtitle }}
|
||||
@@ -44,12 +44,12 @@
|
||||
<div
|
||||
class="tw-relative tw-flex-1 tw-flex tw-flex-col tw-overflow-hidden"
|
||||
[ngClass]="{
|
||||
'tw-min-h-60': loading,
|
||||
'tw-bg-background': background === 'default',
|
||||
'tw-bg-background-alt': background === 'alt',
|
||||
'tw-min-h-60': loading(),
|
||||
'tw-bg-background': background() === 'default',
|
||||
'tw-bg-background-alt': background() === 'alt',
|
||||
}"
|
||||
>
|
||||
@if (loading) {
|
||||
@if (loading()) {
|
||||
<div class="tw-absolute tw-flex tw-size-full tw-items-center tw-justify-center">
|
||||
<i class="bwi bwi-spinner bwi-spin bwi-lg" [attr.aria-label]="'loading' | i18n"></i>
|
||||
</div>
|
||||
@@ -59,8 +59,8 @@
|
||||
[ngClass]="{
|
||||
'tw-p-4': !disablePadding && !isDrawer,
|
||||
'tw-px-6 tw-py-4': !disablePadding && isDrawer,
|
||||
'tw-overflow-y-auto': !loading,
|
||||
'tw-invisible tw-overflow-y-hidden': loading,
|
||||
'tw-overflow-y-auto': !loading(),
|
||||
'tw-invisible tw-overflow-y-hidden': loading(),
|
||||
}"
|
||||
>
|
||||
<ng-content select="[bitDialogContent]"></ng-content>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CdkTrapFocus } from "@angular/cdk/a11y";
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { CdkScrollable } from "@angular/cdk/scrolling";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, HostBinding, Input, inject, viewChild } from "@angular/core";
|
||||
import { Component, HostBinding, Input, inject, viewChild, input } from "@angular/core";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
@@ -40,28 +40,32 @@ export class DialogComponent {
|
||||
protected bodyHasScrolledFrom = hasScrolledFrom(this.scrollableBody);
|
||||
|
||||
/** Background color */
|
||||
@Input()
|
||||
background: "default" | "alt" = "default";
|
||||
readonly background = input<"default" | "alt">("default");
|
||||
|
||||
/**
|
||||
* Dialog size, more complex dialogs should use large, otherwise default is fine.
|
||||
*/
|
||||
@Input() dialogSize: "small" | "default" | "large" = "default";
|
||||
readonly dialogSize = input<"small" | "default" | "large">("default");
|
||||
|
||||
/**
|
||||
* Title to show in the dialog's header
|
||||
*/
|
||||
@Input() title: string;
|
||||
readonly title = input<string>(undefined);
|
||||
|
||||
/**
|
||||
* Subtitle to show in the dialog's header
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input() subtitle: string;
|
||||
|
||||
private _disablePadding = false;
|
||||
/**
|
||||
* Disable the built-in padding on the dialog, for use with tabbed dialogs.
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() set disablePadding(value: boolean | "") {
|
||||
this._disablePadding = coerceBooleanProperty(value);
|
||||
}
|
||||
@@ -72,7 +76,7 @@ export class DialogComponent {
|
||||
/**
|
||||
* Mark the dialog as loading which replaces the content with a spinner.
|
||||
*/
|
||||
@Input() loading = false;
|
||||
readonly loading = input(false);
|
||||
|
||||
@HostBinding("class") get classes() {
|
||||
// `tw-max-h-[90vh]` is needed to prevent dialogs from overlapping the desktop header
|
||||
@@ -92,7 +96,7 @@ export class DialogComponent {
|
||||
}
|
||||
|
||||
get width() {
|
||||
switch (this.dialogSize) {
|
||||
switch (this.dialogSize()) {
|
||||
case "small": {
|
||||
return "md:tw-max-w-sm";
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DialogRef } from "@angular/cdk/dialog";
|
||||
import { Directive, HostBinding, HostListener, Input, Optional } from "@angular/core";
|
||||
import { Directive, HostBinding, HostListener, Optional, input } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "[bitDialogClose]",
|
||||
})
|
||||
export class DialogCloseDirective {
|
||||
@Input("bitDialogClose") dialogResult: any;
|
||||
readonly dialogResult = input<any>(undefined, { alias: "bitDialogClose" });
|
||||
|
||||
constructor(@Optional() public dialogRef: DialogRef) {}
|
||||
|
||||
@@ -20,6 +20,6 @@ export class DialogCloseDirective {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dialogRef.close(this.dialogResult);
|
||||
this.dialogRef.close(this.dialogResult());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CdkDialogContainer, DialogRef } from "@angular/cdk/dialog";
|
||||
import { Directive, HostBinding, Input, OnInit, Optional } from "@angular/core";
|
||||
import { Directive, HostBinding, OnInit, Optional, input } from "@angular/core";
|
||||
|
||||
// Increments for each instance of this component
|
||||
let nextId = 0;
|
||||
@@ -10,7 +10,7 @@ let nextId = 0;
|
||||
export class DialogTitleContainerDirective implements OnInit {
|
||||
@HostBinding("id") id = `bit-dialog-title-${nextId++}`;
|
||||
|
||||
@Input() simple = false;
|
||||
readonly simple = input(false);
|
||||
|
||||
constructor(@Optional() private dialogRef: DialogRef<any>) {}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Directive, HostBinding, HostListener, Input } from "@angular/core";
|
||||
import { Directive, HostBinding, HostListener, input } from "@angular/core";
|
||||
|
||||
import { DisclosureComponent } from "./disclosure.component";
|
||||
|
||||
@@ -12,17 +12,17 @@ export class DisclosureTriggerForDirective {
|
||||
/**
|
||||
* Accepts template reference for a bit-disclosure component instance
|
||||
*/
|
||||
@Input("bitDisclosureTriggerFor") disclosure: DisclosureComponent;
|
||||
readonly disclosure = input<DisclosureComponent>(undefined, { alias: "bitDisclosureTriggerFor" });
|
||||
|
||||
@HostBinding("attr.aria-expanded") get ariaExpanded() {
|
||||
return this.disclosure.open;
|
||||
return this.disclosure().open;
|
||||
}
|
||||
|
||||
@HostBinding("attr.aria-controls") get ariaControls() {
|
||||
return this.disclosure.id;
|
||||
return this.disclosure().id;
|
||||
}
|
||||
|
||||
@HostListener("click") click() {
|
||||
this.disclosure.open = !this.disclosure.open;
|
||||
this.disclosure().open = !this.disclosure().open;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ export class DisclosureComponent {
|
||||
/**
|
||||
* Optionally init the disclosure in its opened state
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input({ transform: booleanAttribute }) set open(isOpen: boolean) {
|
||||
this._open = isOpen;
|
||||
this.openChange.emit(isOpen);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { NgClass } from "@angular/common";
|
||||
import { Component, ContentChild, HostBinding, Input } from "@angular/core";
|
||||
import { Component, ContentChild, HostBinding, Input, input } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
@@ -17,9 +17,11 @@ import { BitFormControlAbstraction } from "./form-control.abstraction";
|
||||
imports: [NgClass, TypographyDirective, I18nPipe],
|
||||
})
|
||||
export class FormControlComponent {
|
||||
@Input() label: string;
|
||||
readonly label = input<string>(undefined);
|
||||
|
||||
private _inline = false;
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() get inline() {
|
||||
return this._inline;
|
||||
}
|
||||
@@ -28,6 +30,8 @@ export class FormControlComponent {
|
||||
}
|
||||
|
||||
private _disableMargin = false;
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() set disableMargin(value: boolean | "") {
|
||||
this._disableMargin = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ export class BitLabel {
|
||||
@Optional() private parentFormControl: FormControlComponent,
|
||||
) {}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding("class") @Input() get classList() {
|
||||
return ["tw-inline-flex", "tw-gap-1", "tw-items-baseline", "tw-flex-row", "tw-min-w-0"];
|
||||
}
|
||||
@@ -27,6 +29,9 @@ export class BitLabel {
|
||||
return this.elementRef.nativeElement.textContent.trim();
|
||||
}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding() @Input() id = `bit-label-${nextId++}`;
|
||||
|
||||
get isInsideFormControl() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component, input } from "@angular/core";
|
||||
import { AbstractControl, UntypedFormGroup } from "@angular/forms";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
@@ -18,11 +18,10 @@ import { I18nPipe } from "@bitwarden/ui-common";
|
||||
imports: [I18nPipe],
|
||||
})
|
||||
export class BitErrorSummary {
|
||||
@Input()
|
||||
formGroup: UntypedFormGroup;
|
||||
readonly formGroup = input<UntypedFormGroup>(undefined);
|
||||
|
||||
get errorCount(): number {
|
||||
return this.getErrorCount(this.formGroup);
|
||||
return this.getErrorCount(this.formGroup());
|
||||
}
|
||||
|
||||
get errorString() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, HostBinding, Input } from "@angular/core";
|
||||
import { Component, HostBinding, input } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
|
||||
@@ -18,37 +18,38 @@ let nextId = 0;
|
||||
export class BitErrorComponent {
|
||||
@HostBinding() id = `bit-error-${nextId++}`;
|
||||
|
||||
@Input() error: [string, any];
|
||||
readonly error = input<[string, any]>(undefined);
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
get displayError() {
|
||||
switch (this.error[0]) {
|
||||
const error = this.error();
|
||||
switch (error[0]) {
|
||||
case "required":
|
||||
return this.i18nService.t("inputRequired");
|
||||
case "email":
|
||||
return this.i18nService.t("inputEmail");
|
||||
case "minlength":
|
||||
return this.i18nService.t("inputMinLength", this.error[1]?.requiredLength);
|
||||
return this.i18nService.t("inputMinLength", error[1]?.requiredLength);
|
||||
case "maxlength":
|
||||
return this.i18nService.t("inputMaxLength", this.error[1]?.requiredLength);
|
||||
return this.i18nService.t("inputMaxLength", error[1]?.requiredLength);
|
||||
case "min":
|
||||
return this.i18nService.t("inputMinValue", this.error[1]?.min);
|
||||
return this.i18nService.t("inputMinValue", error[1]?.min);
|
||||
case "max":
|
||||
return this.i18nService.t("inputMaxValue", this.error[1]?.max);
|
||||
return this.i18nService.t("inputMaxValue", error[1]?.max);
|
||||
case "forbiddenCharacters":
|
||||
return this.i18nService.t("inputForbiddenCharacters", this.error[1]?.characters.join(", "));
|
||||
return this.i18nService.t("inputForbiddenCharacters", error[1]?.characters.join(", "));
|
||||
case "multipleEmails":
|
||||
return this.i18nService.t("multipleInputEmails");
|
||||
case "trim":
|
||||
return this.i18nService.t("inputTrimValidator");
|
||||
default:
|
||||
// Attempt to show a custom error message.
|
||||
if (this.error[1]?.message) {
|
||||
return this.error[1]?.message;
|
||||
if (error[1]?.message) {
|
||||
return error[1]?.message;
|
||||
}
|
||||
|
||||
return this.error;
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Input,
|
||||
ViewChild,
|
||||
signal,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
@@ -38,10 +39,11 @@ export class BitFormFieldComponent implements AfterContentChecked {
|
||||
|
||||
@ViewChild(BitErrorComponent) error: BitErrorComponent;
|
||||
|
||||
@Input({ transform: booleanAttribute })
|
||||
disableMargin = false;
|
||||
readonly disableMargin = input(false, { transform: booleanAttribute });
|
||||
|
||||
/** If `true`, remove the bottom border for `readonly` inputs */
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input({ transform: booleanAttribute })
|
||||
disableReadOnlyBorder = false;
|
||||
|
||||
@@ -76,7 +78,7 @@ export class BitFormFieldComponent implements AfterContentChecked {
|
||||
@HostBinding("class")
|
||||
get classList() {
|
||||
return ["tw-block"]
|
||||
.concat(this.disableMargin ? [] : ["tw-mb-4", "bit-compact:tw-mb-3"])
|
||||
.concat(this.disableMargin() ? [] : ["tw-mb-4", "bit-compact:tw-mb-3"])
|
||||
.concat(this.readOnly ? [] : "tw-pt-2");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ export class BitPasswordInputToggleDirective implements AfterContentInit, OnChan
|
||||
/**
|
||||
* Whether the input is toggled to show the password.
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@HostBinding("attr.aria-pressed") @Input() toggled = false;
|
||||
@Output() toggledChange = new EventEmitter<boolean>();
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import { BitIconButtonComponent } from "../icon-button/icon-button.component";
|
||||
selector: "[bitPrefix]",
|
||||
})
|
||||
export class BitPrefixDirective implements OnInit {
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding("class") @Input() get classList() {
|
||||
return ["tw-text-muted"];
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { BitIconButtonComponent } from "../icon-button/icon-button.component";
|
||||
selector: "[bitSuffix]",
|
||||
})
|
||||
export class BitSuffixDirective implements OnInit {
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding("class") @Input() get classList() {
|
||||
return ["tw-text-muted"];
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { NgClass } from "@angular/common";
|
||||
import { Component, computed, ElementRef, HostBinding, Input, model } from "@angular/core";
|
||||
import { Component, computed, ElementRef, HostBinding, Input, model, input } from "@angular/core";
|
||||
import { toObservable, toSignal } from "@angular/core/rxjs-interop";
|
||||
import { debounce, interval } from "rxjs";
|
||||
|
||||
@@ -167,10 +167,14 @@ const sizes: Record<IconButtonSize, string[]> = {
|
||||
},
|
||||
})
|
||||
export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableElement {
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input("bitIconButton") icon: string;
|
||||
|
||||
@Input() buttonType: IconButtonType = "main";
|
||||
readonly buttonType = input<IconButtonType>("main");
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() size: IconButtonSize = "default";
|
||||
|
||||
@HostBinding("class") get classList() {
|
||||
@@ -183,9 +187,11 @@ export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableE
|
||||
"hover:tw-no-underline",
|
||||
"focus:tw-outline-none",
|
||||
]
|
||||
.concat(styles[this.buttonType])
|
||||
.concat(styles[this.buttonType()])
|
||||
.concat(sizes[this.size])
|
||||
.concat(this.showDisabledStyles() || this.disabled() ? disabledStyles[this.buttonType] : []);
|
||||
.concat(
|
||||
this.showDisabledStyles() || this.disabled() ? disabledStyles[this.buttonType()] : [],
|
||||
);
|
||||
}
|
||||
|
||||
get iconClass() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component, Input, input } from "@angular/core";
|
||||
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
|
||||
|
||||
import { Icon, isIcon } from "./icon";
|
||||
@@ -6,8 +6,8 @@ import { Icon, isIcon } from "./icon";
|
||||
@Component({
|
||||
selector: "bit-icon",
|
||||
host: {
|
||||
"[attr.aria-hidden]": "!ariaLabel",
|
||||
"[attr.aria-label]": "ariaLabel",
|
||||
"[attr.aria-hidden]": "!ariaLabel()",
|
||||
"[attr.aria-label]": "ariaLabel()",
|
||||
"[innerHtml]": "innerHtml",
|
||||
},
|
||||
template: ``,
|
||||
@@ -15,6 +15,8 @@ import { Icon, isIcon } from "./icon";
|
||||
export class BitIconComponent {
|
||||
innerHtml: SafeHtml | null = null;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() set icon(icon: Icon) {
|
||||
if (!isIcon(icon)) {
|
||||
return;
|
||||
@@ -24,7 +26,7 @@ export class BitIconComponent {
|
||||
this.innerHtml = this.domSanitizer.bypassSecurityTrustHtml(svg);
|
||||
}
|
||||
|
||||
@Input() ariaLabel: string | undefined = undefined;
|
||||
readonly ariaLabel = input<string | undefined>(undefined);
|
||||
|
||||
constructor(private domSanitizer: DomSanitizer) {}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import { FocusableElement } from "../shared/focusable-element";
|
||||
selector: "[appAutofocus], [bitAutofocus]",
|
||||
})
|
||||
export class AutofocusDirective implements AfterContentChecked {
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() set appAutofocus(condition: boolean | string) {
|
||||
this.autofocus = condition === "" || condition === true;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
NgZone,
|
||||
Optional,
|
||||
Self,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { NgControl, Validators } from "@angular/forms";
|
||||
|
||||
@@ -32,6 +33,8 @@ export function inputBorderClasses(error: boolean) {
|
||||
providers: [{ provide: BitFormFieldControl, useExisting: BitInputDirective }],
|
||||
})
|
||||
export class BitInputDirective implements BitFormFieldControl {
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding("class") @Input() get classList() {
|
||||
const classes = [
|
||||
"tw-block",
|
||||
@@ -52,6 +55,9 @@ export class BitInputDirective implements BitFormFieldControl {
|
||||
return classes.filter((s) => s != "");
|
||||
}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding() @Input() id = `bit-input-${nextId++}`;
|
||||
|
||||
@HostBinding("attr.aria-describedby") ariaDescribedBy: string;
|
||||
@@ -60,10 +66,18 @@ export class BitInputDirective implements BitFormFieldControl {
|
||||
return this.hasError ? true : undefined;
|
||||
}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding("attr.type") @Input() type?: InputTypes;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding("attr.spellcheck") @Input() spellcheck?: boolean;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding()
|
||||
@Input()
|
||||
get required() {
|
||||
@@ -74,10 +88,10 @@ export class BitInputDirective implements BitFormFieldControl {
|
||||
}
|
||||
private _required: boolean;
|
||||
|
||||
@Input() hasPrefix = false;
|
||||
@Input() hasSuffix = false;
|
||||
readonly hasPrefix = input(false);
|
||||
readonly hasSuffix = input(false);
|
||||
|
||||
@Input() showErrorsWhenDisabled? = false;
|
||||
readonly showErrorsWhenDisabled = input<boolean>(false);
|
||||
|
||||
get labelForId(): string {
|
||||
return this.id;
|
||||
@@ -89,7 +103,7 @@ export class BitInputDirective implements BitFormFieldControl {
|
||||
}
|
||||
|
||||
get hasError() {
|
||||
if (this.showErrorsWhenDisabled) {
|
||||
if (this.showErrorsWhenDisabled()) {
|
||||
return (
|
||||
(this.ngControl?.status === "INVALID" || this.ngControl?.status === "DISABLED") &&
|
||||
this.ngControl?.touched &&
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
>
|
||||
<div
|
||||
[ngClass]="{
|
||||
'tw-truncate': truncate,
|
||||
'tw-text-wrap tw-overflow-auto tw-break-words': !truncate,
|
||||
'tw-truncate': truncate(),
|
||||
'tw-text-wrap tw-overflow-auto tw-break-words': !truncate(),
|
||||
}"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
@@ -22,8 +22,8 @@
|
||||
bitTypography="helper"
|
||||
class="tw-text-muted tw-w-full"
|
||||
[ngClass]="{
|
||||
'tw-truncate': truncate,
|
||||
'tw-text-wrap tw-overflow-auto tw-break-words': !truncate,
|
||||
'tw-truncate': truncate(),
|
||||
'tw-text-wrap tw-overflow-auto tw-break-words': !truncate(),
|
||||
}"
|
||||
>
|
||||
<ng-content select="[slot=secondary]"></ng-content>
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
signal,
|
||||
ViewChild,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
import { TypographyModule } from "../typography";
|
||||
@@ -39,7 +39,7 @@ export class ItemContentComponent implements AfterContentChecked {
|
||||
*
|
||||
* Default behavior is truncation.
|
||||
*/
|
||||
@Input() truncate = true;
|
||||
readonly truncate = input(true);
|
||||
|
||||
ngAfterContentChecked(): void {
|
||||
this.endSlotHasChildren.set(this.endSlot?.nativeElement.childElementCount > 0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Input, HostBinding, Directive } from "@angular/core";
|
||||
import { HostBinding, Directive, input } from "@angular/core";
|
||||
|
||||
export type LinkType = "primary" | "secondary" | "contrast" | "light";
|
||||
|
||||
@@ -62,8 +62,7 @@ const commonStyles = [
|
||||
|
||||
@Directive()
|
||||
abstract class LinkDirective {
|
||||
@Input()
|
||||
linkType: LinkType = "primary";
|
||||
readonly linkType = input<LinkType>("primary");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +80,7 @@ export class AnchorLinkDirective extends LinkDirective {
|
||||
@HostBinding("class") get classList() {
|
||||
return ["before:-tw-inset-y-[0.125rem]"]
|
||||
.concat(commonStyles)
|
||||
.concat(linkStyles[this.linkType] ?? []);
|
||||
.concat(linkStyles[this.linkType()] ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +91,6 @@ export class ButtonLinkDirective extends LinkDirective {
|
||||
@HostBinding("class") get classList() {
|
||||
return ["before:-tw-inset-y-[0.25rem]"]
|
||||
.concat(commonStyles)
|
||||
.concat(linkStyles[this.linkType] ?? []);
|
||||
.concat(linkStyles[this.linkType()] ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ export class MenuItemDirective implements FocusableOption {
|
||||
return this.disabled || null; // native disabled attr must be null when false
|
||||
}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input({ transform: coerceBooleanProperty }) disabled?: boolean = false;
|
||||
|
||||
constructor(public elementRef: ElementRef<HTMLButtonElement>) {}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Input,
|
||||
OnDestroy,
|
||||
ViewContainerRef,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { Observable, Subscription } from "rxjs";
|
||||
import { filter, mergeWith } from "rxjs/operators";
|
||||
@@ -21,13 +22,16 @@ import { MenuComponent } from "./menu.component";
|
||||
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";
|
||||
}
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding("attr.role")
|
||||
@Input()
|
||||
role = "button";
|
||||
|
||||
@Input("bitMenuTriggerFor") menu: MenuComponent;
|
||||
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>(undefined);
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.ariaRole === "menu") {
|
||||
if (this.ariaRole() === "menu") {
|
||||
this.keyManager = new FocusKeyManager(this.menuItems)
|
||||
.withWrap()
|
||||
.skipPredicate((item) => item.disabled);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
bindLabel="listName"
|
||||
groupBy="parentGrouping"
|
||||
[placeholder]="placeholder"
|
||||
[loading]="loading"
|
||||
[loading]="loading()"
|
||||
[loadingText]="loadingText"
|
||||
notFoundText="{{ 'multiSelectNotFound' | i18n }}"
|
||||
clearAllText="{{ 'multiSelectClearAll' | i18n }}"
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
HostBinding,
|
||||
Optional,
|
||||
Self,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
@@ -46,11 +47,17 @@ export class MultiSelectComponent implements OnInit, BitFormFieldControl, Contro
|
||||
@ViewChild(NgSelectComponent) select: NgSelectComponent;
|
||||
|
||||
// Parent component should only pass selectable items (complete list - selected items = baseItems)
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() baseItems: SelectItemView[];
|
||||
// Defaults to native ng-select behavior - set to "true" to clear selected items on dropdown close
|
||||
@Input() removeSelectedItems = false;
|
||||
readonly removeSelectedItems = input(false);
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() placeholder: string;
|
||||
@Input() loading = false;
|
||||
readonly loading = input(false);
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input({ transform: coerceBooleanProperty }) disabled?: boolean;
|
||||
|
||||
// Internal tracking of selected items
|
||||
@@ -119,7 +126,7 @@ export class MultiSelectComponent implements OnInit, BitFormFieldControl, Contro
|
||||
this.onItemsConfirmed.emit(this.selectedItems);
|
||||
|
||||
// Remove selected items from base list based on input property
|
||||
if (this.removeSelectedItems) {
|
||||
if (this.removeSelectedItems()) {
|
||||
let updatedBaseItems = this.baseItems;
|
||||
this.selectedItems.forEach((selectedItem) => {
|
||||
updatedBaseItems = updatedBaseItems.filter((item) => selectedItem.id !== item.id);
|
||||
@@ -186,9 +193,14 @@ export class MultiSelectComponent implements OnInit, BitFormFieldControl, Contro
|
||||
}
|
||||
|
||||
/**Implemented as part of BitFormFieldControl */
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding() @Input() id = `bit-multi-select-${nextId++}`;
|
||||
|
||||
/**Implemented as part of BitFormFieldControl */
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding("attr.required")
|
||||
@Input()
|
||||
get required() {
|
||||
|
||||
@@ -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, Input, Output, input } from "@angular/core";
|
||||
import { RouterLink, RouterLinkActive } from "@angular/router";
|
||||
|
||||
/**
|
||||
@@ -11,16 +11,19 @@ export abstract class NavBaseComponent {
|
||||
/**
|
||||
* Text to display in main content
|
||||
*/
|
||||
@Input() text: string;
|
||||
readonly text = input<string>(undefined);
|
||||
|
||||
/**
|
||||
* `aria-label` for main content
|
||||
*/
|
||||
@Input() ariaLabel: string;
|
||||
readonly ariaLabel = input<string>(undefined);
|
||||
|
||||
/**
|
||||
* Optional icon, e.g. `"bwi-collection-shared"`
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input() icon: string;
|
||||
|
||||
/**
|
||||
@@ -34,41 +37,43 @@ export abstract class NavBaseComponent {
|
||||
*
|
||||
* See: {@link https://github.com/angular/angular/issues/24482}
|
||||
*/
|
||||
@Input() route?: RouterLink["routerLink"];
|
||||
readonly route = input<RouterLink["routerLink"]>(undefined);
|
||||
|
||||
/**
|
||||
* Passed to internal `routerLink`
|
||||
*
|
||||
* See {@link RouterLink.relativeTo}
|
||||
*/
|
||||
@Input() relativeTo?: RouterLink["relativeTo"];
|
||||
readonly relativeTo = input<RouterLink["relativeTo"]>(undefined);
|
||||
|
||||
/**
|
||||
* 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 this item is used within a tree, set `variant` to `"tree"`
|
||||
*/
|
||||
@Input() variant: "default" | "tree" = "default";
|
||||
readonly variant = input<"default" | "tree">("default");
|
||||
|
||||
/**
|
||||
* Depth level when nested inside of a `'tree'` variant
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() treeDepth = 0;
|
||||
|
||||
/**
|
||||
* If `true`, do not change styles when nav item is active.
|
||||
*/
|
||||
@Input() hideActiveStyles = false;
|
||||
readonly hideActiveStyles = input(false);
|
||||
|
||||
/**
|
||||
* Fires when main content is clicked
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<!-- This a higher order component that composes `NavItemComponent` -->
|
||||
@if (!hideIfEmpty || nestedNavComponents.length > 0) {
|
||||
@if (!hideIfEmpty() || nestedNavComponents.length > 0) {
|
||||
<bit-nav-item
|
||||
[text]="text"
|
||||
[text]="text()"
|
||||
[icon]="icon"
|
||||
[route]="route"
|
||||
[relativeTo]="relativeTo"
|
||||
[routerLinkActiveOptions]="routerLinkActiveOptions"
|
||||
[variant]="variant"
|
||||
[route]="route()"
|
||||
[relativeTo]="relativeTo()"
|
||||
[routerLinkActiveOptions]="routerLinkActiveOptions()"
|
||||
[variant]="variant()"
|
||||
[treeDepth]="treeDepth"
|
||||
(mainContentClicked)="handleMainContentClicked()"
|
||||
[ariaLabel]="ariaLabel"
|
||||
[ariaLabel]="ariaLabel()"
|
||||
[hideActiveStyles]="parentHideActiveStyles"
|
||||
>
|
||||
<ng-template #button>
|
||||
@@ -17,7 +17,7 @@
|
||||
type="button"
|
||||
class="tw-ms-auto"
|
||||
[bitIconButton]="
|
||||
open ? 'bwi-angle-up' : variant === 'tree' ? 'bwi-angle-right' : 'bwi-angle-down'
|
||||
open ? 'bwi-angle-up' : variant() === 'tree' ? 'bwi-angle-right' : 'bwi-angle-down'
|
||||
"
|
||||
[buttonType]="'light'"
|
||||
(click)="toggle($event)"
|
||||
@@ -26,18 +26,18 @@
|
||||
aria-haspopup="true"
|
||||
[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>
|
||||
<!-- Show toggle to the left for trees otherwise to the right -->
|
||||
@if (variant === "tree") {
|
||||
@if (variant() === "tree") {
|
||||
<ng-container slot="start">
|
||||
<ng-container *ngTemplateOutlet="button"></ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
<ng-container slot="end">
|
||||
<ng-content select="[slot=end]"></ng-content>
|
||||
@if (variant !== "tree") {
|
||||
@if (variant() !== "tree") {
|
||||
<ng-container *ngTemplateOutlet="button"></ng-container>
|
||||
}
|
||||
</ng-container>
|
||||
@@ -47,7 +47,7 @@
|
||||
@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>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Output,
|
||||
QueryList,
|
||||
SkipSelf,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
@@ -37,7 +38,7 @@ export class NavGroupComponent extends NavBaseComponent implements AfterContentI
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,14 +49,15 @@ export class NavGroupComponent extends NavBaseComponent implements AfterContentI
|
||||
/**
|
||||
* Is `true` if the expanded content is visible
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input()
|
||||
open = 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>();
|
||||
@@ -84,7 +86,7 @@ export class NavGroupComponent extends NavBaseComponent implements AfterContentI
|
||||
* - For any nested NavGroupComponents or NavItemComponents, increment the `treeDepth` by 1.
|
||||
*/
|
||||
private initNestedStyles() {
|
||||
if (this.variant !== "tree") {
|
||||
if (this.variant() !== "tree") {
|
||||
return;
|
||||
}
|
||||
[...this.nestedNavComponents].forEach((navGroupOrItem) => {
|
||||
@@ -94,7 +96,7 @@ export class NavGroupComponent extends NavBaseComponent implements AfterContentI
|
||||
|
||||
protected handleMainContentClicked() {
|
||||
if (!this.sideNavService.open) {
|
||||
if (!this.route) {
|
||||
if (!this.route()) {
|
||||
this.sideNavService.setOpen();
|
||||
}
|
||||
this.open = true;
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
>
|
||||
<div
|
||||
[ngStyle]="{
|
||||
'padding-left': data.open ? (variant === 'tree' ? 2.5 : 1) + treeDepth * 1.5 + 'rem' : '0',
|
||||
'padding-left': data.open
|
||||
? (variant() === 'tree' ? 2.5 : 1) + treeDepth * 1.5 + 'rem'
|
||||
: '0',
|
||||
}"
|
||||
class="tw-relative tw-flex"
|
||||
>
|
||||
<div [ngClass]="[variant === 'tree' ? 'tw-py-1' : 'tw-py-2']">
|
||||
<div [ngClass]="[variant() === 'tree' ? 'tw-py-1' : 'tw-py-2']">
|
||||
<div
|
||||
#slotStart
|
||||
class="[&>*:focus-visible::before]:!tw-ring-text-alt2 [&>*:hover]:!tw-border-text-alt2 [&>*]:!tw-text-alt2"
|
||||
@@ -30,7 +32,7 @@
|
||||
<div
|
||||
*ngIf="slotStart.childElementCount === 0"
|
||||
[ngClass]="{
|
||||
'tw-w-0': variant !== 'tree',
|
||||
'tw-w-0': variant() !== 'tree',
|
||||
}"
|
||||
>
|
||||
<button
|
||||
@@ -43,27 +45,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="route; then isAnchor; else isButton"></ng-container>
|
||||
<ng-container *ngIf="route(); then isAnchor; else isButton"></ng-container>
|
||||
|
||||
<!-- Main content of `NavItem` -->
|
||||
<ng-template #anchorAndButtonContent>
|
||||
<div
|
||||
[title]="text"
|
||||
[title]="text()"
|
||||
class="tw-truncate"
|
||||
[ngClass]="[
|
||||
variant === 'tree' ? 'tw-py-1' : 'tw-py-2',
|
||||
variant() === 'tree' ? 'tw-py-1' : 'tw-py-2',
|
||||
data.open ? 'tw-pe-4' : 'tw-text-center',
|
||||
]"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-fw tw-text-alt2 tw-mx-1 {{ icon }}"
|
||||
[attr.aria-hidden]="data.open"
|
||||
[attr.aria-label]="text"
|
||||
[attr.aria-label]="text()"
|
||||
></i
|
||||
><span
|
||||
*ngIf="data.open"
|
||||
[ngClass]="showActiveStyles ? 'tw-font-bold' : 'tw-font-semibold'"
|
||||
>{{ text }}</span
|
||||
>{{ text() }}</span
|
||||
>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -75,11 +77,11 @@
|
||||
<a
|
||||
class="tw-w-full tw-truncate tw-border-none tw-bg-transparent tw-p-0 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()"
|
||||
@@ -104,7 +106,7 @@
|
||||
<div
|
||||
*ngIf="data.open"
|
||||
class="tw-flex -tw-ms-3 tw-pe-4 tw-gap-1 [&>*:focus-visible::before]:!tw-ring-text-alt2 [&>*:hover]:!tw-border-text-alt2 [&>*]:tw-text-alt2 empty:tw-hidden"
|
||||
[ngClass]="[variant === 'tree' ? 'tw-py-1' : 'tw-py-2']"
|
||||
[ngClass]="[variant() === 'tree' ? 'tw-py-1' : 'tw-py-2']"
|
||||
>
|
||||
<ng-content select="[slot=end]"></ng-content>
|
||||
</div>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
@if (sideNavService.open) {
|
||||
<div class="tw-sticky tw-top-0 tw-z-50">
|
||||
<a
|
||||
[routerLink]="route"
|
||||
[routerLink]="route()"
|
||||
class="tw-px-5 tw-pb-5 tw-pt-7 tw-block tw-bg-background-alt3 tw-outline-none focus-visible:tw-ring focus-visible:tw-ring-inset focus-visible:tw-ring-text-alt2"
|
||||
[attr.aria-label]="label"
|
||||
[title]="label"
|
||||
[attr.aria-label]="label()"
|
||||
[title]="label()"
|
||||
routerLinkActive
|
||||
[ariaCurrentWhenActive]="'page'"
|
||||
>
|
||||
<bit-icon [icon]="openIcon"></bit-icon>
|
||||
<bit-icon [icon]="openIcon()"></bit-icon>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
@@ -16,8 +16,8 @@
|
||||
<bit-nav-item
|
||||
class="tw-block tw-pt-7"
|
||||
[hideActiveStyles]="true"
|
||||
[route]="route"
|
||||
[icon]="closedIcon"
|
||||
[text]="label"
|
||||
[route]="route()"
|
||||
[icon]="closedIcon()"
|
||||
[text]="label()"
|
||||
></bit-nav-item>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component, input } from "@angular/core";
|
||||
import { RouterLinkActive, RouterLink } from "@angular/router";
|
||||
|
||||
import { Icon } from "../icon";
|
||||
@@ -17,18 +17,18 @@ import { SideNavService } from "./side-nav.service";
|
||||
})
|
||||
export class NavLogoComponent {
|
||||
/** Icon that is displayed when the side nav is closed */
|
||||
@Input() closedIcon = "bwi-shield";
|
||||
readonly closedIcon = input("bwi-shield");
|
||||
|
||||
/** 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) {}
|
||||
}
|
||||
|
||||
@@ -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)',
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="tw-mx-auto tw-flex tw-flex-col tw-items-center tw-justify-center tw-pt-6">
|
||||
<div class="tw-max-w-sm tw-flex tw-flex-col tw-items-center">
|
||||
<bit-icon [icon]="icon" aria-hidden="true"></bit-icon>
|
||||
<bit-icon [icon]="icon()" aria-hidden="true"></bit-icon>
|
||||
<h3 class="tw-font-semibold tw-text-center tw-mt-4">
|
||||
<ng-content select="[slot=title]"></ng-content>
|
||||
</h3>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component, input } from "@angular/core";
|
||||
|
||||
import { Icons } from "..";
|
||||
import { BitIconComponent } from "../icon/icon.component";
|
||||
@@ -12,5 +12,5 @@ import { BitIconComponent } from "../icon/icon.component";
|
||||
imports: [BitIconComponent],
|
||||
})
|
||||
export class NoItemsComponent {
|
||||
@Input() icon = Icons.Search;
|
||||
readonly icon = input(Icons.Search);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Input,
|
||||
OnDestroy,
|
||||
ViewContainerRef,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { Observable, Subscription, filter, mergeWith } from "rxjs";
|
||||
|
||||
@@ -22,25 +23,25 @@ import { PopoverComponent } from "./popover.component";
|
||||
exportAs: "popoverTrigger",
|
||||
})
|
||||
export class PopoverTriggerForDirective implements OnDestroy, AfterViewInit {
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input()
|
||||
@HostBinding("attr.aria-expanded")
|
||||
popoverOpen = false;
|
||||
|
||||
@Input("bitPopoverTriggerFor")
|
||||
popover: PopoverComponent;
|
||||
readonly popover = input<PopoverComponent>(undefined, { alias: "bitPopoverTriggerFor" });
|
||||
|
||||
@Input("position")
|
||||
position: string;
|
||||
readonly position = input<string>(undefined);
|
||||
|
||||
private overlayRef: OverlayRef;
|
||||
private closedEventsSub: Subscription;
|
||||
|
||||
get positions() {
|
||||
if (!this.position) {
|
||||
if (!this.position()) {
|
||||
return defaultPositions;
|
||||
}
|
||||
|
||||
const preferredPosition = defaultPositions.find((position) => position.id === this.position);
|
||||
const preferredPosition = defaultPositions.find((position) => position.id === this.position());
|
||||
|
||||
if (preferredPosition) {
|
||||
return [preferredPosition, ...defaultPositions];
|
||||
@@ -83,7 +84,7 @@ export class PopoverTriggerForDirective implements OnDestroy, AfterViewInit {
|
||||
this.popoverOpen = true;
|
||||
this.overlayRef = this.overlay.create(this.defaultPopoverConfig);
|
||||
|
||||
const templatePortal = new TemplatePortal(this.popover.templateRef, this.viewContainerRef);
|
||||
const templatePortal = new TemplatePortal(this.popover().templateRef, this.viewContainerRef);
|
||||
|
||||
this.overlayRef.attach(templatePortal);
|
||||
this.closedEventsSub = this.getClosedEvents().subscribe(() => {
|
||||
@@ -97,7 +98,7 @@ export class PopoverTriggerForDirective implements OnDestroy, AfterViewInit {
|
||||
.keydownEvents()
|
||||
.pipe(filter((event: KeyboardEvent) => event.key === "Escape"));
|
||||
const backdrop = this.overlayRef.backdropClick();
|
||||
const popoverClosed = this.popover.closed;
|
||||
const popoverClosed = this.popover().closed;
|
||||
|
||||
return detachments.pipe(mergeWith(escKey, backdrop, popoverClosed));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
>
|
||||
<div class="tw-mb-1 tw-me-2 tw-flex tw-items-start tw-justify-between tw-gap-4 tw-ps-4">
|
||||
<h2 bitTypography="h5" class="tw-mt-1 tw-font-semibold">
|
||||
{{ title }}
|
||||
{{ title() }}
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { A11yModule } from "@angular/cdk/a11y";
|
||||
import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core";
|
||||
import { Component, EventEmitter, Output, TemplateRef, ViewChild, input } from "@angular/core";
|
||||
|
||||
import { IconButtonModule } from "../icon-button/icon-button.module";
|
||||
import { SharedModule } from "../shared/shared.module";
|
||||
@@ -15,6 +15,6 @@ import { TypographyModule } from "../typography";
|
||||
})
|
||||
export class PopoverComponent {
|
||||
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
|
||||
@Input() title = "";
|
||||
readonly title = input("");
|
||||
@Output() closed = new EventEmitter();
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
role="progressbar"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
attr.aria-valuenow="{{ barWidth }}"
|
||||
[ngStyle]="{ width: barWidth + '%' }"
|
||||
attr.aria-valuenow="{{ barWidth() }}"
|
||||
[ngStyle]="{ width: barWidth() + '%' }"
|
||||
>
|
||||
@if (displayText) {
|
||||
<div class="tw-flex tw-h-full tw-flex-wrap tw-items-center tw-overflow-hidden">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component, input } from "@angular/core";
|
||||
|
||||
type ProgressSizeType = "small" | "default" | "large";
|
||||
type BackgroundType = "danger" | "primary" | "success" | "warning";
|
||||
@@ -26,19 +26,19 @@ const BackgroundClasses: Record<BackgroundType, string[]> = {
|
||||
imports: [CommonModule],
|
||||
})
|
||||
export class ProgressComponent {
|
||||
@Input() barWidth = 0;
|
||||
@Input() bgColor: BackgroundType = "primary";
|
||||
@Input() showText = true;
|
||||
@Input() size: ProgressSizeType = "default";
|
||||
@Input() text?: string;
|
||||
readonly barWidth = input(0);
|
||||
readonly bgColor = input<BackgroundType>("primary");
|
||||
readonly showText = input(true);
|
||||
readonly size = input<ProgressSizeType>("default");
|
||||
readonly text = input<string>(undefined);
|
||||
|
||||
get displayText() {
|
||||
return this.showText && this.size !== "small";
|
||||
return this.showText() && this.size() !== "small";
|
||||
}
|
||||
|
||||
get outerBarStyles() {
|
||||
return ["tw-overflow-hidden", "tw-rounded", "tw-bg-secondary-100"].concat(
|
||||
SizeClasses[this.size],
|
||||
SizeClasses[this.size()],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,11 +53,11 @@ export class ProgressComponent {
|
||||
"tw-text-contrast",
|
||||
"tw-transition-all",
|
||||
]
|
||||
.concat(SizeClasses[this.size])
|
||||
.concat(BackgroundClasses[this.bgColor]);
|
||||
.concat(SizeClasses[this.size()])
|
||||
.concat(BackgroundClasses[this.bgColor()]);
|
||||
}
|
||||
|
||||
get textContent() {
|
||||
return this.text || this.barWidth + "%";
|
||||
return this.text() || this.barWidth() + "%";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
type="radio"
|
||||
bitRadio
|
||||
[id]="inputId"
|
||||
[disabled]="groupDisabled || disabled"
|
||||
[value]="value"
|
||||
[disabled]="groupDisabled || disabled()"
|
||||
[value]="value()"
|
||||
[checked]="selected"
|
||||
(change)="onInputChange()"
|
||||
(blur)="onBlur()"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, HostBinding, Input } from "@angular/core";
|
||||
import { Component, HostBinding, Input, input } from "@angular/core";
|
||||
|
||||
import { FormControlModule } from "../form-control/form-control.module";
|
||||
|
||||
@@ -13,13 +13,16 @@ let nextId = 0;
|
||||
imports: [FormControlModule, RadioInputComponent],
|
||||
})
|
||||
export class RadioButtonComponent {
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding("attr.id") @Input() id = `bit-radio-button-${nextId++}`;
|
||||
@HostBinding("class") get classList() {
|
||||
return [this.block ? "tw-block" : "tw-inline-block", "tw-mb-1", "[&_bit-hint]:tw-mt-0"];
|
||||
}
|
||||
|
||||
@Input() value: unknown;
|
||||
@Input() disabled = false;
|
||||
readonly value = input<unknown>(undefined);
|
||||
readonly disabled = input(false);
|
||||
|
||||
constructor(private groupComponent: RadioGroupComponent) {}
|
||||
|
||||
@@ -32,7 +35,7 @@ export class RadioButtonComponent {
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this.groupComponent.selected === this.value;
|
||||
return this.groupComponent.selected === this.value();
|
||||
}
|
||||
|
||||
get groupDisabled() {
|
||||
@@ -40,11 +43,11 @@ export class RadioButtonComponent {
|
||||
}
|
||||
|
||||
get block() {
|
||||
return this.groupComponent.block;
|
||||
return this.groupComponent.block();
|
||||
}
|
||||
|
||||
protected onInputChange() {
|
||||
this.groupComponent.onInputChange(this.value);
|
||||
this.groupComponent.onInputChange(this.value());
|
||||
}
|
||||
|
||||
protected onBlur() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { NgTemplateOutlet } from "@angular/common";
|
||||
import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core";
|
||||
import { Component, ContentChild, HostBinding, Input, Optional, Self, input } from "@angular/core";
|
||||
import { ControlValueAccessor, NgControl, Validators } from "@angular/forms";
|
||||
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
@@ -20,6 +20,8 @@ export class RadioGroupComponent implements ControlValueAccessor {
|
||||
disabled = false;
|
||||
|
||||
private _name?: string;
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() get name() {
|
||||
return this._name ?? this.ngControl?.name?.toString();
|
||||
}
|
||||
@@ -27,9 +29,12 @@ export class RadioGroupComponent implements ControlValueAccessor {
|
||||
this._name = value;
|
||||
}
|
||||
|
||||
@Input() block = false;
|
||||
readonly block = input(false);
|
||||
|
||||
@HostBinding("attr.role") role = "radiogroup";
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding("attr.id") @Input() id = `bit-radio-group-${nextId++}`;
|
||||
@HostBinding("class") classList = ["tw-block", "tw-mb-4"];
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ let nextId = 0;
|
||||
providers: [{ provide: BitFormControlAbstraction, useExisting: RadioInputComponent }],
|
||||
})
|
||||
export class RadioInputComponent implements BitFormControlAbstraction {
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding("attr.id") @Input() id = `bit-radio-input-${nextId++}`;
|
||||
|
||||
@HostBinding("class")
|
||||
@@ -74,6 +77,8 @@ export class RadioInputComponent implements BitFormControlAbstraction {
|
||||
|
||||
constructor(@Optional() @Self() private ngControl?: NgControl) {}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding()
|
||||
@Input()
|
||||
get disabled() {
|
||||
@@ -84,6 +89,8 @@ export class RadioInputComponent implements BitFormControlAbstraction {
|
||||
}
|
||||
private _disabled: boolean;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get required() {
|
||||
return (
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
bitInput
|
||||
[type]="inputType"
|
||||
[id]="id"
|
||||
[placeholder]="placeholder ?? ('search' | i18n)"
|
||||
[placeholder]="placeholder() ?? ('search' | i18n)"
|
||||
class="tw-ps-9"
|
||||
[ngModel]="searchText"
|
||||
(ngModelChange)="onChange($event)"
|
||||
(blur)="onTouch()"
|
||||
[disabled]="disabled"
|
||||
[attr.autocomplete]="autocomplete"
|
||||
[attr.autocomplete]="autocomplete()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, ElementRef, Input, ViewChild } from "@angular/core";
|
||||
import { Component, ElementRef, Input, ViewChild, input } from "@angular/core";
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
NG_VALUE_ACCESSOR,
|
||||
@@ -43,9 +43,11 @@ export class SearchComponent implements ControlValueAccessor, FocusableElement {
|
||||
// Use `type="text"` for Safari to improve rendering performance
|
||||
protected inputType = isBrowserSafariApi() ? ("text" as const) : ("search" as const);
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() disabled: boolean;
|
||||
@Input() placeholder: string;
|
||||
@Input() autocomplete: string;
|
||||
readonly placeholder = input<string>(undefined);
|
||||
readonly autocomplete = input<string>(undefined);
|
||||
|
||||
getFocusTarget() {
|
||||
return this.input?.nativeElement;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component, input } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "bit-section",
|
||||
@@ -9,7 +9,7 @@ import { Component, Input } from "@angular/core";
|
||||
<section
|
||||
[ngClass]="{
|
||||
'tw-mb-5 bit-compact:tw-mb-4 [&:not(bit-dialog_*):not(popup-page_*)]:md:tw-mb-12':
|
||||
!disableMargin,
|
||||
!disableMargin(),
|
||||
}"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
@@ -17,5 +17,5 @@ import { Component, Input } from "@angular/core";
|
||||
`,
|
||||
})
|
||||
export class SectionComponent {
|
||||
@Input({ transform: coerceBooleanProperty }) disableMargin = false;
|
||||
readonly disableMargin = input(false, { transform: coerceBooleanProperty });
|
||||
}
|
||||
|
||||
@@ -9,15 +9,27 @@ import { Option } from "./option";
|
||||
template: `<ng-template><ng-content></ng-content></ng-template>`,
|
||||
})
|
||||
export class OptionComponent<T = unknown> implements Option<T> {
|
||||
// TODO: Skipped for migration because:
|
||||
// This input overrides a field from a superclass, while the superclass field
|
||||
// is not migrated.
|
||||
@Input()
|
||||
icon?: string;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input overrides a field from a superclass, while the superclass field
|
||||
// is not migrated.
|
||||
@Input({ required: true })
|
||||
value: T;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input overrides a field from a superclass, while the superclass field
|
||||
// is not migrated.
|
||||
@Input({ required: true })
|
||||
label: string;
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// This input overrides a field from a superclass, while the superclass field
|
||||
// is not migrated.
|
||||
@Input({ transform: booleanAttribute })
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
[(ngModel)]="selectedOption"
|
||||
(ngModelChange)="onChange($event)"
|
||||
[disabled]="disabled"
|
||||
[placeholder]="placeholder"
|
||||
[placeholder]="placeholder()"
|
||||
[items]="items"
|
||||
(blur)="onBlur()"
|
||||
[labelForId]="labelForId"
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
ViewChild,
|
||||
Output,
|
||||
EventEmitter,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import {
|
||||
ControlValueAccessor,
|
||||
@@ -43,6 +44,8 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
|
||||
|
||||
private _items: Option<T>[] = [];
|
||||
/** Optional: Options can be provided using an array input or using `bit-option` */
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get items(): Option<T>[] {
|
||||
return this._items;
|
||||
@@ -52,7 +55,7 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
|
||||
this._selectedOption = this.findSelectedOption(next, this.selectedValue);
|
||||
}
|
||||
|
||||
@Input() placeholder = this.i18nService.t("selectPlaceholder");
|
||||
readonly placeholder = input(this.i18nService.t("selectPlaceholder"));
|
||||
@Output() closed = new EventEmitter();
|
||||
|
||||
protected selectedValue: T;
|
||||
@@ -89,6 +92,8 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
|
||||
get disabledAttr() {
|
||||
return this.disabled || null;
|
||||
}
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get disabled() {
|
||||
return this._disabled ?? this.ngControl?.disabled ?? false;
|
||||
@@ -154,9 +159,14 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
|
||||
}
|
||||
|
||||
/**Implemented as part of BitFormFieldControl */
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in combination with `@HostBinding` and migrating would
|
||||
// break.
|
||||
@HostBinding() @Input() id = `bit-multi-select-${nextId++}`;
|
||||
|
||||
/**Implemented as part of BitFormFieldControl */
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@HostBinding("attr.required")
|
||||
@Input()
|
||||
get required() {
|
||||
|
||||
@@ -42,6 +42,8 @@ export class StepperComponent extends CdkStepper {
|
||||
private initialOrientation: StepperOrientation | undefined = undefined;
|
||||
|
||||
// overriding CdkStepper orientation input so we can default to vertical
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
override get orientation() {
|
||||
return this.internalOrientation || "vertical";
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Directive, HostBinding, Input } from "@angular/core";
|
||||
import { Directive, HostBinding, input } from "@angular/core";
|
||||
|
||||
@Directive({
|
||||
selector: "tr[bitRow]",
|
||||
})
|
||||
export class RowDirective {
|
||||
@Input() alignContent: "top" | "middle" | "bottom" | "baseline" = "middle";
|
||||
readonly alignContent = input<"top" | "middle" | "bottom" | "baseline">("middle");
|
||||
|
||||
get alignmentClass(): string {
|
||||
switch (this.alignContent) {
|
||||
switch (this.alignContent()) {
|
||||
case "top":
|
||||
return "tw-align-top";
|
||||
case "middle":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { NgClass } from "@angular/common";
|
||||
import { Component, HostBinding, Input, OnInit } from "@angular/core";
|
||||
import { Component, HostBinding, Input, OnInit, input } from "@angular/core";
|
||||
|
||||
import type { SortDirection, SortFn } from "./table-data-source";
|
||||
import { TableComponent } from "./table.component";
|
||||
@@ -26,12 +26,14 @@ export class SortableComponent implements OnInit {
|
||||
/**
|
||||
* Mark the column as sortable and specify the key to sort by
|
||||
*/
|
||||
@Input() bitSortable: string;
|
||||
readonly bitSortable = input<string>(undefined);
|
||||
|
||||
private _default: SortDirection | boolean = false;
|
||||
/**
|
||||
* Mark the column as the default sort column
|
||||
*/
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input() set default(value: SortDirection | boolean | "") {
|
||||
if (value === "desc" || value === "asc") {
|
||||
this._default = value;
|
||||
@@ -51,7 +53,7 @@ export class SortableComponent implements OnInit {
|
||||
* return direction === 'asc' ? result : -result;
|
||||
* }
|
||||
*/
|
||||
@Input() fn: SortFn;
|
||||
readonly fn = input<SortFn>(undefined);
|
||||
|
||||
constructor(private table: TableComponent) {}
|
||||
|
||||
@@ -69,7 +71,8 @@ export class SortableComponent implements OnInit {
|
||||
}
|
||||
|
||||
protected setActive() {
|
||||
if (this.table.dataSource) {
|
||||
const dataSource = this.table.dataSource();
|
||||
if (dataSource) {
|
||||
const defaultDirection = this._default === "desc" ? "desc" : "asc";
|
||||
const direction = this.isActive
|
||||
? this.direction === "asc"
|
||||
@@ -77,20 +80,20 @@ export class SortableComponent implements OnInit {
|
||||
: "asc"
|
||||
: defaultDirection;
|
||||
|
||||
this.table.dataSource.sort = {
|
||||
column: this.bitSortable,
|
||||
dataSource.sort = {
|
||||
column: this.bitSortable(),
|
||||
direction: direction,
|
||||
fn: this.fn,
|
||||
fn: this.fn(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private get sort() {
|
||||
return this.table.dataSource?.sort;
|
||||
return this.table.dataSource()?.sort;
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
return this.sort?.column === this.bitSortable;
|
||||
return this.sort?.column === this.bitSortable();
|
||||
}
|
||||
|
||||
get direction() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<cdk-virtual-scroll-viewport
|
||||
bitScrollLayout
|
||||
[itemSize]="rowSize"
|
||||
[itemSize]="rowSize()"
|
||||
[ngStyle]="{ paddingBottom: headerHeight + 'px' }"
|
||||
>
|
||||
<table [ngClass]="tableClass">
|
||||
@@ -12,7 +12,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *cdkVirtualFor="let r of rows$; trackBy: trackBy; templateCacheSize: 0" bitRow>
|
||||
<tr *cdkVirtualFor="let r of rows$; trackBy: trackBy(); templateCacheSize: 0" bitRow>
|
||||
<ng-container *ngTemplateOutlet="rowDef.template; context: { $implicit: r }"></ng-container>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
AfterContentChecked,
|
||||
Component,
|
||||
ContentChild,
|
||||
Input,
|
||||
OnDestroy,
|
||||
TemplateRef,
|
||||
Directive,
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
AfterViewInit,
|
||||
ElementRef,
|
||||
TrackByFunction,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
import { ScrollLayoutDirective } from "../layout";
|
||||
@@ -64,10 +64,10 @@ export class TableScrollComponent
|
||||
implements AfterContentChecked, AfterViewInit, OnDestroy
|
||||
{
|
||||
/** The size of the rows in the list (in pixels). */
|
||||
@Input({ required: true }) rowSize: number;
|
||||
readonly rowSize = input.required<number>();
|
||||
|
||||
/** Optional trackBy function. */
|
||||
@Input() trackBy: TrackByFunction<any> | undefined;
|
||||
readonly trackBy = input<TrackByFunction<any> | undefined>(undefined);
|
||||
|
||||
@ContentChild(BitRowDef) protected rowDef: BitRowDef;
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
Component,
|
||||
ContentChild,
|
||||
Directive,
|
||||
Input,
|
||||
OnDestroy,
|
||||
TemplateRef,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
@@ -29,8 +29,8 @@ export class TableBodyDirective {
|
||||
imports: [CommonModule],
|
||||
})
|
||||
export class TableComponent implements OnDestroy, AfterContentChecked {
|
||||
@Input() dataSource: TableDataSource<any>;
|
||||
@Input() layout: "auto" | "fixed" = "auto";
|
||||
readonly dataSource = input<TableDataSource<any>>(undefined);
|
||||
readonly layout = input<"auto" | "fixed">("auto");
|
||||
|
||||
@ContentChild(TableBodyDirective) templateVariable: TableBodyDirective;
|
||||
|
||||
@@ -45,22 +45,24 @@ export class TableComponent implements OnDestroy, AfterContentChecked {
|
||||
"tw-text-main",
|
||||
"tw-border-collapse",
|
||||
"tw-text-start",
|
||||
this.layout === "auto" ? "tw-table-auto" : "tw-table-fixed",
|
||||
this.layout() === "auto" ? "tw-table-auto" : "tw-table-fixed",
|
||||
];
|
||||
}
|
||||
|
||||
ngAfterContentChecked(): void {
|
||||
if (!this._initialized && isDataSource(this.dataSource)) {
|
||||
const dataSource = this.dataSource();
|
||||
if (!this._initialized && isDataSource(dataSource)) {
|
||||
this._initialized = true;
|
||||
|
||||
const dataStream = this.dataSource.connect();
|
||||
const dataStream = dataSource.connect();
|
||||
this.rows$ = dataStream;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (isDataSource(this.dataSource)) {
|
||||
this.dataSource.disconnect();
|
||||
const dataSource = this.dataSource();
|
||||
if (isDataSource(dataSource)) {
|
||||
dataSource.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>(undefined);
|
||||
// TODO: Skipped for 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"];
|
||||
|
||||
@@ -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, HostBinding, Input, input } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "bit-tab-body",
|
||||
@@ -11,13 +11,15 @@ import { Component, HostBinding, Input } from "@angular/core";
|
||||
export class TabBodyComponent {
|
||||
private _firstRender: boolean;
|
||||
|
||||
@Input() content: TemplatePortal;
|
||||
@Input() preserveContent = false;
|
||||
readonly content = input<TemplatePortal>(undefined);
|
||||
readonly preserveContent = input(false);
|
||||
|
||||
@HostBinding("attr.hidden") get hidden() {
|
||||
return !this.active || null;
|
||||
}
|
||||
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get active() {
|
||||
return this._active;
|
||||
@@ -38,10 +40,10 @@ export class TabBodyComponent {
|
||||
*/
|
||||
get tabContent() {
|
||||
if (this.active) {
|
||||
return this.content;
|
||||
return this.content();
|
||||
}
|
||||
if (this.preserveContent && this._firstRender) {
|
||||
return this.content;
|
||||
if (this.preserveContent() && this._firstRender) {
|
||||
return this.content();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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 migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
get selectedIndex(): number | null {
|
||||
return this._selectedIndex;
|
||||
|
||||
@@ -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>(undefined);
|
||||
|
||||
@ViewChild(TemplateRef, { static: true }) implicitContent: TemplateRef<unknown>;
|
||||
@ContentChild(TabLabelDirective) templateLabel: TabLabelDirective;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<a
|
||||
bitTabListItem
|
||||
[routerLink]="disabled ? null : route"
|
||||
[routerLink]="disabled ? null : route()"
|
||||
routerLinkActive
|
||||
[routerLinkActiveOptions]="routerLinkMatchOptions"
|
||||
#rla="routerLinkActive"
|
||||
|
||||
@@ -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[]>(undefined);
|
||||
// TODO: Skipped for 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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
class="tw-mb-1 tw-min-w-[--bit-toast-width] tw-text-main tw-flex tw-flex-col tw-justify-between tw-rounded-md tw-pointer-events-auto tw-cursor-default tw-overflow-hidden tw-shadow-lg {{
|
||||
bgColor
|
||||
}}"
|
||||
[attr.role]="variant === 'error' ? 'alert' : null"
|
||||
[attr.role]="variant() === 'error' ? 'alert' : null"
|
||||
>
|
||||
<div class="tw-flex tw-items-center tw-gap-4 tw-px-2 tw-pb-1 tw-pt-2">
|
||||
<i aria-hidden="true" class="bwi tw-text-xl tw-py-1.5 tw-px-2.5 {{ iconClass }}"></i>
|
||||
<div>
|
||||
<span class="tw-sr-only">{{ variant | i18n }}</span>
|
||||
<span class="tw-sr-only">{{ variant() | i18n }}</span>
|
||||
@if (title) {
|
||||
<p data-testid="toast-title" class="tw-font-semibold tw-mb-0">{{ title }}</p>
|
||||
}
|
||||
@@ -26,5 +26,5 @@
|
||||
(click)="this.onClose.emit()"
|
||||
></button>
|
||||
</div>
|
||||
<div class="tw-h-1 tw-w-full tw-bg-text-main/30" [style.width]="progressWidth + '%'"></div>
|
||||
<div class="tw-h-1 tw-w-full tw-bg-text-main/30" [style.width]="progressWidth() + '%'"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
import { Component, EventEmitter, Input, Output, input } from "@angular/core";
|
||||
|
||||
import { IconButtonModule } from "../icon-button";
|
||||
import { SharedModule } from "../shared";
|
||||
@@ -31,36 +31,39 @@ const variants: Record<ToastVariant, { icon: string; bgColor: string }> = {
|
||||
imports: [SharedModule, IconButtonModule, TypographyModule],
|
||||
})
|
||||
export class ToastComponent {
|
||||
@Input() variant: ToastVariant = "info";
|
||||
readonly variant = input<ToastVariant>("info");
|
||||
|
||||
/**
|
||||
* The message to display
|
||||
*
|
||||
* Pass an array to render multiple paragraphs.
|
||||
**/
|
||||
@Input({ required: true })
|
||||
message!: string | string[];
|
||||
readonly message = input.required<string | string[]>();
|
||||
|
||||
/** An optional title to display over the message. */
|
||||
// TODO: Skipped for migration because:
|
||||
// This input is used in a control flow expression (e.g. `@if` or `*ngIf`)
|
||||
// and migrating would break narrowing currently.
|
||||
@Input() title?: string;
|
||||
|
||||
/**
|
||||
* The percent width of the progress bar, from 0-100
|
||||
**/
|
||||
@Input() progressWidth = 0;
|
||||
readonly progressWidth = input(0);
|
||||
|
||||
/** Emits when the user presses the close button */
|
||||
@Output() onClose = new EventEmitter<void>();
|
||||
|
||||
protected get iconClass(): string {
|
||||
return variants[this.variant].icon;
|
||||
return variants[this.variant()].icon;
|
||||
}
|
||||
|
||||
protected get bgColor(): string {
|
||||
return variants[this.variant].bgColor;
|
||||
return variants[this.variant()].bgColor;
|
||||
}
|
||||
|
||||
protected get messageArray(): string[] {
|
||||
return Array.isArray(this.message) ? this.message : [this.message];
|
||||
const message = this.message();
|
||||
return Array.isArray(message) ? message : [message];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
HostBinding,
|
||||
Input,
|
||||
Output,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
let nextId = 0;
|
||||
@@ -17,14 +18,16 @@ export class ToggleGroupComponent<TValue = unknown> {
|
||||
private id = nextId++;
|
||||
name = `bit-toggle-group-${this.id}`;
|
||||
|
||||
@Input({ transform: booleanAttribute }) fullWidth?: boolean;
|
||||
readonly fullWidth = input<boolean, unknown>(undefined, { transform: booleanAttribute });
|
||||
// TODO: Skipped for migration because:
|
||||
// Your application code writes to the input. This prevents migration.
|
||||
@Input() selected?: TValue;
|
||||
@Output() selectedChange = new EventEmitter<TValue>();
|
||||
|
||||
@HostBinding("attr.role") role = "radiogroup";
|
||||
@HostBinding("class")
|
||||
get classList() {
|
||||
return ["tw-flex"].concat(this.fullWidth ? ["tw-w-full", "[&>*]:tw-flex-1"] : []);
|
||||
return ["tw-flex"].concat(this.fullWidth() ? ["tw-w-full", "[&>*]:tw-flex-1"] : []);
|
||||
}
|
||||
|
||||
onInputInteraction(value: TValue) {
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
Component,
|
||||
ElementRef,
|
||||
HostBinding,
|
||||
Input,
|
||||
signal,
|
||||
ViewChild,
|
||||
input,
|
||||
} from "@angular/core";
|
||||
|
||||
import { ToggleGroupComponent } from "./toggle-group.component";
|
||||
@@ -24,7 +24,7 @@ let nextId = 0;
|
||||
export class ToggleComponent<TValue> implements AfterContentChecked, AfterViewInit {
|
||||
id = nextId++;
|
||||
|
||||
@Input() value?: TValue;
|
||||
readonly value = input<TValue>(undefined);
|
||||
@ViewChild("labelContent") labelContent: ElementRef<HTMLSpanElement>;
|
||||
@ViewChild("bitBadgeContainer") bitBadgeContainer: ElementRef<HTMLSpanElement>;
|
||||
|
||||
@@ -41,7 +41,7 @@ export class ToggleComponent<TValue> implements AfterContentChecked, AfterViewIn
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this.groupComponent.selected === this.value;
|
||||
return this.groupComponent.selected === this.value();
|
||||
}
|
||||
|
||||
get inputClasses() {
|
||||
@@ -95,7 +95,7 @@ export class ToggleComponent<TValue> implements AfterContentChecked, AfterViewIn
|
||||
}
|
||||
|
||||
onInputInteraction() {
|
||||
this.groupComponent.onInputInteraction(this.value);
|
||||
this.groupComponent.onInputInteraction(this.value());
|
||||
}
|
||||
|
||||
ngAfterContentChecked() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { Directive, HostBinding, Input } from "@angular/core";
|
||||
import { Directive, HostBinding, Input, input } from "@angular/core";
|
||||
|
||||
type TypographyType = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "body1" | "body2" | "helper";
|
||||
|
||||
@@ -33,15 +33,17 @@ const margins: Record<TypographyType, string[]> = {
|
||||
selector: "[bitTypography]",
|
||||
})
|
||||
export class TypographyDirective {
|
||||
@Input("bitTypography") bitTypography: TypographyType;
|
||||
readonly bitTypography = input<TypographyType>(undefined);
|
||||
|
||||
private _margin = true;
|
||||
// TODO: Skipped for migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
@Input()
|
||||
set noMargin(value: boolean | "") {
|
||||
this._margin = !coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
@HostBinding("class") get classList() {
|
||||
return styles[this.bitTypography].concat(this._margin ? margins[this.bitTypography] : []);
|
||||
return styles[this.bitTypography()].concat(this._margin ? margins[this.bitTypography()] : []);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user