From 8295dc0295ae3b9ef7007bbb897361f4ac26c51f Mon Sep 17 00:00:00 2001 From: Andrey Dolgachev Date: Tue, 11 Nov 2025 17:47:15 -0800 Subject: [PATCH] refactor(menu): add methods for contextual menu --- src/aria/menu/menu.ts | 5 ++++ src/aria/private/menu/menu.ts | 23 +++++++++++++++++-- .../menu/menu-context/menu-context-example.ts | 7 ++---- .../aria/menu/menu-example.css | 1 - 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/aria/menu/menu.ts b/src/aria/menu/menu.ts index 18d023bc8422..caaaad04295d 100644 --- a/src/aria/menu/menu.ts +++ b/src/aria/menu/menu.ts @@ -230,6 +230,11 @@ export class Menu { }); } + /** Opens the menu. */ + open() { + this._pattern.open(); + } + /** Closes the menu. */ close() { this._pattern.close(); diff --git a/src/aria/private/menu/menu.ts b/src/aria/private/menu/menu.ts index 6c81493cb881..55782ac19c0b 100644 --- a/src/aria/private/menu/menu.ts +++ b/src/aria/private/menu/menu.ts @@ -75,7 +75,9 @@ export class MenuPattern { role = () => 'menu'; /** Whether the menu is visible. */ - isVisible = computed(() => (this.inputs.parent() ? !!this.inputs.parent()?.expanded() : true)); + isVisible = computed(() => + this.inputs.parent() ? !!this.inputs.parent()?.expanded() : this._visible(), + ); /** Controls list behavior for the menu items. */ listBehavior: List, V>; @@ -86,6 +88,8 @@ export class MenuPattern { /** Whether the menu has received focus. */ hasBeenFocused = signal(false); + _visible = signal(false); + /** Timeout used to open sub-menus on hover. */ _openTimeout: any; @@ -395,9 +399,24 @@ export class MenuPattern { } } + /** Opens the menu. */ + open() { + if (this.inputs.parent()) { + this.inputs.parent()!.close(); + } else { + this._visible.set(true); + + this.first(); + } + } + /** Closes the menu. */ close() { - this.inputs.parent()?.close(); + if (this.inputs.parent()) { + this.inputs.parent()!.close(); + } else { + this._visible.set(false); + } } /** Closes the menu and all parent menus. */ diff --git a/src/components-examples/aria/menu/menu-context/menu-context-example.ts b/src/components-examples/aria/menu/menu-context/menu-context-example.ts index a484b5f3f5da..d9ed909fbcd0 100644 --- a/src/components-examples/aria/menu/menu-context/menu-context-example.ts +++ b/src/components-examples/aria/menu/menu-context/menu-context-example.ts @@ -30,23 +30,20 @@ export class MenuContextExample { const relatedTarget = event.relatedTarget as HTMLElement | null; if (menu && !menu.element.contains(relatedTarget)) { - menu.close(); - menu.element.style.visibility = 'hidden'; + menu!.close(); } } open(event: MouseEvent) { const menu = this.menu(); - menu?._pattern.closeAll(); if (menu) { event.preventDefault(); - menu.element.style.visibility = 'visible'; menu.element.style.top = `${event.clientY}px`; menu.element.style.left = `${event.clientX}px`; - setTimeout(() => menu._pattern.first()); + menu.open(); } } } diff --git a/src/components-examples/aria/menu/menu-example.css b/src/components-examples/aria/menu/menu-example.css index 945ebe261fff..30b4a8ae3d56 100644 --- a/src/components-examples/aria/menu/menu-example.css +++ b/src/components-examples/aria/menu/menu-example.css @@ -126,5 +126,4 @@ .example-context-menu { position: absolute; - visibility: hidden; }