+
+
+
+
+
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 df89238f966..c3d6d4bcff3 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
@@ -1,5 +1,13 @@
import { CommonModule } from "@angular/common";
-import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
+import {
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ EventEmitter,
+ inject,
+ Input,
+ Output,
+} from "@angular/core";
import { Router } from "@angular/router";
import { firstValueFrom, switchMap } from "rxjs";
@@ -29,6 +37,7 @@ import { NewCipherMenuComponent, All, RoutedVaultFilterModel } from "@bitwarden/
import { CollectionDialogTabType } from "../../../admin-console/organizations/shared/components/collection-dialog";
import { HeaderModule } from "../../../layouts/header/header.module";
import { SharedModule } from "../../../shared";
+import { CoachmarkComponent, CoachmarkService } from "../../components/coachmark";
import { PipesModule } from "../pipes/pipes.module";
@Component({
@@ -43,6 +52,7 @@ import { PipesModule } from "../pipes/pipes.module";
PipesModule,
JslibModule,
NewCipherMenuComponent,
+ CoachmarkComponent,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
@@ -52,6 +62,13 @@ export class VaultHeaderComponent {
protected readonly CollectionDialogTabType = CollectionDialogTabType;
protected readonly CipherType = CipherType;
+ protected readonly coachmarkService = inject(CoachmarkService);
+
+ /** Computed signal for add item coachmark open state */
+ protected readonly addItemCoachmarkOpen = computed(
+ () => this.coachmarkService.activeStepId() === "addItem",
+ );
+
/**
* Boolean to determine the loading state of the header.
* Shows a loading spinner if set to true
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index dfe86618966..60f9c5dadb1 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -13048,5 +13048,45 @@
"example": "Jan 1, 1970"
}
}
+ },
+ "of": {
+ "message": "of"
+ },
+ "coachmarkImportTitle": {
+ "message": "Quickly import your passwords"
+ },
+ "coachmarkImportDescription": {
+ "message": "Did you have a password manager before? You can import a CSV of existing logins and other data into your vault."
+ },
+ "coachmarkAddItemTitle": {
+ "message": "Add an item"
+ },
+ "coachmarkAddItemDescription": {
+ "message": "Add new passwords, notes, and other info to your vault. Everything is protected with end-to-end encryption."
+ },
+ "coachmarkShareWithCollectionsTitle": {
+ "message": "Safely share items with others"
+ },
+ "coachmarkShareWithCollectionsDescription": {
+ "message": "By moving an item into a collection, you can securely share it with other people in your organization."
+ },
+ "coachmarkMonitorSecurityTitle": {
+ "message": "Monitor your password security"
+ },
+ "coachmarkMonitorSecurityDescription": {
+ "message": "Keep the security of your vault items strong by checking reports for vulnerabilities."
+ },
+ "coachmarkStepIndicator": {
+ "message": "Step $CURRENT$ of $TOTAL$",
+ "placeholders": {
+ "current": {
+ "content": "$1",
+ "example": "1"
+ },
+ "total": {
+ "content": "$2",
+ "example": "4"
+ }
+ }
}
}
diff --git a/libs/components/src/popover/popover-anchor-for.directive.ts b/libs/components/src/popover/popover-anchor-for.directive.ts
index 27c0d003734..6792e974370 100644
--- a/libs/components/src/popover/popover-anchor-for.directive.ts
+++ b/libs/components/src/popover/popover-anchor-for.directive.ts
@@ -57,6 +57,8 @@ export class PopoverAnchorForDirective implements OnDestroy {
/** The popover component to display */
readonly popover = input.required
({ alias: "bitPopoverAnchorFor" });
+ readonly closeOnBackdropClick = input(true);
+
/** Preferred popover position (e.g., "right-start", "below-center") */
readonly position = input();
@@ -171,8 +173,10 @@ export class PopoverAnchorForDirective implements OnDestroy {
const detachments = this.overlayRef.detachments();
const escKey = this.overlayRef
.keydownEvents()
- .pipe(filter((event: KeyboardEvent) => event.key === "Escape"));
- const backdrop = this.overlayRef.backdropClick().pipe(filter(() => !this.spotlight()));
+ .pipe(filter((event: KeyboardEvent) => event.key === "Escape" && !this.spotlight()));
+ const backdrop = this.overlayRef
+ .backdropClick()
+ .pipe(filter(() => !this.spotlight() && this.closeOnBackdropClick()));
const popoverClosed = this.popover().closed;
return detachments.pipe(mergeWith(escKey, backdrop, popoverClosed));
diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css
index 1327c5d0476..98ecb8a76a9 100644
--- a/libs/components/src/tw-theme.css
+++ b/libs/components/src/tw-theme.css
@@ -517,7 +517,7 @@
/* Hover & Overlay */
--color-bg-hover: rgba(var(--color-white-rgb), 0.05);
- --color-bg-overlay: rgba(var(--color-gray-950-rgb), 0.85);
+ --color-bg-overlay: rgba(var(--color-gray-950-rgb), 0.65);
/* ========================================
* SEMANTIC BORDER COLORS (Dark Mode Overrides)
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
index d816b69bc58..5b70b432656 100644
--- 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
@@ -8,6 +8,11 @@
(click)="handleButtonClick()"
id="newItemDropdown"
[appA11yTitle]="getButtonLabel() | i18n"
+ [bitPopoverAnchorFor]="coachmarkPopover()"
+ [popoverOpen]="coachmarkPopoverOpen()"
+ [position]="coachmarkPosition()"
+ [spotlight]="!!coachmarkPopover()"
+ [closeOnBackdropClick]="false"
>
{{ getButtonLabel() | i18n }}
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
index 1a592809691..76204a131aa 100644
--- 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
@@ -7,7 +7,13 @@ 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 {
+ ButtonModule,
+ MenuModule,
+ PopoverComponent,
+ PopoverModule,
+ PositionIdentifier,
+} from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -15,7 +21,7 @@ import { I18nPipe } from "@bitwarden/ui-common";
@Component({
selector: "vault-new-cipher-menu",
templateUrl: "new-cipher-menu.component.html",
- imports: [ButtonModule, CommonModule, MenuModule, I18nPipe, JslibModule],
+ imports: [ButtonModule, CommonModule, MenuModule, PopoverModule, I18nPipe, JslibModule],
})
export class NewCipherMenuComponent {
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
@@ -30,6 +36,14 @@ export class NewCipherMenuComponent {
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
canCreateSshKey = input(false);
+
+ /** Optional popover to anchor to the "New" button for coachmark tours */
+ readonly coachmarkPopover = input();
+ /** Whether the coachmark popover is open */
+ readonly coachmarkPopoverOpen = input(false);
+ /** Popover position */
+ readonly coachmarkPosition = input();
+
folderAdded = output();
collectionAdded = output();
cipherAdded = output();