1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 17:53:39 +00:00

[BEEEP] [PM-28239] Migrate generators to standalone (#17386)

* Migrate generators to use standalone and control flow

* Resolve feedback

* Add variable for account

* Fix generators
This commit is contained in:
Oscar Hinton
2025-12-11 12:04:15 +01:00
committed by GitHub
parent 7183d77f7b
commit 267e488390
24 changed files with 481 additions and 288 deletions

View File

@@ -136,6 +136,7 @@ import {
DialogService, DialogService,
ToastService, ToastService,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { GeneratorServicesModule } from "@bitwarden/generator-components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { import {
BiometricsService, BiometricsService,
@@ -743,7 +744,7 @@ const safeProviders: SafeProvider[] = [
]; ];
@NgModule({ @NgModule({
imports: [JslibServicesModule], imports: [JslibServicesModule, GeneratorServicesModule],
declarations: [], declarations: [],
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
providers: safeProviders, providers: safeProviders,

View File

@@ -102,6 +102,7 @@ import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/s
import { SyncService } from "@bitwarden/common/platform/sync"; import { SyncService } from "@bitwarden/common/platform/sync";
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
import { DialogService, ToastService } from "@bitwarden/components"; import { DialogService, ToastService } from "@bitwarden/components";
import { GeneratorServicesModule } from "@bitwarden/generator-components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { import {
KdfConfigService, KdfConfigService,
@@ -499,7 +500,7 @@ const safeProviders: SafeProvider[] = [
]; ];
@NgModule({ @NgModule({
imports: [JslibServicesModule], imports: [JslibServicesModule, GeneratorServicesModule],
declarations: [], declarations: [],
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
providers: safeProviders, providers: safeProviders,

View File

@@ -112,6 +112,7 @@ import {
} from "@bitwarden/common/platform/theming/theme-state.service"; } from "@bitwarden/common/platform/theming/theme-state.service";
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
import { DialogService, ToastService } from "@bitwarden/components"; import { DialogService, ToastService } from "@bitwarden/components";
import { GeneratorServicesModule } from "@bitwarden/generator-components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { import {
KdfConfigService, KdfConfigService,
@@ -484,7 +485,7 @@ const safeProviders: SafeProvider[] = [
@NgModule({ @NgModule({
declarations: [], declarations: [],
imports: [CommonModule, JslibServicesModule], imports: [CommonModule, JslibServicesModule, GeneratorServicesModule],
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
providers: safeProviders, providers: safeProviders,
}) })

View File

@@ -8,15 +8,18 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule } from "@bitwarden/components";
import { import {
CatchallGenerationOptions, CatchallGenerationOptions,
CredentialGeneratorService, CredentialGeneratorService,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for catchall emails */ /** Options group for catchall emails */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({ @Component({
selector: "tools-catchall-settings", selector: "tools-catchall-settings",
templateUrl: "catchall-settings.component.html", templateUrl: "catchall-settings.component.html",
standalone: false, imports: [ReactiveFormsModule, FormFieldModule, JslibModule, I18nPipe],
}) })
export class CatchallSettingsComponent implements OnInit, OnDestroy, OnChanges { export class CatchallSettingsComponent implements OnInit, OnDestroy, OnChanges {
/** Instantiates the component /** Instantiates the component

View File

@@ -1,8 +1,11 @@
<bit-dialog #dialog background="alt"> <bit-dialog #dialog background="alt">
<span bitDialogTitle>{{ "generatorHistory" | i18n }}</span> <span bitDialogTitle>{{ "generatorHistory" | i18n }}</span>
<ng-container bitDialogContent> <ng-container bitDialogContent>
<bit-empty-credential-history *ngIf="!(hasHistory$ | async)" style="display: contents" /> @if (hasHistory$ | async) {
<bit-credential-generator-history [account]="account$ | async" *ngIf="hasHistory$ | async" /> <bit-credential-generator-history [account]="account$ | async" />
} @else {
<bit-empty-credential-history style="display: contents" />
}
</ng-container> </ng-container>
<ng-container bitDialogFooter> <ng-container bitDialogFooter>
<button <button

View File

@@ -1,22 +1,24 @@
<bit-item *ngFor="let credential of credentials$ | async"> @for (credential of credentials$ | async; track credential) {
<bit-item-content> <bit-item>
<bit-color-password class="tw-font-mono" [password]="credential.credential" /> <bit-item-content>
<div slot="secondary"> <bit-color-password class="tw-font-mono" [password]="credential.credential" />
{{ credential.generationDate | date: "medium" }} <div slot="secondary">
</div> {{ credential.generationDate | date: "medium" }}
</bit-item-content> </div>
<ng-container slot="end"> </bit-item-content>
<bit-item-action> <ng-container slot="end">
<button <bit-item-action>
type="button" <button
bitIconButton="bwi-clone" type="button"
[appCopyClick]="credential.credential" bitIconButton="bwi-clone"
[valueLabel]="getGeneratedValueText(credential)" [appCopyClick]="credential.credential"
[label]="getCopyText(credential)" [valueLabel]="getGeneratedValueText(credential)"
showToast [label]="getCopyText(credential)"
> showToast
{{ getCopyText(credential) }} >
</button> {{ getCopyText(credential) }}
</bit-item-action> </button>
</ng-container> </bit-item-action>
</bit-item> </ng-container>
</bit-item>
}

View File

@@ -23,7 +23,6 @@ import {
import { AlgorithmsByType, CredentialGeneratorService } from "@bitwarden/generator-core"; import { AlgorithmsByType, CredentialGeneratorService } from "@bitwarden/generator-core";
import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generator-history";
import { GeneratorModule } from "./generator.module";
import { translate } from "./util"; import { translate } from "./util";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -32,13 +31,12 @@ import { translate } from "./util";
selector: "bit-credential-generator-history", selector: "bit-credential-generator-history",
templateUrl: "credential-generator-history.component.html", templateUrl: "credential-generator-history.component.html",
imports: [ imports: [
ColorPasswordModule,
CommonModule, CommonModule,
ColorPasswordModule,
IconButtonModule, IconButtonModule,
NoItemsModule, NoItemsModule,
JslibModule, JslibModule,
ItemModule, ItemModule,
GeneratorModule,
], ],
}) })
export class CredentialGeneratorHistoryComponent implements OnChanges, OnInit, OnDestroy { export class CredentialGeneratorHistoryComponent implements OnChanges, OnInit, OnDestroy {

View File

@@ -6,9 +6,11 @@
(selectedChange)="onRootChanged({ nav: $event })" (selectedChange)="onRootChanged({ nav: $event })"
attr.aria-label="{{ 'type' | i18n }}" attr.aria-label="{{ 'type' | i18n }}"
> >
<bit-toggle *ngFor="let option of rootOptions$ | async" [value]="option.value"> @for (option of rootOptions$ | async; track option) {
{{ option.label }} <bit-toggle [value]="option.value">
</bit-toggle> {{ option.label }}
</bit-toggle>
}
</bit-toggle-group> </bit-toggle-group>
<nudge-generator-spotlight></nudge-generator-spotlight> <nudge-generator-spotlight></nudge-generator-spotlight>
@@ -40,69 +42,80 @@
></button> ></button>
</div> </div>
</bit-card> </bit-card>
<tools-password-settings @let showAlgorithm = showAlgorithm$ | async;
class="tw-mt-6" @let account = account$ | async;
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.password" @switch (showAlgorithm?.id) {
[account]="account$ | async" @case (Algorithm.password) {
(onUpdated)="generate('password settings')" <tools-password-settings
/> class="tw-mt-6"
<tools-passphrase-settings [account]="account"
class="tw-mt-6" (onUpdated)="generate('password settings')"
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.passphrase" />
[account]="account$ | async" }
(onUpdated)="generate('passphrase settings')" @case (Algorithm.passphrase) {
/> <tools-passphrase-settings
<bit-section *ngIf="(category$ | async) !== 'password'"> class="tw-mt-6"
<bit-section-header> [account]="account"
<h2 bitTypography="h6">{{ "options" | i18n }}</h2> (onUpdated)="generate('passphrase settings')"
</bit-section-header> />
<div class="tw-mb-4"> }
<bit-card> }
<form [formGroup]="username" class="tw-container"> @if ((category$ | async) !== "password") {
<bit-form-field> <bit-section>
<bit-label>{{ "type" | i18n }}</bit-label> <bit-section-header>
<bit-select <h2 bitTypography="h6">{{ "options" | i18n }}</h2>
[items]="usernameOptions$ | async" </bit-section-header>
formControlName="nav" <div class="tw-mb-4">
data-testid="username-type" <bit-card>
> <form [formGroup]="username" class="tw-container">
</bit-select> <bit-form-field>
<bit-hint *ngIf="!!(credentialTypeHint$ | async)">{{ <bit-label>{{ "type" | i18n }}</bit-label>
credentialTypeHint$ | async <bit-select
}}</bit-hint> [items]="usernameOptions$ | async"
</bit-form-field> formControlName="nav"
</form> data-testid="username-type"
<form *ngIf="showForwarder$ | async" [formGroup]="forwarder" class="tw-container"> >
<bit-form-field> </bit-select>
<bit-label>{{ "service" | i18n }}</bit-label> @if (credentialTypeHint$ | async) {
<bit-select <bit-hint>{{ credentialTypeHint$ | async }}</bit-hint>
[items]="forwarderOptions$ | async" }
formControlName="nav" </bit-form-field>
data-testid="email-forwarding-service" </form>
> @if (showForwarder$ | async) {
</bit-select> <form [formGroup]="forwarder" class="tw-container">
</bit-form-field> <bit-form-field>
</form> <bit-label>{{ "service" | i18n }}</bit-label>
<tools-catchall-settings <bit-select
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.catchall" [items]="forwarderOptions$ | async"
[account]="account$ | async" formControlName="nav"
(onUpdated)="generate('catchall settings')" data-testid="email-forwarding-service"
/> >
<tools-forwarder-settings </bit-select>
*ngIf="!!(forwarderId$ | async)" </bit-form-field>
[account]="account$ | async" </form>
[forwarder]="forwarderId$ | async" }
/> @if (showAlgorithm?.id === Algorithm.catchall) {
<tools-subaddress-settings <tools-catchall-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.plusAddress" [account]="account"
[account]="account$ | async" (onUpdated)="generate('catchall settings')"
(onUpdated)="generate('subaddress settings')" />
/> }
<tools-username-settings @if (forwarderId$ | async; as forwarderId) {
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.username" <tools-forwarder-settings [account]="account" [forwarder]="forwarderId" />
[account]="account$ | async" }
(onUpdated)="generate('username settings')" @if (showAlgorithm?.id === Algorithm.plusAddress) {
/> <tools-subaddress-settings
</bit-card> [account]="account"
</div> (onUpdated)="generate('subaddress settings')"
</bit-section> />
}
@if (showAlgorithm?.id === Algorithm.username) {
<tools-username-settings
[account]="account"
(onUpdated)="generate('username settings')"
/>
}
</bit-card>
</div>
</bit-section>
}

View File

@@ -1,4 +1,5 @@
import { LiveAnnouncer } from "@angular/cdk/a11y"; import { LiveAnnouncer } from "@angular/cdk/a11y";
import { AsyncPipe } from "@angular/common";
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -10,7 +11,7 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { import {
BehaviorSubject, BehaviorSubject,
catchError, catchError,
@@ -27,6 +28,7 @@ import {
withLatestFrom, withLatestFrom,
} from "rxjs"; } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -37,7 +39,23 @@ import {
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components"; import {
ToastService,
Option,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
SectionComponent,
SectionHeaderComponent,
ToggleGroupModule,
TypographyModule,
FormFieldModule,
SelectModule,
} from "@bitwarden/components";
import { import {
CredentialType, CredentialType,
CredentialGeneratorService, CredentialGeneratorService,
@@ -55,7 +73,15 @@ import {
Type, Type,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { NudgeGeneratorSpotlightComponent } from "./nudge-generator-spotlight.component";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameSettingsComponent } from "./username-settings.component";
import { translate } from "./util"; import { translate } from "./util";
// constants used to identify navigation selections that are not // constants used to identify navigation selections that are not
@@ -69,7 +95,32 @@ const NONE_SELECTED = "none";
@Component({ @Component({
selector: "tools-credential-generator", selector: "tools-credential-generator",
templateUrl: "credential-generator.component.html", templateUrl: "credential-generator.component.html",
standalone: false, imports: [
ToggleGroupModule,
NudgeGeneratorSpotlightComponent,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
PasswordSettingsComponent,
PassphraseSettingsComponent,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
FormFieldModule,
SelectModule,
CatchallSettingsComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
UsernameSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class CredentialGeneratorComponent implements OnInit, OnChanges, OnDestroy { export class CredentialGeneratorComponent implements OnInit, OnChanges, OnDestroy {
private readonly destroyed = new Subject<void>(); private readonly destroyed = new Subject<void>();

View File

@@ -1,28 +1,34 @@
<form [formGroup]="settings" class="tw-container"> <form [formGroup]="settings" class="tw-container">
<bit-form-field *ngIf="displayDomain"> @if (displayDomain) {
<bit-label>{{ "forwarderDomainName" | i18n }}</bit-label> <bit-form-field>
<input <bit-label>{{ "forwarderDomainName" | i18n }}</bit-label>
bitInput <input
formControlName="domain" bitInput
type="text" formControlName="domain"
placeholder="example.com" type="text"
(change)="save('domain')" placeholder="example.com"
/> (change)="save('domain')"
<bit-hint>{{ "forwarderDomainNameHint" | i18n }}</bit-hint> />
</bit-form-field> <bit-hint>{{ "forwarderDomainNameHint" | i18n }}</bit-hint>
<bit-form-field *ngIf="displayToken"> </bit-form-field>
<bit-label>{{ "apiKey" | i18n }}</bit-label> }
<input bitInput formControlName="token" type="password" (change)="save('password')" /> @if (displayToken) {
<button <bit-form-field>
type="button" <bit-label>{{ "apiKey" | i18n }}</bit-label>
bitIconButton <input bitInput formControlName="token" type="password" (change)="save('password')" />
bitSuffix <button
bitPasswordInputToggle type="button"
(change)="save('token')" bitIconButton
></button> bitSuffix
</bit-form-field> bitPasswordInputToggle
<bit-form-field *ngIf="displayBaseUrl" disableMargin> (change)="save('token')"
<bit-label>{{ "selfHostBaseUrl" | i18n }}</bit-label> ></button>
<input bitInput formControlName="baseUrl" type="text" (change)="save('baseUrl')" /> </bit-form-field>
</bit-form-field> }
@if (displayBaseUrl) {
<bit-form-field disableMargin>
<bit-label>{{ "selfHostBaseUrl" | i18n }}</bit-label>
<input bitInput formControlName="baseUrl" type="text" (change)="save('baseUrl')" />
</bit-form-field>
}
</form> </form>

View File

@@ -8,16 +8,24 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, switchAll, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, switchAll, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { VendorId } from "@bitwarden/common/tools/extension"; import { VendorId } from "@bitwarden/common/tools/extension";
import {
FormFieldModule,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
ForwarderOptions, ForwarderOptions,
GeneratorMetadata, GeneratorMetadata,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
const Controls = Object.freeze({ const Controls = Object.freeze({
domain: "domain", domain: "domain",
@@ -31,7 +39,15 @@ const Controls = Object.freeze({
@Component({ @Component({
selector: "tools-forwarder-settings", selector: "tools-forwarder-settings",
templateUrl: "forwarder-settings.component.html", templateUrl: "forwarder-settings.component.html",
standalone: false, imports: [
ReactiveFormsModule,
FormFieldModule,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
JslibModule,
I18nPipe,
],
}) })
export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy { export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -1,67 +1,13 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { ReactiveFormsModule } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
CardComponent,
ColorPasswordModule,
CheckboxModule,
FormFieldModule,
IconButtonModule,
InputModule,
ItemModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
ToggleGroupModule,
TypographyModule,
} from "@bitwarden/components";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { CredentialGeneratorComponent } from "./credential-generator.component"; import { CredentialGeneratorComponent } from "./credential-generator.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { GeneratorServicesModule } from "./generator-services.module";
import { NudgeGeneratorSpotlightComponent } from "./nudge-generator-spotlight.component";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordGeneratorComponent } from "./password-generator.component"; import { PasswordGeneratorComponent } from "./password-generator.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameGeneratorComponent } from "./username-generator.component"; import { UsernameGeneratorComponent } from "./username-generator.component";
import { UsernameSettingsComponent } from "./username-settings.component";
/** Shared module containing generator component dependencies */ /** Shared module containing generator component dependencies */
/** @deprecated Use individual components instead. */
@NgModule({ @NgModule({
imports: [ imports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent],
CardComponent,
ColorPasswordModule,
CheckboxModule,
CommonModule,
FormFieldModule,
GeneratorServicesModule,
IconButtonModule,
InputModule,
ItemModule,
JslibModule,
ReactiveFormsModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
ToggleGroupModule,
TypographyModule,
NudgeGeneratorSpotlightComponent,
],
declarations: [
CatchallSettingsComponent,
CredentialGeneratorComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
PasswordGeneratorComponent,
PassphraseSettingsComponent,
PasswordSettingsComponent,
UsernameGeneratorComponent,
UsernameSettingsComponent,
],
exports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent], exports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent],
}) })
export class GeneratorModule { export class GeneratorModule {

View File

@@ -1,5 +1,15 @@
/**
* This file contains the public interface for the generator components library.
*
* Be mindful of what you export here, as those components should be considered stable
* and part of the public API contract.
*/
export { CredentialGeneratorComponent } from "./credential-generator.component";
export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component"; export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component";
export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component"; export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component";
export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component"; export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
export { GeneratorModule } from "./generator.module"; export { GeneratorModule } from "./generator.module";
export { GeneratorServicesModule, SYSTEM_SERVICE_PROVIDER } from "./generator-services.module"; export { GeneratorServicesModule, SYSTEM_SERVICE_PROVIDER } from "./generator-services.module";
export { PasswordGeneratorComponent } from "./password-generator.component";
export { UsernameGeneratorComponent } from "./username-generator.component";

View File

@@ -1,16 +1,18 @@
<div class="tw-mb-4" *ngIf="showGeneratorSpotlight$ | async"> @if (showGeneratorSpotlight$ | async) {
<bit-spotlight <div class="tw-mb-4">
[title]="'generatorNudgeTitle' | i18n" <bit-spotlight
(onDismiss)="dismissGeneratorSpotlight(NudgeType.GeneratorNudgeStatus)" [title]="'generatorNudgeTitle' | i18n"
> (onDismiss)="dismissGeneratorSpotlight(NudgeType.GeneratorNudgeStatus)"
<p class="tw-text-main tw-mb-0" bitTypography="body2"> >
<span class="tw-sr-only"> <p class="tw-text-main tw-mb-0" bitTypography="body2">
{{ "generatorNudgeBodyAria" | i18n }} <span class="tw-sr-only">
</span> {{ "generatorNudgeBodyAria" | i18n }}
<span aria-hidden="true"> </span>
{{ "generatorNudgeBodyOne" | i18n }} <i class="bwi bwi-generate"></i> <span aria-hidden="true">
{{ "generatorNudgeBodyTwo" | i18n }} {{ "generatorNudgeBodyOne" | i18n }} <i class="bwi bwi-generate"></i>
</span> {{ "generatorNudgeBodyTwo" | i18n }}
</p> </span>
</bit-spotlight> </p>
</div> </bit-spotlight>
</div>
}

View File

@@ -1,7 +1,9 @@
<bit-section [disableMargin]="disableMargin"> <bit-section [disableMargin]="disableMargin">
<bit-section-header *ngIf="showHeader"> @if (showHeader) {
<h6 bitTypography="h6">{{ "options" | i18n }}</h6> <bit-section-header>
</bit-section-header> <h6 bitTypography="h6">{{ "options" | i18n }}</h6>
</bit-section-header>
}
<form [formGroup]="settings" class="tw-container"> <form [formGroup]="settings" class="tw-container">
<div class="tw-mb-4"> <div class="tw-mb-4">
<bit-card> <bit-card>
@@ -51,7 +53,9 @@
/> />
<bit-label>{{ "includeNumber" | i18n }}</bit-label> <bit-label>{{ "includeNumber" | i18n }}</bit-label>
</bit-form-control> </bit-form-control>
<p *ngIf="policyInEffect" bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p> @if (policyInEffect) {
<p bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
}
</bit-card> </bit-card>
</div> </div>
</form> </form>

View File

@@ -1,4 +1,5 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import { import {
OnInit, OnInit,
Input, Input,
@@ -9,9 +10,10 @@ import {
SimpleChanges, SimpleChanges,
OnChanges, OnChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { skip, takeUntil, Subject, map, withLatestFrom, ReplaySubject, tap } from "rxjs"; import { skip, takeUntil, Subject, map, withLatestFrom, ReplaySubject, tap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -20,11 +22,21 @@ import {
disabledSemanticLoggerProvider, disabledSemanticLoggerProvider,
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import {
SectionComponent,
SectionHeaderComponent,
BaseCardDirective,
CardComponent,
TypographyModule,
FormFieldModule,
CheckboxModule,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
PassphraseGenerationOptions, PassphraseGenerationOptions,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
const Controls = Object.freeze({ const Controls = Object.freeze({
numWords: "numWords", numWords: "numWords",
@@ -39,7 +51,19 @@ const Controls = Object.freeze({
@Component({ @Component({
selector: "tools-passphrase-settings", selector: "tools-passphrase-settings",
templateUrl: "passphrase-settings.component.html", templateUrl: "passphrase-settings.component.html",
standalone: false, imports: [
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
BaseCardDirective,
CardComponent,
FormFieldModule,
CheckboxModule,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class PassphraseSettingsComponent implements OnInit, OnChanges, OnDestroy { export class PassphraseSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -1,15 +1,18 @@
<bit-toggle-group @if (showCredentialTypes$ | async) {
fullWidth <bit-toggle-group
class="tw-mb-4" fullWidth
[selected]="credentialType$ | async" class="tw-mb-4"
(selectedChange)="onCredentialTypeChanged($event)" [selected]="credentialType$ | async"
*ngIf="showCredentialTypes$ | async" (selectedChange)="onCredentialTypeChanged($event)"
attr.aria-label="{{ 'type' | i18n }}" attr.aria-label="{{ 'type' | i18n }}"
> >
<bit-toggle *ngFor="let option of passwordOptions$ | async" [value]="option.value"> @for (option of passwordOptions$ | async; track option) {
{{ option.label }} <bit-toggle [value]="option.value">
</bit-toggle> {{ option.label }}
</bit-toggle-group> </bit-toggle>
}
</bit-toggle-group>
}
<bit-card class="tw-flex tw-justify-between tw-mb-4"> <bit-card class="tw-flex tw-justify-between tw-mb-4">
<div class="tw-grow tw-flex tw-items-center tw-min-w-0"> <div class="tw-grow tw-flex tw-items-center tw-min-w-0">
<bit-color-password class="tw-font-mono" [password]="value$ | async"></bit-color-password> <bit-color-password class="tw-font-mono" [password]="value$ | async"></bit-color-password>
@@ -37,17 +40,19 @@
></button> ></button>
</div> </div>
</bit-card> </bit-card>
<tools-password-settings @if ((algorithm$ | async)?.id === Algorithm.password) {
class="tw-mt-6" <tools-password-settings
*ngIf="(algorithm$ | async)?.id === Algorithm.password" class="tw-mt-6"
[account]="account$ | async" [account]="account$ | async"
[disableMargin]="disableMargin" [disableMargin]="disableMargin"
(onUpdated)="generate('password settings')" (onUpdated)="generate('password settings')"
/> />
<tools-passphrase-settings }
class="tw-mt-6" @if ((algorithm$ | async)?.id === Algorithm.passphrase) {
*ngIf="(algorithm$ | async)?.id === Algorithm.passphrase" <tools-passphrase-settings
[account]="account$ | async" class="tw-mt-6"
(onUpdated)="generate('passphrase settings')" [account]="account$ | async"
[disableMargin]="disableMargin" (onUpdated)="generate('passphrase settings')"
/> [disableMargin]="disableMargin"
/>
}

View File

@@ -1,5 +1,6 @@
import { LiveAnnouncer } from "@angular/cdk/a11y"; import { LiveAnnouncer } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -24,6 +25,7 @@ import {
withLatestFrom, withLatestFrom,
} from "rxjs"; } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -33,7 +35,18 @@ import {
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components"; import {
ToastService,
Option,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
ToggleGroupModule,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
GeneratedCredential, GeneratedCredential,
@@ -49,7 +62,10 @@ import {
Profile, Profile,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { toAlgorithmInfo, translate } from "./util"; import { toAlgorithmInfo, translate } from "./util";
/** Options group for passwords */ /** Options group for passwords */
@@ -58,7 +74,21 @@ import { toAlgorithmInfo, translate } from "./util";
@Component({ @Component({
selector: "tools-password-generator", selector: "tools-password-generator",
templateUrl: "password-generator.component.html", templateUrl: "password-generator.component.html",
standalone: false, imports: [
ToggleGroupModule,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
PasswordSettingsComponent,
PassphraseSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class PasswordGeneratorComponent implements OnInit, OnChanges, OnDestroy { export class PasswordGeneratorComponent implements OnInit, OnChanges, OnDestroy {
constructor( constructor(

View File

@@ -1,7 +1,9 @@
<bit-section [disableMargin]="disableMargin"> <bit-section [disableMargin]="disableMargin">
<bit-section-header *ngIf="showHeader"> @if (showHeader) {
<h2 bitTypography="h6">{{ "options" | i18n }}</h2> <bit-section-header>
</bit-section-header> <h2 bitTypography="h6">{{ "options" | i18n }}</h2>
</bit-section-header>
}
<form [formGroup]="settings" class="tw-container"> <form [formGroup]="settings" class="tw-container">
<div class="tw-mb-4"> <div class="tw-mb-4">
<bit-card> <bit-card>
@@ -62,9 +64,9 @@
(change)="save('special')" (change)="save('special')"
/> />
<!-- hard-coded the special characters string because `$` indicates an i18n interpolation, <!-- hard-coded the special characters string because `$` indicates an i18n interpolation,
and is handled inconsistently across browsers. Angular template syntax is used to and is handled inconsistently across browsers. Angular template syntax is used to
ensure special characters are entity-encoded. ensure special characters are entity-encoded.
--> -->
<bit-label>{{ "!@#$%^&*" }}</bit-label> <bit-label>{{ "!@#$%^&*" }}</bit-label>
</bit-form-control> </bit-form-control>
</div> </div>
@@ -97,7 +99,9 @@
/> />
<bit-label>{{ "avoidAmbiguous" | i18n }}</bit-label> <bit-label>{{ "avoidAmbiguous" | i18n }}</bit-label>
</bit-form-control> </bit-form-control>
<p *ngIf="policyInEffect" bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p> @if (policyInEffect) {
<p bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
}
</bit-card> </bit-card>
</div> </div>
</form> </form>

View File

@@ -1,4 +1,5 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import { import {
OnInit, OnInit,
Input, Input,
@@ -9,16 +10,27 @@ import {
SimpleChanges, SimpleChanges,
OnChanges, OnChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { takeUntil, Subject, map, filter, tap, skip, ReplaySubject, withLatestFrom } from "rxjs"; import { takeUntil, Subject, map, filter, tap, skip, ReplaySubject, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
SectionComponent,
SectionHeaderComponent,
BaseCardDirective,
CardComponent,
FormFieldModule,
TypographyModule,
CheckboxModule,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
PasswordGenerationOptions, PasswordGenerationOptions,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
import { hasRangeOfValues } from "./util"; import { hasRangeOfValues } from "./util";
@@ -39,7 +51,19 @@ const Controls = Object.freeze({
@Component({ @Component({
selector: "tools-password-settings", selector: "tools-password-settings",
templateUrl: "password-settings.component.html", templateUrl: "password-settings.component.html",
standalone: false, imports: [
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
BaseCardDirective,
CardComponent,
FormFieldModule,
CheckboxModule,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class PasswordSettingsComponent implements OnInit, OnChanges, OnDestroy { export class PasswordSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -8,15 +8,18 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule } from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
BuiltIn, BuiltIn,
SubaddressGenerationOptions, SubaddressGenerationOptions,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for plus-addressed emails */ /** Options group for plus-addressed emails */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({ @Component({
selector: "tools-subaddress-settings", selector: "tools-subaddress-settings",
templateUrl: "subaddress-settings.component.html", templateUrl: "subaddress-settings.component.html",
standalone: false, imports: [ReactiveFormsModule, FormFieldModule, JslibModule, I18nPipe],
}) })
export class SubaddressSettingsComponent implements OnInit, OnChanges, OnDestroy { export class SubaddressSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -42,42 +42,41 @@
data-testid="username-type" data-testid="username-type"
> >
</bit-select> </bit-select>
<bit-hint *ngIf="!!(credentialTypeHint$ | async)">{{ @if (credentialTypeHint$ | async) {
credentialTypeHint$ | async <bit-hint>{{ credentialTypeHint$ | async }}</bit-hint>
}}</bit-hint> }
</bit-form-field> </bit-form-field>
</form> </form>
<form *ngIf="showForwarder$ | async" [formGroup]="forwarder" class="tw-container"> @if (showForwarder$ | async) {
<bit-form-field> <form [formGroup]="forwarder" class="tw-container">
<bit-label>{{ "service" | i18n }}</bit-label> <bit-form-field>
<bit-select <bit-label>{{ "service" | i18n }}</bit-label>
[items]="forwarderOptions$ | async" <bit-select
formControlName="nav" [items]="forwarderOptions$ | async"
data-testid="email-forwarding-service" formControlName="nav"
> data-testid="email-forwarding-service"
</bit-select> >
</bit-form-field> </bit-select>
</form> </bit-form-field>
<tools-catchall-settings </form>
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.catchall" }
[account]="account$ | async" @let showAlgorithm = showAlgorithm$ | async;
(onUpdated)="generate('catchall settings')" @let account = account$ | async;
/> @if (showAlgorithm?.id === Algorithm.catchall) {
<tools-forwarder-settings <tools-catchall-settings [account]="account" (onUpdated)="generate('catchall settings')" />
*ngIf="!!(forwarderId$ | async)" }
[forwarder]="forwarderId$ | async" @if (forwarderId$ | async; as forwarderId) {
[account]="account$ | async" <tools-forwarder-settings [forwarder]="forwarderId" [account]="account" />
/> }
<tools-subaddress-settings @if (showAlgorithm?.id === Algorithm.plusAddress) {
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.plusAddress" <tools-subaddress-settings
[account]="account$ | async" [account]="account"
(onUpdated)="generate('subaddress settings')" (onUpdated)="generate('subaddress settings')"
/> />
<tools-username-settings }
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.username" @if (showAlgorithm?.id === Algorithm.username) {
[account]="account$ | async" <tools-username-settings [account]="account" (onUpdated)="generate('username settings')" />
(onUpdated)="generate('username settings')" }
/>
</bit-card> </bit-card>
</div> </div>
</bit-section> </bit-section>

View File

@@ -1,5 +1,6 @@
import { LiveAnnouncer } from "@angular/cdk/a11y"; import { LiveAnnouncer } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { NgClass, AsyncPipe } from "@angular/common";
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -11,7 +12,7 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { import {
BehaviorSubject, BehaviorSubject,
catchError, catchError,
@@ -28,6 +29,7 @@ import {
withLatestFrom, withLatestFrom,
} from "rxjs"; } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -38,7 +40,22 @@ import {
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components"; import {
ToastService,
Option,
AriaDisableDirective,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
CopyClickDirective,
BitIconButtonComponent,
TooltipDirective,
SectionComponent,
SectionHeaderComponent,
SelectComponent,
TypographyModule,
FormFieldModule,
} from "@bitwarden/components";
import { import {
AlgorithmInfo, AlgorithmInfo,
CredentialGeneratorService, CredentialGeneratorService,
@@ -55,7 +72,12 @@ import {
Algorithm, Algorithm,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameSettingsComponent } from "./username-settings.component";
import { toAlgorithmInfo, translate } from "./util"; import { toAlgorithmInfo, translate } from "./util";
// constants used to identify navigation selections that are not // constants used to identify navigation selections that are not
@@ -69,7 +91,29 @@ const NONE_SELECTED = "none";
@Component({ @Component({
selector: "tools-username-generator", selector: "tools-username-generator",
templateUrl: "username-generator.component.html", templateUrl: "username-generator.component.html",
standalone: false, imports: [
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
NgClass,
ReactiveFormsModule,
SelectComponent,
FormFieldModule,
CatchallSettingsComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
UsernameSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class UsernameGeneratorComponent implements OnInit, OnChanges, OnDestroy { export class UsernameGeneratorComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the username generator /** Instantiates the username generator

View File

@@ -8,15 +8,18 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule, CheckboxModule } from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
EffUsernameGenerationOptions, EffUsernameGenerationOptions,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for usernames */ /** Options group for usernames */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({ @Component({
selector: "tools-username-settings", selector: "tools-username-settings",
templateUrl: "username-settings.component.html", templateUrl: "username-settings.component.html",
standalone: false, imports: [ReactiveFormsModule, FormFieldModule, CheckboxModule, JslibModule, I18nPipe],
}) })
export class UsernameSettingsComponent implements OnInit, OnChanges, OnDestroy { export class UsernameSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component