igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
423 lines (417 loc) • 16.6 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, ChangeDetectorRef, EventEmitter, booleanAttribute, ContentChildren, Output, Input, HostBinding, Component, NgModule } from '@angular/core';
import { Subject, fromEvent } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { ACCORDION_NAVIGATION_KEYS } from 'igniteui-angular/core';
import * as i2 from 'igniteui-angular/expansion-panel';
import { IgxExpansionPanelComponent, IgxExpansionPanelHeaderComponent, IgxExpansionPanelBodyComponent, IgxExpansionPanelDescriptionDirective, IgxExpansionPanelTitleDirective, IgxExpansionPanelIconDirective } from 'igniteui-angular/expansion-panel';
let NEXT_ID = 0;
/**
* IgxAccordion is a container-based component that contains that can house multiple expansion panels.
*
* @igxModule IgxAccordionModule
*
* @igxKeywords accordion
*
* @igxGroup Layouts
*
* @remarks
* The Ignite UI for Angular Accordion component enables the user to navigate among multiple collapsing panels
* displayed in a single container.
* The accordion offers keyboard navigation and API to control the underlying panels' expansion state.
*
* @example
* ```html
* <igx-accordion>
* <igx-expansion-panel *ngFor="let panel of panels">
* ...
* </igx-expansion-panel>
* </igx-accordion>
* ```
*/
class IgxAccordionComponent {
constructor() {
this.cdr = inject(ChangeDetectorRef);
/**
* Get/Set the `id` of the accordion component.
* Default value is `"igx-accordion-0"`;
* ```html
* <igx-accordion id="my-first-accordion"></igx-accordion>
* ```
* ```typescript
* const accordionId = this.accordion.id;
* ```
*/
this.id = `igx-accordion-${NEXT_ID++}`;
/** @hidden @internal **/
this.cssClass = 'igx-accordion';
/** @hidden @internal **/
this.displayStyle = 'block';
/**
* Emitted before a panel is expanded.
*
* @remarks
* This event is cancelable.
*
* ```html
* <igx-accordion (panelExpanding)="handlePanelExpanding($event)">
* </igx-accordion>
* ```
*
*```typescript
* public handlePanelExpanding(event: IExpansionPanelCancelableEventArgs){
* const expandedPanel: IgxExpansionPanelComponent = event.panel;
* if (expandedPanel.disabled) {
* event.cancel = true;
* }
* }
*```
*/
this.panelExpanding = new EventEmitter();
/**
* Emitted after a panel has been expanded.
*
* ```html
* <igx-accordion (panelExpanded)="handlePanelExpanded($event)">
* </igx-accordion>
* ```
*
*```typescript
* public handlePanelExpanded(event: IExpansionPanelCancelableEventArgs) {
* const expandedPanel: IgxExpansionPanelComponent = event.panel;
* console.log("Panel is expanded: ", expandedPanel.id);
* }
*```
*/
this.panelExpanded = new EventEmitter();
/**
* Emitted before a panel is collapsed.
*
* @remarks
* This event is cancelable.
*
* ```html
* <igx-accordion (panelCollapsing)="handlePanelCollapsing($event)">
* </igx-accordion>
* ```
*/
this.panelCollapsing = new EventEmitter();
/**
* Emitted after a panel has been collapsed.
*
* ```html
* <igx-accordion (panelCollapsed)="handlePanelCollapsed($event)">
* </igx-accordion>
* ```
*/
this.panelCollapsed = new EventEmitter();
this._destroy$ = new Subject();
this._unsubChildren$ = new Subject();
this._singleBranchExpand = false;
}
/**
* Get/Set the animation settings that panels should use when expanding/collpasing.
*
* ```html
* <igx-accordion [animationSettings]="customAnimationSettings"></igx-accordion>
* ```
*
* ```typescript
* const customAnimationSettings: ToggleAnimationSettings = {
* openAnimation: growVerIn,
* closeAnimation: growVerOut
* };
*
* this.accordion.animationSettings = customAnimationSettings;
* ```
*/
get animationSettings() {
return this._animationSettings;
}
set animationSettings(value) {
this._animationSettings = value;
this.updatePanelsAnimation();
}
/**
* Get/Set how the accordion handles the expansion of the projected expansion panels.
* If set to `true`, only a single panel can be expanded at a time, collapsing all others
*
* ```html
* <igx-accordion [singleBranchExpand]="true">
* ...
* </igx-accordion>
* ```
*
* ```typescript
* this.accordion.singleBranchExpand = false;
* ```
*/
get singleBranchExpand() {
return this._singleBranchExpand;
}
set singleBranchExpand(val) {
this._singleBranchExpand = val;
if (val) {
this.collapseAllExceptLast();
}
}
/**
* Get all panels.
*
* ```typescript
* const panels: IgxExpansionPanelComponent[] = this.accordion.panels;
* ```
*/
get panels() {
return this._panels?.toArray();
}
/** @hidden @internal **/
ngAfterContentInit() {
this.updatePanelsAnimation();
if (this.singleBranchExpand) {
this.collapseAllExceptLast();
}
}
/** @hidden @internal **/
ngAfterViewInit() {
this._expandedPanels = new Set(this._panels.filter(panel => !panel.collapsed));
this._expandingPanels = new Set();
this._panels.changes.pipe(takeUntil(this._destroy$)).subscribe(() => {
this.subToChanges();
});
this.subToChanges();
}
/** @hidden @internal */
ngOnDestroy() {
this._unsubChildren$.next();
this._unsubChildren$.complete();
this._destroy$.next();
this._destroy$.complete();
}
/**
* Expands all collapsed expansion panels.
*
* ```typescript
* accordion.expandAll();
* ```
*/
expandAll() {
if (this.singleBranchExpand) {
for (let i = 0; i < this.panels.length - 1; i++) {
this.panels[i].collapse();
}
this._panels.last.expand();
return;
}
this.panels.forEach(panel => panel.expand());
}
/**
* Collapses all expanded expansion panels.
*
* ```typescript
* accordion.collapseAll();
* ```
*/
collapseAll() {
this.panels.forEach(panel => panel.collapse());
}
collapseAllExceptLast() {
const lastExpanded = this.panels?.filter(p => !p.collapsed && !p.header.disabled).pop();
this.panels?.forEach((p) => {
if (p !== lastExpanded && !p.header.disabled) {
p.collapsed = true;
}
});
this.cdr.markForCheck();
}
handleKeydown(event, panel) {
const key = event.key.toLowerCase();
if (!(ACCORDION_NAVIGATION_KEYS.has(key))) {
return;
}
// TO DO: if we ever want to improve the performance of the accordion,
// enabledPanels could be cached (by making a disabledChange emitter on the panel header)
this._enabledPanels = this._panels.filter(p => !p.header.disabled);
event.preventDefault();
this.handleNavigation(event, panel);
}
handleNavigation(event, panel) {
switch (event.key.toLowerCase()) {
case 'home':
this._enabledPanels[0].header.innerElement.focus();
break;
case 'end':
this._enabledPanels[this._enabledPanels.length - 1].header.innerElement.focus();
break;
case 'arrowup':
case 'up':
this.handleUpDownArrow(true, event, panel);
break;
case 'arrowdown':
case 'down':
this.handleUpDownArrow(false, event, panel);
break;
}
}
handleUpDownArrow(isUp, event, panel) {
if (!event.altKey) {
const focusedPanel = panel;
const next = this.getNextPanel(focusedPanel, isUp ? -1 : 1);
if (next === focusedPanel) {
return;
}
next.header.innerElement.focus();
}
if (event.altKey && event.shiftKey) {
if (isUp) {
this._enabledPanels.forEach(p => p.collapse());
}
else {
if (this.singleBranchExpand) {
for (let i = 0; i < this._enabledPanels.length - 1; i++) {
this._enabledPanels[i].collapse();
}
this._enabledPanels[this._enabledPanels.length - 1].expand();
return;
}
this._enabledPanels.forEach(p => p.expand());
}
}
}
getNextPanel(panel, dir = 1) {
const panelIndex = this._enabledPanels.indexOf(panel);
return this._enabledPanels[panelIndex + dir] || panel;
}
subToChanges() {
this._unsubChildren$.next();
this._panels.forEach(panel => {
panel.contentExpanded.pipe(takeUntil(this._unsubChildren$)).subscribe((args) => {
this._expandedPanels.add(args.owner);
this._expandingPanels.delete(args.owner);
const evArgs = { ...args, owner: this, panel: args.owner };
this.panelExpanded.emit(evArgs);
});
panel.contentExpanding.pipe(takeUntil(this._unsubChildren$)).subscribe((args) => {
if (args.cancel) {
return;
}
const evArgs = { ...args, owner: this, panel: args.owner };
this.panelExpanding.emit(evArgs);
if (evArgs.cancel) {
args.cancel = true;
return;
}
if (this.singleBranchExpand) {
this._expandedPanels.forEach(p => {
if (!p.header.disabled) {
p.collapse();
}
});
this._expandingPanels.forEach(p => {
if (!p.header.disabled) {
if (!p.animationSettings.closeAnimation) {
p.openAnimationPlayer?.reset();
}
if (!p.animationSettings.openAnimation) {
p.closeAnimationPlayer?.reset();
}
p.collapse();
}
});
this._expandingPanels.add(args.owner);
}
});
panel.contentCollapsed.pipe(takeUntil(this._unsubChildren$)).subscribe((args) => {
this._expandedPanels.delete(args.owner);
this._expandingPanels.delete(args.owner);
const evArgs = { ...args, owner: this, panel: args.owner };
this.panelCollapsed.emit(evArgs);
});
panel.contentCollapsing.pipe(takeUntil(this._unsubChildren$)).subscribe((args) => {
const evArgs = { ...args, owner: this, panel: args.owner };
this.panelCollapsing.emit(evArgs);
if (evArgs.cancel) {
args.cancel = true;
}
});
fromEvent(panel.header.innerElement, 'keydown')
.pipe(takeUntil(this._unsubChildren$))
.subscribe((e) => {
this.handleKeydown(e, panel);
});
});
}
updatePanelsAnimation() {
if (this.animationSettings !== undefined) {
this.panels?.forEach(panel => panel.animationSettings = this.animationSettings);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "21.0.2", type: IgxAccordionComponent, isStandalone: true, selector: "igx-accordion", inputs: { id: "id", animationSettings: "animationSettings", singleBranchExpand: ["singleBranchExpand", "singleBranchExpand", booleanAttribute] }, outputs: { panelExpanding: "panelExpanding", panelExpanded: "panelExpanded", panelCollapsing: "panelCollapsing", panelCollapsed: "panelCollapsed" }, host: { properties: { "attr.id": "this.id", "class.igx-accordion": "this.cssClass", "style.display": "this.displayStyle" } }, queries: [{ propertyName: "_panels", predicate: IgxExpansionPanelComponent }], ngImport: i0, template: "<ng-content select=\"igx-expansion-panel\"></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxAccordionComponent, decorators: [{
type: Component,
args: [{ selector: 'igx-accordion', standalone: true, template: "<ng-content select=\"igx-expansion-panel\"></ng-content>\n" }]
}], propDecorators: { id: [{
type: HostBinding,
args: ['attr.id']
}, {
type: Input
}], cssClass: [{
type: HostBinding,
args: ['class.igx-accordion']
}], displayStyle: [{
type: HostBinding,
args: ['style.display']
}], animationSettings: [{
type: Input
}], singleBranchExpand: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], panelExpanding: [{
type: Output
}], panelExpanded: [{
type: Output
}], panelCollapsing: [{
type: Output
}], panelCollapsed: [{
type: Output
}], _panels: [{
type: ContentChildren,
args: [IgxExpansionPanelComponent]
}] } });
/* Accordion directives collection for ease-of-use import in standalone components scenario */
const IGX_ACCORDION_DIRECTIVES = [
IgxAccordionComponent,
IgxExpansionPanelComponent,
IgxExpansionPanelHeaderComponent,
IgxExpansionPanelBodyComponent,
IgxExpansionPanelDescriptionDirective,
IgxExpansionPanelTitleDirective,
IgxExpansionPanelIconDirective
];
/**
* @hidden
* IMPORTANT: The following is NgModule exported for backwards-compatibility before standalone components
*/
class IgxAccordionModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxAccordionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: IgxAccordionModule, imports: [IgxAccordionComponent, i2.IgxExpansionPanelComponent, i2.IgxExpansionPanelHeaderComponent, i2.IgxExpansionPanelBodyComponent, i2.IgxExpansionPanelDescriptionDirective, i2.IgxExpansionPanelTitleDirective, i2.IgxExpansionPanelIconDirective], exports: [IgxAccordionComponent, i2.IgxExpansionPanelComponent, i2.IgxExpansionPanelHeaderComponent, i2.IgxExpansionPanelBodyComponent, i2.IgxExpansionPanelDescriptionDirective, i2.IgxExpansionPanelTitleDirective, i2.IgxExpansionPanelIconDirective] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxAccordionModule, imports: [i2.IgxExpansionPanelComponent, i2.IgxExpansionPanelHeaderComponent, i2.IgxExpansionPanelBodyComponent] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxAccordionModule, decorators: [{
type: NgModule,
args: [{
imports: [
...IGX_ACCORDION_DIRECTIVES
],
exports: [
...IGX_ACCORDION_DIRECTIVES
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { IGX_ACCORDION_DIRECTIVES, IgxAccordionComponent, IgxAccordionModule };
//# sourceMappingURL=igniteui-angular-accordion.mjs.map