diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html index 60214a9fd61..dc5d4c7929e 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.html @@ -103,48 +103,11 @@ *ngIf="filter.type !== 'trash' && filter.collectionId !== Unassigned && organization" class="tw-shrink-0" > - - -
- - - - - - - - - - - - - -
-
+ diff --git a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts index b343d5874bc..121a5c03ffe 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault-header/vault-header.component.ts @@ -25,6 +25,7 @@ import { SearchModule, SimpleDialogOptions, } from "@bitwarden/components"; +import { NewCipherMenuComponent } from "@bitwarden/vault"; import { HeaderModule } from "../../../../layouts/header/header.module"; import { SharedModule } from "../../../../shared"; @@ -45,6 +46,7 @@ import { CollectionDialogTabType } from "../../shared/components/collection-dial HeaderModule, SearchModule, JslibModule, + NewCipherMenuComponent, ], }) export class VaultHeaderComponent { diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index 3793db6f76a..18dfa73ac5a 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -510,7 +510,7 @@ export class VaultItemsComponent { private compareNames(a: VaultItem, b: VaultItem): number { const getName = (item: VaultItem) => item.collection?.name || item.cipher?.name; - return getName(a).localeCompare(getName(b)); + return getName(a)?.localeCompare(getName(b)) ?? -1; } /** diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html index 4ef8204cdfc..711d1166d7b 100644 --- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html +++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html @@ -68,35 +68,13 @@
-
- - - @for (item of cipherMenuItems$ | async; track item.type) { - - } - - - - -
+
diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts index 49e159143dd..f4f3ba32428 100644 --- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts @@ -3,7 +3,7 @@ import { CommonModule } from "@angular/common"; import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core"; import { Router } from "@angular/router"; -import { firstValueFrom, map, shareReplay } from "rxjs"; +import { firstValueFrom } from "rxjs"; import { Unassigned, @@ -18,13 +18,13 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; -import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { BreadcrumbsModule, DialogService, MenuModule, SimpleDialogOptions, } from "@bitwarden/components"; +import { NewCipherMenuComponent } from "@bitwarden/vault"; import { CollectionDialogTabType } from "../../../admin-console/organizations/shared/components/collection-dialog"; import { HeaderModule } from "../../../layouts/header/header.module"; @@ -46,6 +46,7 @@ import { HeaderModule, PipesModule, JslibModule, + NewCipherMenuComponent, ], changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -54,21 +55,6 @@ export class VaultHeaderComponent { protected All = All; protected CollectionDialogTabType = CollectionDialogTabType; 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. @@ -109,7 +95,6 @@ export class VaultHeaderComponent { private dialogService: DialogService, private router: Router, private configService: ConfigService, - private restrictedItemTypesService: RestrictedItemTypesService, ) {} /** diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index b272dc32e3b..a58126adac5 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -647,6 +647,9 @@ "typeSecureNote": { "message": "Secure note" }, + "typeNote": { + "message": "Note" + }, "typeSshKey": { "message": "SSH key" }, @@ -8928,7 +8931,7 @@ }, "uriMatchDefaultStrategyHint": { "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": { "message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.", diff --git a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.html b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.html new file mode 100644 index 00000000000..38b5875a605 --- /dev/null +++ b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.html @@ -0,0 +1,37 @@ + +
+ + + @for (item of cipherMenuItems$ | async; track item.type) { + + } + + + + +
+
diff --git a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts new file mode 100644 index 00000000000..eb3194bc5f0 --- /dev/null +++ b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts @@ -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(); + + 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 }), + ); +} diff --git a/libs/vault/src/index.ts b/libs/vault/src/index.ts index b39bb85ab30..9d037d8fe5e 100644 --- a/libs/vault/src/index.ts +++ b/libs/vault/src/index.ts @@ -19,6 +19,7 @@ export { DecryptionFailureDialogComponent } from "./components/decryption-failur export { openPasswordHistoryDialog } from "./components/password-history/password-history.component"; export * from "./components/add-edit-folder-dialog/add-edit-folder-dialog.component"; export * from "./components/carousel"; +export * from "./components/new-cipher-menu/new-cipher-menu.component"; export * as VaultIcons from "./icons";