diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/BUILD.bazel
index ede65a4f3f0..2d9dc845e79 100644
--- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/BUILD.bazel
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/BUILD.bazel
@@ -6,7 +6,6 @@ sass_binary(
name = "property-view-body_styles",
src = "property-view-body.component.scss",
deps = [
- "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:prop-action-btn",
"//devtools/projects/ng-devtools/src/styles:typography",
],
)
@@ -23,8 +22,8 @@ ng_project(
deps = [
"//:node_modules/@angular/core",
"//:node_modules/@angular/material",
- "//devtools/projects/ng-devtools/src/lib/application-providers:supported_apis",
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/dependency-viewer",
+ "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu",
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver",
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-manager",
"//devtools/projects/ng-devtools/src/lib/shared/docs-ref-button",
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/BUILD.bazel
new file mode 100644
index 00000000000..9e47be13e5b
--- /dev/null
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/BUILD.bazel
@@ -0,0 +1,43 @@
+load("//devtools/tools:defaults.bzl", "ng_project", "sass_binary", "sass_library")
+
+package(default_visibility = ["//devtools:__subpackages__"])
+
+sass_binary(
+ name = "prop-actions-menu_styles",
+ src = "prop-actions-menu.component.scss",
+ deps = [
+ "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:prop-action-btn",
+ "//devtools/projects/ng-devtools/src/styles:typography",
+ ],
+)
+
+ng_project(
+ name = "prop-actions-menu",
+ srcs = [
+ "prop-actions-menu.component.ts",
+ ],
+ angular_assets = [
+ "prop-actions-menu.component.html",
+ ":prop-actions-menu_styles",
+ ],
+ deps = [
+ "//:node_modules/@angular/aria",
+ "//:node_modules/@angular/cdk",
+ "//:node_modules/@angular/core",
+ "//:node_modules/@angular/material",
+ "//devtools/projects/ng-devtools/src/lib/application-providers:supported_apis",
+ "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver",
+ "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-manager",
+ "//devtools/projects/ng-devtools/src/lib/shared/object-tree-explorer:types",
+ "//devtools/projects/ng-devtools/src/lib/shared/signal-graph",
+ "//devtools/projects/protocol",
+ ],
+)
+
+sass_library(
+ name = "prop-actions-menu-cdk-styles",
+ srcs = ["_prop-actions-menu-cdk.scss"],
+ deps = [
+ "//devtools/projects/ng-devtools/src/styles:typography",
+ ],
+)
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/_prop-actions-menu-cdk.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/_prop-actions-menu-cdk.scss
new file mode 100644
index 00000000000..5765988c43f
--- /dev/null
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/_prop-actions-menu-cdk.scss
@@ -0,0 +1,32 @@
+@use '../../../../../../../styles/typography';
+
+/*
+ * The file contains the styles intended for the CDK Overlay content
+ * meaning they should be loaded in the global styles file.
+ */
+
+.ng-ctx-menu-item {
+ @extend %body-01;
+ width: 100%;
+ display: flex;
+ align-items: center;
+ display: flex;
+ justify-content: flex-start;
+ background: transparent;
+ border: none;
+ gap: 0.25rem;
+ text-align: left;
+ white-space: nowrap;
+ padding: 0.2rem 0.25rem;
+ cursor: pointer;
+
+ &:hover {
+ background-color: var(--quinary-contrast);
+ }
+
+ mat-icon {
+ width: 16px;
+ height: 16px;
+ font-size: 16px;
+ }
+}
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.html
new file mode 100644
index 00000000000..7d548cc9d37
--- /dev/null
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.html
@@ -0,0 +1,85 @@
+@let availableActions = this.availableActions();
+
+@if (availableActionsCount() === 1) {
+ @if (availableActions.showSignalGraph) {
+ @let graphBtnTooltip = `Show '${node().prop.name}' signal graph`;
+
+
+ }
+
+ @if (availableActions.logToConsole) {
+ @let tooltip = "Log '" + node().prop.name + "' to console";
+
+
+ }
+} @else if (availableActionsCount() > 1) {
+
+
+
+
+
+}
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.scss b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.scss
new file mode 100644
index 00000000000..5a539b58c9b
--- /dev/null
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.scss
@@ -0,0 +1,25 @@
+@use '../../../../../../shared/object-tree-explorer/prop-action-btn';
+
+/* IMPORTANT: The width is based on the current longest menu item. */
+$ctx-menu-width: 145px;
+
+:host {
+ display: inline;
+
+ .show-signal {
+ mat-icon {
+ width: 14px;
+ height: 14px;
+ font-size: 14px;
+ }
+ }
+
+ .ctx-menu {
+ border: 1px solid var(--quinary-contrast);
+ background-color: var(--senary-contrast);
+ box-shadow: 0 0 5px var(--transparent-03);
+ border-radius: 0.25rem;
+ overflow: hidden;
+ min-width: $ctx-menu-width;
+ }
+}
diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.ts
new file mode 100644
index 00000000000..116981c6d66
--- /dev/null
+++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-pane/property-view/property-view-body/prop-actions-menu/prop-actions-menu.component.ts
@@ -0,0 +1,219 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.dev/license
+ */
+
+import {
+ afterRenderEffect,
+ AfterRenderRef,
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ effect,
+ ElementRef,
+ inject,
+ Injector,
+ input,
+ output,
+ Renderer2,
+ untracked,
+ viewChild,
+} from '@angular/core';
+import {MatIcon} from '@angular/material/icon';
+import {MatTooltip} from '@angular/material/tooltip';
+import {MatSnackBar} from '@angular/material/snack-bar';
+import {Menu, MenuContent, MenuItem, MenuTrigger} from '@angular/aria/menu';
+import {CdkConnectedOverlay, ConnectedPosition} from '@angular/cdk/overlay';
+
+import {FlatNode} from '../../../../../../shared/object-tree-explorer/object-tree-types';
+import {SUPPORTED_APIS} from '../../../../../../application-providers/supported_apis';
+import {DirectivePropertyResolver} from '../../../../property-resolver/directive-property-resolver';
+import {SignalGraphManager} from '../../../../signal-graph-manager/signal-graph-manager';
+import {DevtoolsSignalGraphNode} from '../../../../../../shared/signal-graph';
+
+// Based on the current design.
+// Update accordingly if that changes.
+const CTX_MENU_ITEM_HEIGHT = 22;
+
+const CTX_MENU_POSITIONS: ConnectedPosition[] = [
+ {
+ // Primary: bottom-left
+ originX: 'start',
+ originY: 'bottom',
+ overlayX: 'start',
+ overlayY: 'top',
+ offsetY: 4,
+ },
+ {
+ // Fallback: bottom-right
+ originX: 'end',
+ originY: 'bottom',
+ overlayX: 'end',
+ overlayY: 'top',
+ offsetY: 4,
+ },
+ {
+ // Fallback: top-left
+ originX: 'start',
+ originY: 'top',
+ overlayX: 'start',
+ overlayY: 'bottom',
+ offsetY: -4,
+ },
+ {
+ // Fallback: top-right
+ originX: 'end',
+ originY: 'top',
+ overlayX: 'end',
+ overlayY: 'bottom',
+ offsetY: -4,
+ },
+];
+
+interface AvailableActions {
+ logToConsole: boolean;
+ showSignalGraph: boolean;
+}
+
+@Component({
+ selector: 'ng-prop-actions-menu',
+ templateUrl: './prop-actions-menu.component.html',
+ styleUrl: './prop-actions-menu.component.scss',
+ imports: [MatTooltip, MatIcon, Menu, MenuContent, MenuItem, MenuTrigger, CdkConnectedOverlay],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class PropActionsMenuComponent {
+ private readonly signalGraph = inject(SignalGraphManager);
+ private readonly snackBar = inject(MatSnackBar);
+ private readonly supportedApis = inject(SUPPORTED_APIS);
+ private readonly elementRef = inject(ElementRef);
+ private readonly renderer = inject(Renderer2);
+ private readonly injector = inject(Injector);
+
+ protected readonly ctxMenu = viewChild