Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/aria/accordion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
'role': 'region',
'[attr.id]': '_pattern.id()',
'[attr.aria-labelledby]': '_pattern.accordionTrigger()?.id()',
'[attr.inert]': '_pattern.hidden() ? true : null',
'[attr.inert]': '!visible() ? true : null',
},
})
export class AccordionPanel {
Expand All @@ -60,6 +60,9 @@ export class AccordionPanel {
/** A local unique identifier for the panel, used to match with its trigger's value. */
value = input.required<string>();

/** Whether the accordion panel is visible. True if the associated trigger is expanded. */
readonly visible = computed(() => !this._pattern.hidden());

/** The parent accordion trigger pattern that controls this panel. This is set by AccordionGroup. */
readonly accordionTrigger: WritableSignal<AccordionTriggerPattern | undefined> =
signal(undefined);
Expand All @@ -74,7 +77,7 @@ export class AccordionPanel {
constructor() {
// Connect the panel's hidden state to the DeferredContentAware's visibility.
afterRenderEffect(() => {
this._deferredContentAware.contentVisible.set(!this._pattern.hidden());
this._deferredContentAware.contentVisible.set(this.visible());
});
}
}
Expand All @@ -88,10 +91,10 @@ export class AccordionPanel {
exportAs: 'ngAccordionTrigger',
host: {
'class': 'ng-accordion-trigger',
'[attr.data-active]': '_pattern.active()',
'[attr.data-active]': 'active()',
'role': 'button',
'[id]': '_pattern.id()',
'[attr.aria-expanded]': '_pattern.expanded()',
'[attr.aria-expanded]': 'expanded()',
'[attr.aria-controls]': '_pattern.controls()',
'[attr.aria-disabled]': '_pattern.disabled()',
'[attr.disabled]': 'hardDisabled() ? true : null',
Expand All @@ -117,6 +120,12 @@ export class AccordionTrigger {
/** Whether the trigger is disabled. */
disabled = input(false, {transform: booleanAttribute});

/** Whether the trigger is active. */
readonly active = computed(() => this._pattern.active());

/** Whether the trigger is expanded. */
readonly expanded = computed(() => this._pattern.expanded());

/**
* Whether this trigger is completely inaccessible.
*
Expand Down
3 changes: 3 additions & 0 deletions src/aria/combobox/combobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ export class Combobox<V> {
/** Whether the combobox is expanded. */
readonly expanded = computed(() => this._pattern.expanded());

/** The currently highlighted item in the combobox. */
readonly highlightedItem = computed(() => this._pattern.highlightedItem());

/** Input element connected to the combobox, if any. */
readonly inputElement = computed(() => this._pattern.inputs.inputEl());

Expand Down
15 changes: 15 additions & 0 deletions src/aria/grid/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,21 @@ export class Grid {
/** The wrapping behavior for keyboard navigation along the column axis. */
readonly colWrap = input<'continuous' | 'loop' | 'nowrap'>('loop');

/** Whether multiple cells in the grid can be selected. */
readonly multi = input(false, {transform: booleanAttribute});

/** The selection strategy used by the grid. */
readonly selectionMode = input<'follow' | 'explicit'>('follow');

/** Whether enable range selections (with modifier keys or dragging). */
readonly enableRangeSelection = input(false, {transform: booleanAttribute});

/** Whether the user is currently dragging to select a range of cells. */
readonly dragging = computed(() => this._pattern.dragging());

/** Whether the focus is in the grid. */
readonly isFocused = computed(() => this._pattern.isFocused());

/** The UI pattern for the grid. */
readonly _pattern = new GridPattern({
...this,
Expand Down
39 changes: 30 additions & 9 deletions src/aria/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ import {Directionality} from '@angular/cdk/bidi';
exportAs: 'ngMenuTrigger',
host: {
'class': 'ng-menu-trigger',
'[attr.tabindex]': '_pattern.tabindex()',
'[attr.aria-haspopup]': '_pattern.hasPopup()',
'[attr.aria-expanded]': '_pattern.expanded()',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.aria-haspopup]': 'hasPopup()',
'[attr.aria-expanded]': 'expanded()',
'[attr.aria-controls]': '_pattern.menu()?.id()',
'(click)': '_pattern.onClick()',
'(keydown)': '_pattern.onKeydown($event)',
Expand All @@ -70,6 +70,12 @@ export class MenuTrigger<V> {
/** Whether the menu item has been focused. */
readonly hasBeenFocused = signal(false);

/** Whether the menu is expanded. */
readonly expanded = computed(() => this._pattern.expanded());

/** Whether the menu trigger has a popup. */
readonly hasPopup = computed(() => this._pattern.hasPopup());

/** The menu trigger ui pattern instance. */
_pattern: MenuTriggerPattern<V> = new MenuTriggerPattern({
element: computed(() => this._elementRef.nativeElement),
Expand Down Expand Up @@ -109,7 +115,7 @@ export class MenuTrigger<V> {
'role': 'menu',
'class': 'ng-menu',
'[attr.id]': '_pattern.id()',
'[attr.data-visible]': '_pattern.isVisible()',
'[attr.data-visible]': 'isVisible()',
'(keydown)': '_pattern.onKeydown($event)',
'(mouseover)': '_pattern.onMouseOver($event)',
'(mouseout)': '_pattern.onMouseOut($event)',
Expand Down Expand Up @@ -174,8 +180,11 @@ export class Menu<V> {
*/
readonly items = () => this._items().map(i => i._pattern);

/** Whether the menu or any of its child elements are currently focused. */
readonly isFocused = computed(() => this._pattern.isFocused());

/** Whether the menu is visible. */
isVisible = computed(() => this._pattern.isVisible());
readonly isVisible = computed(() => this._pattern.isVisible());

/** A callback function triggered when a menu item is selected. */
onSelect = output<V>();
Expand Down Expand Up @@ -294,6 +303,9 @@ export class MenuBar<V> {
/** The delay in seconds before the typeahead buffer is cleared. */
readonly typeaheadDelay = input<number>(0.5);

/** Whether the menubar or any of its child elements are currently focused. */
readonly isFocused = computed(() => this._pattern.isFocused());

/** The menu ui pattern instance. */
readonly _pattern: MenuBarPattern<V>;

Expand Down Expand Up @@ -340,10 +352,10 @@ export class MenuBar<V> {
'role': 'menuitem',
'class': 'ng-menu-item',
'(focusin)': 'onFocusIn()',
'[attr.tabindex]': '_pattern.tabindex()',
'[attr.data-active]': '_pattern.isActive()',
'[attr.aria-haspopup]': '_pattern.hasPopup()',
'[attr.aria-expanded]': '_pattern.expanded()',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.data-active]': 'isActive()',
'[attr.aria-haspopup]': 'hasPopup()',
'[attr.aria-expanded]': 'expanded()',
'[attr.aria-disabled]': '_pattern.disabled()',
'[attr.aria-controls]': '_pattern.submenu()?.id()',
},
Expand Down Expand Up @@ -381,9 +393,18 @@ export class MenuItem<V> {
/** The submenu associated with the menu item. */
readonly submenu = input<Menu<V> | undefined>(undefined);

/** Whether the menu item is active. */
readonly isActive = computed(() => this._pattern.isActive());

/** Whether the menu item has been focused. */
readonly hasBeenFocused = signal(false);

/** Whether the menuis expanded. */
readonly expanded = computed(() => this._pattern.expanded());

/** Whether the menu item has a popup. */
readonly hasPopup = computed(() => this._pattern.hasPopup());

/** The menu item ui pattern instance. */
readonly _pattern: MenuItemPattern<V> = new MenuItemPattern<V>({
id: this.id,
Expand Down
2 changes: 1 addition & 1 deletion src/aria/private/tree/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export class TreePattern<V> {
/** The root is always expanded. */
readonly expanded = () => true;

/** The roow is always visible. */
/** The root is always visible. */
readonly visible = () => true;

/** The tabindex of the tree. */
Expand Down
24 changes: 18 additions & 6 deletions src/aria/tabs/tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,10 @@ export class TabList implements OnInit, OnDestroy {
host: {
'role': 'tab',
'class': 'ng-tab',
'[attr.data-active]': '_pattern.active()',
'[attr.data-active]': 'active()',
'[attr.id]': '_pattern.id()',
'[attr.tabindex]': '_pattern.tabindex()',
'[attr.aria-selected]': '_pattern.selected()',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.aria-selected]': 'selected()',
'[attr.aria-disabled]': '_pattern.disabled()',
'[attr.aria-controls]': '_pattern.controls()',
},
Expand Down Expand Up @@ -268,6 +268,15 @@ export class Tab implements HasElement, OnInit, OnDestroy {
/** A local unique identifier for the tab. */
readonly value = input.required<string>();

/** Whether the tab is active. */
readonly active = computed(() => this._pattern.active());

/** Whether the tab is expanded. */
readonly expanded = computed(() => this._pattern.expanded());

/** Whether the tab is selected. */
readonly selected = computed(() => this._pattern.selected());

/** The Tab UIPattern. */
readonly _pattern: TabPattern = new TabPattern({
...this,
Expand Down Expand Up @@ -301,8 +310,8 @@ export class Tab implements HasElement, OnInit, OnDestroy {
'role': 'tabpanel',
'class': 'ng-tabpanel',
'[attr.id]': '_pattern.id()',
'[attr.tabindex]': '_pattern.tabindex()',
'[attr.inert]': '_pattern.hidden() ? true : null',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.inert]': '!visible() ? true : null',
'[attr.aria-labelledby]': '_pattern.labelledBy()',
},
hostDirectives: [
Expand All @@ -328,6 +337,9 @@ export class TabPanel implements OnInit, OnDestroy {
/** A local unique identifier for the tabpanel. */
readonly value = input.required<string>();

/** Whether the tab panel is visible. */
readonly visible = computed(() => !this._pattern.hidden());

/** The TabPanel UIPattern. */
readonly _pattern: TabPanelPattern = new TabPanelPattern({
...this,
Expand All @@ -336,7 +348,7 @@ export class TabPanel implements OnInit, OnDestroy {
});

constructor() {
afterRenderEffect(() => this._deferredContentAware.contentVisible.set(!this._pattern.hidden()));
afterRenderEffect(() => this._deferredContentAware.contentVisible.set(this.visible()));
}

ngOnInit() {
Expand Down
19 changes: 17 additions & 2 deletions src/aria/toolbar/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ export class Toolbar<V> {
exportAs: 'ngToolbarWidget',
host: {
'class': 'ng-toolbar-widget',
'[attr.data-active]': '_pattern.active()',
'[attr.tabindex]': '_pattern.tabindex()',
'[attr.data-active]': 'active()',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.inert]': 'hardDisabled() ? true : null',
'[attr.disabled]': 'hardDisabled() ? true : null',
'[attr.aria-disabled]': '_pattern.disabled()',
Expand Down Expand Up @@ -202,6 +202,21 @@ export class ToolbarWidget<V> implements OnInit, OnDestroy {
/** Whether the widget is 'hard' disabled, which is different from `aria-disabled`. A hard disabled widget cannot receive focus. */
readonly hardDisabled = computed(() => this._pattern.disabled() && !this._toolbar.softDisabled());

/** The optional ToolbarWidgetGroup this widget belongs to. */
readonly _group = inject(ToolbarWidgetGroup, {optional: true});

/** The value associated with the widget. */
readonly value = input.required<V>();

/** Whether the widget is currently the active one (focused). */
readonly active = computed(() => this._pattern.active());

/** Whether the widget is selected (only relevant in a selection group). */
readonly selected = () => this._pattern.selected();

readonly group: SignalLike<ToolbarWidgetGroupPattern<ToolbarWidgetPattern<V>, V> | undefined> =
() => this._group?._pattern;

/** The ToolbarWidget UIPattern. */
readonly _pattern = new ToolbarWidgetPattern<V>({
...this,
Expand Down
25 changes: 21 additions & 4 deletions src/aria/tree/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,14 @@ export class Tree<V> {
exportAs: 'ngTreeItem',
host: {
'class': 'ng-treeitem',
'[attr.data-active]': '_pattern.active()',
'[attr.data-active]': 'active()',
'role': 'treeitem',
'[id]': '_pattern.id()',
'[attr.aria-expanded]': '_pattern.expandable() ? _pattern.expanded() : null',
'[attr.aria-selected]': '_pattern.selected()',
'[attr.aria-expanded]': 'expanded()',
'[attr.aria-selected]': 'selected()',
'[attr.aria-current]': '_pattern.current()',
'[attr.aria-disabled]': '_pattern.disabled()',
'[attr.aria-level]': '_pattern.level()',
'[attr.aria-level]': 'level()',
'[attr.aria-setsize]': '_pattern.setsize()',
'[attr.aria-posinset]': '_pattern.posinset()',
'[attr.tabindex]': '_pattern.tabindex()',
Expand Down Expand Up @@ -272,6 +272,23 @@ export class TreeItem<V> extends DeferredContentAware implements OnInit, OnDestr
return (this.parent() as TreeItemGroup<V>).ownedBy().tree();
});

/** Whether the item is active. */
readonly active = computed(() => this._pattern.active());

/** Whether this item is currently expanded, returning null if not expandable. */
readonly expanded = computed(() =>
this._pattern.expandable() ? this._pattern.expanded() : null,
);

/** The level of the current item in a tree. */
readonly level = computed(() => this._pattern.level());

/** Whether the item is selected. */
readonly selected = computed(() => this._pattern.selected());

/** Whether this item is visible due to all of its parents being expanded. */
readonly visible = computed(() => this._pattern.visible());

/** The UI pattern for this item. */
_pattern: TreeItemPattern<V>;

Expand Down