UNPKG

primeng

Version:

PrimeNG is an open source UI library for Angular featuring a rich set of 80+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeB

1,159 lines (1,138 loc) 56 kB
import { trigger, state, transition, style, animate } from '@angular/animations'; import * as i2 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { Injectable, inject, forwardRef, model, input, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, HostListener, ContentChild, EventEmitter, numberAttribute, booleanAttribute, ContentChildren, Output, Input, HostBinding, signal, NgModule } from '@angular/core'; import { findSingle, getAttribute, focus, uuid } from '@primeuix/utils'; import { Header, PrimeTemplate, SharedModule } from 'primeng/api'; import { BaseComponent } from 'primeng/basecomponent'; import { ChevronDownIcon, ChevronUpIcon } from 'primeng/icons'; import * as i1 from 'primeng/ripple'; import { Ripple } from 'primeng/ripple'; import { transformToBoolean } from 'primeng/utils'; import { BaseStyle } from 'primeng/base'; const theme = ({ dt }) => ` .p-accordionpanel { display: flex; flex-direction: column; border-style: solid; border-width: ${dt('accordion.panel.border.width')}; border-color: ${dt('accordion.panel.border.color')}; } .p-accordionheader { all: unset; cursor: pointer; display: flex; align-items: center; justify-content: space-between; padding: ${dt('accordion.header.padding')}; color: ${dt('accordion.header.color')}; background: ${dt('accordion.header.background')}; border-style: solid; border-width: ${dt('accordion.header.border.width')}; border-color: ${dt('accordion.header.border.color')}; font-weight: ${dt('accordion.header.font.weight')}; border-radius: ${dt('accordion.header.border.radius')}; transition: background ${dt('accordion.transition.duration')}; color ${dt('accordion.transition.duration')}color ${dt('accordion.transition.duration')}, outline-color ${dt('accordion.transition.duration')}, box-shadow ${dt('accordion.transition.duration')}; outline-color: transparent; position: relative; overflow: hidden; } .p-accordionpanel:first-child > .p-accordionheader { border-width: ${dt('accordion.header.first.border.width')}; border-start-start-radius: ${dt('accordion.header.first.top.border.radius')}; border-start-end-radius: ${dt('accordion.header.first.top.border.radius')}; } .p-accordionpanel:last-child > .p-accordionheader { border-end-start-radius: ${dt('accordion.header.last.bottom.border.radius')}; border-end-end-radius: ${dt('accordion.header.last.bottom.border.radius')}; } .p-accordionpanel:last-child.p-accordionpanel-active > .p-accordionheader { border-end-start-radius: ${dt('accordion.header.last.active.bottom.border.radius')}; border-end-end-radius:${dt('accordion.header.last.active.bottom.border.radius')}; } .p-accordionheader-toggle-icon { color: ${dt('accordion.header.toggle.icon.color')}; } .p-accordionpanel:not(.p-disabled) .p-accordionheader:focus-visible { box-shadow: ${dt('accordion.header.focus.ring.shadow')}; outline: ${dt('accordion.header.focus.ring.width')} ${dt('accordion.header.focus.ring.style')} ${dt('accordion.header.focus.ring.color')}; outline-offset: ${dt('accordion.header.focus.ring.offset')}; } .p-accordionpanel:not(.p-accordionpanel-active):not(.p-disabled) > .p-accordionheader:hover { background: ${dt('accordion.header.hover.background')}; color: ${dt('accordion.header.hover.color')} } .p-accordionpanel:not(.p-accordionpanel-active):not(.p-disabled) .p-accordionheader:hover .p-accordionheader-toggle-icon { color: ${dt('accordion.header.toggle.icon.hover.color')}; } .p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader { background: ${dt('accordion.header.active.background')}; color: ${dt('accordion.header.active.color')} } .p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader .p-accordionheader-toggle-icon { color: ${dt('accordion.header.toggle.icon.active.color')}; } .p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader:hover { background: ${dt('accordion.header.active.hover.background')}; color: ${dt('accordion.header.active.hover.color')} } .p-accordionpanel:not(.p-disabled).p-accordionpanel-active > .p-accordionheader:hover .p-accordionheader-toggle-icon { color: ${dt('accordion.header.toggle.icon.active.hover.color')}; } .p-accordioncontent-content { border-style: solid; border-width: ${dt('accordion.content.border.width')}; border-color: ${dt('accordion.content.border.color')}; background-color: ${dt('accordion.content.background')}; color: ${dt('accordion.content.color')}; padding: ${dt('accordion.content.padding')} } /*For PrimeNG*/ .p-accordion .p-accordioncontent { overflow: hidden; } .p-accordionpanel.p-accordioncontent:not(.ng-animating) { overflow: inherit; } .p-accordionheader-toggle-icon.icon-start { order: -1; } .p-accordionheader:has(.p-accordionheader-toggle-icon.icon-start) { justify-content: flex-start; gap: ${dt('accordion.header.padding')}; } `; const classes = { root: 'p-accordion p-component' }; class AccordionStyle extends BaseStyle { name = 'accordion'; theme = theme; classes = classes; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionStyle, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionStyle }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionStyle, decorators: [{ type: Injectable }] }); /** * * Accordion groups a collection of contents in tabs. * * [Live Demo](https://www.primeng.org/accordion/) * * @module accordionstyle * */ var AccordionClasses; (function (AccordionClasses) { /** * Class name of the root element */ AccordionClasses["root"] = "p-accordion"; /** * Class name of the content wrapper */ AccordionClasses["contentwrapper"] = "p-accordioncontent"; /** * Class name of the content */ AccordionClasses["content"] = "p-accordioncontent-content"; /** * Class name of the header */ AccordionClasses["header"] = "p-accordionheader"; /** * Class name of the toggle icon */ AccordionClasses["toggleicon"] = "p-accordionheader-toggle-icon"; /** * Class name of the panel */ AccordionClasses["panel"] = "p-accordionpanel"; })(AccordionClasses || (AccordionClasses = {})); /** * AccordionPanel is a helper component for Accordion component. * @group Components */ class AccordionPanel extends BaseComponent { pcAccordion = inject(forwardRef(() => Accordion)); /** * Value of the active tab. * @defaultValue undefined * @group Props */ value = model(undefined); /** * Disables the tab when enabled. * @defaultValue false * @group Props */ disabled = input(false, { transform: (v) => transformToBoolean(v) }); active = computed(() => (this.pcAccordion.multiple() ? this.valueEquals(this.pcAccordion.value(), this.value()) : this.pcAccordion.value() === this.value())); valueEquals(currentValue, value) { if (Array.isArray(currentValue)) { return currentValue.includes(value); } return currentValue === value; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionPanel, deps: null, target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.2", type: AccordionPanel, isStandalone: true, selector: "p-accordion-panel, p-accordionpanel", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.p-accordionpanel": "true", "class.p-accordionpanel-active": "active()", "class.p-disabled": "disabled()", "attr.data-pc-name": "\"accordionpanel\"", "attr.data-p-disabled": "disabled()", "attr.data-p-active": "active()" } }, usesInheritance: true, ngImport: i0, template: `<ng-content />`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionPanel, decorators: [{ type: Component, args: [{ selector: 'p-accordion-panel, p-accordionpanel', imports: [CommonModule], standalone: true, template: `<ng-content />`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { '[class.p-accordionpanel]': 'true', '[class.p-accordionpanel-active]': 'active()', '[class.p-disabled]': 'disabled()', '[attr.data-pc-name]': '"accordionpanel"', '[attr.data-p-disabled]': 'disabled()', '[attr.data-p-active]': 'active()' } }] }] }); /** * AccordionHeader is a helper component for Accordion component. * @group Components */ class AccordionHeader extends BaseComponent { pcAccordion = inject(forwardRef(() => Accordion)); pcAccordionPanel = inject(forwardRef(() => AccordionPanel)); id = computed(() => `${this.pcAccordion.id()}_accordionheader_${this.pcAccordionPanel.value()}`); active = computed(() => this.pcAccordionPanel.active()); disabled = computed(() => this.pcAccordionPanel.disabled()); ariaControls = computed(() => `${this.pcAccordion.id()}_accordioncontent_${this.pcAccordionPanel.value()}`); /** * Toggle icon template. * @type {TemplateRef<AccordionToggleIconTemplateContext>} context - Context of the template * @example * ```html * <ng-template #toggleicon let-active="active"> </ng-template> * ``` * @see {@link AccordionToggleIconTemplateContext} * @group Templates */ toggleicon; onClick(event) { const wasActive = this.active(); this.changeActiveValue(); const isActive = this.active(); const index = this.pcAccordionPanel.value(); if (!wasActive && isActive) { this.pcAccordion.onOpen.emit({ originalEvent: event, index }); } else if (wasActive && !isActive) { this.pcAccordion.onClose.emit({ originalEvent: event, index }); } } onFocus() { this.pcAccordion.selectOnFocus() && this.changeActiveValue(); } onKeydown(event) { switch (event.code) { case 'ArrowDown': this.arrowDownKey(event); break; case 'ArrowUp': this.arrowUpKey(event); break; case 'Home': this.onHomeKey(event); break; case 'End': this.onEndKey(event); break; case 'Enter': case 'Space': case 'NumpadEnter': this.onEnterKey(event); break; default: break; } } changeActiveValue() { this.pcAccordion.updateValue(this.pcAccordionPanel.value()); } findPanel(headerElement) { return headerElement?.closest('[data-pc-name="accordionpanel"]'); } findHeader(panelElement) { return findSingle(panelElement, '[data-pc-name="accordionheader"]'); } findNextPanel(panelElement, selfCheck = false) { const element = selfCheck ? panelElement : panelElement.nextElementSibling; return element ? (getAttribute(element, 'data-p-disabled') ? this.findNextPanel(element) : this.findHeader(element)) : null; } findPrevPanel(panelElement, selfCheck = false) { const element = selfCheck ? panelElement : panelElement.previousElementSibling; return element ? (getAttribute(element, 'data-p-disabled') ? this.findPrevPanel(element) : this.findHeader(element)) : null; } findFirstPanel() { return this.findNextPanel(this.pcAccordion.el.nativeElement.firstElementChild, true); } findLastPanel() { return this.findPrevPanel(this.pcAccordion.el.nativeElement.lastElementChild, true); } changeFocusedPanel(event, element) { focus(element); } arrowDownKey(event) { const nextPanel = this.findNextPanel(this.findPanel(event.currentTarget)); nextPanel ? this.changeFocusedPanel(event, nextPanel) : this.onHomeKey(event); event.preventDefault(); } arrowUpKey(event) { const prevPanel = this.findPrevPanel(this.findPanel(event.currentTarget)); prevPanel ? this.changeFocusedPanel(event, prevPanel) : this.onEndKey(event); event.preventDefault(); } onHomeKey(event) { const firstPanel = this.findFirstPanel(); this.changeFocusedPanel(event, firstPanel); event.preventDefault(); } onEndKey(event) { const lastPanel = this.findLastPanel(); this.changeFocusedPanel(event, lastPanel); event.preventDefault(); } onEnterKey(event) { this.changeActiveValue(); event.preventDefault(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionHeader, deps: null, target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.2", type: AccordionHeader, isStandalone: true, selector: "p-accordion-header, p-accordionheader", host: { listeners: { "click": "onClick($event)", "focus": "onFocus($event)", "keydown": "onKeydown($event)" }, properties: { "class.p-accordionheader": "true", "attr.id": "id()", "attr.aria-expanded": "active()", "attr.aria-controls": "ariaControls()", "attr.role": "\"button\"", "attr.tabindex": "\"0\"", "attr.data-p-active": "active()", "attr.data-p-disabled": "disabled()", "attr.data-pc-name": "\"accordionheader\"", "style.user-select": "\"none\"" } }, queries: [{ propertyName: "toggleicon", first: true, predicate: ["toggleicon"], descendants: true }], usesInheritance: true, hostDirectives: [{ directive: i1.Ripple }], ngImport: i0, template: ` <ng-content /> @if (toggleicon) { <ng-template *ngTemplateOutlet="toggleicon; context: { active: active() }"></ng-template> } @else { <ng-container *ngIf="active()"> <span *ngIf="pcAccordion.collapseIcon" [class]="pcAccordion.collapseIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true"></span> <ChevronDownIcon *ngIf="!pcAccordion.collapseIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true" /> </ng-container> <ng-container *ngIf="!active()"> <span *ngIf="pcAccordion.expandIcon" [class]="pcAccordion.expandIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true"></span> <ChevronUpIcon *ngIf="!pcAccordion.expandIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true" /> </ng-container> } `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ChevronDownIcon, selector: "ChevronDownIcon" }, { kind: "component", type: ChevronUpIcon, selector: "ChevronUpIcon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionHeader, decorators: [{ type: Component, args: [{ selector: 'p-accordion-header, p-accordionheader', imports: [CommonModule, ChevronDownIcon, ChevronUpIcon], standalone: true, template: ` <ng-content /> @if (toggleicon) { <ng-template *ngTemplateOutlet="toggleicon; context: { active: active() }"></ng-template> } @else { <ng-container *ngIf="active()"> <span *ngIf="pcAccordion.collapseIcon" [class]="pcAccordion.collapseIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true"></span> <ChevronDownIcon *ngIf="!pcAccordion.collapseIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true" /> </ng-container> <ng-container *ngIf="!active()"> <span *ngIf="pcAccordion.expandIcon" [class]="pcAccordion.expandIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true"></span> <ChevronUpIcon *ngIf="!pcAccordion.expandIcon" [ngClass]="pcAccordion.iconClass" [attr.aria-hidden]="true" /> </ng-container> } `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { '[class.p-accordionheader]': 'true', '[attr.id]': 'id()', '[attr.aria-expanded]': 'active()', '[attr.aria-controls]': 'ariaControls()', '[attr.role]': '"button"', '[attr.tabindex]': '"0"', '[attr.data-p-active]': 'active()', '[attr.data-p-disabled]': 'disabled()', '[attr.data-pc-name]': '"accordionheader"', '[style.user-select]': '"none"' }, hostDirectives: [Ripple] }] }], propDecorators: { toggleicon: [{ type: ContentChild, args: ['toggleicon'] }], onClick: [{ type: HostListener, args: ['click', ['$event']] }], onFocus: [{ type: HostListener, args: ['focus', ['$event']] }], onKeydown: [{ type: HostListener, args: ['keydown', ['$event']] }] } }); class AccordionContent extends BaseComponent { pcAccordion = inject(forwardRef(() => Accordion)); pcAccordionPanel = inject(forwardRef(() => AccordionPanel)); active = computed(() => this.pcAccordionPanel.active()); ariaLabelledby = computed(() => `${this.pcAccordion.id()}_accordionheader_${this.pcAccordionPanel.value()}`); id = computed(() => `${this.pcAccordion.id()}_accordioncontent_${this.pcAccordionPanel.value()}`); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionContent, deps: null, target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.2", type: AccordionContent, isStandalone: true, selector: "p-accordion-content, p-accordioncontent", host: { properties: { "class.p-accordioncontent": "true", "attr.id": "id()", "attr.role": "\"region\"", "attr.data-pc-name": "\"accordioncontent\"", "attr.data-p-active": "active()", "attr.aria-labelledby": "ariaLabelledby()", "@content": "active()\n ? { value: 'visible', params: { transitionParams: pcAccordion.transitionOptions } }\n : { value: 'hidden', params: { transitionParams: pcAccordion.transitionOptions } }" } }, usesInheritance: true, ngImport: i0, template: ` <div class="p-accordioncontent-content"> <ng-content /> </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }], animations: [ trigger('content', [ state('hidden', style({ height: '0', visibility: 'hidden' })), state('visible', style({ height: '*', visibility: 'visible' })), transition('visible <=> hidden', [animate('{{transitionParams}}')]), transition('void => *', animate(0)) ]) ], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionContent, decorators: [{ type: Component, args: [{ selector: 'p-accordion-content, p-accordioncontent', imports: [CommonModule], standalone: true, template: ` <div class="p-accordioncontent-content"> <ng-content /> </div>`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { '[class.p-accordioncontent]': 'true', '[attr.id]': 'id()', '[attr.role]': '"region"', '[attr.data-pc-name]': '"accordioncontent"', '[attr.data-p-active]': 'active()', '[attr.aria-labelledby]': 'ariaLabelledby()', '[@content]': `active() ? { value: 'visible', params: { transitionParams: pcAccordion.transitionOptions } } : { value: 'hidden', params: { transitionParams: pcAccordion.transitionOptions } }` }, animations: [ trigger('content', [ state('hidden', style({ height: '0', visibility: 'hidden' })), state('visible', style({ height: '*', visibility: 'visible' })), transition('visible <=> hidden', [animate('{{transitionParams}}')]), transition('void => *', animate(0)) ]) ] }] }] }); /** * AccordionTab is a helper component for Accordion. * @deprecated Use AccordionPanel, AccordionHeader, AccordionContent instead. * @group Components */ class AccordionTab extends BaseComponent { get hostClass() { return this.tabStyleClass; } get hostStyle() { return this.tabStyle; } /** * Current id state as a string. * @group Props */ id = uuid('pn_id_'); /** * Used to define the header of the tab. * @group Props */ header; /** * Inline style of the tab header. * @group Props */ headerStyle; /** * Inline style of the tab. * @group Props */ tabStyle; /** * Inline style of the tab content. * @group Props */ contentStyle; /** * Style class of the tab. * @group Props */ tabStyleClass; /** * Style class of the tab header. * @group Props */ headerStyleClass; /** * Style class of the tab content. * @group Props */ contentStyleClass; /** * Whether the tab is disabled. * @group Props */ disabled; /** * Whether a lazy loaded panel should avoid getting loaded again on reselection. * @group Props */ cache = true; /** * Transition options of the animation. * @group Props */ transitionOptions = '400ms cubic-bezier(0.86, 0, 0.07, 1)'; /** * Position of the icon. * @group Props */ iconPos = 'start'; /** * The value that returns the selection. * @group Props */ get selected() { return this._selected; } set selected(val) { this._selected = val; if (!this.loaded) { if (this._selected && this.cache) { this.loaded = true; } this.cd.detectChanges(); } } /** * The aria-level that each accordion header will have. The default value is 2 as per W3C specifications * @group Props */ headerAriaLevel = 2; /** * Event triggered by changing the choice. * @param {boolean} value - Boolean value indicates that the option is changed. * @group Emits */ selectedChange = new EventEmitter(); headerFacet; _selected = false; get iconClass() { if (this.iconPos === 'end') { return 'p-accordionheader-toggle-icon icon-end'; } else { return 'p-accordionheader-toggle-icon icon-start'; } } /** * Content template for the content of the drawer. * @group Templates */ headerTemplate; /** * Template for the header icon. * @group Templates */ iconTemplate; /** * Content template for the footer of the drawer. * @group Templates */ contentTemplate; templates; _headerTemplate; _iconTemplate; _contentTemplate; loaded = false; accordion = inject(forwardRef(() => Accordion)); _componentStyle = inject(AccordionStyle); ngOnInit() { super.ngOnInit(); console.log('AccordionTab is deprecated as of v18, please use the new structure instead.'); } ngAfterContentInit() { this.templates.forEach((item) => { switch (item.getType()) { case 'content': this._contentTemplate = item.template; break; case 'header': this._headerTemplate = item.template; break; case 'icon': this._iconTemplate = item.template; break; default: this._contentTemplate = item.template; break; } }); } toggle(event) { if (this.disabled) { return false; } let index = this.findTabIndex(); if (this.selected) { this.selected = false; this.accordion.onClose.emit({ originalEvent: event, index: index }); } else { if (!this.accordion.multiple()) { for (var i = 0; i < this.accordion.tabs.length; i++) { if (this.accordion.tabs[i].selected) { this.accordion.tabs[i].selected = false; this.accordion.tabs[i].selectedChange.emit(false); this.accordion.tabs[i].cd.markForCheck(); } } } this.selected = true; this.loaded = true; this.accordion.onOpen.emit({ originalEvent: event, index: index }); } this.selectedChange.emit(this.selected); this.accordion.updateActiveIndex(); this.cd.markForCheck(); event?.preventDefault(); } findTabIndex() { let index = -1; for (var i = 0; i < this.accordion.tabs.length; i++) { if (this.accordion.tabs[i] == this) { index = i; break; } } return index; } onKeydown(event) { switch (event.code) { case 'Enter': case 'Space': this.toggle(event); event.preventDefault(); break; default: break; } } getTabHeaderActionId(tabId) { return `${tabId}_header_action`; } getTabContentId(tabId) { return `${tabId}_content`; } ngOnDestroy() { this.accordion.tabs.splice(this.findTabIndex(), 1); super.ngOnDestroy(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionTab, deps: null, target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.2", type: AccordionTab, isStandalone: true, selector: "p-accordionTab, p-accordion-tab, p-accordiontab", inputs: { id: "id", header: "header", headerStyle: "headerStyle", tabStyle: "tabStyle", contentStyle: "contentStyle", tabStyleClass: "tabStyleClass", headerStyleClass: "headerStyleClass", contentStyleClass: "contentStyleClass", disabled: ["disabled", "disabled", booleanAttribute], cache: ["cache", "cache", booleanAttribute], transitionOptions: "transitionOptions", iconPos: "iconPos", selected: "selected", headerAriaLevel: ["headerAriaLevel", "headerAriaLevel", numberAttribute] }, outputs: { selectedChange: "selectedChange" }, host: { properties: { "class.p-accordionpanel": "true", "class.p-accordionpanel-active": "selected", "attr.data-pc-name": "\"accordiontab\"", "class": "this.hostClass", "style": "this.hostStyle" } }, providers: [AccordionStyle], queries: [{ propertyName: "headerTemplate", first: true, predicate: ["header"] }, { propertyName: "iconTemplate", first: true, predicate: ["icon"] }, { propertyName: "contentTemplate", first: true, predicate: ["content"] }, { propertyName: "headerFacet", predicate: Header }, { propertyName: "templates", predicate: PrimeTemplate }], usesInheritance: true, ngImport: i0, template: ` <button class="p-accordionheader" type="button" [disabled]="disabled" [attr.aria-expanded]="selected" [attr.aria-level]="headerAriaLevel" [class.p-disabled]="disabled" [attr.data-p-disabled]="disabled" [attr.data-pc-section]="'accordionheader'" (click)="toggle($event)" (keydown)="onKeydown($event)" [ngClass]="headerStyleClass" [ngStyle]="headerStyle" [attr.tabindex]="disabled ? null : 0" [attr.id]="getTabHeaderActionId(id)" [attr.aria-controls]="getTabContentId(id)" > @if (!headerTemplate && !_headerTemplate) { {{ header }} } @else { @if (headerTemplate || _headerTemplate) { <ng-container *ngTemplateOutlet="headerTemplate || _headerTemplate"></ng-container> } @if (headerFacet) { <ng-content select="p-header" /> } } @if (iconTemplate || _iconTemplate) { <ng-template *ngTemplateOutlet="iconTemplate || _iconTemplate; context: { $implicit: selected }"></ng-template> } @else { <ng-container *ngIf="selected"> <span *ngIf="accordion.collapseIcon" [class]="accordion.collapseIcon" [ngClass]="iconClass" [attr.aria-hidden]="true"></span> <ChevronDownIcon *ngIf="!accordion.collapseIcon" [ngClass]="iconClass" [attr.aria-hidden]="true" /> </ng-container> <ng-container *ngIf="!selected"> <span *ngIf="accordion.expandIcon" [class]="accordion.expandIcon" [ngClass]="iconClass" [attr.aria-hidden]="true"></span> <ChevronUpIcon *ngIf="!accordion.expandIcon" [ngClass]="iconClass" [attr.aria-hidden]="true" /> </ng-container> } </button> <div [attr.id]="getTabContentId(id)" class="p-accordioncontent" [@tabContent]="selected ? { value: 'visible', params: { transitionParams: transitionOptions } } : { value: 'hidden', params: { transitionParams: transitionOptions } }" role="region" [attr.aria-hidden]="!selected" [attr.aria-labelledby]="getTabHeaderActionId(id)" [attr.data-pc-section]="'toggleablecontent'" > <div class="p-accordioncontent-content" [ngClass]="contentStyleClass" [ngStyle]="contentStyle"> <ng-content /> <ng-container *ngIf="(contentTemplate || _contentTemplate) && (cache ? loaded : selected)"> <ng-container *ngTemplateOutlet="contentTemplate || _contentTemplate"></ng-container> </ng-container> </div> </div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: ChevronDownIcon, selector: "ChevronDownIcon" }, { kind: "component", type: ChevronUpIcon, selector: "ChevronUpIcon" }], animations: [ trigger('tabContent', [ state('hidden', style({ height: '0', visibility: 'hidden' })), state('visible', style({ height: '*', visibility: 'visible' })), transition('visible <=> hidden', [animate('{{transitionParams}}')]), transition('void => *', animate(0)) ]) ], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: AccordionTab, decorators: [{ type: Component, args: [{ selector: 'p-accordionTab, p-accordion-tab, p-accordiontab', standalone: true, imports: [CommonModule, ChevronDownIcon, ChevronUpIcon], template: ` <button class="p-accordionheader" type="button" [disabled]="disabled" [attr.aria-expanded]="selected" [attr.aria-level]="headerAriaLevel" [class.p-disabled]="disabled" [attr.data-p-disabled]="disabled" [attr.data-pc-section]="'accordionheader'" (click)="toggle($event)" (keydown)="onKeydown($event)" [ngClass]="headerStyleClass" [ngStyle]="headerStyle" [attr.tabindex]="disabled ? null : 0" [attr.id]="getTabHeaderActionId(id)" [attr.aria-controls]="getTabContentId(id)" > @if (!headerTemplate && !_headerTemplate) { {{ header }} } @else { @if (headerTemplate || _headerTemplate) { <ng-container *ngTemplateOutlet="headerTemplate || _headerTemplate"></ng-container> } @if (headerFacet) { <ng-content select="p-header" /> } } @if (iconTemplate || _iconTemplate) { <ng-template *ngTemplateOutlet="iconTemplate || _iconTemplate; context: { $implicit: selected }"></ng-template> } @else { <ng-container *ngIf="selected"> <span *ngIf="accordion.collapseIcon" [class]="accordion.collapseIcon" [ngClass]="iconClass" [attr.aria-hidden]="true"></span> <ChevronDownIcon *ngIf="!accordion.collapseIcon" [ngClass]="iconClass" [attr.aria-hidden]="true" /> </ng-container> <ng-container *ngIf="!selected"> <span *ngIf="accordion.expandIcon" [class]="accordion.expandIcon" [ngClass]="iconClass" [attr.aria-hidden]="true"></span> <ChevronUpIcon *ngIf="!accordion.expandIcon" [ngClass]="iconClass" [attr.aria-hidden]="true" /> </ng-container> } </button> <div [attr.id]="getTabContentId(id)" class="p-accordioncontent" [@tabContent]="selected ? { value: 'visible', params: { transitionParams: transitionOptions } } : { value: 'hidden', params: { transitionParams: transitionOptions } }" role="region" [attr.aria-hidden]="!selected" [attr.aria-labelledby]="getTabHeaderActionId(id)" [attr.data-pc-section]="'toggleablecontent'" > <div class="p-accordioncontent-content" [ngClass]="contentStyleClass" [ngStyle]="contentStyle"> <ng-content /> <ng-container *ngIf="(contentTemplate || _contentTemplate) && (cache ? loaded : selected)"> <ng-container *ngTemplateOutlet="contentTemplate || _contentTemplate"></ng-container> </ng-container> </div> </div> `, animations: [ trigger('tabContent', [ state('hidden', style({ height: '0', visibility: 'hidden' })), state('visible', style({ height: '*', visibility: 'visible' })), transition('visible <=> hidden', [animate('{{transitionParams}}')]), transition('void => *', animate(0)) ]) ], host: { '[class.p-accordionpanel]': 'true', '[class.p-accordionpanel-active]': 'selected', '[attr.data-pc-name]': '"accordiontab"' }, providers: [AccordionStyle], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None }] }], propDecorators: { hostClass: [{ type: HostBinding, args: ['class'] }], hostStyle: [{ type: HostBinding, args: ['style'] }], id: [{ type: Input }], header: [{ type: Input }], headerStyle: [{ type: Input }], tabStyle: [{ type: Input }], contentStyle: [{ type: Input }], tabStyleClass: [{ type: Input }], headerStyleClass: [{ type: Input }], contentStyleClass: [{ type: Input }], disabled: [{ type: Input, args: [{ transform: booleanAttribute }] }], cache: [{ type: Input, args: [{ transform: booleanAttribute }] }], transitionOptions: [{ type: Input }], iconPos: [{ type: Input }], selected: [{ type: Input }], headerAriaLevel: [{ type: Input, args: [{ transform: numberAttribute }] }], selectedChange: [{ type: Output }], headerFacet: [{ type: ContentChildren, args: [Header] }], headerTemplate: [{ type: ContentChild, args: ['header', { descendants: false }] }], iconTemplate: [{ type: ContentChild, args: ['icon', { descendants: false }] }], contentTemplate: [{ type: ContentChild, args: ['content', { descendants: false }] }], templates: [{ type: ContentChildren, args: [PrimeTemplate] }] } }); /** * Accordion groups a collection of contents in tabs. * @group Components */ class Accordion extends BaseComponent { get hostClass() { return this.styleClass; } get hostStyle() { return this.style; } /** * Value of the active tab. * @defaultValue undefined * @group Props */ value = model(undefined); /** * When enabled, multiple tabs can be activated at the same time. * @defaultValue false * @group Props */ multiple = input(false, { transform: (v) => transformToBoolean(v) }); /** * Inline style of the tab header and content. * @group Props */ style; /** * Class of the element. * @group Props */ styleClass; /** * Icon of a collapsed tab. * @group Props */ expandIcon; /** * Icon of an expanded tab. * @group Props */ collapseIcon; /** * When enabled, the focused tab is activated. * @defaultValue false * @group Props */ selectOnFocus = input(false, { transform: (v) => transformToBoolean(v) }); set activeIndex(val) { this._activeIndex = val; if (this.preventActiveIndexPropagation) { this.preventActiveIndexPropagation = false; return; } this.updateSelectionState(); } /** * Transition options of the animation. * @group Props */ transitionOptions = '400ms cubic-bezier(0.86, 0, 0.07, 1)'; /** * Returns the active index. * @param {number | number[]} value - New index. * @deprecated use native valueChange emitter of the value model. * @group Emits */ activeIndexChange = new EventEmitter(); set headerAriaLevel(val) { if (typeof val === 'number' && val > 0) { this._headerAriaLevel = val; } else if (this._headerAriaLevel !== 2) { this._headerAriaLevel = 2; } } /** * Callback to invoke when an active tab is collapsed by clicking on the header. * @param {AccordionTabCloseEvent} event - Custom tab close event. * @group Emits */ onClose = new EventEmitter(); /** * Callback to invoke when a tab gets expanded. * @param {AccordionTabOpenEvent} event - Custom tab open event. * @group Emits */ onOpen = new EventEmitter(); id = signal(uuid('pn_id_')); tabList; tabListSubscription = null; _activeIndex; _headerAriaLevel = 2; preventActiveIndexPropagation = false; tabs = []; _componentStyle = inject(AccordionStyle); /** * Index of the active tab or an array of indexes in multiple mode. * @deprecated use value property with new architecture instead. * @group Props */ get activeIndex() { return this._activeIndex; } /** * The aria-level that each accordion header will have. The default value is 2 as per W3C specifications * @deprecated use AccoridonHeader component and bind attribute to the host. * @group Props */ get headerAriaLevel() { return this._headerAriaLevel; } onKeydown(event) { switch (event.code) { case 'ArrowDown': this.onTabArrowDownKey(event); break; case 'ArrowUp': this.onTabArrowUpKey(event); break; case 'Home': if (!event.shiftKey) { this.onTabHomeKey(event); } break; case 'End': if (!event.shiftKey) { this.onTabEndKey(event); } break; } } onTabArrowDownKey(event) { const nextHeaderAction = this.findNextHeaderAction(event.target.parentElement); nextHeaderAction ? this.changeFocusedTab(nextHeaderAction) : this.onTabHomeKey(event); event.preventDefault(); } onTabArrowUpKey(event) { const prevHeaderAction = this.findPrevHeaderAction(event.target.parentElement); prevHeaderAction ? this.changeFocusedTab(prevHeaderAction) : this.onTabEndKey(event); event.preventDefault(); } onTabHomeKey(event) { const firstHeaderAction = this.findFirstHeaderAction(); this.changeFocusedTab(firstHeaderAction); event.preventDefault(); } changeFocusedTab(element) { if (element) { focus(element); if (this.selectOnFocus()) { this.tabs.forEach((tab, i) => { let selected = this.multiple() ? this._activeIndex.includes(i) : i === this._activeIndex; if (this.multiple()) { if (!this._activeIndex) { this._activeIndex = []; } if (tab.id == element.id) { tab.selected = !tab.selected; if (!this._activeIndex.includes(i)) { this._activeIndex.push(i); } else { this._activeIndex = this._activeIndex.filter((ind) => ind !== i); } } } else { if (tab.id == element.id) { tab.selected = !tab.selected; this._activeIndex = i; } else { tab.selected = false; } } tab.selectedChange.emit(selected); this.activeIndexChange.emit(this._activeIndex); tab.cd.markForCheck(); }); } } } findNextHeaderAction(tabElement, selfCheck = false) { const nextTabElement = selfCheck ? tabElement : tabElement.nextElementSibling; const headerElement = findSingle(nextTabElement, '[data-pc-section="accordionheader"]'); return headerElement ? (getAttribute(headerElement, 'data-p-disabled') ? this.findNextHeaderAction(headerElement.parentElement) : findSingle(headerElement.parentElement, '[data-pc-section="accordionheader"]')) : null; } findPrevHeaderAction(tabElement, selfCheck = false) { const prevTabElement = selfCheck ? tabElement : tabElement.previousElementSibling; const headerElement = findSingle(prevTabElement, '[data-pc-section="accordionheader"]'); return headerElement ? (getAttribute(headerElement, 'data-p-disabled') ? this.findPrevHeaderAction(headerElement.parentElement) : findSingle(headerElement.parentElement, '[data-pc-section="accordionheader"]')) : null; } findFirstHeaderAction() { const firstEl = this.el.nativeElement.firstElementChild; return this.findNextHeaderAction(firstEl, true); } findLastHeaderAction() { const lastEl = this.el.nativeElement.lastElementChild; return this.findPrevHeaderAction(lastEl, true); } onTabEndKey(event) { const lastHeaderAction = this.findLastHeaderAction(); this.changeFocusedTab(lastHeaderAction); event.preventDefault(); } ngAfterContentInit() { this.initTabs(); this.tabListSubscription = this.tabList.changes.subscribe((_) => { this.initTabs(); }); } initTabs() { this.tabs = this.tabList.toArray(); this.tabs.forEach((tab) => { tab.headerAriaLevel = this._headerAriaLevel; }); this.updateSelectionState(); this.cd.markForCheck(); } getBlockableElement() { return this.el.nativeElement.children[0]; } updateSelectionState() { if (this.tabs && this.tabs.length && this._activeIndex != null) { for (let i = 0; i < this.tabs.length; i++) { let selected = this.multiple() ? this._activeIndex.includes(i) : i === this._activeIndex; let changed = selected !== this.tabs[i].selected; if (changed) { this.tabs[i].selected = selected; this.tabs[i].selectedChange.emit(selected); this.tabs[i].cd.markForCheck(); } } } } isTabActive(index) { return this.multiple() ? this._activeIndex && this._activeIndex.includes(index) : this._activeIndex === index; } getTabProp(tab, name) { return tab.props ? tab.props[name] : undefined; } updateActiveIndex() { let index = this.multiple() ? [] : null; this.tabs.forEach((tab, i) => { if (tab.selected) { if (this.multiple()) { index.push(i); } else { index = i; return; } } }); this.preventActiveIndexPropagation = true; this._activeIndex = index; this.activeIndexChange.emit(index); } updateValue(value) { const currentValue = this.value(); if (this.multiple()) { const newValue = Array.isArray(currentValue) ? [...currentValue] : []; const index = newValue.indexOf(value); if (index !== -1) { newValue.splice(index, 1); } else { newValue.push(value); }