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
12 changes: 9 additions & 3 deletions goldens/aria/accordion/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ export class AccordionGroup {
expandAll(): void;
readonly multiExpandable: _angular_core.InputSignalWithTransform<boolean, unknown>;
readonly _pattern: AccordionGroupPattern;
_registerTrigger(trigger: AccordionTrigger): void;
readonly softDisabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
readonly textDirection: _angular_core.WritableSignal<_angular_cdk_bidi.Direction>;
_unregisterTrigger(trigger: AccordionTrigger): void;
readonly wrap: _angular_core.InputSignalWithTransform<boolean, unknown>;
// (undocumented)
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AccordionGroup, "[ngAccordionGroup]", ["ngAccordionGroup"], { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "multiExpandable": { "alias": "multiExpandable"; "required": false; "isSignal": true; }; "softDisabled": { "alias": "softDisabled"; "required": false; "isSignal": true; }; "wrap": { "alias": "wrap"; "required": false; "isSignal": true; }; }, {}, ["_triggers"], never, true, never>;
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AccordionGroup, "[ngAccordionGroup]", ["ngAccordionGroup"], { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "multiExpandable": { "alias": "multiExpandable"; "required": false; "isSignal": true; }; "softDisabled": { "alias": "softDisabled"; "required": false; "isSignal": true; }; "wrap": { "alias": "wrap"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
// (undocumented)
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AccordionGroup, never>;
}
Expand All @@ -38,6 +40,7 @@ export class AccordionGroup {
export class AccordionPanel {
constructor();
collapse(): void;
readonly element: HTMLElement;
expand(): void;
readonly id: _angular_core.InputSignal<string>;
_pattern?: AccordionTriggerPattern;
Expand All @@ -50,22 +53,25 @@ export class AccordionPanel {
}

// @public
export class AccordionTrigger implements OnInit {
export class AccordionTrigger implements OnInit, OnDestroy {
readonly active: _angular_core.Signal<boolean>;
collapse(): void;
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
readonly element: HTMLElement;
expand(): void;
readonly expanded: _angular_core.ModelSignal<boolean>;
readonly id: _angular_core.InputSignal<string>;
readonly index: _angular_core.InputSignal<number | undefined>;
// (undocumented)
ngOnDestroy(): void;
// (undocumented)
ngOnInit(): void;
readonly panel: _angular_core.InputSignal<AccordionPanel>;
readonly panelId: _angular_core.Signal<string>;
_pattern: AccordionTriggerPattern;
toggle(): void;
// (undocumented)
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AccordionTrigger, "[ngAccordionTrigger]", ["ngAccordionTrigger"], { "panel": { "alias": "panel"; "required": true; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; }, { "expanded": "expandedChange"; }, never, never, true, never>;
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AccordionTrigger, "[ngAccordionTrigger]", ["ngAccordionTrigger"], { "panel": { "alias": "panel"; "required": true; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "index": { "alias": "index"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; }, { "expanded": "expandedChange"; }, never, never, true, never>;
// (undocumented)
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AccordionTrigger, never>;
}
Expand Down
9 changes: 9 additions & 0 deletions goldens/aria/private/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,12 @@ export class GridRowPattern {
rowIndex: SignalLike<number | undefined>;
}

// @public (undocumented)
export interface HasElement {
// (undocumented)
element: HTMLElement;
}

// @public (undocumented)
export function linkedSignal<T>(sourceFn: () => T): WritableSignalLike<T>;

Expand Down Expand Up @@ -663,6 +669,9 @@ export function signal<T>(initialValue: T): WritableSignalLike<T>;
// @public (undocumented)
export type SignalLike<T> = () => T;

// @public
export function sortDirectives(a: HasElement, b: HasElement): 1 | -1;

// @public
export interface TabInputs extends Omit<ListNavigationItem, 'index'>, Omit<ExpansionItem, 'expandable'> {
tablist: SignalLike<TabListPattern>;
Expand Down
24 changes: 21 additions & 3 deletions src/aria/accordion/accordion-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
ElementRef,
booleanAttribute,
computed,
contentChildren,
inject,
input,
signal,
Expand Down Expand Up @@ -75,10 +74,17 @@ export class AccordionGroup {
readonly element = this._elementRef.nativeElement as HTMLElement;

/** The AccordionTriggers nested inside this group. */
private readonly _triggers = contentChildren(AccordionTrigger, {descendants: true});
private readonly _triggers = signal(new Set<AccordionTrigger>());

/** The AccordionTriggers nested inside this group. */
private readonly _sortedTriggers = computed(() => {
return [...this._triggers()].sort((a, b) => a.index()! - b.index()!);
});

/** The corresponding patterns for the accordion triggers. */
private readonly _triggerPatterns = computed(() => this._triggers().map(t => t._pattern));
private readonly _triggerPatterns = computed(() => {
return this._sortedTriggers().map(t => t._pattern);
});

/** The text direction (ltr or rtl). */
readonly textDirection = inject(Directionality).valueSignal;
Expand Down Expand Up @@ -117,4 +123,16 @@ export class AccordionGroup {
collapseAll() {
this._pattern.collapseAll();
}

/** Internal method to register each trigger as we can not use contentChildren. */
_registerTrigger(trigger: AccordionTrigger) {
this._triggers().add(trigger);
this._triggers.set(new Set(this._triggers()));
}

/** Internal method to unregister each trigger as we can not use contentChildren. */
_unregisterTrigger(trigger: AccordionTrigger) {
this._triggers().delete(trigger);
this._triggers.set(new Set(this._triggers()));
}
}
8 changes: 7 additions & 1 deletion 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, ElementRef, afterRenderEffect, computed, inject, input} from '@angular/core';
import {_IdGenerator} from '@angular/cdk/a11y';
import {DeferredContentAware, AccordionTriggerPattern} from '../private';

Expand Down Expand Up @@ -48,6 +48,12 @@ import {DeferredContentAware, AccordionTriggerPattern} from '../private';
},
})
export class AccordionPanel {
/** A reference to the trigger element. */
private readonly _elementRef = inject(ElementRef);

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

/** The DeferredContentAware host directive. */
private readonly _deferredContentAware = inject(DeferredContentAware);

Expand Down
16 changes: 14 additions & 2 deletions src/aria/accordion/accordion-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import {
Directive,
ElementRef,
OnDestroy,
OnInit,
booleanAttribute,
computed,
Expand Down Expand Up @@ -53,7 +54,7 @@ import {AccordionPanel} from './accordion-panel';
'[attr.tabindex]': '_pattern.tabIndex()',
},
})
export class AccordionTrigger implements OnInit {
export class AccordionTrigger implements OnInit, OnDestroy {
/** A reference to the trigger element. */
private readonly _elementRef = inject(ElementRef);

Expand All @@ -69,12 +70,15 @@ export class AccordionTrigger implements OnInit {
/** The unique identifier for the trigger. */
readonly id = input(inject(_IdGenerator).getId('ng-accordion-trigger-', true));

/** The unique identifier for the correspondingtrigger panel. */
/** The unique identifier for the corresponding trigger panel. */
readonly panelId = computed(() => this.panel().id());

/** Whether the trigger is disabled. */
readonly disabled = input(false, {transform: booleanAttribute});

/** The index of the trigger within the accordion group. */
readonly index = input<number>();

/** Whether the corresponding panel is expanded. */
readonly expanded = model<boolean>(false);

Expand All @@ -93,6 +97,14 @@ export class AccordionTrigger implements OnInit {
});

this.panel()._pattern = this._pattern;

this._accordionGroup._registerTrigger(this);
}

ngOnDestroy() {
this.panel()._pattern = undefined;

this._accordionGroup._unregisterTrigger(this);
}

/** Expands this item. */
Expand Down
Loading
Loading