mirror of
https://github.com/bitwarden/desktop.git
synced 2024-11-27 12:26:38 +01:00
Username generator (#1456)
* username generator implemented * disable type when coming from add/edit * restyle buttons to new icon-btn * update generated-wrapper styles * only show policy messages for passwords * make generated-wrapper a standalone style * Update src/app/vault/password-generator.component.html Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * aria-expanded on show options Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
parent
bc21703a2b
commit
9e0cc45704
@ -3,7 +3,9 @@
|
|||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-body form">
|
<div class="modal-body form">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<label class="settingsTitle">{{ "settingsTitle" | i18n: currentUserEmail }} </label>
|
<div class="box-header">
|
||||||
|
{{ "settingsTitle" | i18n: currentUserEmail }}
|
||||||
|
</div>
|
||||||
<div class="box-content box-content-padded">
|
<div class="box-content box-content-padded">
|
||||||
<h2>
|
<h2>
|
||||||
<button
|
<button
|
||||||
|
@ -317,10 +317,10 @@ export class AppComponent implements OnInit {
|
|||||||
case "newFolder":
|
case "newFolder":
|
||||||
await this.addFolder();
|
await this.addFolder();
|
||||||
break;
|
break;
|
||||||
case "openPasswordGenerator":
|
case "openGenerator":
|
||||||
// openPasswordGenerator has extended functionality if called in the vault
|
// openGenerator has extended functionality if called in the vault
|
||||||
if (!this.router.url.includes("vault")) {
|
if (!this.router.url.includes("vault")) {
|
||||||
await this.openPasswordGenerator();
|
await this.openGenerator();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "convertAccountToKeyConnector":
|
case "convertAccountToKeyConnector":
|
||||||
@ -402,14 +402,14 @@ export class AppComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async openPasswordGenerator() {
|
async openGenerator() {
|
||||||
if (this.modal != null) {
|
if (this.modal != null) {
|
||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
[this.modal] = await this.modalService.openViewRef(
|
[this.modal] = await this.modalService.openViewRef(
|
||||||
PasswordGeneratorComponent,
|
PasswordGeneratorComponent,
|
||||||
this.folderAddEditModalRef,
|
this.passwordGeneratorModalRef,
|
||||||
(comp) => (comp.showSelect = false)
|
(comp) => (comp.showSelect = false)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -27,15 +27,30 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Login -->
|
<!-- Login -->
|
||||||
<div *ngIf="cipher.type === cipherType.Login">
|
<div *ngIf="cipher.type === cipherType.Login">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<label for="loginUsername">{{ "username" | i18n }}</label>
|
<div class="row-main">
|
||||||
<input
|
<label for="loginUsername">{{ "username" | i18n }}</label>
|
||||||
id="loginUsername"
|
<input
|
||||||
type="text"
|
id="loginUsername"
|
||||||
name="Login.Username"
|
type="text"
|
||||||
[(ngModel)]="cipher.login.username"
|
name="Login.Username"
|
||||||
appInputVerbatim
|
[(ngModel)]="cipher.login.username"
|
||||||
/>
|
appInputVerbatim
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<a
|
||||||
|
class="row-btn"
|
||||||
|
href="#"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
role="button"
|
||||||
|
appA11yTitle="{{ 'generateUsername' | i18n }}"
|
||||||
|
(click)="generateUsername()"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<div
|
<div
|
||||||
class="password-wrapper monospaced"
|
class="generated-wrapper monospaced"
|
||||||
appSelectCopy
|
appSelectCopy
|
||||||
[innerHTML]="h.password | colorPassword"
|
[innerHTML]="h.password | colorPassword"
|
||||||
></div>
|
></div>
|
||||||
|
@ -4,52 +4,84 @@
|
|||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
attr.aria-label="{{ 'generatePassword' | i18n }}"
|
attr.aria-label="{{ 'generatePassword' | i18n }}"
|
||||||
>
|
>
|
||||||
<div class="modal-dialog modal-sm" role="document">
|
<div class="modal-dialog modal-md" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect()">
|
<div class="modal-title">
|
||||||
|
{{ "generator" | i18n }}
|
||||||
|
</div>
|
||||||
|
<app-callout
|
||||||
|
type="info"
|
||||||
|
*ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'"
|
||||||
|
>
|
||||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="password-block">
|
<div class="generated-block" *ngIf="type === 'password'">
|
||||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
<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">
|
||||||
|
<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)="regenerate()"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="regenerate()">
|
|
||||||
<i class="bwi bwi-fw bwi-generate" aria-hidden="true"></i>
|
|
||||||
{{ "regeneratePassword" | i18n }}
|
|
||||||
</a>
|
|
||||||
<a class="box-content-row" href="#" appStopClick appBlurClick (click)="copy()">
|
|
||||||
<i class="bwi bwi-fw bwi-clone" aria-hidden="true"></i> {{ "copyPassword" | i18n }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-header">
|
|
||||||
<button type="button" (click)="toggleOptions()" [attr.aria-expanded]="showOptions">
|
|
||||||
<i class="bwi bwi-plus-square" aria-hidden="true" [hidden]="showOptions"></i>
|
|
||||||
<i class="bwi bwi-minus-square" aria-hidden="true" [hidden]="!showOptions"></i>
|
|
||||||
{{ "options" | i18n }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="box-content condensed" [hidden]="!showOptions">
|
|
||||||
<div class="box-content-row box-content-row-radio">
|
<div class="box-content-row box-content-row-radio">
|
||||||
<label class="sr-only radio-header">{{ "type" | i18n }}</label>
|
<label class="radio-header">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
|
||||||
<div
|
<div
|
||||||
class="radio-group text-default"
|
class="radio-group text-default"
|
||||||
appBoxRow
|
appBoxRow
|
||||||
name="PassTypeOptions"
|
name="TypeOptions"
|
||||||
*ngFor="let o of passTypeOptions"
|
*ngFor="let o of typeOptions"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
class="radio"
|
class="radio"
|
||||||
[(ngModel)]="passwordOptions.type"
|
[(ngModel)]="type"
|
||||||
name="Type_{{ o.value }}"
|
name="Type_{{ o.value }}"
|
||||||
id="type_{{ o.value }}"
|
id="type_{{ o.value }}"
|
||||||
[value]="o.value"
|
[value]="o.value"
|
||||||
(change)="savePasswordOptions()"
|
(change)="typeChanged()"
|
||||||
[checked]="passwordOptions.type === o.value"
|
[checked]="type === o.value"
|
||||||
|
[disabled]="showSelect"
|
||||||
/>
|
/>
|
||||||
<label class="unstyled" for="type_{{ o.value }}">
|
<label class="unstyled" for="type_{{ o.value }}">
|
||||||
{{ o.name }}
|
{{ o.name }}
|
||||||
@ -58,148 +90,358 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" [hidden]="!showOptions" *ngIf="passwordOptions.type === 'passphrase'">
|
<ng-container *ngIf="type === 'password'">
|
||||||
<div class="box-content condensed">
|
<div class="box">
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-header">
|
||||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
<button
|
||||||
<input
|
type="button"
|
||||||
id="num-words"
|
(click)="toggleOptions()"
|
||||||
type="number"
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
min="3"
|
[attr.aria-expanded]="showOptions"
|
||||||
max="20"
|
>
|
||||||
(blur)="savePasswordOptions()"
|
<i class="bwi bwi-plus-square" aria-hidden="true" [hidden]="showOptions"></i>
|
||||||
[(ngModel)]="passwordOptions.numWords"
|
<i class="bwi bwi-minus-square" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||||
/>
|
{{ "options" | i18n }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content condensed" [hidden]="!showOptions">
|
||||||
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
<div class="box-content-row box-content-row-radio">
|
||||||
<input
|
<label class="radio-header">{{ "passwordType" | i18n }}</label>
|
||||||
id="word-separator"
|
<div
|
||||||
type="text"
|
class="radio-group text-default"
|
||||||
maxlength="1"
|
appBoxRow
|
||||||
(input)="savePasswordOptions()"
|
name="PassTypeOptions"
|
||||||
[(ngModel)]="passwordOptions.wordSeparator"
|
*ngFor="let o of passTypeOptions"
|
||||||
/>
|
>
|
||||||
</div>
|
<input
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
type="radio"
|
||||||
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
class="radio"
|
||||||
<input
|
[(ngModel)]="passwordOptions.type"
|
||||||
id="capitalize"
|
name="PasswordType_{{ o.value }}"
|
||||||
type="checkbox"
|
id="passwordType_{{ o.value }}"
|
||||||
(change)="savePasswordOptions()"
|
[value]="o.value"
|
||||||
[(ngModel)]="passwordOptions.capitalize"
|
(change)="savePasswordOptions()"
|
||||||
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
[checked]="passwordOptions.type === o.value"
|
||||||
/>
|
/>
|
||||||
</div>
|
<label class="unstyled" for="passwordType_{{ o.value }}">
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
{{ o.name }}
|
||||||
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
</label>
|
||||||
<input
|
</div>
|
||||||
id="include-number"
|
</div>
|
||||||
type="checkbox"
|
|
||||||
(change)="savePasswordOptions()"
|
|
||||||
[(ngModel)]="passwordOptions.includeNumber"
|
|
||||||
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="box" [hidden]="!showOptions" *ngIf="passwordOptions.type === 'passphrase'">
|
||||||
<ng-container *ngIf="passwordOptions.type === 'password'">
|
|
||||||
<div class="box" [hidden]="!showOptions">
|
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
<label for="length">{{ "length" | i18n }}</label>
|
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="length"
|
id="num-words"
|
||||||
type="number"
|
type="number"
|
||||||
min="5"
|
min="3"
|
||||||
max="128"
|
max="20"
|
||||||
[(ngModel)]="passwordOptions.length"
|
|
||||||
(blur)="savePasswordOptions()"
|
(blur)="savePasswordOptions()"
|
||||||
|
[(ngModel)]="passwordOptions.numWords"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
|
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="lengthRange"
|
id="word-separator"
|
||||||
type="range"
|
type="text"
|
||||||
min="5"
|
maxlength="1"
|
||||||
max="128"
|
(input)="savePasswordOptions()"
|
||||||
step="1"
|
[(ngModel)]="passwordOptions.wordSeparator"
|
||||||
[(ngModel)]="passwordOptions.length"
|
|
||||||
(change)="sliderChanged()"
|
|
||||||
(input)="sliderInput()"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="uppercase">A-Z</label>
|
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="uppercase"
|
id="capitalize"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
(change)="savePasswordOptions()"
|
(change)="savePasswordOptions()"
|
||||||
[disabled]="enforcedPasswordPolicyOptions?.useUppercase"
|
[(ngModel)]="passwordOptions.capitalize"
|
||||||
[(ngModel)]="passwordOptions.uppercase"
|
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="lowercase">a-z</label>
|
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="lowercase"
|
id="include-number"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
(change)="savePasswordOptions()"
|
(change)="savePasswordOptions()"
|
||||||
[disabled]="enforcedPasswordPolicyOptions?.useLowercase"
|
[(ngModel)]="passwordOptions.includeNumber"
|
||||||
[(ngModel)]="passwordOptions.lowercase"
|
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
||||||
/>
|
|
||||||
</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"
|
|
||||||
/>
|
|
||||||
</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"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" [hidden]="!showOptions">
|
<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()"
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
/>
|
||||||
|
</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"
|
||||||
|
(blur)="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"
|
||||||
|
(blur)="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">
|
||||||
|
<div class="box-header">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
(click)="toggleOptions()"
|
||||||
|
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||||
|
[attr.aria-expanded]="showOptions"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-plus-square" aria-hidden="true" [hidden]="showOptions"></i>
|
||||||
|
<i class="bwi bwi-minus-square" aria-hidden="true" [hidden]="!showOptions"></i>
|
||||||
|
{{ "options" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="box-content condensed" [hidden]="!showOptions">
|
||||||
|
<div class="box-content-row box-content-row-radio">
|
||||||
|
<label class="radio-header">{{ "usernameType" | i18n }}</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_{{ o.value }}"
|
||||||
|
id="usernameType_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="saveUsernameOptions()"
|
||||||
|
[checked]="usernameOptions.type === o.value"
|
||||||
|
/>
|
||||||
|
<label class="unstyled" for="usernameType_{{ 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'" [hidden]="!showOptions">
|
||||||
<div class="box-content condensed">
|
<div class="box-content condensed">
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row">
|
||||||
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
<label class="radio-header">{{ "service" | i18n }}</label>
|
||||||
|
<div class="radio-group text-default" appBoxRow *ngFor="let o of forwardOptions">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedService"
|
||||||
|
name="ForwardType_{{ o.value }}"
|
||||||
|
id="forwardtype_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="saveUsernameOptions()"
|
||||||
|
[checked]="usernameOptions.forwardedService === o.value"
|
||||||
|
/>
|
||||||
|
<label for="forwardtype_{{ o.value }}">
|
||||||
|
{{ o.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</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
|
<input
|
||||||
id="min-number"
|
id="subaddress-email"
|
||||||
type="number"
|
type="text"
|
||||||
min="0"
|
name="SubaddressEmail"
|
||||||
max="9"
|
[(ngModel)]="usernameOptions.subaddressEmail"
|
||||||
(blur)="savePasswordOptions()"
|
(blur)="saveUsernameOptions()"
|
||||||
[(ngModel)]="passwordOptions.minNumber"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row" *ngIf="subaddressOptions.length > 1">
|
||||||
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
<label 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_{{ o.value }}"
|
||||||
|
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="showWebsiteOption">
|
||||||
|
<label for="subaddress-website">{{ "website" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="min-special"
|
id="subaddress-website"
|
||||||
type="number"
|
type="text"
|
||||||
min="0"
|
name="SubaddressWebsite"
|
||||||
max="9"
|
[value]="usernameOptions.website"
|
||||||
(blur)="savePasswordOptions()"
|
disabled
|
||||||
[(ngModel)]="passwordOptions.minSpecial"
|
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" *ngIf="catchallOptions.length > 1">
|
||||||
|
<label 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_{{ o.value }}"
|
||||||
|
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="showWebsiteOption">
|
||||||
|
<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>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="ambiguous">{{ "ambiguous" | i18n }}</label>
|
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="ambiguous"
|
id="include-number"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
(change)="savePasswordOptions()"
|
(change)="saveUsernameOptions()"
|
||||||
[(ngModel)]="avoidAmbiguous"
|
[(ngModel)]="usernameOptions.wordIncludeNumber"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,7 +37,8 @@
|
|||||||
(onCancelled)="cancelledAddEdit($event)"
|
(onCancelled)="cancelledAddEdit($event)"
|
||||||
(onShareCipher)="shareCipher($event)"
|
(onShareCipher)="shareCipher($event)"
|
||||||
(onEditCollections)="cipherCollections($event)"
|
(onEditCollections)="cipherCollections($event)"
|
||||||
(onGeneratePassword)="openPasswordGenerator(true)"
|
(onGeneratePassword)="openGenerator(true, true)"
|
||||||
|
(onGenerateUsername)="openGenerator(true, false)"
|
||||||
>
|
>
|
||||||
</app-vault-add-edit>
|
</app-vault-add-edit>
|
||||||
<div
|
<div
|
||||||
|
@ -120,8 +120,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
(document.querySelector("#search") as HTMLInputElement).select();
|
(document.querySelector("#search") as HTMLInputElement).select();
|
||||||
detectChanges = false;
|
detectChanges = false;
|
||||||
break;
|
break;
|
||||||
case "openPasswordGenerator":
|
case "openGenerator":
|
||||||
await this.openPasswordGenerator(false);
|
await this.openGenerator(false);
|
||||||
break;
|
break;
|
||||||
case "syncCompleted":
|
case "syncCompleted":
|
||||||
await this.load();
|
await this.load();
|
||||||
@ -599,28 +599,39 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
this.go();
|
this.go();
|
||||||
}
|
}
|
||||||
|
|
||||||
async openPasswordGenerator(showSelect: boolean) {
|
async openGenerator(showSelect: boolean, passwordType = true) {
|
||||||
if (this.modal != null) {
|
if (this.modal != null) {
|
||||||
this.modal.close();
|
this.modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cipher = this.addEditComponent?.cipher;
|
||||||
|
const loginType = cipher != null && cipher.type === CipherType.Login && cipher.login != null;
|
||||||
|
|
||||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||||
PasswordGeneratorComponent,
|
PasswordGeneratorComponent,
|
||||||
this.passwordGeneratorModalRef,
|
this.passwordGeneratorModalRef,
|
||||||
(comp) => (comp.showSelect = showSelect)
|
(comp) => {
|
||||||
|
comp.showSelect = showSelect;
|
||||||
|
if (showSelect) {
|
||||||
|
comp.type = passwordType ? "password" : "username";
|
||||||
|
if (loginType && cipher.login.hasUris && cipher.login.uris[0].hostname != null) {
|
||||||
|
comp.usernameWebsite = cipher.login.uris[0].hostname;
|
||||||
|
comp.showWebsiteOption = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
this.modal = modal;
|
this.modal = modal;
|
||||||
|
|
||||||
childComponent.onSelected.subscribe((password: string) => {
|
childComponent.onSelected.subscribe((value: string) => {
|
||||||
this.modal.close();
|
this.modal.close();
|
||||||
if (
|
if (loginType) {
|
||||||
this.addEditComponent != null &&
|
|
||||||
this.addEditComponent.cipher != null &&
|
|
||||||
this.addEditComponent.cipher.type === CipherType.Login &&
|
|
||||||
this.addEditComponent.cipher.login != null
|
|
||||||
) {
|
|
||||||
this.addEditComponent.markPasswordAsDirty();
|
this.addEditComponent.markPasswordAsDirty();
|
||||||
this.addEditComponent.cipher.login.password = password;
|
if (passwordType) {
|
||||||
|
this.addEditComponent.cipher.login.password = value;
|
||||||
|
} else {
|
||||||
|
this.addEditComponent.cipher.login.username = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
*ngIf="showPassword"
|
*ngIf="showPassword"
|
||||||
class="monospaced password-wrapper"
|
class="monospaced generated-wrapper"
|
||||||
appSelectCopy
|
appSelectCopy
|
||||||
[innerHTML]="cipher.login.password | colorPassword"
|
[innerHTML]="cipher.login.password | colorPassword"
|
||||||
></div>
|
></div>
|
||||||
|
@ -369,6 +369,12 @@
|
|||||||
"overwritePasswordConfirmation": {
|
"overwritePasswordConfirmation": {
|
||||||
"message": "Are you sure you want to overwrite the current password?"
|
"message": "Are you sure you want to overwrite the current password?"
|
||||||
},
|
},
|
||||||
|
"overwriteUsername": {
|
||||||
|
"message": "Overwrite Username"
|
||||||
|
},
|
||||||
|
"overwriteUsernameConfirmation": {
|
||||||
|
"message": "Are you sure you want to overwrite the current username?"
|
||||||
|
},
|
||||||
"noneFolder": {
|
"noneFolder": {
|
||||||
"message": "No Folder",
|
"message": "No Folder",
|
||||||
"description": "This is the folder for uncategorized items"
|
"description": "This is the folder for uncategorized items"
|
||||||
@ -1188,7 +1194,12 @@
|
|||||||
"message": "This password was not found in any known data breaches. It should be safe to use."
|
"message": "This password was not found in any known data breaches. It should be safe to use."
|
||||||
},
|
},
|
||||||
"baseDomain": {
|
"baseDomain": {
|
||||||
"message": "Base domain"
|
"message": "Base domain",
|
||||||
|
"description": "Domain name. Ex. website.com"
|
||||||
|
},
|
||||||
|
"domainName": {
|
||||||
|
"message": "Domain Name",
|
||||||
|
"description": "Domain name. Ex. website.com"
|
||||||
},
|
},
|
||||||
"host": {
|
"host": {
|
||||||
"message": "Host",
|
"message": "Host",
|
||||||
@ -1828,5 +1839,47 @@
|
|||||||
"example": "name@example.com"
|
"example": "name@example.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"generator": {
|
||||||
|
"message": "Generator"
|
||||||
|
},
|
||||||
|
"whatWouldYouLikeToGenerate": {
|
||||||
|
"message": "What would you like to generate?"
|
||||||
|
},
|
||||||
|
"passwordType": {
|
||||||
|
"message": "Password Type"
|
||||||
|
},
|
||||||
|
"regenerateUsername": {
|
||||||
|
"message": "Regenerate Username"
|
||||||
|
},
|
||||||
|
"generateUsername": {
|
||||||
|
"message": "Generate Username"
|
||||||
|
},
|
||||||
|
"usernameType": {
|
||||||
|
"message": "Username Type"
|
||||||
|
},
|
||||||
|
"plusAddressedEmail": {
|
||||||
|
"message": "Plus Addressed Email"
|
||||||
|
},
|
||||||
|
"plusAddressedEmailDesc": {
|
||||||
|
"message": "Use your email provider's sub-addressing capabilities."
|
||||||
|
},
|
||||||
|
"catchallEmail": {
|
||||||
|
"message": "Catch-all Email"
|
||||||
|
},
|
||||||
|
"catchallEmailDesc": {
|
||||||
|
"message": "Use your domain's configured catch-all inbox."
|
||||||
|
},
|
||||||
|
"random": {
|
||||||
|
"message": "Random"
|
||||||
|
},
|
||||||
|
"randomWord": {
|
||||||
|
"message": "Random Word"
|
||||||
|
},
|
||||||
|
"websiteName": {
|
||||||
|
"message": "Website Name"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"message": "Service"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export class ViewMenu implements IMenubarMenu {
|
|||||||
return [
|
return [
|
||||||
this.searchVault,
|
this.searchVault,
|
||||||
this.separator,
|
this.separator,
|
||||||
this.passwordGenerator,
|
this.generator,
|
||||||
this.passwordHistory,
|
this.passwordHistory,
|
||||||
this.separator,
|
this.separator,
|
||||||
this.zoomIn,
|
this.zoomIn,
|
||||||
@ -54,11 +54,11 @@ export class ViewMenu implements IMenubarMenu {
|
|||||||
return { type: "separator" };
|
return { type: "separator" };
|
||||||
}
|
}
|
||||||
|
|
||||||
private get passwordGenerator(): MenuItemConstructorOptions {
|
private get generator(): MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
id: "passwordGenerator",
|
id: "generator",
|
||||||
label: this.localize("passwordGenerator"),
|
label: this.localize("generator"),
|
||||||
click: () => this.sendMessage("openPasswordGenerator"),
|
click: () => this.sendMessage("openGenerator"),
|
||||||
accelerator: "CmdOrCtrl+G",
|
accelerator: "CmdOrCtrl+G",
|
||||||
enabled: !this._isLocked,
|
enabled: !this._isLocked,
|
||||||
};
|
};
|
||||||
|
@ -4,15 +4,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.settingsTitle {
|
|
||||||
margin: 0 10px 5px 10px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("headingColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.box-header {
|
.box-header {
|
||||||
margin: 0 10px 5px 10px;
|
margin: 0 10px 5px 10px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@ -302,7 +293,7 @@
|
|||||||
&.box-content-row-slider {
|
&.box-content-row-slider {
|
||||||
input[type="range"] {
|
input[type="range"] {
|
||||||
height: 10px;
|
height: 10px;
|
||||||
width: 110px !important;
|
width: 220px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
@ -364,33 +355,7 @@
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
||||||
.row-btn {
|
.row-btn {
|
||||||
cursor: pointer;
|
@extend .icon-btn;
|
||||||
padding: 10px 8px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("boxRowButtonColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("boxRowButtonHoverColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("disabledIconColor");
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
@include themify($themes) {
|
|
||||||
color: themed("disabledIconColor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.no-pad .row-btn {
|
&.no-pad .row-btn {
|
||||||
@ -485,4 +450,36 @@
|
|||||||
min-width: 25px;
|
min-width: 25px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radio-group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 0 0 0 5px;
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: $font-size-base;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("textColor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-start {
|
||||||
|
align-items: start;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-top: -4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,3 +115,57 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10px 8px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("boxRowButtonColor");
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("buttonPrimaryColor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("buttonDangerColor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("boxRowButtonHoverColor");
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: darken(themed("buttonPrimaryColor"), 6%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: darken(themed("buttonDangerColor"), 6%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("disabledIconColor");
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("disabledIconColor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
@import "variables.scss";
|
@import "variables.scss";
|
||||||
|
|
||||||
small {
|
small,
|
||||||
|
.small {
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,22 +174,44 @@ p.lead {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-block {
|
.modal-title {
|
||||||
|
margin: 0 10px 5px 10px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
@include themify($themes) {
|
||||||
|
color: themed("headingColor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.generated-block {
|
||||||
font-size: $font-size-large;
|
font-size: $font-size-large;
|
||||||
font-family: $font-family-monospace;
|
font-family: $font-family-monospace;
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.modal-body & {
|
.modal-body & {
|
||||||
margin-top: 10px;
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generated-wrapper {
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
display: flex;
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-wrapper {
|
.generated-wrapper {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user