UNPKG

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
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