bitwarden-estensione-browser/apps/browser/src/popup/generator/generator.component.html

528 lines
19 KiB
HTML

<header>
<div class="left">
<app-pop-out [show]="!comingFromAddEdit"></app-pop-out>
<button type="button" (click)="close()" *ngIf="comingFromAddEdit">
{{ "cancel" | i18n }}
</button>
</div>
<h1 class="center">
<span class="title">{{ "generator" | i18n }}</span>
</h1>
<div class="right">
<button type="button" (click)="select()" *ngIf="comingFromAddEdit">
{{ "select" | i18n }}
</button>
</div>
</header>
<main tabindex="-1">
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'">
{{ "passwordGeneratorPolicyInEffect" | i18n }}
</app-callout>
<div class="generated-block" *ngIf="type === 'password'">
<div class="generated-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
<div class="action-buttons">
<button
type="button"
class="row-btn"
appStopClick
appA11yTitle="{{ 'copyPassword' | i18n }}"
(click)="copy()"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
<button
type="button"
appStopClick
appA11yTitle="{{ 'regeneratePassword' | i18n }}"
(click)="regenerate()"
>
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="generated-block" *ngIf="type === 'username'">
<div class="generated-wrapper" [innerHTML]="username | colorPassword" appSelectCopy></div>
<div class="action-buttons" #form [appApiAction]="usernameGeneratingPromise">
<button
type="button"
class="row-btn"
appStopClick
appA11yTitle="{{ 'copyUsername' | i18n }}"
(click)="copy()"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
<button
type="button"
appStopClick
appA11yTitle="{{ 'regenerateUsername' | i18n }}"
(click)="regenerate()"
[disabled]="$any(form).loading"
>
<i
class="bwi bwi-lg bwi-generate"
[ngClass]="$any(form).loading ? 'bwi-spin' : ''"
aria-hidden="true"
></i>
</button>
</div>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row" role="radiogroup" aria-labelledby="typeHeading">
<label id="typeHeading" class="radio-header">{{
"whatWouldYouLikeToGenerate" | i18n
}}</label>
<div class="radio-group text-default" appBoxRow *ngFor="let o of typeOptions">
<input
type="radio"
[(ngModel)]="type"
name="Type"
id="type_{{ o.value }}"
[value]="o.value"
(change)="typeChanged()"
[checked]="type === o.value"
[disabled]="comingFromAddEdit && type !== o.value"
/>
<label for="type_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
</div>
</div>
<ng-container *ngIf="type === 'password'">
<div class="box">
<h2 class="box-header">
{{ "options" | i18n }}
</h2>
<div class="box-content">
<div class="box-content-row" role="radiogroup" aria-labelledby="passwordTypeHeading">
<label id="passwordTypeHeading" class="radio-header">{{ "passwordType" | i18n }}</label>
<div class="radio-group text-default" appBoxRow *ngFor="let o of passTypeOptions">
<input
type="radio"
[(ngModel)]="passwordOptions.type"
name="PasswordType"
id="passwordtype_{{ o.value }}"
[value]="o.value"
(change)="savePasswordOptions()"
[checked]="passwordOptions.type === o.value"
/>
<label for="passwordtype_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
</div>
</div>
<div class="box" *ngIf="passwordOptions.type === 'passphrase'">
<div class="box-content">
<div class="box-content-row box-content-row-input" appBoxRow>
<label for="num-words">{{ "numWords" | i18n }}</label>
<input
id="num-words"
type="number"
min="3"
max="20"
(change)="savePasswordOptions()"
[(ngModel)]="passwordOptions.numWords"
/>
</div>
<div class="box-content-row box-content-row-input" appBoxRow>
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
<input
id="word-separator"
type="text"
maxlength="1"
(input)="savePasswordOptions()"
[(ngModel)]="passwordOptions.wordSeparator"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="capitalize">{{ "capitalize" | i18n }}</label>
<input
id="capitalize"
type="checkbox"
(change)="savePasswordOptions()"
[(ngModel)]="passwordOptions.capitalize"
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="include-number">{{ "includeNumber" | i18n }}</label>
<input
id="include-number"
type="checkbox"
(change)="savePasswordOptions()"
[(ngModel)]="passwordOptions.includeNumber"
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
/>
</div>
</div>
</div>
<ng-container *ngIf="passwordOptions.type === 'password'">
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-slider" appBoxRow>
<label for="length">{{ "length" | i18n }}</label>
<input
id="length"
type="number"
min="5"
max="128"
[(ngModel)]="passwordOptions.length"
(change)="savePasswordOptions()"
/>
<input
id="lengthRange"
type="range"
min="5"
max="128"
step="1"
[(ngModel)]="passwordOptions.length"
(change)="sliderChanged()"
(input)="sliderInput()"
attr.aria-label="{{ 'length' | i18n }}"
tabindex="-1"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="uppercase">A-Z</label>
<input
id="uppercase"
type="checkbox"
(change)="savePasswordOptions()"
attr.aria-label="{{ 'uppercase' | i18n }}"
[disabled]="enforcedPasswordPolicyOptions.useUppercase"
[(ngModel)]="passwordOptions.uppercase"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="lowercase">a-z</label>
<input
id="lowercase"
type="checkbox"
(change)="savePasswordOptions()"
attr.aria-label="{{ 'lowercase' | i18n }}"
[disabled]="enforcedPasswordPolicyOptions.useLowercase"
[(ngModel)]="passwordOptions.lowercase"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="numbers">0-9</label>
<input
id="numbers"
type="checkbox"
(change)="savePasswordOptions()"
attr.aria-label="{{ 'numbers' | i18n }}"
[disabled]="enforcedPasswordPolicyOptions.useNumbers"
[(ngModel)]="passwordOptions.number"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="special">!@#$%^&*</label>
<input
id="special"
type="checkbox"
(change)="savePasswordOptions()"
attr.aria-label="{{ 'specialCharacters' | i18n }}"
[disabled]="enforcedPasswordPolicyOptions.useSpecial"
[(ngModel)]="passwordOptions.special"
/>
</div>
</div>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-input" appBoxRow>
<label for="min-number">{{ "minNumbers" | i18n }}</label>
<input
id="min-number"
type="number"
min="0"
max="9"
(change)="savePasswordOptions()"
[(ngModel)]="passwordOptions.minNumber"
/>
</div>
<div class="box-content-row box-content-row-input" appBoxRow>
<label for="min-special">{{ "minSpecial" | i18n }}</label>
<input
id="min-special"
type="number"
min="0"
max="9"
(change)="savePasswordOptions()"
[(ngModel)]="passwordOptions.minSpecial"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="ambiguous">{{ "avoidAmbChar" | i18n }}</label>
<input
id="ambiguous"
type="checkbox"
(change)="savePasswordOptions()"
[(ngModel)]="avoidAmbiguous"
/>
</div>
</div>
</div>
</ng-container>
<div class="box list">
<div class="box-content single-line">
<a class="box-content-row box-content-row-flex" routerLink="/generator-history">
<div class="row-main">{{ "passwordHistory" | i18n }}</div>
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
</a>
</div>
</div>
</ng-container>
<ng-container *ngIf="type === 'username'">
<div class="box">
<h2 class="box-header">
{{ "options" | i18n }}
</h2>
<div class="box-content">
<div class="box-content-row" role="radiogroup" aria-labelledby="usernameTypeHeading">
<label id="usernameTypeHeading" class="radio-header">
{{ "usernameType" | i18n }}
<a
href="https://bitwarden.com/help/generator/#username-types"
target="_blank"
rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>
</label>
<div
class="radio-group align-start text-default"
appBoxRow
*ngFor="let o of usernameTypeOptions"
>
<input
type="radio"
[(ngModel)]="usernameOptions.type"
name="UsernameType"
id="type_{{ o.value }}"
[value]="o.value"
(change)="saveUsernameOptions()"
[checked]="usernameOptions.type === o.value"
/>
<label for="type_{{ o.value }}">
{{ o.name }}
<div class="small text-muted" *ngIf="o.desc">
{{ o.desc }}
</div>
</label>
</div>
</div>
</div>
</div>
<div class="box" *ngIf="usernameOptions.type === 'forwarded'">
<div class="box-content">
<div class="box-content-row" role="listbox" aria-labelledby="forwardTypeHeading">
<label id="forwardTypeHeading">{{ "service" | i18n }}</label>
<select
id="ForwardTypeDropdown"
name="ForwardType"
[(ngModel)]="usernameOptions.forwardedService"
(change)="saveUsernameOptions()"
>
<option *ngFor="let o of forwardOptions" [ngValue]="o.value" role="option">
{{ o.name }}
</option>
</select>
</div>
<ng-container *ngIf="usernameOptions.forwardedService === 'simplelogin'">
<div class="box-content-row" appBoxRow>
<label for="simplelogin-apikey">{{ "apiKey" | i18n }}</label>
<input
id="simplelogin-apikey"
type="password"
name="SimpleLoginApiKey"
[(ngModel)]="usernameOptions.forwardedSimpleLoginApiKey"
(blur)="saveUsernameOptions()"
/>
</div>
</ng-container>
<ng-container *ngIf="usernameOptions.forwardedService === 'duckduckgo'">
<div class="box-content-row" appBoxRow>
<label for="duckduckgo-apikey">{{ "apiKey" | i18n }}</label>
<input
id="duckduckgo-apikey"
type="password"
name="DuckDuckGoApiKey"
[(ngModel)]="usernameOptions.forwardedDuckDuckGoToken"
(blur)="saveUsernameOptions()"
/>
</div>
</ng-container>
<ng-container *ngIf="usernameOptions.forwardedService === 'anonaddy'">
<div class="box-content-row" appBoxRow>
<label for="anonaddy-accessToken">{{ "apiAccessToken" | i18n }}</label>
<input
id="anonaddy-accessToken"
type="password"
name="AnonAddyAccessToken"
[(ngModel)]="usernameOptions.forwardedAnonAddyApiToken"
(blur)="saveUsernameOptions()"
/>
</div>
<div class="box-content-row" appBoxRow>
<label for="anonaddy-domain">{{ "domainName" | i18n }}</label>
<input
id="anonaddy-domain"
type="text"
name="AnonAddyDomain"
[(ngModel)]="usernameOptions.forwardedAnonAddyDomain"
(blur)="saveUsernameOptions()"
/>
</div>
</ng-container>
<ng-container *ngIf="usernameOptions.forwardedService === 'firefoxrelay'">
<div class="box-content-row" appBoxRow>
<label for="firefox-apikey">{{ "apiAccessToken" | i18n }}</label>
<input
id="firefox-apikey"
type="password"
name="FirefoxApiKey"
[(ngModel)]="usernameOptions.forwardedFirefoxApiToken"
(blur)="saveUsernameOptions()"
/>
</div>
</ng-container>
<ng-container *ngIf="usernameOptions.forwardedService === 'fastmail'">
<div class="box-content-row" appBoxRow>
<label for="fastmail-apiToken">{{ "apiAccessToken" | i18n }}</label>
<input
id="fastmail-apiToken"
type="password"
name="FastmailApiToken"
[(ngModel)]="usernameOptions.forwardedFastmailApiToken"
(blur)="saveUsernameOptions()"
/>
</div>
</ng-container>
</div>
</div>
<div class="box" *ngIf="usernameOptions.type === 'subaddress'">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="subaddress-email">{{ "emailAddress" | i18n }}</label>
<input
id="subaddress-email"
type="text"
name="SubaddressEmail"
[(ngModel)]="usernameOptions.subaddressEmail"
(blur)="saveUsernameOptions()"
/>
</div>
<div
class="box-content-row"
role="radiogroup"
aria-labelledby="subaddressTypeHeading"
*ngIf="subaddressOptions.length > 1"
>
<label id="subaddressTypeHeading" class="radio-header">{{ "type" | i18n }}</label>
<div class="radio-group text-default" appBoxRow *ngFor="let o of subaddressOptions">
<input
type="radio"
[(ngModel)]="usernameOptions.subaddressType"
name="SubaddressType"
id="subaddresstype_{{ o.value }}"
[value]="o.value"
(change)="saveUsernameOptions()"
[checked]="usernameOptions.subaddressType === o.value"
/>
<label for="subaddresstype_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
<div class="box-content-row" appBoxRow *ngIf="usernameWebsite">
<label for="subaddress-website">{{ "website" | i18n }}</label>
<input
id="subaddress-website"
type="text"
name="SubaddressWebsite"
[value]="usernameOptions.website"
disabled
readonly
/>
</div>
</div>
</div>
<div class="box" *ngIf="usernameOptions.type === 'catchall'">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="catchall-domain">{{ "domainName" | i18n }}</label>
<input
id="catchall-domain"
type="text"
name="CatchallDomain"
[(ngModel)]="usernameOptions.catchallDomain"
(blur)="saveUsernameOptions()"
/>
</div>
<div
class="box-content-row"
role="radiogroup"
aria-labelledby="catchallTypeHeading"
*ngIf="catchallOptions.length > 1"
>
<label id="catchallTypeHeading" class="radio-header">{{ "type" | i18n }}</label>
<div class="radio-group text-default" appBoxRow *ngFor="let o of catchallOptions">
<input
type="radio"
[(ngModel)]="usernameOptions.catchallType"
name="CatchallType"
id="catchalltype_{{ o.value }}"
[value]="o.value"
(change)="saveUsernameOptions()"
[checked]="usernameOptions.catchallType === o.value"
/>
<label for="catchalltype_{{ o.value }}">
{{ o.name }}
</label>
</div>
</div>
<div class="box-content-row" appBoxRow *ngIf="usernameWebsite">
<label for="catchall-website">{{ "website" | i18n }}</label>
<input
id="catchall-website"
type="text"
name="CatchallWebsite"
[value]="usernameOptions.website"
disabled
readonly
/>
</div>
</div>
</div>
<div class="box" *ngIf="usernameOptions.type === 'word'">
<div class="box-content">
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="capitalize">{{ "capitalize" | i18n }}</label>
<input
id="capitalize"
type="checkbox"
(change)="saveUsernameOptions()"
[(ngModel)]="usernameOptions.wordCapitalize"
/>
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="include-number">{{ "includeNumber" | i18n }}</label>
<input
id="include-number"
type="checkbox"
(change)="saveUsernameOptions()"
[(ngModel)]="usernameOptions.wordIncludeNumber"
/>
</div>
</div>
</div>
</ng-container>
</main>