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

[CL-553] Migrate CL to Control Flow syntax (#12390)

This commit is contained in:
Oscar Hinton
2025-02-03 20:11:59 +01:00
committed by GitHub
parent 444e928895
commit e5ffc162b8
47 changed files with 480 additions and 428 deletions

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { NgIf, NgClass } from "@angular/common"; import { NgClass } from "@angular/common";
import { Component, Input, OnChanges } from "@angular/core"; import { Component, Input, OnChanges } from "@angular/core";
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
@@ -18,9 +18,11 @@ const SizeClasses: Record<SizeTypes, string[]> = {
@Component({ @Component({
selector: "bit-avatar", selector: "bit-avatar",
template: `<img *ngIf="src" [src]="src" title="{{ title || text }}" [ngClass]="classList" />`, template: `@if (src) {
<img [src]="src" title="{{ title || text }}" [ngClass]="classList" />
}`,
standalone: true, standalone: true,
imports: [NgIf, NgClass], imports: [NgClass],
}) })
export class AvatarComponent implements OnChanges { export class AvatarComponent implements OnChanges {
@Input() border = false; @Input() border = false;

View File

@@ -1,11 +1,15 @@
<div class="tw-inline-flex tw-flex-wrap tw-gap-2"> <div class="tw-inline-flex tw-flex-wrap tw-gap-2">
<ng-container *ngFor="let item of filteredItems; let last = last"> @for (item of filteredItems; track item; let last = $last) {
<span bitBadge [variant]="variant" [truncate]="truncate"> <span bitBadge [variant]="variant" [truncate]="truncate">
{{ item }} {{ item }}
</span> </span>
<span class="tw-sr-only" *ngIf="!last || isFiltered">, </span> @if (!last || isFiltered) {
</ng-container> <span class="tw-sr-only">, </span>
<span *ngIf="isFiltered" bitBadge [variant]="variant"> }
{{ "plusNMore" | i18n: (items.length - filteredItems.length).toString() }} }
</span> @if (isFiltered) {
<span bitBadge [variant]="variant">
{{ "plusNMore" | i18n: (items.length - filteredItems.length).toString() }}
</span>
}
</div> </div>

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { Component, Input, OnChanges } from "@angular/core"; import { Component, Input, OnChanges } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common"; import { I18nPipe } from "@bitwarden/ui-common";
@@ -11,7 +11,7 @@ import { BadgeModule, BadgeVariant } from "../badge";
selector: "bit-badge-list", selector: "bit-badge-list",
templateUrl: "badge-list.component.html", templateUrl: "badge-list.component.html",
standalone: true, standalone: true,
imports: [CommonModule, BadgeModule, I18nPipe], imports: [BadgeModule, I18nPipe],
}) })
export class BadgeListComponent implements OnChanges { export class BadgeListComponent implements OnChanges {
private _maxItems: number; private _maxItems: number;

View File

@@ -4,21 +4,24 @@
[attr.role]="useAlertRole ? 'status' : null" [attr.role]="useAlertRole ? 'status' : null"
[attr.aria-live]="useAlertRole ? 'polite' : null" [attr.aria-live]="useAlertRole ? 'polite' : null"
> >
<i class="bwi tw-align-middle tw-text-base" [ngClass]="icon" *ngIf="icon" aria-hidden="true"></i> @if (icon) {
<i class="bwi tw-align-middle tw-text-base" [ngClass]="icon" aria-hidden="true"></i>
}
<!-- Overriding focus-visible color for link buttons for a11y against colored background --> <!-- Overriding focus-visible color for link buttons for a11y against colored background -->
<span class="tw-grow tw-text-base [&>button[bitlink]:focus-visible:before]:!tw-ring-text-main"> <span class="tw-grow tw-text-base [&>button[bitlink]:focus-visible:before]:!tw-ring-text-main">
<ng-content></ng-content> <ng-content></ng-content>
</span> </span>
<!-- Overriding hover and focus-visible colors for a11y against colored background --> <!-- Overriding hover and focus-visible colors for a11y against colored background -->
<button @if (showClose) {
*ngIf="showClose" <button
class="hover:tw-border-text-main focus-visible:before:tw-ring-text-main" class="hover:tw-border-text-main focus-visible:before:tw-ring-text-main"
type="button" type="button"
bitIconButton="bwi-close" bitIconButton="bwi-close"
buttonType="main" buttonType="main"
size="default" size="default"
(click)="onClose.emit()" (click)="onClose.emit()"
[attr.title]="'close' | i18n" [attr.title]="'close' | i18n"
[attr.aria-label]="'close' | i18n" [attr.aria-label]="'close' | i18n"
></button> ></button>
}
</div> </div>

View File

@@ -1,3 +1,6 @@
<ng-template> <ng-template>
<i *ngIf="icon" class="bwi {{ icon }} !tw-mr-2" aria-hidden="true"></i><ng-content></ng-content> @if (icon) {
<i class="bwi {{ icon }} !tw-mr-2" aria-hidden="true"></i>
}
<ng-content></ng-content>
</ng-template> </ng-template>

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { NgIf } from "@angular/common";
import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core"; import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core";
import { QueryParamsHandling } from "@angular/router"; import { QueryParamsHandling } from "@angular/router";
@@ -8,7 +8,6 @@ import { QueryParamsHandling } from "@angular/router";
selector: "bit-breadcrumb", selector: "bit-breadcrumb",
templateUrl: "./breadcrumb.component.html", templateUrl: "./breadcrumb.component.html",
standalone: true, standalone: true,
imports: [NgIf],
}) })
export class BreadcrumbComponent { export class BreadcrumbComponent {
@Input() @Input()

View File

@@ -1,5 +1,5 @@
<ng-container *ngFor="let breadcrumb of beforeOverflow; let last = last"> @for (breadcrumb of beforeOverflow; track breadcrumb; let last = $last) {
<ng-container *ngIf="breadcrumb.route"> @if (breadcrumb.route) {
<a <a
bitLink bitLink
linkType="primary" linkType="primary"
@@ -10,8 +10,8 @@
> >
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container> <ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
</a> </a>
</ng-container> }
<ng-container *ngIf="!breadcrumb.route"> @if (!breadcrumb.route) {
<button <button
type="button" type="button"
bitLink bitLink
@@ -21,13 +21,16 @@
> >
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container> <ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
</button> </button>
</ng-container> }
<i *ngIf="!last" class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i> @if (!last) {
</ng-container> <i class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i>
}
<ng-container *ngIf="hasOverflow"> }
<i *ngIf="beforeOverflow.length > 0" class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i>
@if (hasOverflow) {
@if (beforeOverflow.length > 0) {
<i class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i>
}
<button <button
type="button" type="button"
bitIconButton="bwi-ellipsis-h" bitIconButton="bwi-ellipsis-h"
@@ -35,10 +38,9 @@
size="small" size="small"
aria-haspopup aria-haspopup
></button> ></button>
<bit-menu #overflowMenu> <bit-menu #overflowMenu>
<ng-container *ngFor="let breadcrumb of overflow"> @for (breadcrumb of overflow; track breadcrumb) {
<ng-container *ngIf="breadcrumb.route"> @if (breadcrumb.route) {
<a <a
bitMenuItem bitMenuItem
linkType="primary" linkType="primary"
@@ -48,18 +50,17 @@
> >
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container> <ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
</a> </a>
</ng-container> }
<ng-container *ngIf="!breadcrumb.route"> @if (!breadcrumb.route) {
<button type="button" bitMenuItem linkType="primary" (click)="breadcrumb.onClick($event)"> <button type="button" bitMenuItem linkType="primary" (click)="breadcrumb.onClick($event)">
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container> <ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
</button> </button>
</ng-container> }
</ng-container> }
</bit-menu> </bit-menu>
<i class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i> <i class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i>
@for (breadcrumb of afterOverflow; track breadcrumb; let last = $last) {
<ng-container *ngFor="let breadcrumb of afterOverflow; let last = last"> @if (breadcrumb.route) {
<ng-container *ngIf="breadcrumb.route">
<a <a
bitLink bitLink
linkType="primary" linkType="primary"
@@ -70,8 +71,8 @@
> >
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container> <ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
</a> </a>
</ng-container> }
<ng-container *ngIf="!breadcrumb.route"> @if (!breadcrumb.route) {
<button <button
type="button" type="button"
bitLink bitLink
@@ -81,7 +82,9 @@
> >
<ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container> <ng-container [ngTemplateOutlet]="breadcrumb.content"></ng-container>
</button> </button>
</ng-container> }
<i *ngIf="!last" class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i> @if (!last) {
</ng-container> <i class="bwi bwi-angle-right tw-mx-1.5 tw-text-main"></i>
</ng-container> }
}
}

View File

@@ -86,16 +86,15 @@ export const DisabledWithAttribute: Story = {
render: (args) => ({ render: (args) => ({
props: args, props: args,
template: ` template: `
<ng-container *ngIf="disabled"> @if (disabled) {
<button bitButton disabled [loading]="loading" [block]="block" buttonType="primary" class="tw-mr-2">Primary</button> <button bitButton disabled [loading]="loading" [block]="block" buttonType="primary" class="tw-mr-2">Primary</button>
<button bitButton disabled [loading]="loading" [block]="block" buttonType="secondary" class="tw-mr-2">Secondary</button> <button bitButton disabled [loading]="loading" [block]="block" buttonType="secondary" class="tw-mr-2">Secondary</button>
<button bitButton disabled [loading]="loading" [block]="block" buttonType="danger" class="tw-mr-2">Danger</button> <button bitButton disabled [loading]="loading" [block]="block" buttonType="danger" class="tw-mr-2">Danger</button>
</ng-container> } @else {
<ng-container *ngIf="!disabled">
<button bitButton [loading]="loading" [block]="block" buttonType="primary" class="tw-mr-2">Primary</button> <button bitButton [loading]="loading" [block]="block" buttonType="primary" class="tw-mr-2">Primary</button>
<button bitButton [loading]="loading" [block]="block" buttonType="secondary" class="tw-mr-2">Secondary</button> <button bitButton [loading]="loading" [block]="block" buttonType="secondary" class="tw-mr-2">Secondary</button>
<button bitButton [loading]="loading" [block]="block" buttonType="danger" class="tw-mr-2">Danger</button> <button bitButton [loading]="loading" [block]="block" buttonType="danger" class="tw-mr-2">Danger</button>
</ng-container> }
`, `,
}), }),
args: { args: {

View File

@@ -3,10 +3,14 @@
[ngClass]="calloutClass" [ngClass]="calloutClass"
[attr.aria-labelledby]="titleId" [attr.aria-labelledby]="titleId"
> >
<header id="{{ titleId }}" class="tw-mb-1 tw-mt-0 tw-text-base tw-font-semibold" *ngIf="title"> @if (title) {
<i class="bwi" [ngClass]="[icon, headerClass]" *ngIf="icon" aria-hidden="true"></i> <header id="{{ titleId }}" class="tw-mb-1 tw-mt-0 tw-text-base tw-font-semibold">
{{ title }} @if (icon) {
</header> <i class="bwi" [ngClass]="[icon, headerClass]" aria-hidden="true"></i>
}
{{ title }}
</header>
}
<div bitTypography="body2"> <div bitTypography="body2">
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>

View File

@@ -1,10 +1,8 @@
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component } from "@angular/core"; import { ChangeDetectionStrategy, Component } from "@angular/core";
@Component({ @Component({
selector: "bit-card", selector: "bit-card",
standalone: true, standalone: true,
imports: [CommonModule],
template: `<ng-content></ng-content>`, template: `<ng-content></ng-content>`,
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
host: { host: {

View File

@@ -30,78 +30,80 @@
<i class="bwi !tw-text-[inherit]" [ngClass]="icon"></i> <i class="bwi !tw-text-[inherit]" [ngClass]="icon"></i>
<span class="tw-truncate">{{ label }}</span> <span class="tw-truncate">{{ label }}</span>
</span> </span>
<i @if (!selectedOption) {
*ngIf="!selectedOption" <i
class="bwi tw-mt-0.5" class="bwi tw-mt-0.5"
[ngClass]="menuTrigger.isOpen ? 'bwi-angle-up' : 'bwi-angle-down'" [ngClass]="menuTrigger.isOpen ? 'bwi-angle-up' : 'bwi-angle-down'"
></i> ></i>
}
</button> </button>
<!-- Close button --> <!-- Close button -->
<button @if (selectedOption) {
*ngIf="selectedOption" <button
type="button" type="button"
[attr.aria-label]="'removeItem' | i18n: label" [attr.aria-label]="'removeItem' | i18n: label"
[disabled]="disabled" [disabled]="disabled"
class="tw-bg-transparent hover:tw-bg-transparent tw-outline-none tw-rounded-full tw-py-0.5 tw-px-1 tw-mr-1 tw-text-[color:inherit] tw-text-[length:inherit] tw-border-solid tw-border tw-border-transparent hover:tw-border-text-contrast hover:disabled:tw-border-transparent tw-flex tw-items-center tw-justify-center focus-visible:tw-ring-2 tw-ring-text-contrast focus-visible:hover:tw-border-transparent" class="tw-bg-transparent hover:tw-bg-transparent tw-outline-none tw-rounded-full tw-py-0.5 tw-px-1 tw-mr-1 tw-text-[color:inherit] tw-text-[length:inherit] tw-border-solid tw-border tw-border-transparent hover:tw-border-text-contrast hover:disabled:tw-border-transparent tw-flex tw-items-center tw-justify-center focus-visible:tw-ring-2 tw-ring-text-contrast focus-visible:hover:tw-border-transparent"
[ngClass]="{ [ngClass]="{
'tw-cursor-not-allowed': disabled, 'tw-cursor-not-allowed': disabled,
}" }"
(click)="clear()" (click)="clear()"
> >
<i class="bwi bwi-close tw-text-xs"></i> <i class="bwi bwi-close tw-text-xs"></i>
</button> </button>
}
</div> </div>
<bit-menu #menu (closed)="handleMenuClosed()"> <bit-menu #menu (closed)="handleMenuClosed()">
<div @if (renderedOptions) {
*ngIf="renderedOptions" <div
class="tw-max-h-80 tw-min-w-32 tw-max-w-80 tw-text-sm" class="tw-max-h-80 tw-min-w-32 tw-max-w-80 tw-text-sm"
[ngStyle]="menuWidth && { width: menuWidth + 'px' }" [ngStyle]="menuWidth && { width: menuWidth + 'px' }"
>
<ng-container *ngIf="getParent(renderedOptions) as parent">
<button
type="button"
bitMenuItem
(click)="viewOption(parent, $event)"
class="tw-text-[length:inherit]"
[title]="'backTo' | i18n: parent.label ?? placeholderText"
>
<i slot="start" class="bwi bwi-angle-left" aria-hidden="true"></i>
{{ "backTo" | i18n: parent.label ?? placeholderText }}
</button>
<button
type="button"
bitMenuItem
(click)="selectOption(renderedOptions, $event)"
[title]="'viewItemsIn' | i18n: renderedOptions.label"
class="tw-text-[length:inherit]"
>
<i slot="start" class="bwi bwi-list" aria-hidden="true"></i>
{{ "viewItemsIn" | i18n: renderedOptions.label }}
</button>
</ng-container>
<button
type="button"
bitMenuItem
*ngFor="let option of renderedOptions.children"
(click)="option.children?.length ? viewOption(option, $event) : selectOption(option, $event)"
[disabled]="option.disabled"
[title]="option.label"
class="tw-text-[length:inherit]"
[attr.aria-haspopup]="option.children?.length ? 'menu' : null"
> >
<i @if (getParent(renderedOptions); as parent) {
*ngIf="option.icon" <button
slot="start" type="button"
class="bwi" bitMenuItem
[ngClass]="option.icon" (click)="viewOption(parent, $event)"
aria-hidden="true" class="tw-text-[length:inherit]"
></i> [title]="'backTo' | i18n: parent.label ?? placeholderText"
{{ option.label }} >
<i *ngIf="option.children?.length" slot="end" class="bwi bwi-angle-right"></i> <i slot="start" class="bwi bwi-angle-left" aria-hidden="true"></i>
</button> {{ "backTo" | i18n: parent.label ?? placeholderText }}
</div> </button>
<button
type="button"
bitMenuItem
(click)="selectOption(renderedOptions, $event)"
[title]="'viewItemsIn' | i18n: renderedOptions.label"
class="tw-text-[length:inherit]"
>
<i slot="start" class="bwi bwi-list" aria-hidden="true"></i>
{{ "viewItemsIn" | i18n: renderedOptions.label }}
</button>
}
@for (option of renderedOptions.children; track option) {
<button
type="button"
bitMenuItem
(click)="
option.children?.length ? viewOption(option, $event) : selectOption(option, $event)
"
[disabled]="option.disabled"
[title]="option.label"
class="tw-text-[length:inherit]"
[attr.aria-haspopup]="option.children?.length ? 'menu' : null"
>
@if (option.icon) {
<i slot="start" class="bwi" [ngClass]="option.icon" aria-hidden="true"></i>
}
{{ option.label }}
@if (option.children?.length) {
<i slot="end" class="bwi bwi-angle-right"></i>
}
</button>
}
</div>
}
</bit-menu> </bit-menu>

View File

@@ -46,6 +46,7 @@ export type ChipSelectOption<T> = Option<T> & {
multi: true, multi: true,
}, },
], ],
preserveWhitespaces: false,
}) })
export class ChipSelectComponent<T = unknown> implements ControlValueAccessor, AfterViewInit { export class ChipSelectComponent<T = unknown> implements ControlValueAccessor, AfterViewInit {
@ViewChild(MenuComponent) menu: MenuComponent; @ViewChild(MenuComponent) menu: MenuComponent;

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { NgFor, NgIf } from "@angular/common";
import { Component, HostBinding, Input } from "@angular/core"; import { Component, HostBinding, Input } from "@angular/core";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -14,18 +14,16 @@ enum CharacterType {
@Component({ @Component({
selector: "bit-color-password", selector: "bit-color-password",
template: `<span template: `@for (character of passwordArray; track character; let i = $index) {
*ngFor="let character of passwordArray; index as i" <span [class]="getCharacterClass(character)">
[class]="getCharacterClass(character)" <span>{{ character }}</span>
> @if (showCount) {
<span>{{ character }}</span> <span class="tw-whitespace-nowrap tw-text-xs tw-leading-5 tw-text-main">{{ i + 1 }}</span>
<span *ngIf="showCount" class="tw-whitespace-nowrap tw-text-xs tw-leading-5 tw-text-main">{{ }
i + 1 </span>
}}</span> }`,
</span>`,
preserveWhitespaces: false, preserveWhitespaces: false,
standalone: true, standalone: true,
imports: [NgFor, NgIf],
}) })
export class ColorPasswordComponent { export class ColorPasswordComponent {
@Input() password: string = null; @Input() password: string = null;

View File

@@ -1,4 +1,3 @@
import { CommonModule } from "@angular/common";
import { Component } from "@angular/core"; import { Component } from "@angular/core";
/** /**
@@ -7,7 +6,6 @@ import { Component } from "@angular/core";
@Component({ @Component({
selector: "bit-container", selector: "bit-container",
templateUrl: "container.component.html", templateUrl: "container.component.html",
imports: [CommonModule],
standalone: true, standalone: true,
}) })
export class ContainerComponent {} export class ContainerComponent {}

View File

@@ -13,9 +13,11 @@
class="tw-text-main tw-mb-0 tw-truncate" class="tw-text-main tw-mb-0 tw-truncate"
> >
{{ title }} {{ title }}
<span *ngIf="subtitle" class="tw-text-muted tw-font-normal tw-text-sm"> @if (subtitle) {
{{ subtitle }} <span class="tw-text-muted tw-font-normal tw-text-sm">
</span> {{ subtitle }}
</span>
}
<ng-content select="[bitDialogTitle]"></ng-content> <ng-content select="[bitDialogTitle]"></ng-content>
</h1> </h1>
<button <button
@@ -35,9 +37,11 @@
'tw-min-h-60': loading, 'tw-min-h-60': loading,
}" }"
> >
<div *ngIf="loading" class="tw-absolute tw-flex tw-size-full tw-items-center tw-justify-center"> @if (loading) {
<i class="bwi bwi-spinner bwi-spin bwi-lg" [attr.aria-label]="'loading' | i18n"></i> <div class="tw-absolute tw-flex tw-size-full tw-items-center tw-justify-center">
</div> <i class="bwi bwi-spinner bwi-spin bwi-lg" [attr.aria-label]="'loading' | i18n"></i>
</div>
}
<div <div
[ngClass]="{ [ngClass]="{
'tw-p-4': !disablePadding, 'tw-p-4': !disablePadding,

View File

@@ -11,16 +11,17 @@
{{ acceptButtonText }} {{ acceptButtonText }}
</button> </button>
<button @if (showCancelButton) {
*ngIf="showCancelButton" <button
type="button" type="button"
bitButton bitButton
bitFormButton bitFormButton
buttonType="secondary" buttonType="secondary"
(click)="dialogRef.close(false)" (click)="dialogRef.close(false)"
> >
{{ cancelButtonText }} {{ cancelButtonText }}
</button> </button>
}
</ng-container> </ng-container>
</bit-simple-dialog> </bit-simple-dialog>
</form> </form>

View File

@@ -1,7 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { NgIf } from "@angular/common";
import { Component, Inject } from "@angular/core"; import { Component, Inject } from "@angular/core";
import { FormGroup, ReactiveFormsModule } from "@angular/forms"; import { FormGroup, ReactiveFormsModule } from "@angular/forms";
@@ -39,7 +38,6 @@ const DEFAULT_COLOR: Record<SimpleDialogType, string> = {
IconDirective, IconDirective,
ButtonComponent, ButtonComponent,
BitFormButtonDirective, BitFormButtonDirective,
NgIf,
], ],
}) })
export class SimpleConfigurableDialogComponent { export class SimpleConfigurableDialogComponent {

View File

@@ -12,23 +12,24 @@ import { DialogModule } from "../../dialog.module";
@Component({ @Component({
template: ` template: `
<div *ngFor="let group of dialogs"> @for (group of dialogs; track group) {
<h2>{{ group.title }}</h2> <div>
<div class="tw-mb-4 tw-flex tw-flex-row tw-gap-2"> <h2>{{ group.title }}</h2>
<button <div class="tw-mb-4 tw-flex tw-flex-row tw-gap-2">
type="button" @for (dialog of group.dialogs; track dialog) {
*ngFor="let dialog of group.dialogs" <button type="button" bitButton (click)="openSimpleConfigurableDialog(dialog)">
bitButton {{ dialog.title }}
(click)="openSimpleConfigurableDialog(dialog)" </button>
> }
{{ dialog.title }} </div>
</button>
</div> </div>
</div> }
<bit-callout *ngIf="showCallout" [type]="calloutType" title="Dialog Close Result"> @if (showCallout) {
{{ dialogCloseResult }} <bit-callout [type]="calloutType" title="Dialog Close Result">
</bit-callout> {{ dialogCloseResult }}
</bit-callout>
}
`, `,
}) })
class StoryDialogComponent { class StoryDialogComponent {

View File

@@ -3,12 +3,11 @@
@fadeIn @fadeIn
> >
<div class="tw-flex tw-flex-col tw-items-center tw-gap-2 tw-px-4 tw-pt-4 tw-text-center"> <div class="tw-flex tw-flex-col tw-items-center tw-gap-2 tw-px-4 tw-pt-4 tw-text-center">
<ng-container *ngIf="hasIcon; else elseBlock"> @if (hasIcon) {
<ng-content select="[bitDialogIcon]"></ng-content> <ng-content select="[bitDialogIcon]"></ng-content>
</ng-container> } @else {
<ng-template #elseBlock>
<i class="bwi bwi-exclamation-triangle tw-text-3xl tw-text-warning" aria-hidden="true"></i> <i class="bwi bwi-exclamation-triangle tw-text-3xl tw-text-warning" aria-hidden="true"></i>
</ng-template> }
<h1 <h1
bitDialogTitleContainer bitDialogTitleContainer
bitTypography="h3" bitTypography="h3"

View File

@@ -1,4 +1,3 @@
import { NgIf } from "@angular/common";
import { Component, ContentChild, Directive } from "@angular/core"; import { Component, ContentChild, Directive } from "@angular/core";
import { TypographyDirective } from "../../typography/typography.directive"; import { TypographyDirective } from "../../typography/typography.directive";
@@ -16,7 +15,7 @@ export class IconDirective {}
templateUrl: "./simple-dialog.component.html", templateUrl: "./simple-dialog.component.html",
animations: [fadeIn], animations: [fadeIn],
standalone: true, standalone: true,
imports: [NgIf, DialogTitleContainerDirective, TypographyDirective], imports: [DialogTitleContainerDirective, TypographyDirective],
}) })
export class SimpleDialogComponent { export class SimpleDialogComponent {
@ContentChild(IconDirective) icon!: IconDirective; @ContentChild(IconDirective) icon!: IconDirective;

View File

@@ -9,11 +9,17 @@
> >
<span bitTypography="body2"> <span bitTypography="body2">
<ng-content select="bit-label"></ng-content> <ng-content select="bit-label"></ng-content>
<span *ngIf="required" class="tw-text-xs tw-font-normal"> ({{ "required" | i18n }})</span> @if (required) {
<span class="tw-text-xs tw-font-normal"> ({{ "required" | i18n }})</span>
}
</span> </span>
<ng-content select="bit-hint" *ngIf="!hasError"></ng-content> @if (!hasError) {
<ng-content select="bit-hint"></ng-content>
}
</span> </span>
</label> </label>
<div *ngIf="hasError" class="tw-mt-1 tw-text-danger tw-text-xs tw-ml-0.5"> @if (hasError) {
<i class="bwi bwi-error"></i> {{ displayError }} <div class="tw-mt-1 tw-text-danger tw-text-xs tw-ml-0.5">
</div> <i class="bwi bwi-error"></i> {{ displayError }}
</div>
}

View File

@@ -1,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { NgClass, NgIf } from "@angular/common"; import { NgClass } from "@angular/common";
import { Component, ContentChild, HostBinding, Input } from "@angular/core"; import { Component, ContentChild, HostBinding, Input } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -15,7 +15,7 @@ import { BitFormControlAbstraction } from "./form-control.abstraction";
selector: "bit-form-control", selector: "bit-form-control",
templateUrl: "form-control.component.html", templateUrl: "form-control.component.html",
standalone: true, standalone: true,
imports: [NgClass, TypographyDirective, NgIf, I18nPipe], imports: [NgClass, TypographyDirective, I18nPipe],
}) })
export class FormControlComponent { export class FormControlComponent {
@Input() label: string; @Input() label: string;

View File

@@ -5,10 +5,10 @@
<!-- labels inside a form control (checkbox, radio button) should not truncate --> <!-- labels inside a form control (checkbox, radio button) should not truncate -->
<span [ngClass]="{ 'tw-truncate': !isInsideFormControl }"> <span [ngClass]="{ 'tw-truncate': !isInsideFormControl }">
<ng-content></ng-content> <ng-content></ng-content>
<ng-container *ngIf="isInsideFormControl"> @if (isInsideFormControl) {
<ng-container *ngTemplateOutlet="endSlotContent"></ng-container> <ng-container *ngTemplateOutlet="endSlotContent"></ng-container>
</ng-container> }
</span> </span>
<ng-container *ngIf="!isInsideFormControl"> @if (!isInsideFormControl) {
<ng-container *ngTemplateOutlet="endSlotContent"></ng-container> <ng-container *ngTemplateOutlet="endSlotContent"></ng-container>
</ng-container> }

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { NgIf } from "@angular/common";
import { Component, Input } from "@angular/core"; import { Component, Input } from "@angular/core";
import { AbstractControl, UntypedFormGroup } from "@angular/forms"; import { AbstractControl, UntypedFormGroup } from "@angular/forms";
@@ -8,15 +8,15 @@ import { I18nPipe } from "@bitwarden/ui-common";
@Component({ @Component({
selector: "bit-error-summary", selector: "bit-error-summary",
template: ` <ng-container *ngIf="errorCount > 0"> template: ` @if (errorCount > 0) {
<i class="bwi bwi-error"></i> {{ "fieldsNeedAttention" | i18n: errorString }} <i class="bwi bwi-error"></i> {{ "fieldsNeedAttention" | i18n: errorString }}
</ng-container>`, }`,
host: { host: {
class: "tw-block tw-text-danger tw-mt-2", class: "tw-block tw-text-danger tw-mt-2",
"aria-live": "assertive", "aria-live": "assertive",
}, },
standalone: true, standalone: true,
imports: [NgIf, I18nPipe], imports: [I18nPipe],
}) })
export class BitErrorSummary { export class BitErrorSummary {
@Input() @Input()

View File

@@ -15,63 +15,65 @@
<ng-content select="[bitSuffix]"></ng-content> <ng-content select="[bitSuffix]"></ng-content>
</ng-template> </ng-template>
<div *ngIf="!readOnly; else readOnlyView" class="tw-w-full tw-relative tw-group/bit-form-field"> @if (!readOnly) {
<div class="tw-absolute tw-size-full tw-top-0 tw-pointer-events-none tw-z-20"> <div class="tw-w-full tw-relative tw-group/bit-form-field">
<div class="tw-size-full tw-flex"> <div class="tw-absolute tw-size-full tw-top-0 tw-pointer-events-none tw-z-20">
<div <div class="tw-size-full tw-flex">
class="tw-min-w-3 tw-border-r-0 group-focus-within/bit-form-field:tw-border-r-0 !tw-rounded-l-lg" <div
[ngClass]="inputBorderClasses" class="tw-min-w-3 tw-border-r-0 group-focus-within/bit-form-field:tw-border-r-0 !tw-rounded-l-lg"
></div> [ngClass]="inputBorderClasses"
<div ></div>
class="tw-px-1 tw-shrink tw-min-w-0 tw-mt-px tw-border-x-0 tw-border-t-0 group-focus-within/bit-form-field:tw-border-x-0 group-focus-within/bit-form-field:tw-border-t-0 tw-hidden group-has-[bit-label]/bit-form-field:tw-block" <div
[ngClass]="inputBorderClasses" class="tw-px-1 tw-shrink tw-min-w-0 tw-mt-px tw-border-x-0 tw-border-t-0 group-focus-within/bit-form-field:tw-border-x-0 group-focus-within/bit-form-field:tw-border-t-0 tw-hidden group-has-[bit-label]/bit-form-field:tw-block"
> [ngClass]="inputBorderClasses"
<label
class="tw-flex tw-gap-1 tw-text-sm tw-text-muted -tw-translate-y-[0.675rem] tw-mb-0 tw-max-w-full tw-pointer-events-auto"
[attr.for]="input.labelForId"
> >
<ng-container *ngTemplateOutlet="labelContent"></ng-container> <label
<span *ngIf="input.required" class="tw-text-[0.625rem] tw-relative tw-bottom-[-1px]"> class="tw-flex tw-gap-1 tw-text-sm tw-text-muted -tw-translate-y-[0.675rem] tw-mb-0 tw-max-w-full tw-pointer-events-auto"
({{ "required" | i18n }})</span [attr.for]="input.labelForId"
> >
</label> <ng-container *ngTemplateOutlet="labelContent"></ng-container>
@if (input.required) {
<span class="tw-text-[0.625rem] tw-relative tw-bottom-[-1px]">
({{ "required" | i18n }})</span
>
}
</label>
</div>
<div
class="tw-min-w-3 tw-grow tw-border-l-0 group-focus-within/bit-form-field:tw-border-l-0 !tw-rounded-r-lg"
[ngClass]="inputBorderClasses"
></div>
</div>
</div>
<div
class="tw-gap-1 tw-bg-background tw-rounded-lg tw-flex tw-min-h-11 [&:not(:has(button:enabled)):has(input:read-only)]:tw-bg-secondary-100 [&:not(:has(button:enabled)):has(textarea:read-only)]:tw-bg-secondary-100"
>
<div
#prefixContainer
class="tw-flex tw-items-center tw-gap-1 tw-pl-3 tw-py-2"
[hidden]="!prefixHasChildren()"
>
<ng-container *ngTemplateOutlet="prefixContent"></ng-container>
</div> </div>
<div <div
class="tw-min-w-3 tw-grow tw-border-l-0 group-focus-within/bit-form-field:tw-border-l-0 !tw-rounded-r-lg" class="default-content tw-w-full tw-relative tw-py-2 has-[bit-select]:tw-p-0 has-[bit-multi-select]:tw-p-0 has-[input:read-only:not([hidden])]:tw-bg-secondary-100 has-[textarea:read-only:not([hidden])]:tw-bg-secondary-100"
[ngClass]="inputBorderClasses" [ngClass]="[
></div> prefixHasChildren() ? '' : 'tw-rounded-l-lg tw-pl-3',
suffixHasChildren() ? '' : 'tw-rounded-r-lg tw-pr-3',
]"
>
<ng-container *ngTemplateOutlet="defaultContent"></ng-container>
</div>
<div
#suffixContainer
class="tw-flex tw-items-center tw-gap-1 tw-pr-3 tw-py-2"
[hidden]="!suffixHasChildren()"
>
<ng-container *ngTemplateOutlet="suffixContent"></ng-container>
</div>
</div> </div>
</div> </div>
<div } @else {
class="tw-gap-1 tw-bg-background tw-rounded-lg tw-flex tw-min-h-11 [&:not(:has(button:enabled)):has(input:read-only)]:tw-bg-secondary-100 [&:not(:has(button:enabled)):has(textarea:read-only)]:tw-bg-secondary-100"
>
<div
#prefixContainer
class="tw-flex tw-items-center tw-gap-1 tw-pl-3 tw-py-2"
[hidden]="!prefixHasChildren()"
>
<ng-container *ngTemplateOutlet="prefixContent"></ng-container>
</div>
<div
class="default-content tw-w-full tw-relative tw-py-2 has-[bit-select]:tw-p-0 has-[bit-multi-select]:tw-p-0 has-[input:read-only:not([hidden])]:tw-bg-secondary-100 has-[textarea:read-only:not([hidden])]:tw-bg-secondary-100"
[ngClass]="[
prefixHasChildren() ? '' : 'tw-rounded-l-lg tw-pl-3',
suffixHasChildren() ? '' : 'tw-rounded-r-lg tw-pr-3',
]"
>
<ng-container *ngTemplateOutlet="defaultContent"></ng-container>
</div>
<div
#suffixContainer
class="tw-flex tw-items-center tw-gap-1 tw-pr-3 tw-py-2"
[hidden]="!suffixHasChildren()"
>
<ng-container *ngTemplateOutlet="suffixContent"></ng-container>
</div>
</div>
</div>
<ng-template #readOnlyView>
<div class="tw-w-full tw-relative"> <div class="tw-w-full tw-relative">
<label <label
class="tw-flex tw-gap-1 tw-text-sm tw-text-muted tw-mb-0 tw-max-w-full" class="tw-flex tw-gap-1 tw-text-sm tw-text-muted tw-mb-0 tw-max-w-full"
@@ -107,9 +109,13 @@
</div> </div>
</div> </div>
</div> </div>
</ng-template> }
<ng-container [ngSwitch]="input.hasError"> @switch (input.hasError) {
<ng-content select="bit-hint" *ngSwitchCase="false"></ng-content> @case (false) {
<bit-error [error]="input.error" *ngSwitchCase="true"></bit-error> <ng-content select="bit-hint"></ng-content>
</ng-container> }
@case (true) {
<bit-error [error]="input.error"></bit-error>
}
}

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { import {
AfterContentChecked, AfterContentChecked,
ChangeDetectionStrategy, ChangeDetectionStrategy,
@@ -16,7 +16,7 @@ import { TypographyModule } from "../typography";
@Component({ @Component({
selector: "bit-item-content, [bit-item-content]", selector: "bit-item-content, [bit-item-content]",
standalone: true, standalone: true,
imports: [CommonModule, TypographyModule], imports: [TypographyModule],
templateUrl: `item-content.component.html`, templateUrl: `item-content.component.html`,
host: { host: {
class: class:

View File

@@ -1,4 +1,3 @@
import { CommonModule } from "@angular/common";
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
@@ -14,7 +13,7 @@ import { ItemActionComponent } from "./item-action.component";
@Component({ @Component({
selector: "bit-item", selector: "bit-item",
standalone: true, standalone: true,
imports: [CommonModule, ItemActionComponent], imports: [ItemActionComponent],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: "item.component.html", templateUrl: "item.component.html",
providers: [{ provide: A11yRowDirective, useExisting: ItemComponent }], providers: [{ provide: A11yRowDirective, useExisting: ItemComponent }],

View File

@@ -23,19 +23,21 @@
<ng-content></ng-content> <ng-content></ng-content>
<!-- overlay backdrop for side-nav --> <!-- overlay backdrop for side-nav -->
<div @if (
*ngIf="{ {
open: sideNavService.open$ | async, open: sideNavService.open$ | async,
} as data" };
class="tw-pointer-events-none tw-fixed tw-inset-0 tw-z-10 tw-bg-black tw-bg-opacity-0 motion-safe:tw-transition-colors md:tw-hidden" as data
[ngClass]="[data.open ? 'tw-bg-opacity-30 md:tw-bg-opacity-0' : 'tw-bg-opacity-0']" ) {
>
<div <div
*ngIf="data.open" class="tw-pointer-events-none tw-fixed tw-inset-0 tw-z-10 tw-bg-black tw-bg-opacity-0 motion-safe:tw-transition-colors md:tw-hidden"
(click)="sideNavService.toggle()" [ngClass]="[data.open ? 'tw-bg-opacity-30 md:tw-bg-opacity-0' : 'tw-bg-opacity-0']"
class="tw-pointer-events-auto tw-size-full" >
></div> @if (data.open) {
</div> <div (click)="sideNavService.toggle()" class="tw-pointer-events-auto tw-size-full"></div>
}
</div>
}
</main> </main>
<ng-template [cdkPortalOutlet]="drawerPortal()"></ng-template> <ng-template [cdkPortalOutlet]="drawerPortal()"></ng-template>
</div> </div>

View File

@@ -31,7 +31,9 @@
[disabled]="disabled" [disabled]="disabled"
(click)="clear(item)" (click)="clear(item)"
> >
<i *ngIf="item.icon != null" class="bwi bwi-fw {{ item.icon }}" aria-hidden="true"></i> @if (item.icon != null) {
<i class="bwi bwi-fw {{ item.icon }}" aria-hidden="true"></i>
}
<span class="tw-truncate"> <span class="tw-truncate">
{{ item.labelName }} {{ item.labelName }}
</span> </span>
@@ -41,10 +43,14 @@
<ng-template ng-option-tmp let-item="item"> <ng-template ng-option-tmp let-item="item">
<div class="tw-flex"> <div class="tw-flex">
<div class="tw-w-7 tw-flex-none"> <div class="tw-w-7 tw-flex-none">
<i *ngIf="isSelected(item)" class="bwi bwi-fw bwi-check" aria-hidden="true"></i> @if (isSelected(item)) {
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
}
</div> </div>
<div class="tw-mr-2 tw-flex-initial"> <div class="tw-mr-2 tw-flex-initial">
<i *ngIf="item.icon != null" class="bwi bwi-fw {{ item.icon }}" aria-hidden="true"></i> @if (item.icon != null) {
<i class="bwi bwi-fw {{ item.icon }}" aria-hidden="true"></i>
}
</div> </div>
<div class="tw-flex-1"> <div class="tw-flex-1">
{{ item.listName }} {{ item.listName }}

View File

@@ -2,7 +2,6 @@
// @ts-strict-ignore // @ts-strict-ignore
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { hasModifierKey } from "@angular/cdk/keycodes"; import { hasModifierKey } from "@angular/cdk/keycodes";
import { NgIf } from "@angular/common";
import { import {
Component, Component,
Input, Input,
@@ -39,7 +38,7 @@ let nextId = 0;
templateUrl: "./multi-select.component.html", templateUrl: "./multi-select.component.html",
providers: [{ provide: BitFormFieldControl, useExisting: MultiSelectComponent }], providers: [{ provide: BitFormFieldControl, useExisting: MultiSelectComponent }],
standalone: true, standalone: true,
imports: [NgSelectModule, ReactiveFormsModule, FormsModule, BadgeModule, NgIf, I18nPipe], imports: [NgSelectModule, ReactiveFormsModule, FormsModule, BadgeModule, I18nPipe],
}) })
/** /**
* This component has been implemented to only support Multi-select list events * This component has been implemented to only support Multi-select list events

View File

@@ -1 +1,3 @@
<div *ngIf="sideNavService.open$ | async" class="tw-h-px tw-w-full tw-bg-secondary-300"></div> @if (sideNavService.open$ | async) {
<div class="tw-h-px tw-w-full tw-bg-secondary-300"></div>
}

View File

@@ -1,5 +1,5 @@
<!-- This a higher order component that composes `NavItemComponent` --> <!-- This a higher order component that composes `NavItemComponent` -->
<ng-container *ngIf="!hideIfEmpty || nestedNavComponents.length > 0"> @if (!hideIfEmpty || nestedNavComponents.length > 0) {
<bit-nav-item <bit-nav-item
[text]="text" [text]="text"
[icon]="icon" [icon]="icon"
@@ -29,28 +29,29 @@
[attr.aria-label]="['toggleCollapse' | i18n, text].join(' ')" [attr.aria-label]="['toggleCollapse' | i18n, text].join(' ')"
></button> ></button>
</ng-template> </ng-template>
<!-- Show toggle to the left for trees otherwise to the right --> <!-- Show toggle to the left for trees otherwise to the right -->
<ng-container slot="start" *ngIf="variant === 'tree'"> @if (variant === "tree") {
<ng-container *ngTemplateOutlet="button"></ng-container> <ng-container slot="start">
</ng-container>
<ng-container slot="end">
<ng-content select="[slot=end]"></ng-content>
<ng-container *ngIf="variant !== 'tree'">
<ng-container *ngTemplateOutlet="button"></ng-container> <ng-container *ngTemplateOutlet="button"></ng-container>
</ng-container> </ng-container>
}
<ng-container slot="end">
<ng-content select="[slot=end]"></ng-content>
@if (variant !== "tree") {
<ng-container *ngTemplateOutlet="button"></ng-container>
}
</ng-container> </ng-container>
</bit-nav-item> </bit-nav-item>
<!-- [attr.aria-controls] of the above button expects a unique ID on the controlled element --> <!-- [attr.aria-controls] of the above button expects a unique ID on the controlled element -->
<ng-container *ngIf="sideNavService.open$ | async"> @if (sideNavService.open$ | async) {
<div @if (open) {
*ngIf="open" <div
[attr.id]="contentId" [attr.id]="contentId"
[attr.aria-label]="[text, 'submenu' | i18n].join(' ')" [attr.aria-label]="[text, 'submenu' | i18n].join(' ')"
role="group" role="group"
> >
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>
</ng-container> }
</ng-container> }
}

View File

@@ -29,6 +29,7 @@ import { SideNavService } from "./side-nav.service";
], ],
standalone: true, standalone: true,
imports: [CommonModule, NavItemComponent, IconButtonModule, I18nPipe], imports: [CommonModule, NavItemComponent, IconButtonModule, I18nPipe],
preserveWhitespaces: false,
}) })
export class NavGroupComponent extends NavBaseComponent implements AfterContentInit { export class NavGroupComponent extends NavBaseComponent implements AfterContentInit {
@ContentChildren(NavBaseComponent, { @ContentChildren(NavBaseComponent, {

View File

@@ -1,20 +1,23 @@
<div *ngIf="sideNavService.open" class="tw-sticky tw-top-0 tw-z-50"> @if (sideNavService.open) {
<a <div class="tw-sticky tw-top-0 tw-z-50">
[routerLink]="route" <a
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" [routerLink]="route"
[attr.aria-label]="label" 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"
[title]="label" [attr.aria-label]="label"
routerLinkActive [title]="label"
[ariaCurrentWhenActive]="'page'" routerLinkActive
> [ariaCurrentWhenActive]="'page'"
<bit-icon [icon]="openIcon"></bit-icon> >
</a> <bit-icon [icon]="openIcon"></bit-icon>
</div> </a>
<bit-nav-item </div>
class="tw-block tw-pt-7" }
[hideActiveStyles]="true" @if (!sideNavService.open) {
[route]="route" <bit-nav-item
[icon]="closedIcon" class="tw-block tw-pt-7"
*ngIf="!sideNavService.open" [hideActiveStyles]="true"
[text]="label" [route]="route"
></bit-nav-item> [icon]="closedIcon"
[text]="label"
></bit-nav-item>
}

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { NgIf } from "@angular/common";
import { Component, Input } from "@angular/core"; import { Component, Input } from "@angular/core";
import { RouterLinkActive, RouterLink } from "@angular/router"; import { RouterLinkActive, RouterLink } from "@angular/router";
@@ -14,7 +14,7 @@ import { SideNavService } from "./side-nav.service";
selector: "bit-nav-logo", selector: "bit-nav-logo",
templateUrl: "./nav-logo.component.html", templateUrl: "./nav-logo.component.html",
standalone: true, standalone: true,
imports: [NgIf, RouterLinkActive, RouterLink, BitIconComponent, NavItemComponent], imports: [RouterLinkActive, RouterLink, BitIconComponent, NavItemComponent],
}) })
export class NavLogoComponent { export class NavLogoComponent {
/** Icon that is displayed when the side nav is closed */ /** Icon that is displayed when the side nav is closed */

View File

@@ -1,42 +1,46 @@
<nav @if (
*ngIf="{ {
open: sideNavService.open$ | async, open: sideNavService.open$ | async,
isOverlay: sideNavService.isOverlay$ | async, isOverlay: sideNavService.isOverlay$ | async,
} as data" };
id="bit-side-nav" as data
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 }" <nav
[ngStyle]=" id="bit-side-nav"
variant === 'secondary' && { 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"
'--color-text-alt2': 'var(--color-text-main)', [ngClass]="{ 'tw-w-60': data.open }"
'--color-background-alt3': 'var(--color-secondary-100)', [ngStyle]="
'--color-background-alt4': 'var(--color-secondary-300)', variant === 'secondary' && {
} '--color-text-alt2': 'var(--color-text-main)',
" '--color-background-alt3': 'var(--color-secondary-100)',
[cdkTrapFocus]="data.isOverlay" '--color-background-alt4': 'var(--color-secondary-300)',
[attr.role]="data.isOverlay ? 'dialog' : null" }
[attr.aria-modal]="data.isOverlay ? 'true' : null" "
(keydown)="handleKeyDown($event)" [cdkTrapFocus]="data.isOverlay"
> [attr.role]="data.isOverlay ? 'dialog' : null"
<ng-content></ng-content> [attr.aria-modal]="data.isOverlay ? 'true' : null"
<div class="tw-sticky tw-bottom-0 tw-left-0 tw-z-20 tw-mt-auto tw-w-full tw-bg-background-alt3"> (keydown)="handleKeyDown($event)"
<bit-nav-divider></bit-nav-divider> >
<ng-container *ngIf="data.open"> <ng-content></ng-content>
<ng-content select="[slot=footer]"></ng-content> <div class="tw-sticky tw-bottom-0 tw-left-0 tw-z-20 tw-mt-auto tw-w-full tw-bg-background-alt3">
</ng-container> <bit-nav-divider></bit-nav-divider>
<div class="tw-mx-0.5 tw-my-4 tw-w-[3.75rem]"> @if (data.open) {
<button <ng-content select="[slot=footer]"></ng-content>
#toggleButton }
type="button" <div class="tw-mx-0.5 tw-my-4 tw-w-[3.75rem]">
class="tw-mx-auto tw-block tw-max-w-fit" <button
[bitIconButton]="data.open ? 'bwi-angle-left' : 'bwi-angle-right'" #toggleButton
buttonType="light" type="button"
size="small" class="tw-mx-auto tw-block tw-max-w-fit"
(click)="sideNavService.toggle()" [bitIconButton]="data.open ? 'bwi-angle-left' : 'bwi-angle-right'"
[attr.aria-label]="'toggleSideNavigation' | i18n" buttonType="light"
[attr.aria-expanded]="data.open" size="small"
aria-controls="bit-side-nav" (click)="sideNavService.toggle()"
></button> [attr.aria-label]="'toggleSideNavigation' | i18n"
[attr.aria-expanded]="data.open"
aria-controls="bit-side-nav"
></button>
</div>
</div> </div>
</div> </nav>
</nav> }

View File

@@ -7,13 +7,12 @@
attr.aria-valuenow="{{ barWidth }}" attr.aria-valuenow="{{ barWidth }}"
[ngStyle]="{ width: barWidth + '%' }" [ngStyle]="{ width: barWidth + '%' }"
> >
<div @if (displayText) {
*ngIf="displayText" <div class="tw-flex tw-h-full tw-flex-wrap tw-items-center tw-overflow-hidden">
class="tw-flex tw-h-full tw-flex-wrap tw-items-center tw-overflow-hidden" <!-- If text is too long to fit, wrap it below to hide -->
> <div class="tw-h-full">&nbsp;</div>
<!-- If text is too long to fit, wrap it below to hide --> <div class="tw-pr-1">{{ textContent }}</div>
<div class="tw-h-full">&nbsp;</div> </div>
<div class="tw-pr-1">{{ textContent }}</div> }
</div>
</div> </div>
</div> </div>

View File

@@ -1,16 +1,18 @@
<ng-container *ngIf="label"> @if (label) {
<fieldset> <fieldset>
<legend class="tw-mb-1 tw-block tw-text-sm tw-font-semibold tw-text-main"> <legend class="tw-mb-1 tw-block tw-text-sm tw-font-semibold tw-text-main">
<ng-content select="bit-label"></ng-content> <ng-content select="bit-label"></ng-content>
<span *ngIf="required" class="tw-text-xs tw-font-normal"> ({{ "required" | i18n }})</span> @if (required) {
<span class="tw-text-xs tw-font-normal"> ({{ "required" | i18n }})</span>
}
</legend> </legend>
<ng-container *ngTemplateOutlet="content"></ng-container> <ng-container *ngTemplateOutlet="content"></ng-container>
</fieldset> </fieldset>
</ng-container> }
<ng-container *ngIf="!label"> @if (!label) {
<ng-container *ngTemplateOutlet="content"></ng-container> <ng-container *ngTemplateOutlet="content"></ng-container>
</ng-container> }
<ng-template #content> <ng-template #content>
<div> <div>

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { NgIf, NgTemplateOutlet } from "@angular/common"; import { NgTemplateOutlet } from "@angular/common";
import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core"; import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core";
import { ControlValueAccessor, NgControl, Validators } from "@angular/forms"; import { ControlValueAccessor, NgControl, Validators } from "@angular/forms";
@@ -14,7 +14,7 @@ let nextId = 0;
selector: "bit-radio-group", selector: "bit-radio-group",
templateUrl: "radio-group.component.html", templateUrl: "radio-group.component.html",
standalone: true, standalone: true,
imports: [NgIf, NgTemplateOutlet, I18nPipe], imports: [NgTemplateOutlet, I18nPipe],
}) })
export class RadioGroupComponent implements ControlValueAccessor { export class RadioGroupComponent implements ControlValueAccessor {
selected: unknown; selected: unknown;

View File

@@ -13,7 +13,9 @@
<ng-template ng-option-tmp let-item="item"> <ng-template ng-option-tmp let-item="item">
<div class="tw-flex" [title]="item.label"> <div class="tw-flex" [title]="item.label">
<div class="tw-mr-2 tw-flex-initial"> <div class="tw-mr-2 tw-flex-initial">
<i *ngIf="item.icon != null" class="bwi bwi-fw {{ item.icon }}" aria-hidden="true"></i> @if (item.icon != null) {
<i class="bwi bwi-fw {{ item.icon }}" aria-hidden="true"></i>
}
</div> </div>
<div class="tw-flex-1 tw-text-ellipsis tw-overflow-hidden"> <div class="tw-flex-1 tw-text-ellipsis tw-overflow-hidden">
{{ item.label }} {{ item.label }}

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { NgIf } from "@angular/common";
import { import {
Component, Component,
ContentChildren, ContentChildren,
@@ -36,7 +36,7 @@ let nextId = 0;
templateUrl: "select.component.html", templateUrl: "select.component.html",
providers: [{ provide: BitFormFieldControl, useExisting: SelectComponent }], providers: [{ provide: BitFormFieldControl, useExisting: SelectComponent }],
standalone: true, standalone: true,
imports: [NgSelectModule, ReactiveFormsModule, FormsModule, NgIf], imports: [NgSelectModule, ReactiveFormsModule, FormsModule],
}) })
export class SelectComponent<T> implements BitFormFieldControl, ControlValueAccessor { export class SelectComponent<T> implements BitFormFieldControl, ControlValueAccessor {
@ViewChild(NgSelectComponent) select: NgSelectComponent; @ViewChild(NgSelectComponent) select: NgSelectComponent;

View File

@@ -49,11 +49,9 @@ import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module";
<bit-form-field> <bit-form-field>
<bit-label>Your favorite color</bit-label> <bit-label>Your favorite color</bit-label>
<bit-select formControlName="favColor"> <bit-select formControlName="favColor">
<bit-option @for (color of colors; track color) {
*ngFor="let color of colors" <bit-option [value]="color.value" [label]="color.name"></bit-option>
[value]="color.value" }
[label]="color.name"
></bit-option>
</bit-select> </bit-select>
</bit-form-field> </bit-form-field>

View File

@@ -36,9 +36,11 @@ class KitchenSinkDialog {
<p class="tw-mt-4"> <p class="tw-mt-4">
<bit-breadcrumbs> <bit-breadcrumbs>
<bit-breadcrumb *ngFor="let item of navItems" [icon]="item.icon" [route]="[item.route]"> @for (item of navItems; track item) {
{{ item.name }} <bit-breadcrumb [icon]="item.icon" [route]="[item.route]">
</bit-breadcrumb> {{ item.name }}
</bit-breadcrumb>
}
</bit-breadcrumbs> </bit-breadcrumbs>
</p> </p>

View File

@@ -16,11 +16,15 @@ import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module";
<bit-toggle value="small"> Mid-sized <span bitBadge variant="info">1</span> </bit-toggle> <bit-toggle value="small"> Mid-sized <span bitBadge variant="info">1</span> </bit-toggle>
</bit-toggle-group> </bit-toggle-group>
</div> </div>
<ul *ngFor="let company of companyList"> @for (company of companyList; track company) {
<li *ngIf="company.size === selectedToggle || selectedToggle === 'all'"> <ul>
{{ company.name }} @if (company.size === selectedToggle || selectedToggle === "all") {
</li> <li>
</ul> {{ company.name }}
</li>
}
</ul>
}
`, `,
}) })
export class KitchenSinkToggleList { export class KitchenSinkToggleList {

View File

@@ -5,40 +5,41 @@
[attr.aria-label]="label" [attr.aria-label]="label"
(keydown)="keyManager.onKeydown($event)" (keydown)="keyManager.onKeydown($event)"
> >
<button @for (tab of tabs; track tab; let i = $index) {
bitTabListItem <button
*ngFor="let tab of tabs; let i = index" bitTabListItem
type="button" type="button"
role="tab" role="tab"
[id]="getTabLabelId(i)" [id]="getTabLabelId(i)"
[active]="tab.isActive" [active]="tab.isActive"
[disabled]="tab.disabled" [disabled]="tab.disabled"
[attr.aria-selected]="selectedIndex === i" [attr.aria-selected]="selectedIndex === i"
[attr.tabindex]="selectedIndex === i ? 0 : -1" [attr.tabindex]="selectedIndex === i ? 0 : -1"
(click)="selectTab(i)" (click)="selectTab(i)"
> >
<ng-container [ngTemplateOutlet]="content"></ng-container> <ng-container [ngTemplateOutlet]="content"></ng-container>
<ng-template #content>
<ng-template #content> @if (tab.templateLabel) {
<ng-template [ngIf]="tab.templateLabel" [ngIfElse]="tabTextLabel"> <ng-container [ngTemplateOutlet]="tab.templateLabel.templateRef"></ng-container>
<ng-container [ngTemplateOutlet]="tab.templateLabel.templateRef"></ng-container> } @else {
{{ tab.textLabel }}
}
</ng-template> </ng-template>
</button>
<ng-template #tabTextLabel>{{ tab.textLabel }}</ng-template> }
</ng-template>
</button>
</div> </div>
</bit-tab-header> </bit-tab-header>
<div class="tw-px-4 tw-pt-5"> <div class="tw-px-4 tw-pt-5">
<bit-tab-body @for (tab of tabs; track tab; let i = $index) {
role="tabpanel" <bit-tab-body
*ngFor="let tab of tabs; let i = index" role="tabpanel"
[id]="getTabContentId(i)" [id]="getTabContentId(i)"
[attr.tabindex]="tab.contentTabIndex" [attr.tabindex]="tab.contentTabIndex"
[attr.labeledby]="getTabLabelId(i)" [attr.labeledby]="getTabLabelId(i)"
[active]="tab.isActive" [active]="tab.isActive"
[content]="tab.content" [content]="tab.content"
[preserveContent]="preserveContent" [preserveContent]="preserveContent"
> >
</bit-tab-body> </bit-tab-body>
}
</div> </div>

View File

@@ -2,7 +2,7 @@
// @ts-strict-ignore // @ts-strict-ignore
import { FocusKeyManager } from "@angular/cdk/a11y"; import { FocusKeyManager } from "@angular/cdk/a11y";
import { coerceNumberProperty } from "@angular/cdk/coercion"; import { coerceNumberProperty } from "@angular/cdk/coercion";
import { CommonModule } from "@angular/common"; import { NgTemplateOutlet } from "@angular/common";
import { import {
AfterContentChecked, AfterContentChecked,
AfterContentInit, AfterContentInit,
@@ -33,7 +33,7 @@ let nextId = 0;
templateUrl: "./tab-group.component.html", templateUrl: "./tab-group.component.html",
standalone: true, standalone: true,
imports: [ imports: [
CommonModule, NgTemplateOutlet,
TabHeaderComponent, TabHeaderComponent,
TabListContainerDirective, TabListContainerDirective,
TabListItemDirective, TabListItemDirective,

View File

@@ -7,15 +7,14 @@
<i aria-hidden="true" class="bwi tw-text-xl tw-py-1.5 tw-px-2.5 {{ iconClass }}"></i> <i aria-hidden="true" class="bwi tw-text-xl tw-py-1.5 tw-px-2.5 {{ iconClass }}"></i>
<div> <div>
<span class="tw-sr-only">{{ variant | i18n }}</span> <span class="tw-sr-only">{{ variant | i18n }}</span>
<p *ngIf="title" data-testid="toast-title" class="tw-font-semibold tw-mb-0">{{ title }}</p> @if (title) {
<p <p data-testid="toast-title" class="tw-font-semibold tw-mb-0">{{ title }}</p>
bitTypography="body2" }
*ngFor="let m of messageArray" @for (m of messageArray; track m) {
data-testid="toast-message" <p bitTypography="body2" data-testid="toast-message" class="tw-mb-2 last:tw-mb-0">
class="tw-mb-2 last:tw-mb-0" {{ m }}
> </p>
{{ m }} }
</p>
</div> </div>
<!-- Overriding hover and focus-visible colors for a11y against colored background --> <!-- Overriding hover and focus-visible colors for a11y against colored background -->
<button <button