diff --git a/src/aria/accordion/accordion.ts b/src/aria/accordion/accordion.ts index 0b21b144578f..36edba8fd5a2 100644 --- a/src/aria/accordion/accordion.ts +++ b/src/aria/accordion/accordion.ts @@ -77,6 +77,21 @@ export class AccordionPanel { this._deferredContentAware.contentVisible.set(!this._pattern.hidden()); }); } + + /** Expands this item. */ + expand() { + this.accordionTrigger()?.expansionControl.open(); + } + + /** Collapses this item. */ + collapse() { + this.accordionTrigger()?.expansionControl.close(); + } + + /** Toggles the expansion state of this item. */ + toggle() { + this.accordionTrigger()?.expansionControl.toggle(); + } } /** @@ -136,6 +151,21 @@ export class AccordionTrigger { accordionGroup: computed(() => this._accordionGroup._pattern), accordionPanel: this.accordionPanel, }); + + /** Expands this item. */ + expand() { + this._pattern.expansionControl.open(); + } + + /** Collapses this item. */ + collapse() { + this._pattern.expansionControl.close(); + } + + /** Toggles the expansion state of this item. */ + toggle() { + this._pattern.expansionControl.toggle(); + } } /** @@ -205,6 +235,16 @@ export class AccordionGroup { } }); } + + /** Expands all accordion panels if multi-expandable. */ + expandAll() { + this._pattern.expansionManager.openAll(); + } + + /** Collapses all accordion panels. */ + collapseAll() { + this._pattern.expansionManager.closeAll(); + } } /** diff --git a/src/aria/combobox/combobox.ts b/src/aria/combobox/combobox.ts index b901c88facdc..d5fbbb35a58c 100644 --- a/src/aria/combobox/combobox.ts +++ b/src/aria/combobox/combobox.ts @@ -114,6 +114,21 @@ export class Combobox { } }); } + + /** Opens the combobox. */ + open(nav?: {first?: boolean; last?: boolean; selected?: boolean}) { + this._pattern.open(nav); + } + + /** Closes the combobox. */ + close(opts?: {reset: boolean}) { + this._pattern.close(opts); + } + + /** Selects an item in the combobox popup. */ + select(opts: {item?: V; commit?: boolean; close?: boolean} = {}) { + this._pattern.select(opts); + } } @Directive({ diff --git a/src/aria/menu/menu.ts b/src/aria/menu/menu.ts index 4d7a74b65314..55c230ca5f84 100644 --- a/src/aria/menu/menu.ts +++ b/src/aria/menu/menu.ts @@ -84,6 +84,16 @@ export class MenuTrigger { onFocusIn() { this.hasBeenFocused.set(true); } + + /** Opens the menu. */ + open(opts?: {first?: boolean; last?: boolean}) { + this._pattern.open(opts); + } + + /** Closes the menu. */ + close(opts: {refocus?: boolean} = {}) { + this._pattern.close(opts); + } } /** @@ -223,24 +233,14 @@ export class Menu { }); } - // TODO(wagnermaciel): Author close, closeAll, and open methods for each directive. - /** Closes the menu. */ close(opts?: {refocus?: boolean}) { - this._pattern.inputs.parent()?.close(opts); + this._pattern.close(opts); } /** Closes all parent menus. */ closeAll(opts?: {refocus?: boolean}) { - const root = this._pattern.root(); - - if (root instanceof MenuTriggerPattern) { - root.close(opts); - } - - if (root instanceof MenuPattern || root instanceof MenuBarPattern) { - root.inputs.activeItem()?.close(opts); - } + this._pattern.closeAll(opts); } } @@ -326,6 +326,11 @@ export class MenuBar { } }); } + + /** Closes the menubar and refocuses the root menu bar item. */ + close(opts?: {refocus?: boolean}) { + this._pattern.close(opts); + } } /** @@ -403,6 +408,16 @@ export class MenuItem { onFocusIn() { this.hasBeenFocused.set(true); } + + /** Opens the submenu. */ + open(opts?: {first?: boolean; last?: boolean}) { + this._pattern.open(opts); + } + + /** Closes the submenu. */ + close(opts: {refocus?: boolean} = {}) { + this._pattern.close(opts); + } } /** Defers the rendering of the menu content. */ diff --git a/src/aria/private/menu/menu.ts b/src/aria/private/menu/menu.ts index db8cd421571d..236c48b695a8 100644 --- a/src/aria/private/menu/menu.ts +++ b/src/aria/private/menu/menu.ts @@ -136,7 +136,7 @@ export class MenuPattern { .on('Home', () => this.first()) .on('End', () => this.last()) .on('Enter', () => this.trigger()) - .on('Escape', () => this.closeAll()) + .on('Escape', () => this.closeAll({refocus: true})) .on(this._expandKey, () => this.expand()) .on(this._collapseKey, () => this.collapse()) .on(this.dynamicSpaceKey, () => this.trigger()) @@ -345,12 +345,17 @@ export class MenuPattern { } } + /** Closes the menu. */ + close(opts?: {refocus?: boolean}) { + this.inputs.parent()?.close(opts); + } + /** Closes the menu and all parent menus. */ - closeAll() { + closeAll(opts?: {refocus?: boolean}) { const root = this.root(); if (root instanceof MenuTriggerPattern) { - root.close({refocus: true}); + root.close(opts); } if (root instanceof MenuBarPattern) { @@ -358,7 +363,7 @@ export class MenuPattern { } if (root instanceof MenuPattern) { - root.inputs.activeItem()?.close({refocus: true}); + root.inputs.activeItem()?.close(opts); } } } @@ -496,8 +501,10 @@ export class MenuBarPattern { } /** Closes the menubar and refocuses the root menu bar item. */ - close() { - this.inputs.activeItem()?.close({refocus: this.isFocused()}); + close(opts?: {refocus?: boolean}) { + opts ??= {refocus: this.isFocused()}; + + this.inputs.activeItem()?.close(opts); } } diff --git a/src/aria/tabs/tabs.ts b/src/aria/tabs/tabs.ts index 9fc4bf5ff096..cbe4ead50c4a 100644 --- a/src/aria/tabs/tabs.ts +++ b/src/aria/tabs/tabs.ts @@ -112,6 +112,17 @@ export class Tabs { this._unorderedPanels.set(new Set(this._unorderedPanels())); } } + + /** Opens the tab panel with the specified value. */ + open(value: string) { + const tab = this._findTabPatternByValue(value); + + tab?.expansion.open(); + } + + _findTabPatternByValue(value: string) { + return this.tabs()?.find(t => t.value() === value); + } } /** @@ -277,6 +288,11 @@ export class Tab implements HasElement, OnInit, OnDestroy { value: this.value, }); + /** Opens this tab panel. */ + open() { + this._pattern.expansion.open(); + } + ngOnInit() { this._tabList.register(this); } diff --git a/src/aria/tree/tree.ts b/src/aria/tree/tree.ts index 6e9d679018a8..3928ee63311c 100644 --- a/src/aria/tree/tree.ts +++ b/src/aria/tree/tree.ts @@ -210,6 +210,16 @@ export class Tree { scrollActiveItemIntoView(options: ScrollIntoViewOptions = {block: 'nearest'}) { this._pattern.inputs.activeItem()?.element().scrollIntoView(options); } + + /** Expands all tree items if multi-expandable. */ + expandAll() { + this._pattern.expansionManager.openAll(); + } + + /** Collapses all tree items. */ + collapseAll() { + this._pattern.expansionManager.closeAll(); + } } /**