mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
add new-cipher-menu component (#15467)
* add new-cipher-menu component * move new cipher menu to vault. clean up template
This commit is contained in:
@@ -103,48 +103,11 @@
|
|||||||
*ngIf="filter.type !== 'trash' && filter.collectionId !== Unassigned && organization"
|
*ngIf="filter.type !== 'trash' && filter.collectionId !== Unassigned && organization"
|
||||||
class="tw-shrink-0"
|
class="tw-shrink-0"
|
||||||
>
|
>
|
||||||
<!-- "New" menu is always shown unless the user cannot create a cipher and cannot create a collection-->
|
<vault-new-cipher-menu
|
||||||
<ng-container *ngIf="canCreateCipher || canCreateCollection">
|
[canCreateCipher]="canCreateCipher"
|
||||||
<div appListDropdown>
|
[canCreateCollection]="canCreateCollection"
|
||||||
<button
|
(cipherAdded)="addCipher($event)"
|
||||||
bitButton
|
(collectionAdded)="addCollection()"
|
||||||
buttonType="primary"
|
/>
|
||||||
type="button"
|
|
||||||
[bitMenuTriggerFor]="addOptions"
|
|
||||||
id="newItemDropdown"
|
|
||||||
appA11yTitle="{{ 'new' | i18n }}"
|
|
||||||
>
|
|
||||||
<i class="bwi bwi-plus" aria-hidden="true"></i>
|
|
||||||
{{ "new" | i18n }}<i class="bwi tw-ml-2" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<bit-menu #addOptions aria-labelledby="newItemDropdown">
|
|
||||||
<ng-container *ngIf="canCreateCipher">
|
|
||||||
<button type="button" bitMenuItem (click)="addCipher(CipherType.Login)">
|
|
||||||
<i class="bwi bwi-globe" slot="start" aria-hidden="true"></i>
|
|
||||||
{{ "typeLogin" | i18n }}
|
|
||||||
</button>
|
|
||||||
<button type="button" bitMenuItem (click)="addCipher(CipherType.Card)">
|
|
||||||
<i class="bwi bwi-credit-card" slot="start" aria-hidden="true"></i>
|
|
||||||
{{ "typeCard" | i18n }}
|
|
||||||
</button>
|
|
||||||
<button type="button" bitMenuItem (click)="addCipher(CipherType.Identity)">
|
|
||||||
<i class="bwi bwi-id-card" slot="start" aria-hidden="true"></i>
|
|
||||||
{{ "typeIdentity" | i18n }}
|
|
||||||
</button>
|
|
||||||
<button type="button" bitMenuItem (click)="addCipher(CipherType.SecureNote)">
|
|
||||||
<i class="bwi bwi-sticky-note" slot="start" aria-hidden="true"></i>
|
|
||||||
{{ "note" | i18n }}
|
|
||||||
</button>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container *ngIf="canCreateCollection">
|
|
||||||
<bit-menu-divider *ngIf="canCreateCipher"></bit-menu-divider>
|
|
||||||
<button type="button" bitMenuItem (click)="addCollection()">
|
|
||||||
<i class="bwi bwi-fw bwi-collection-shared" aria-hidden="true"></i>
|
|
||||||
{{ "collection" | i18n }}
|
|
||||||
</button>
|
|
||||||
</ng-container>
|
|
||||||
</bit-menu>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
</div>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
SearchModule,
|
SearchModule,
|
||||||
SimpleDialogOptions,
|
SimpleDialogOptions,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { NewCipherMenuComponent } from "@bitwarden/vault";
|
||||||
|
|
||||||
import { HeaderModule } from "../../../../layouts/header/header.module";
|
import { HeaderModule } from "../../../../layouts/header/header.module";
|
||||||
import { SharedModule } from "../../../../shared";
|
import { SharedModule } from "../../../../shared";
|
||||||
@@ -45,6 +46,7 @@ import { CollectionDialogTabType } from "../../shared/components/collection-dial
|
|||||||
HeaderModule,
|
HeaderModule,
|
||||||
SearchModule,
|
SearchModule,
|
||||||
JslibModule,
|
JslibModule,
|
||||||
|
NewCipherMenuComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class VaultHeaderComponent {
|
export class VaultHeaderComponent {
|
||||||
|
|||||||
@@ -510,7 +510,7 @@ export class VaultItemsComponent {
|
|||||||
|
|
||||||
private compareNames(a: VaultItem, b: VaultItem): number {
|
private compareNames(a: VaultItem, b: VaultItem): number {
|
||||||
const getName = (item: VaultItem) => item.collection?.name || item.cipher?.name;
|
const getName = (item: VaultItem) => item.collection?.name || item.cipher?.name;
|
||||||
return getName(a).localeCompare(getName(b));
|
return getName(a)?.localeCompare(getName(b)) ?? -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -68,35 +68,13 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div *ngIf="filter.type !== 'trash'" class="tw-shrink-0">
|
<div *ngIf="filter.type !== 'trash'" class="tw-shrink-0">
|
||||||
<div appListDropdown>
|
<vault-new-cipher-menu
|
||||||
<button
|
[canCreateCipher]="true"
|
||||||
bitButton
|
[canCreateFolder]="true"
|
||||||
buttonType="primary"
|
[canCreateCollection]="canCreateCollections"
|
||||||
type="button"
|
(cipherAdded)="addCipher($event)"
|
||||||
[bitMenuTriggerFor]="addOptions"
|
(folderAdded)="addFolder()"
|
||||||
id="newItemDropdown"
|
(collectionAdded)="addCollection()"
|
||||||
appA11yTitle="{{ 'new' | i18n }}"
|
/>
|
||||||
>
|
|
||||||
<i class="bwi bwi-plus" aria-hidden="true"></i>
|
|
||||||
{{ "new" | i18n }}<i class="bwi tw-ml-2" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<bit-menu #addOptions aria-labelledby="newItemDropdown">
|
|
||||||
@for (item of cipherMenuItems$ | async; track item.type) {
|
|
||||||
<button type="button" bitMenuItem (click)="addCipher(item.type)">
|
|
||||||
<i class="bwi {{ item.icon }}" slot="start" aria-hidden="true"></i>
|
|
||||||
{{ item.labelKey | i18n }}
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
<bit-menu-divider />
|
|
||||||
<button type="button" bitMenuItem (click)="addFolder()">
|
|
||||||
<i class="bwi bwi-fw bwi-folder" aria-hidden="true"></i>
|
|
||||||
{{ "folder" | i18n }}
|
|
||||||
</button>
|
|
||||||
<button *ngIf="canCreateCollections" type="button" bitMenuItem (click)="addCollection()">
|
|
||||||
<i class="bwi bwi-fw bwi-collection-shared" aria-hidden="true"></i>
|
|
||||||
{{ "collection" | i18n }}
|
|
||||||
</button>
|
|
||||||
</bit-menu>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom, map, shareReplay } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Unassigned,
|
Unassigned,
|
||||||
@@ -18,13 +18,13 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||||
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
|
|
||||||
import {
|
import {
|
||||||
BreadcrumbsModule,
|
BreadcrumbsModule,
|
||||||
DialogService,
|
DialogService,
|
||||||
MenuModule,
|
MenuModule,
|
||||||
SimpleDialogOptions,
|
SimpleDialogOptions,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { NewCipherMenuComponent } from "@bitwarden/vault";
|
||||||
|
|
||||||
import { CollectionDialogTabType } from "../../../admin-console/organizations/shared/components/collection-dialog";
|
import { CollectionDialogTabType } from "../../../admin-console/organizations/shared/components/collection-dialog";
|
||||||
import { HeaderModule } from "../../../layouts/header/header.module";
|
import { HeaderModule } from "../../../layouts/header/header.module";
|
||||||
@@ -46,6 +46,7 @@ import {
|
|||||||
HeaderModule,
|
HeaderModule,
|
||||||
PipesModule,
|
PipesModule,
|
||||||
JslibModule,
|
JslibModule,
|
||||||
|
NewCipherMenuComponent,
|
||||||
],
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
@@ -54,21 +55,6 @@ export class VaultHeaderComponent {
|
|||||||
protected All = All;
|
protected All = All;
|
||||||
protected CollectionDialogTabType = CollectionDialogTabType;
|
protected CollectionDialogTabType = CollectionDialogTabType;
|
||||||
protected CipherType = CipherType;
|
protected CipherType = CipherType;
|
||||||
protected allCipherMenuItems = [
|
|
||||||
{ type: CipherType.Login, icon: "bwi-globe", labelKey: "typeLogin" },
|
|
||||||
{ type: CipherType.Card, icon: "bwi-credit-card", labelKey: "typeCard" },
|
|
||||||
{ type: CipherType.Identity, icon: "bwi-id-card", labelKey: "typeIdentity" },
|
|
||||||
{ type: CipherType.SecureNote, icon: "bwi-sticky-note", labelKey: "note" },
|
|
||||||
{ type: CipherType.SshKey, icon: "bwi-key", labelKey: "typeSshKey" },
|
|
||||||
];
|
|
||||||
protected cipherMenuItems$ = this.restrictedItemTypesService.restricted$.pipe(
|
|
||||||
map((restrictedTypes) => {
|
|
||||||
return this.allCipherMenuItems.filter((item) => {
|
|
||||||
return !restrictedTypes.some((restrictedType) => restrictedType.cipherType === item.type);
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
shareReplay({ bufferSize: 1, refCount: true }),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean to determine the loading state of the header.
|
* Boolean to determine the loading state of the header.
|
||||||
@@ -109,7 +95,6 @@ export class VaultHeaderComponent {
|
|||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private restrictedItemTypesService: RestrictedItemTypesService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -647,6 +647,9 @@
|
|||||||
"typeSecureNote": {
|
"typeSecureNote": {
|
||||||
"message": "Secure note"
|
"message": "Secure note"
|
||||||
},
|
},
|
||||||
|
"typeNote": {
|
||||||
|
"message": "Note"
|
||||||
|
},
|
||||||
"typeSshKey": {
|
"typeSshKey": {
|
||||||
"message": "SSH key"
|
"message": "SSH key"
|
||||||
},
|
},
|
||||||
@@ -8928,7 +8931,7 @@
|
|||||||
},
|
},
|
||||||
"uriMatchDefaultStrategyHint": {
|
"uriMatchDefaultStrategyHint": {
|
||||||
"message": "URI match detection is how Bitwarden identifies autofill suggestions.",
|
"message": "URI match detection is how Bitwarden identifies autofill suggestions.",
|
||||||
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
|
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
|
||||||
},
|
},
|
||||||
"regExAdvancedOptionWarning": {
|
"regExAdvancedOptionWarning": {
|
||||||
"message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.",
|
"message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.",
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<ng-container *ngIf="canCreateCipher || canCreateCollection || canCreateFolder">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
bitButton
|
||||||
|
buttonType="primary"
|
||||||
|
type="button"
|
||||||
|
[bitMenuTriggerFor]="addOptions"
|
||||||
|
id="newItemDropdown"
|
||||||
|
[appA11yTitle]="'new' | i18n"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-plus" aria-hidden="true"></i>
|
||||||
|
{{ "new" | i18n }}
|
||||||
|
</button>
|
||||||
|
<bit-menu #addOptions aria-labelledby="newItemDropdown">
|
||||||
|
@for (item of cipherMenuItems$ | async; track item.type) {
|
||||||
|
<button type="button" bitMenuItem (click)="cipherAdded.emit(item.type)">
|
||||||
|
<i class="bwi {{ item.icon }}" slot="start" aria-hidden="true"></i>
|
||||||
|
{{ item.labelKey | i18n }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<bit-menu-divider *ngIf="canCreateCipher"></bit-menu-divider>
|
||||||
|
<button *ngIf="canCreateFolder" type="button" bitMenuItem (click)="folderAdded.emit()">
|
||||||
|
<i class="bwi bwi-fw bwi-folder" aria-hidden="true"></i>
|
||||||
|
{{ "folder" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
*ngIf="canCreateCollection"
|
||||||
|
type="button"
|
||||||
|
bitMenuItem
|
||||||
|
(click)="collectionAdded.emit()"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-fw bwi-collection-shared" aria-hidden="true"></i>
|
||||||
|
{{ "collection" | i18n }}
|
||||||
|
</button>
|
||||||
|
</bit-menu>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component, input, output } from "@angular/core";
|
||||||
|
import { map, shareReplay } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
|
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
|
||||||
|
import { CIPHER_MENU_ITEMS } from "@bitwarden/common/vault/types/cipher-menu-items";
|
||||||
|
import { ButtonModule, MenuModule } from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "vault-new-cipher-menu",
|
||||||
|
templateUrl: "new-cipher-menu.component.html",
|
||||||
|
imports: [ButtonModule, CommonModule, MenuModule, I18nPipe, JslibModule],
|
||||||
|
})
|
||||||
|
export class NewCipherMenuComponent {
|
||||||
|
canCreateCipher = input(false);
|
||||||
|
canCreateFolder = input(false);
|
||||||
|
canCreateCollection = input(false);
|
||||||
|
folderAdded = output();
|
||||||
|
collectionAdded = output();
|
||||||
|
cipherAdded = output<CipherType>();
|
||||||
|
|
||||||
|
constructor(private restrictedItemTypesService: RestrictedItemTypesService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an observable that emits the cipher menu items, filtered by the restricted types.
|
||||||
|
*/
|
||||||
|
cipherMenuItems$ = this.restrictedItemTypesService.restricted$.pipe(
|
||||||
|
map((restrictedTypes) => {
|
||||||
|
return CIPHER_MENU_ITEMS.filter((item) => {
|
||||||
|
return !restrictedTypes.some((restrictedType) => restrictedType.cipherType === item.type);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
shareReplay({ bufferSize: 1, refCount: true }),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ export { DecryptionFailureDialogComponent } from "./components/decryption-failur
|
|||||||
export { openPasswordHistoryDialog } from "./components/password-history/password-history.component";
|
export { openPasswordHistoryDialog } from "./components/password-history/password-history.component";
|
||||||
export * from "./components/add-edit-folder-dialog/add-edit-folder-dialog.component";
|
export * from "./components/add-edit-folder-dialog/add-edit-folder-dialog.component";
|
||||||
export * from "./components/carousel";
|
export * from "./components/carousel";
|
||||||
|
export * from "./components/new-cipher-menu/new-cipher-menu.component";
|
||||||
|
|
||||||
export * as VaultIcons from "./icons";
|
export * as VaultIcons from "./icons";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user