Skip to content
Merged
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
12 changes: 0 additions & 12 deletions src/aria/combobox/combobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,6 @@ export class Combobox<V> {
/** The filter mode for the combobox. */
filterMode = input<'manual' | 'auto-select' | 'highlight'>('manual');

/** Whether the combobox is focused. */
readonly isFocused = signal(false);

/** Whether the combobox has received focus yet. */
private _hasBeenFocused = signal(false);

/** Whether the combobox is disabled. */
readonly disabled = input(false);

Expand Down Expand Up @@ -122,12 +116,6 @@ export class Combobox<V> {
this._deferredContentAware?.contentVisible.set(true);
}
});

afterRenderEffect(() => {
if (!this._hasBeenFocused() && this._pattern.isFocused()) {
this._hasBeenFocused.set(true);
}
});
}
}

Expand Down
47 changes: 23 additions & 24 deletions src/aria/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ import {Directionality} from '@angular/cdk/bidi';
host: {
'class': 'ng-menu-trigger',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.aria-haspopup]': '_pattern.hasPopup()',
'[attr.aria-expanded]': '_pattern.expanded()',
'[attr.aria-haspopup]': 'hasPopup()',
'[attr.aria-expanded]': 'expanded()',
'[attr.aria-controls]': '_pattern.menu()?.id()',
'(click)': '_pattern.onClick()',
'(keydown)': '_pattern.onKeydown($event)',
'(focusout)': '_pattern.onFocusOut($event)',
'(focusin)': 'onFocusIn()',
'(focusin)': '_pattern.onFocusIn()',
},
})
export class MenuTrigger<V> {
Expand All @@ -67,8 +67,11 @@ export class MenuTrigger<V> {
/** The menu associated with the trigger. */
menu = input<Menu<V> | undefined>(undefined);

/** 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({
Expand All @@ -80,11 +83,6 @@ export class MenuTrigger<V> {
constructor() {
effect(() => this.menu()?.parent.set(this));
}

/** Marks the menu trigger as having been focused. */
onFocusIn() {
this.hasBeenFocused.set(true);
}
}

/**
Expand All @@ -110,7 +108,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 @@ -171,7 +169,7 @@ export class Menu<V> {
readonly items = () => this._items().map(i => i._pattern);

/** 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 @@ -199,7 +197,7 @@ export class Menu<V> {
this._deferredContentAware?.contentVisible.set(true);
} else {
this._deferredContentAware?.contentVisible.set(
this._pattern.isVisible() || !!this.parent()?.hasBeenFocused(),
this._pattern.isVisible() || !!this.parent()?._pattern.hasBeenFocused(),
);
}
});
Expand Down Expand Up @@ -333,11 +331,11 @@ export class MenuBar<V> {
host: {
'role': 'menuitem',
'class': 'ng-menu-item',
'(focusin)': 'onFocusIn()',
'(focusin)': '_pattern.onFocusIn()',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.data-active]': '_pattern.isActive()',
'[attr.aria-haspopup]': '_pattern.hasPopup()',
'[attr.aria-expanded]': '_pattern.expanded()',
'[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 @@ -375,8 +373,14 @@ export class MenuItem<V> {
/** The submenu associated with the menu item. */
readonly submenu = input<Menu<V> | undefined>(undefined);

/** Whether the menu item has been focused. */
readonly hasBeenFocused = signal(false);
/** Whether the menu item is active. */
readonly isActive = computed(() => this._pattern.isActive());

/** Whether the menu is 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>({
Expand All @@ -392,11 +396,6 @@ export class MenuItem<V> {
constructor() {
effect(() => this.submenu()?.parent.set(this));
}

/** Marks the menu item as having been focused. */
onFocusIn() {
this.hasBeenFocused.set(true);
}
}

/** Defers the rendering of the menu content. */
Expand Down
16 changes: 16 additions & 0 deletions src/aria/private/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,9 @@ export class MenuTriggerPattern<V> {
/** Whether the menu is expanded. */
expanded = signal(false);

/** Whether the menu trigger has received focus. */
hasBeenFocused = signal(false);

/** The role of the menu trigger. */
role = () => 'button';

Expand Down Expand Up @@ -614,6 +617,11 @@ export class MenuTriggerPattern<V> {
this.expanded() ? this.close() : this.open({first: true});
}

/** Handles focusin events for the menu trigger. */
onFocusIn() {
this.hasBeenFocused.set(true);
}

/** Handles focusout events for the menu trigger. */
onFocusOut(event: FocusEvent) {
const element = this.inputs.element();
Expand Down Expand Up @@ -679,6 +687,9 @@ export class MenuItemPattern<V> implements ListItem<V> {
/** Whether the menu item is active. */
isActive = computed(() => this.inputs.parent()?.inputs.activeItem() === this);

/** Whether the menu item has received focus. */
hasBeenFocused = signal(false);

/** The tab index of the menu item. */
tabIndex = computed(() => {
if (this.submenu() && this.submenu()?.inputs.activeItem()) {
Expand Down Expand Up @@ -756,4 +767,9 @@ export class MenuItemPattern<V> implements ListItem<V> {
}
}
}

/** Handles focusin events for the menu item. */
onFocusIn() {
this.hasBeenFocused.set(true);
}
}
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 tab index of the tree. */
Expand Down
20 changes: 16 additions & 4 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.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 @@ -302,7 +311,7 @@ export class Tab implements HasElement, OnInit, OnDestroy {
'class': 'ng-tabpanel',
'[attr.id]': '_pattern.id()',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.inert]': '_pattern.hidden() ? true : null',
'[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
2 changes: 1 addition & 1 deletion src/aria/toolbar/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export class Toolbar<V> {
exportAs: 'ngToolbarWidget',
host: {
'class': 'ng-toolbar-widget',
'[attr.data-active]': '_pattern.active()',
'[attr.data-active]': 'active()',
'[attr.tabindex]': '_pattern.tabIndex()',
'[attr.inert]': 'hardDisabled() ? true : null',
'[attr.disabled]': 'hardDisabled() ? true : null',
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
Loading