Skip to content
5 changes: 3 additions & 2 deletions goldens/aria/combobox/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ export class ComboboxDialog {
// (undocumented)
close(): void;
readonly combobox: Combobox<any>;
readonly element: HTMLElement;
readonly element: HTMLDialogElement;
readonly id: _angular_core.InputSignal<string>;
// (undocumented)
_pattern: ComboboxDialogPattern;
// (undocumented)
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ComboboxDialog, "dialog[ngComboboxDialog]", ["ngComboboxDialog"], {}, {}, never, never, true, [{ directive: typeof ComboboxPopup; inputs: {}; outputs: {}; }]>;
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ComboboxDialog, "dialog[ngComboboxDialog]", ["ngComboboxDialog"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof ComboboxPopup; inputs: {}; outputs: {}; }]>;
// (undocumented)
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ComboboxDialog, never>;
}
Expand Down
6 changes: 3 additions & 3 deletions src/aria/accordion/accordion-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {Directive, afterRenderEffect, computed, inject, input} from '@angular/core';
import {Directive, effect, computed, inject, input} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {DeferredContentAware, AccordionTriggerPattern} from '../private';

Expand Down Expand Up @@ -65,8 +65,8 @@ export class AccordionPanel {
_pattern?: AccordionTriggerPattern;

constructor() {
// Connect the panel's hidden state to the DeferredContentAware's visibility.
afterRenderEffect(() => {
effect(() => {
// Connect the panel's hidden state to the DeferredContentAware's visibility.
this._deferredContentAware.contentVisible.set(this.visible());
});
}
Expand Down
1 change: 1 addition & 0 deletions src/aria/combobox/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ng_project(
deps = [
"//:node_modules/@angular/core",
"//src/aria/private",
"//src/cdk/a11y",
"//src/cdk/bidi",
],
)
Expand Down
30 changes: 15 additions & 15 deletions src/aria/combobox/combobox-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {afterRenderEffect, Directive, ElementRef, inject} from '@angular/core';
import {afterRenderEffect, Directive, ElementRef, inject, input} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {ComboboxDialogPattern} from '../private';
import {Combobox} from './combobox';
import {ComboboxPopup} from './combobox-popup';
Expand Down Expand Up @@ -46,35 +47,34 @@ export class ComboboxDialog {
private readonly _elementRef = inject(ElementRef<HTMLDialogElement>);

/** A reference to the dialog element. */
readonly element = this._elementRef.nativeElement as HTMLElement;
readonly element = this._elementRef.nativeElement as HTMLDialogElement;

/** The combobox that the dialog belongs to. */
readonly combobox = inject(Combobox);

/** The unique identifier for the trigger. */
readonly id = input(inject(_IdGenerator).getId('ng-combobox-dialog-', true));

/** A reference to the parent combobox popup, if one exists. */
private readonly _popup = inject<ComboboxPopup<unknown>>(ComboboxPopup, {
optional: true,
});

_pattern: ComboboxDialogPattern;
_pattern: ComboboxDialogPattern = new ComboboxDialogPattern({
id: this.id,
element: () => this.element,
combobox: this.combobox._pattern,
});

constructor() {
this._pattern = new ComboboxDialogPattern({
id: () => '',
element: () => this._elementRef.nativeElement,
combobox: this.combobox._pattern,
});

if (this._popup) {
this._popup._controls.set(this._pattern);
}

afterRenderEffect(() => {
if (this._elementRef) {
this.combobox._pattern.expanded()
? this._elementRef.nativeElement.showModal()
: this._elementRef.nativeElement.close();
}
afterRenderEffect({
write: () => {
this.combobox._pattern.expanded() ? this.element.showModal() : this.element.close();
},
});
}

Expand Down
10 changes: 5 additions & 5 deletions src/aria/combobox/combobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
*/

import {
afterRenderEffect,
Directive,
ElementRef,
booleanAttribute,
computed,
contentChild,
Directive,
ElementRef,
effect,
inject,
input,
signal,
Expand Down Expand Up @@ -134,13 +134,13 @@ export class Combobox<V> {
});

constructor() {
afterRenderEffect(() => {
effect(() => {
if (this.alwaysExpanded()) {
this._pattern.expanded.set(true);
}
});

afterRenderEffect(() => {
effect(() => {
if (
!this._deferredContentAware?.contentVisible() &&
(this._pattern.isFocused() || this.alwaysExpanded())
Expand Down
24 changes: 14 additions & 10 deletions src/aria/grid/grid-cell-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,22 @@ export class GridCellWidget {
}

constructor() {
afterRenderEffect(() => {
const activateEvent = this._pattern.lastActivateEvent();
if (activateEvent) {
this.activated.emit(activateEvent);
}
afterRenderEffect({
read: () => {
const activateEvent = this._pattern.lastActivateEvent();
if (activateEvent) {
this.activated.emit(activateEvent);
}
},
});

afterRenderEffect(() => {
const deactivateEvent = this._pattern.lastDeactivateEvent();
if (deactivateEvent) {
this.deactivated.emit(deactivateEvent);
}
afterRenderEffect({
read: () => {
const deactivateEvent = this._pattern.lastDeactivateEvent();
if (deactivateEvent) {
this.deactivated.emit(deactivateEvent);
}
},
});
}

Expand Down
12 changes: 7 additions & 5 deletions src/aria/grid/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import {
afterNextRender,
afterRenderEffect,
booleanAttribute,
computed,
Expand Down Expand Up @@ -150,11 +151,12 @@ export class Grid {
);
});

afterRenderEffect(() => this._pattern.setDefaultStateEffect());
afterRenderEffect(() => this._pattern.resetStateEffect());
afterRenderEffect(() => this._pattern.resetFocusEffect());
afterRenderEffect(() => this._pattern.restoreFocusEffect());
afterRenderEffect(() => this._pattern.focusEffect());
// Use Write mode for all direct DOM focus management actions.
afterNextRender({write: () => this._pattern.setDefaultStateEffect()});
afterRenderEffect({write: () => this._pattern.resetStateEffect()});
afterRenderEffect({write: () => this._pattern.resetFocusEffect()});
afterRenderEffect({write: () => this._pattern.restoreFocusEffect()});
afterRenderEffect({write: () => this._pattern.focusEffect()});
}

/** Gets the cell pattern for a given element. */
Expand Down
7 changes: 3 additions & 4 deletions src/aria/listbox/listbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import {
afterNextRender,
afterRenderEffect,
booleanAttribute,
computed,
Expand Down Expand Up @@ -158,6 +159,8 @@ export class Listbox<V> {
this._popup._controls.set(this._pattern as ComboboxListboxPattern<V>);
}

afterNextRender({write: () => this._pattern.setDefaultState()});

afterRenderEffect(() => {
if (typeof ngDevMode === 'undefined' || ngDevMode) {
const violations = this._pattern.validate();
Expand All @@ -167,10 +170,6 @@ export class Listbox<V> {
}
});

afterRenderEffect(() => {
this._pattern.setDefaultStateEffect();
});

// Ensure that if the active item is removed from
// the list, the listbox updates it's focus state.
afterRenderEffect(() => {
Expand Down
12 changes: 3 additions & 9 deletions src/aria/menu/menu-bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {
afterRenderEffect,
afterNextRender,
booleanAttribute,
computed,
contentChildren,
Expand Down Expand Up @@ -104,7 +104,7 @@ export class MenuBar<V> {
readonly _pattern: MenuBarPattern<V>;

/** The menu items as a writable signal. */
private readonly _itemPatterns = signal<any[]>([]);
private readonly _itemPatterns = computed(() => this._items().map(i => i._pattern));

/** A callback function triggered when a menu item is selected. */
readonly itemSelected = output<V>();
Expand All @@ -123,13 +123,7 @@ export class MenuBar<V> {
element: computed(() => this._elementRef.nativeElement),
});

afterRenderEffect(() => {
this._itemPatterns.set(this._items().map(i => i._pattern));
});

afterRenderEffect(() => {
this._pattern.setDefaultStateEffect();
});
afterNextRender({write: () => this._pattern.setDefaultState()});
}

/** Closes the menubar. */
Expand Down
19 changes: 11 additions & 8 deletions src/aria/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
*/

import {
Directive,
ElementRef,
afterRenderEffect,
booleanAttribute,
computed,
contentChildren,
Directive,
ElementRef,
effect,
inject,
input,
output,
Expand Down Expand Up @@ -154,7 +155,7 @@ export class Menu<V> {
itemSelected: (value: V) => this.itemSelected.emit(value),
});

afterRenderEffect(() => {
effect(() => {
const parent = this.parent();
if (parent instanceof MenuItem && parent.parent instanceof MenuBar) {
this._deferredContentAware?.contentVisible.set(true);
Expand All @@ -169,11 +170,13 @@ export class Menu<V> {
// submenus. In those cases, the ui pattern is calling focus() before the ui has a chance to
// update the display property. The result is focus() being called on an element that is not
// focusable. This simply retries focusing the element after render.
afterRenderEffect(() => {
if (this._pattern.visible()) {
const activeItem = untracked(() => this._pattern.inputs.activeItem());
this._pattern.listBehavior.goto(activeItem!);
}
afterRenderEffect({
write: () => {
if (this.visible()) {
const activeItem = untracked(() => this._pattern.inputs.activeItem());
this._pattern.listBehavior.goto(activeItem!);
}
},
});

afterRenderEffect(() => {
Expand Down
20 changes: 11 additions & 9 deletions src/aria/private/deferred-content/deferred-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,19 @@ export class DeferredContent implements OnDestroy {
readonly deferredContentAware = signal(this._deferredContentAware);

constructor() {
afterRenderEffect(() => {
if (this.deferredContentAware()?.contentVisible()) {
if (!this._isRendered) {
afterRenderEffect({
write: () => {
if (this.deferredContentAware()?.contentVisible()) {
if (!this._isRendered) {
this._destroyContent();
this._currentViewRef = this._viewContainerRef.createEmbeddedView(this._templateRef);
this._isRendered = true;
}
} else if (!this.deferredContentAware()?.preserveContent()) {
this._destroyContent();
this._currentViewRef = this._viewContainerRef.createEmbeddedView(this._templateRef);
this._isRendered = true;
this._isRendered = false;
}
} else if (!this.deferredContentAware()?.preserveContent()) {
this._destroyContent();
this._isRendered = false;
}
},
});
}

Expand Down
7 changes: 3 additions & 4 deletions src/aria/tabs/tab-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import {Directionality} from '@angular/cdk/bidi';
import {
afterNextRender,
afterRenderEffect,
booleanAttribute,
computed,
Directive,
Expand All @@ -16,7 +18,6 @@ import {
input,
model,
signal,
afterRenderEffect,
OnInit,
OnDestroy,
} from '@angular/core';
Expand Down Expand Up @@ -118,9 +119,7 @@ export class TabList implements OnInit, OnDestroy {
});

constructor() {
afterRenderEffect(() => {
this._pattern.setDefaultStateEffect();
});
afterNextRender({write: () => this._pattern.setDefaultState()});

afterRenderEffect(() => {
const tab = this._pattern.selectedTab();
Expand Down
4 changes: 2 additions & 2 deletions src/aria/tabs/tab-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
computed,
Directive,
ElementRef,
effect,
inject,
input,
afterRenderEffect,
OnInit,
OnDestroy,
} from '@angular/core';
Expand Down Expand Up @@ -90,7 +90,7 @@ export class TabPanel implements OnInit, OnDestroy {
});

constructor() {
afterRenderEffect(() => this._deferredContentAware.contentVisible.set(this.visible()));
effect(() => this._deferredContentAware.contentVisible.set(this.visible()));
}

ngOnInit() {
Expand Down
6 changes: 2 additions & 4 deletions src/aria/toolbar/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {
afterRenderEffect,
afterNextRender,
Directive,
ElementRef,
inject,
Expand Down Expand Up @@ -106,9 +106,7 @@ export class Toolbar<V> {
});

constructor() {
afterRenderEffect(() => {
this._pattern.setDefaultStateEffect();
});
afterNextRender({write: () => this._pattern.setDefaultState()});
}

_register(widget: ToolbarWidget<V>) {
Expand Down
Loading
Loading