mirror of
https://github.com/bitwarden/browser
synced 2025-12-31 15:43:28 +00:00
[PM-328] Move generator to tools (#4980)
* Move generator to tools libs/angular: - Move generator.component to tools libs/common: - Move password generation to tools - Move username generation including email-forwarders to tools apps/* - create tools-subfolder and move files regarding generator functionality - Update all the imports .github/: - Cleaned up whitelist-capital-letters.txt - Added team-tools-dev folders to CODEOWNERS * Remove unused barrel file
This commit is contained in:
committed by
GitHub
parent
e9d0f75b8a
commit
d4c812160f
571
apps/desktop/src/app/tools/generator.component.html
Normal file
571
apps/desktop/src/app/tools/generator.component.html
Normal file
@@ -0,0 +1,571 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="generatorTitle">
|
||||
<div class="modal-dialog modal-md" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<h1 class="modal-title" id="generatorTitle">
|
||||
{{ "generator" | i18n }}
|
||||
</h1>
|
||||
<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="icon-btn primary"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||
(click)="copy()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn primary"
|
||||
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="icon-btn primary"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'copyUsername' | i18n }}"
|
||||
(click)="copy()"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="icon-btn primary"
|
||||
appStopClick
|
||||
appA11yTitle="{{ 'regenerateUsername' | i18n }}"
|
||||
(click)="$any(form).loading ? false : regenerate()"
|
||||
[attr.aria-disabled]="$any(form).loading ? 'true' : null"
|
||||
>
|
||||
<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 condensed">
|
||||
<div
|
||||
class="box-content-row box-content-row-radio"
|
||||
role="radiogroup"
|
||||
aria-labelledby="typeHeading"
|
||||
>
|
||||
<label id="typeHeading" class="radio-header">{{
|
||||
"whatWouldYouLikeToGenerate" | i18n
|
||||
}}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
name="TypeOptions"
|
||||
*ngFor="let o of typeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="type"
|
||||
name="Type"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged()"
|
||||
[checked]="type === o.value"
|
||||
[disabled]="comingFromAddEdit && type !== o.value"
|
||||
/>
|
||||
<label class="unstyled" for="type_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="type === 'password'">
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
<button type="button" (click)="toggleOptions()" [attr.aria-expanded]="showOptions">
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-angle-right': !showOptions, 'bwi-angle-down': showOptions }"
|
||||
></i>
|
||||
{{ "options" | i18n }}
|
||||
</button>
|
||||
</h2>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div
|
||||
class="box-content-row box-content-row-radio"
|
||||
role="radiogroup"
|
||||
aria-labelledby="passwordTypeHeading"
|
||||
>
|
||||
<label id="passwordTypeHeading" class="radio-header">{{
|
||||
"passwordType" | i18n
|
||||
}}</label>
|
||||
<div
|
||||
class="radio-group text-default"
|
||||
appBoxRow
|
||||
name="PassTypeOptions"
|
||||
*ngFor="let o of passTypeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="passwordOptions.type"
|
||||
name="PasswordType"
|
||||
id="passwordType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="savePasswordOptions()"
|
||||
[checked]="passwordOptions.type === o.value"
|
||||
/>
|
||||
<label class="unstyled" for="passwordType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions" *ngIf="passwordOptions.type === 'passphrase'">
|
||||
<div class="box-content condensed">
|
||||
<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" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<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"
|
||||
(blur)="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()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
||||
[(ngModel)]="passwordOptions.uppercase"
|
||||
attr.aria-label="{{ 'uppercase' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="lowercase">a-z</label>
|
||||
<input
|
||||
id="lowercase"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
||||
[(ngModel)]="passwordOptions.lowercase"
|
||||
attr.aria-label="{{ 'lowercase' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="numbers">0-9</label>
|
||||
<input
|
||||
id="numbers"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useNumbers"
|
||||
[(ngModel)]="passwordOptions.number"
|
||||
attr.aria-label="{{ 'numbers' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="special">!@#$%^&*</label>
|
||||
<input
|
||||
id="special"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[disabled]="enforcedPasswordPolicyOptions?.useSpecial"
|
||||
[(ngModel)]="passwordOptions.special"
|
||||
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<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">{{ "ambiguous" | i18n }}</label>
|
||||
<input
|
||||
id="ambiguous"
|
||||
type="checkbox"
|
||||
(change)="savePasswordOptions()"
|
||||
[(ngModel)]="avoidAmbiguous"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="type === 'username'">
|
||||
<div class="box">
|
||||
<h2 class="box-header">
|
||||
<button type="button" (click)="toggleOptions()" [attr.aria-expanded]="showOptions">
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-angle-right': !showOptions, 'bwi-angle-down': showOptions }"
|
||||
></i>
|
||||
{{ "options" | i18n }}
|
||||
</button>
|
||||
</h2>
|
||||
<div class="box-content condensed" [hidden]="!showOptions">
|
||||
<div
|
||||
class="box-content-row box-content-row-radio"
|
||||
role="radiogroup"
|
||||
aria-labelledby="usernameTypeHeading"
|
||||
>
|
||||
<label id="usernameTypeHeading" class="radio-header">
|
||||
{{ "usernameType" | i18n }}
|
||||
<a
|
||||
href="#"
|
||||
appStopClick
|
||||
(click)="usernameTypesLearnMore()"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
</label>
|
||||
<div
|
||||
class="radio-group align-start text-default"
|
||||
appBoxRow
|
||||
name="UsernameTypeOptions"
|
||||
*ngFor="let o of usernameTypeOptions"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
class="radio"
|
||||
[(ngModel)]="usernameOptions.type"
|
||||
name="UsernameType"
|
||||
id="usernameType_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.type === o.value"
|
||||
/>
|
||||
<label class="unstyled" for="usernameType_{{ o.value }}">
|
||||
{{ o.name }}
|
||||
<small class="help-block" *ngIf="o.desc">{{ o.desc }}</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="usernameOptions.type === 'forwarded'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<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'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<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'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<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'" [hidden]="!showOptions">
|
||||
<div class="box-content condensed">
|
||||
<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>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="primary"
|
||||
*ngIf="comingFromAddEdit"
|
||||
(click)="select()"
|
||||
appA11yTitle="{{ 'select' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-fw bwi-check" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">
|
||||
{{ (comingFromAddEdit ? "cancel" : "close") | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user