UNPKG

@ng-bootstrap/ng-bootstrap

Version:
1,103 lines (1,092 loc) 568 kB
import * as i0 from '@angular/core'; import { Injectable, Directive, EventEmitter, Input, Output, ContentChildren, forwardRef, Inject, Optional, Host, Component, ViewEncapsulation, inject, ApplicationRef, ElementRef, TemplateRef, ContentChild, NgModule, ChangeDetectionStrategy, PLATFORM_ID, LOCALE_ID, ViewChild, Injector, EnvironmentInjector, createComponent, Attribute, ViewChildren, HostBinding, ChangeDetectorRef, NgZone, DestroyRef, InjectionToken } from '@angular/core'; import { Observable, EMPTY, of, Subject, fromEvent, timer, race, BehaviorSubject, combineLatest, NEVER, zip, merge } from 'rxjs'; import { endWith, takeUntil, filter, take, map, startWith, distinctUntilChanged, switchMap, tap, withLatestFrom, delay, mergeMap, skip, share, finalize } from 'rxjs/operators'; import { NgFor, NgTemplateOutlet, NgIf, isPlatformBrowser, getLocaleMonthNames, FormStyle, TranslationWidth, getLocaleDayNames, formatDate, DOCUMENT, PercentPipe, getLocaleDayPeriods } from '@angular/common'; import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms'; import { flip, preventOverflow, arrow, createPopperLite, offset } from '@popperjs/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; function toInteger(value) { return parseInt(`${value}`, 10); } function toString(value) { return value !== undefined && value !== null ? `${value}` : ''; } function getValueInRange(value, max, min = 0) { return Math.max(Math.min(value, max), min); } function isString(value) { return typeof value === 'string'; } function isNumber(value) { return !isNaN(toInteger(value)); } function isInteger(value) { return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; } function isDefined(value) { return value !== undefined && value !== null; } function isPromise(v) { return v && v.then; } function padNumber(value) { if (isNumber(value)) { return `0${value}`.slice(-2); } else { return ''; } } function regExpEscape(text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); } function hasClassName(element, className) { return (element && element.className && element.className.split && element.className.split(/\s+/).indexOf(className) >= 0); } function closest(element, selector) { if (!selector) { return null; } /* * In certain browsers (e.g. Edge 44.18362.449.0) HTMLDocument does * not support `Element.prototype.closest`. To emulate the correct behaviour * we return null when the method is missing. * * Note that in evergreen browsers `closest(document.documentElement, 'html')` * will return the document element whilst in Edge null will be returned. This * compromise was deemed good enough. */ if (typeof element.closest === 'undefined') { return null; } return element.closest(selector); } /** * Force a browser reflow * @param element element where to apply the reflow */ function reflow(element) { return (element || document.body).getBoundingClientRect(); } /** * Creates an observable where all callbacks are executed inside a given zone * * @param zone */ function runInZone(zone) { return (source) => { return new Observable((observer) => { const next = (value) => zone.run(() => observer.next(value)); const error = (e) => zone.run(() => observer.error(e)); const complete = () => zone.run(() => observer.complete()); return source.subscribe({ next, error, complete }); }); }; } function removeAccents(str) { return str.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); } /** * Returns the active element in the given root. * If the active element is inside a shadow root, it is searched recursively. */ function getActiveElement(root = document) { const activeEl = root?.activeElement; if (!activeEl) { return null; } return activeEl.shadowRoot ? getActiveElement(activeEl.shadowRoot) : activeEl; } function getTransitionDurationMs(element) { const { transitionDelay, transitionDuration } = window.getComputedStyle(element); const transitionDelaySec = parseFloat(transitionDelay); const transitionDurationSec = parseFloat(transitionDuration); return (transitionDelaySec + transitionDurationSec) * 1000; } const environment = { animation: true, transitionTimerDelayMs: 5, }; const noopFn = () => { }; const { transitionTimerDelayMs } = environment; const runningTransitions = new Map(); const ngbRunTransition = (zone, element, startFn, options) => { // Getting initial context from options let context = options.context || {}; // Checking if there are already running transitions on the given element. const running = runningTransitions.get(element); if (running) { switch (options.runningTransition) { // If there is one running and we want for it to 'continue' to run, we have to cancel the new one. // We're not emitting any values, but simply completing the observable (EMPTY). case 'continue': return EMPTY; // If there is one running and we want for it to 'stop', we have to complete the running one. // We're simply completing the running one and not emitting any values and merging newly provided context // with the one coming from currently running transition. case 'stop': zone.run(() => running.transition$.complete()); context = Object.assign(running.context, context); runningTransitions.delete(element); } } // Running the start function const endFn = startFn(element, options.animation, context) || noopFn; // If 'prefer-reduced-motion' is enabled, the 'transition' will be set to 'none'. // If animations are disabled, we have to emit a value and complete the observable // In this case we have to call the end function, but can finish immediately by emitting a value, // completing the observable and executing end functions synchronously. if (!options.animation || window.getComputedStyle(element).transitionProperty === 'none') { zone.run(() => endFn()); return of(undefined).pipe(runInZone(zone)); } // Starting a new transition const transition$ = new Subject(); const finishTransition$ = new Subject(); const stop$ = transition$.pipe(endWith(true)); runningTransitions.set(element, { transition$, complete: () => { finishTransition$.next(); finishTransition$.complete(); }, context, }); const transitionDurationMs = getTransitionDurationMs(element); // 1. We have to both listen for the 'transitionend' event and have a 'just-in-case' timer, // because 'transitionend' event might not be fired in some browsers, if the transitioning // element becomes invisible (ex. when scrolling, making browser tab inactive, etc.). The timer // guarantees, that we'll release the DOM element and complete 'ngbRunTransition'. // 2. We need to filter transition end events, because they might bubble from shorter transitions // on inner DOM elements. We're only interested in the transition on the 'element' itself. zone.runOutsideAngular(() => { const transitionEnd$ = fromEvent(element, 'transitionend').pipe(takeUntil(stop$), filter(({ target }) => target === element)); const timer$ = timer(transitionDurationMs + transitionTimerDelayMs).pipe(takeUntil(stop$)); race(timer$, transitionEnd$, finishTransition$) .pipe(takeUntil(stop$)) .subscribe(() => { runningTransitions.delete(element); zone.run(() => { endFn(); transition$.next(); transition$.complete(); }); }); }); return transition$.asObservable(); }; const ngbCompleteTransition = (element) => { runningTransitions.get(element)?.complete(); }; function measureCollapsingElementDimensionPx(element, dimension) { // SSR fix for without injecting the PlatformId if (typeof navigator === 'undefined') { return '0px'; } const { classList } = element; const hasShownClass = classList.contains('show'); if (!hasShownClass) { classList.add('show'); } element.style[dimension] = ''; const dimensionSize = element.getBoundingClientRect()[dimension] + 'px'; if (!hasShownClass) { classList.remove('show'); } return dimensionSize; } const ngbCollapsingTransition = (element, animation, context) => { let { direction, maxSize, dimension } = context; const { classList } = element; function setInitialClasses() { classList.add('collapse'); if (direction === 'show') { classList.add('show'); } else { classList.remove('show'); } } // without animations we just need to set initial classes if (!animation) { setInitialClasses(); return; } // No maxHeight -> running the transition for the first time if (!maxSize) { maxSize = measureCollapsingElementDimensionPx(element, dimension); context.maxSize = maxSize; // Fix the height before starting the animation element.style[dimension] = direction !== 'show' ? maxSize : '0px'; classList.remove('collapse'); classList.remove('collapsing'); classList.remove('show'); reflow(element); // Start the animation classList.add('collapsing'); } // Start or revert the animation element.style[dimension] = direction === 'show' ? maxSize : '0px'; return () => { setInitialClasses(); classList.remove('collapsing'); element.style[dimension] = ''; }; }; /** * Global ng-bootstrap config * * @since 8.0.0 */ class NgbConfig { constructor() { this.animation = environment.animation; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbConfig, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbConfig, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); /** * A configuration service for the [`NgbAccordionDirective`](#/components/accordion/api#NgbAccordionDirective). * * You can inject this service, typically in your root component, and customize its properties * to provide default values for all accordions used in the application. */ class NgbAccordionConfig { constructor(_ngbConfig) { this._ngbConfig = _ngbConfig; this.closeOthers = false; } get animation() { return this._animation === undefined ? this._ngbConfig.animation : this._animation; } set animation(animation) { this._animation = animation; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionConfig, deps: [{ token: NgbConfig }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionConfig, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionConfig, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: NgbConfig }]; } }); /* eslint-disable deprecation/deprecation */ let nextId$4 = 0; /** * A directive that wraps an accordion panel header with any HTML markup and a toggling button * marked with [`NgbPanelToggle`](#/components/accordion/api#NgbPanelToggle). * See the [header customization demo](#/components/accordion/examples#header) for more details. * * You can also use [`NgbPanelTitle`](#/components/accordion/api#NgbPanelTitle) to customize only the panel title. * * @since 4.1.0 * @deprecated 14.1.0 */ class NgbPanelHeader { constructor(templateRef) { this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelHeader, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbPanelHeader, isStandalone: true, selector: "ng-template[ngbPanelHeader]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelHeader, decorators: [{ type: Directive, args: [{ selector: 'ng-template[ngbPanelHeader]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * A directive that wraps only the panel title with HTML markup inside. * * You can also use [`NgbPanelHeader`](#/components/accordion/api#NgbPanelHeader) to customize the full panel header. * * @deprecated 14.1.0 */ class NgbPanelTitle { constructor(templateRef) { this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelTitle, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbPanelTitle, isStandalone: true, selector: "ng-template[ngbPanelTitle]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelTitle, decorators: [{ type: Directive, args: [{ selector: 'ng-template[ngbPanelTitle]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * A directive that wraps the accordion panel content. * * @deprecated 14.1.0 */ class NgbPanelContent { constructor(templateRef) { this.templateRef = templateRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelContent, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbPanelContent, isStandalone: true, selector: "ng-template[ngbPanelContent]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelContent, decorators: [{ type: Directive, args: [{ selector: 'ng-template[ngbPanelContent]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } }); /** * A directive that wraps an individual accordion panel with title and collapsible content. * * @deprecated 14.1.0 */ class NgbPanel { constructor() { /** * If `true`, the panel is disabled an can't be toggled. */ this.disabled = false; /** * An optional id for the panel that must be unique on the page. * * If not provided, it will be auto-generated in the `ngb-panel-xxx` format. */ this.id = `ngb-panel-${nextId$4++}`; this.isOpen = false; /* A flag to specified that the transition panel classes have been initialized */ this.initClassDone = false; /* A flag to specified if the panel is currently being animated, to ensure its presence in the dom */ this.transitionRunning = false; /** * An event emitted when the panel is shown, after the transition. It has no payload. * * @since 8.0.0 */ this.shown = new EventEmitter(); /** * An event emitted when the panel is hidden, after the transition. It has no payload. * * @since 8.0.0 */ this.hidden = new EventEmitter(); } ngAfterContentChecked() { // We are using @ContentChildren instead of @ContentChild as in the Angular version being used // only @ContentChildren allows us to specify the {descendants: false} option. // Without {descendants: false} we are hitting bugs described in: // https://github.com/ng-bootstrap/ng-bootstrap/issues/2240 this.titleTpl = this.titleTpls.first; this.headerTpl = this.headerTpls.first; this.contentTpl = this.contentTpls.first; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbPanel, isStandalone: true, selector: "ngb-panel", inputs: { disabled: "disabled", id: "id", title: "title", type: "type", cardClass: "cardClass" }, outputs: { shown: "shown", hidden: "hidden" }, queries: [{ propertyName: "titleTpls", predicate: NgbPanelTitle }, { propertyName: "headerTpls", predicate: NgbPanelHeader }, { propertyName: "contentTpls", predicate: NgbPanelContent }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanel, decorators: [{ type: Directive, args: [{ selector: 'ngb-panel', standalone: true }] }], propDecorators: { disabled: [{ type: Input }], id: [{ type: Input }], title: [{ type: Input }], type: [{ type: Input }], cardClass: [{ type: Input }], shown: [{ type: Output }], hidden: [{ type: Output }], titleTpls: [{ type: ContentChildren, args: [NgbPanelTitle, { descendants: false }] }], headerTpls: [{ type: ContentChildren, args: [NgbPanelHeader, { descendants: false }] }], contentTpls: [{ type: ContentChildren, args: [NgbPanelContent, { descendants: false }] }] } }); class NgbRefDirective { constructor(_El) { this._El = _El; this.ngbRef = new EventEmitter(); } ngOnInit() { this.ngbRef.emit(this._El.nativeElement); } ngOnDestroy() { this.ngbRef.emit(null); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbRefDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbRefDirective, isStandalone: true, selector: "[ngbRef]", outputs: { ngbRef: "ngbRef" }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbRefDirective, decorators: [{ type: Directive, args: [{ selector: '[ngbRef]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { ngbRef: [{ type: Output }] } }); /** * A directive to put on a button that toggles panel opening and closing. * * To be used inside the [`NgbPanelHeader`](#/components/accordion/api#NgbPanelHeader) * * @since 4.1.0 * @deprecated 14.1.0 */ class NgbPanelToggle { set ngbPanelToggle(panel) { if (panel) { this.panel = panel; } } constructor(accordion, panel) { this.accordion = accordion; this.panel = panel; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelToggle, deps: [{ token: forwardRef(() => NgbAccordion) }, { token: NgbPanel, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbPanelToggle, isStandalone: true, selector: "button[ngbPanelToggle]", inputs: { ngbPanelToggle: "ngbPanelToggle" }, host: { attributes: { "type": "button" }, listeners: { "click": "accordion.toggle(panel.id)" }, properties: { "disabled": "panel.disabled", "class.collapsed": "!panel.isOpen", "attr.aria-expanded": "panel.isOpen", "attr.aria-controls": "panel.id" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbPanelToggle, decorators: [{ type: Directive, args: [{ selector: 'button[ngbPanelToggle]', standalone: true, host: { type: 'button', '[disabled]': 'panel.disabled', '[class.collapsed]': '!panel.isOpen', '[attr.aria-expanded]': 'panel.isOpen', '[attr.aria-controls]': 'panel.id', '(click)': 'accordion.toggle(panel.id)', }, }] }], ctorParameters: function () { return [{ type: NgbAccordion, decorators: [{ type: Inject, args: [forwardRef(() => NgbAccordion)] }] }, { type: NgbPanel, decorators: [{ type: Optional }, { type: Host }] }]; }, propDecorators: { ngbPanelToggle: [{ type: Input }] } }); /** * Accordion is a collection of collapsible panels (bootstrap cards). * * It can ensure only one panel is opened at a time and allows to customize panel * headers. * * @deprecated 14.1.0 */ class NgbAccordion { constructor(config, _ngZone, _changeDetector) { this._ngZone = _ngZone; this._changeDetector = _changeDetector; /** * An array or comma separated strings of panel ids that should be opened **initially**. * * For subsequent changes use methods like `expand()`, `collapse()`, etc. and * the `(panelChange)` event. */ this.activeIds = []; /** * If `true`, panel content will be detached from DOM and not simply hidden when the panel is collapsed. */ this.destroyOnHide = true; /** * Event emitted right before the panel toggle happens. * * See [NgbPanelChangeEvent](#/components/accordion/api#NgbPanelChangeEvent) for payload details. */ this.panelChange = new EventEmitter(); /** * An event emitted when the expanding animation is finished on the panel. The payload is the panel id. * * @since 8.0.0 */ this.shown = new EventEmitter(); /** * An event emitted when the collapsing animation is finished on the panel, and before the panel element is removed. * The payload is the panel id. * * @since 8.0.0 */ this.hidden = new EventEmitter(); this.animation = config.animation; this.type = config.type; this.closeOtherPanels = config.closeOthers; } /** * Checks if a panel with a given id is expanded. */ isExpanded(panelId) { return this.activeIds.indexOf(panelId) > -1; } /** * Expands a panel with a given id. * * Has no effect if the panel is already expanded or disabled. */ expand(panelId) { this._changeOpenState(this._findPanelById(panelId), true); } /** * Expands all panels, if `[closeOthers]` is `false`. * * If `[closeOthers]` is `true`, it will expand the first panel, unless there is already a panel opened. */ expandAll() { if (this.closeOtherPanels) { if (this.activeIds.length === 0 && this.panels.length) { this._changeOpenState(this.panels.first, true); } } else { this.panels.forEach((panel) => this._changeOpenState(panel, true)); } } /** * Collapses a panel with the given id. * * Has no effect if the panel is already collapsed or disabled. */ collapse(panelId) { this._changeOpenState(this._findPanelById(panelId), false); } /** * Collapses all opened panels. */ collapseAll() { this.panels.forEach((panel) => { this._changeOpenState(panel, false); }); } /** * Toggles a panel with the given id. * * Has no effect if the panel is disabled. */ toggle(panelId) { const panel = this._findPanelById(panelId); if (panel) { this._changeOpenState(panel, !panel.isOpen); } } ngAfterContentChecked() { // active id updates if (isString(this.activeIds)) { this.activeIds = this.activeIds.split(/\s*,\s*/); } // update panels open states this.panels.forEach((panel) => { panel.isOpen = !panel.disabled && this.activeIds.indexOf(panel.id) > -1; }); // closeOthers updates if (this.activeIds.length > 1 && this.closeOtherPanels) { this._closeOthers(this.activeIds[0], false); this._updateActiveIds(); } // Setup the initial classes here this._ngZone.onStable.pipe(take(1)).subscribe(() => { this.panels.forEach((panel) => { const panelElement = panel.panelDiv; if (panelElement) { if (!panel.initClassDone) { panel.initClassDone = true; ngbRunTransition(this._ngZone, panelElement, ngbCollapsingTransition, { animation: false, runningTransition: 'continue', context: { direction: panel.isOpen ? 'show' : 'hide', dimension: 'height' }, }); } } else { // Classes must be initialized next time it will be in the dom panel.initClassDone = false; } }); }); } _changeOpenState(panel, nextState) { if (panel != null && !panel.disabled && panel.isOpen !== nextState) { let defaultPrevented = false; this.panelChange.emit({ panelId: panel.id, nextState: nextState, preventDefault: () => { defaultPrevented = true; }, }); if (!defaultPrevented) { panel.isOpen = nextState; panel.transitionRunning = true; if (nextState && this.closeOtherPanels) { this._closeOthers(panel.id); } this._updateActiveIds(); this._runTransitions(this.animation); } } } _closeOthers(panelId, enableTransition = true) { this.panels.forEach((panel) => { if (panel.id !== panelId && panel.isOpen) { panel.isOpen = false; panel.transitionRunning = enableTransition; } }); } _findPanelById(panelId) { return this.panels.find((p) => p.id === panelId) || null; } _updateActiveIds() { this.activeIds = this.panels.filter((panel) => panel.isOpen && !panel.disabled).map((panel) => panel.id); } _runTransitions(animation) { // detectChanges is performed to ensure that all panels are in the dom (via transitionRunning = true) // before starting the animation this._changeDetector.detectChanges(); this.panels.forEach((panel) => { // When panel.transitionRunning is true, the transition needs to be started OR reversed, // The direction (show or hide) is choosen by each panel.isOpen state if (panel.transitionRunning) { const panelElement = panel.panelDiv; ngbRunTransition(this._ngZone, panelElement, ngbCollapsingTransition, { animation, runningTransition: 'stop', context: { direction: panel.isOpen ? 'show' : 'hide', dimension: 'height' }, }).subscribe(() => { panel.transitionRunning = false; const { id } = panel; if (panel.isOpen) { panel.shown.emit(); this.shown.emit(id); } else { panel.hidden.emit(); this.hidden.emit(id); } }); } }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordion, deps: [{ token: NgbAccordionConfig }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.6", type: NgbAccordion, isStandalone: true, selector: "ngb-accordion", inputs: { animation: "animation", activeIds: "activeIds", closeOtherPanels: ["closeOthers", "closeOtherPanels"], destroyOnHide: "destroyOnHide", type: "type" }, outputs: { panelChange: "panelChange", shown: "shown", hidden: "hidden" }, host: { attributes: { "role": "tablist" }, properties: { "attr.aria-multiselectable": "!closeOtherPanels" }, classAttribute: "accordion" }, queries: [{ propertyName: "panels", predicate: NgbPanel }], exportAs: ["ngbAccordion"], ngImport: i0, template: ` <ng-template #t ngbPanelHeader let-panel> <button class="accordion-button" [ngbPanelToggle]="panel"> {{ panel.title }} <ng-template [ngTemplateOutlet]="panel.titleTpl?.templateRef"></ng-template> </button> </ng-template> <ng-template ngFor let-panel [ngForOf]="panels"> <div [class]="'accordion-item ' + (panel.cardClass || '')"> <div role="tab" id="{{ panel.id }}-header" [class]="'accordion-header ' + (panel.type ? 'bg-' + panel.type : type ? 'bg-' + type : '')" > <ng-template [ngTemplateOutlet]="panel.headerTpl?.templateRef || t" [ngTemplateOutletContext]="{ $implicit: panel, opened: panel.isOpen }" ></ng-template> </div> <div id="{{ panel.id }}" (ngbRef)="panel.panelDiv = $event" role="tabpanel" [attr.aria-labelledby]="panel.id + '-header'" *ngIf="!destroyOnHide || panel.isOpen || panel.transitionRunning" > <div class="accordion-body"> <ng-template [ngTemplateOutlet]="panel.contentTpl?.templateRef || null"></ng-template> </div> </div> </div> </ng-template> `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgbPanelToggle, selector: "button[ngbPanelToggle]", inputs: ["ngbPanelToggle"] }, { kind: "directive", type: NgbRefDirective, selector: "[ngbRef]", outputs: ["ngbRef"] }, { kind: "directive", type: NgbPanelHeader, selector: "ng-template[ngbPanelHeader]" }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordion, decorators: [{ type: Component, args: [{ selector: 'ngb-accordion', exportAs: 'ngbAccordion', standalone: true, imports: [NgFor, NgTemplateOutlet, NgbPanelToggle, NgbRefDirective, NgbPanelHeader, NgIf], encapsulation: ViewEncapsulation.None, host: { class: 'accordion', role: 'tablist', '[attr.aria-multiselectable]': '!closeOtherPanels' }, template: ` <ng-template #t ngbPanelHeader let-panel> <button class="accordion-button" [ngbPanelToggle]="panel"> {{ panel.title }} <ng-template [ngTemplateOutlet]="panel.titleTpl?.templateRef"></ng-template> </button> </ng-template> <ng-template ngFor let-panel [ngForOf]="panels"> <div [class]="'accordion-item ' + (panel.cardClass || '')"> <div role="tab" id="{{ panel.id }}-header" [class]="'accordion-header ' + (panel.type ? 'bg-' + panel.type : type ? 'bg-' + type : '')" > <ng-template [ngTemplateOutlet]="panel.headerTpl?.templateRef || t" [ngTemplateOutletContext]="{ $implicit: panel, opened: panel.isOpen }" ></ng-template> </div> <div id="{{ panel.id }}" (ngbRef)="panel.panelDiv = $event" role="tabpanel" [attr.aria-labelledby]="panel.id + '-header'" *ngIf="!destroyOnHide || panel.isOpen || panel.transitionRunning" > <div class="accordion-body"> <ng-template [ngTemplateOutlet]="panel.contentTpl?.templateRef || null"></ng-template> </div> </div> </div> </ng-template> `, }] }], ctorParameters: function () { return [{ type: NgbAccordionConfig }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { panels: [{ type: ContentChildren, args: [NgbPanel] }], animation: [{ type: Input }], activeIds: [{ type: Input }], closeOtherPanels: [{ type: Input, args: ['closeOthers'] }], destroyOnHide: [{ type: Input }], type: [{ type: Input }], panelChange: [{ type: Output }], shown: [{ type: Output }], hidden: [{ type: Output }] } }); /** * A configuration service for the [NgbCollapse](#/components/collapse/api#NgbCollapse) component. * * You can inject this service, typically in your root component, and customize its properties * to provide default values for all collapses used in the application. */ class NgbCollapseConfig { constructor(_ngbConfig) { this._ngbConfig = _ngbConfig; this.horizontal = false; } get animation() { return this._animation === undefined ? this._ngbConfig.animation : this._animation; } set animation(animation) { this._animation = animation; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbCollapseConfig, deps: [{ token: NgbConfig }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbCollapseConfig, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbCollapseConfig, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: NgbConfig }]; } }); /** * A directive to provide a simple way of hiding and showing elements on the * page. */ class NgbCollapse { /** * If `true`, will collapse the element or show it otherwise. */ set collapsed(isCollapsed) { if (this._isCollapsed !== isCollapsed) { this._isCollapsed = isCollapsed; if (this._afterInit) { this._runTransitionWithEvents(isCollapsed, this.animation); } } } constructor(_element, config, _zone) { this._element = _element; this._zone = _zone; /** * Flag used to track if the collapse setter is invoked during initialization * or not. This distinction is made in order to avoid running the transition during initialization. */ this._afterInit = false; this._isCollapsed = false; this.ngbCollapseChange = new EventEmitter(); /** * An event emitted when the collapse element is shown, after the transition. * It has no payload. * * @since 8.0.0 */ this.shown = new EventEmitter(); /** * An event emitted when the collapse element is hidden, after the transition. * It has no payload. * * @since 8.0.0 */ this.hidden = new EventEmitter(); this.animation = config.animation; this.horizontal = config.horizontal; } ngOnInit() { this._runTransition(this._isCollapsed, false); this._afterInit = true; } /** * Triggers collapsing programmatically. * * If there is a collapsing transition running already, it will be reversed. * If the animations are turned off this happens synchronously. * * @since 8.0.0 */ toggle(open = this._isCollapsed) { this.collapsed = !open; this.ngbCollapseChange.next(this._isCollapsed); } _runTransition(collapsed, animation) { return ngbRunTransition(this._zone, this._element.nativeElement, ngbCollapsingTransition, { animation, runningTransition: 'stop', context: { direction: collapsed ? 'hide' : 'show', dimension: this.horizontal ? 'width' : 'height' }, }); } _runTransitionWithEvents(collapsed, animation) { this._runTransition(collapsed, animation).subscribe(() => { if (collapsed) { this.hidden.emit(); } else { this.shown.emit(); } }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbCollapse, deps: [{ token: i0.ElementRef }, { token: NgbCollapseConfig }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbCollapse, isStandalone: true, selector: "[ngbCollapse]", inputs: { animation: "animation", collapsed: ["ngbCollapse", "collapsed"], horizontal: "horizontal" }, outputs: { ngbCollapseChange: "ngbCollapseChange", shown: "shown", hidden: "hidden" }, host: { properties: { "class.collapse-horizontal": "horizontal" } }, exportAs: ["ngbCollapse"], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbCollapse, decorators: [{ type: Directive, args: [{ selector: '[ngbCollapse]', exportAs: 'ngbCollapse', standalone: true, host: { '[class.collapse-horizontal]': 'horizontal' }, }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: NgbCollapseConfig }, { type: i0.NgZone }]; }, propDecorators: { animation: [{ type: Input }], collapsed: [{ type: Input, args: ['ngbCollapse'] }], ngbCollapseChange: [{ type: Output }], horizontal: [{ type: Input }], shown: [{ type: Output }], hidden: [{ type: Output }] } }); let nextId$3 = 0; /** * A directive that wraps the content of an accordion item's collapsible body. * * The actual content is provided in a child `ng-template` element. * Depending on the state of the accordion, the template will be either inserted or removed from the DOM. * * @since 14.1.0 */ class NgbAccordionBody { constructor() { this._appRef = inject(ApplicationRef); this._element = inject((ElementRef)).nativeElement; this._item = inject(NgbAccordionItem); this._viewRef = null; } ngAfterContentChecked() { if (this._bodyTpl) { if (this._item.animatingBodyCollapse || !this._item.destroyOnHide) { this._createViewIfNotExists(); } else { this._destroyViewIfExists(); } } } ngOnDestroy() { this._destroyViewIfExists(); } _destroyViewIfExists() { if (this._viewRef) { this._appRef.detachView(this._viewRef); this._viewRef.destroy(); this._viewRef = null; } } _createViewIfNotExists() { if (!this._viewRef) { this._viewRef = this._bodyTpl.createEmbeddedView(null); this._viewRef.detectChanges(); this._appRef.attachView(this._viewRef); for (const node of this._viewRef.rootNodes) { this._element.appendChild(node); } } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionBody, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbAccordionBody, isStandalone: true, selector: "[ngbAccordionBody]", host: { properties: { "class.accordion-body": "true" } }, queries: [{ propertyName: "_bodyTpl", first: true, predicate: TemplateRef, descendants: true, static: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionBody, decorators: [{ type: Directive, args: [{ selector: '[ngbAccordionBody]', standalone: true, host: { '[class.accordion-body]': 'true' }, }] }], propDecorators: { _bodyTpl: [{ type: ContentChild, args: [TemplateRef, { static: true }] }] } }); /** * A directive that wraps the collapsible item's content of the accordion. * * Internally it reuses the [`NgbCollapse` directive](#/components/collapse) * * @since 14.1.0 */ class NgbAccordionCollapse { constructor(item, ngbCollapse) { this.item = item; this.ngbCollapse = ngbCollapse; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionCollapse, deps: [{ token: forwardRef(() => NgbAccordionItem) }, { token: NgbCollapse }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbAccordionCollapse, isStandalone: true, selector: "[ngbAccordionCollapse]", host: { attributes: { "role": "region" }, properties: { "class.accordion-collapse": "true", "id": "item.collapseId", "attr.aria-labelledby": "item.toggleId" } }, exportAs: ["ngbAccordionCollapse"], hostDirectives: [{ directive: NgbCollapse }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionCollapse, decorators: [{ type: Directive, args: [{ exportAs: 'ngbAccordionCollapse', standalone: true, selector: '[ngbAccordionCollapse]', host: { role: 'region', '[class.accordion-collapse]': 'true', '[id]': 'item.collapseId', '[attr.aria-labelledby]': 'item.toggleId', }, hostDirectives: [ { directive: NgbCollapse, }, ], }] }], ctorParameters: function () { return [{ type: NgbAccordionItem, decorators: [{ type: Inject, args: [forwardRef(() => NgbAccordionItem)] }] }, { type: NgbCollapse }]; } }); /** * A directive to put on a toggling element inside the accordion item's header. * It will register click handlers that toggle the associated panel and will handle accessibility attributes. * * This directive is used internally by the [`NgbAccordionButton` directive](#/components/accordion/api#NgbAccordionButton). * * @since 14.1.0 */ class NgbAccordionToggle { constructor(item, accordion) { this.item = item; this.accordion = accordion; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionToggle, deps: [{ token: forwardRef(() => NgbAccordionItem) }, { token: forwardRef(() => NgbAccordionDirective) }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbAccordionToggle, isStandalone: true, selector: "[ngbAccordionToggle]", host: { listeners: { "click": "!item.disabled && accordion.toggle(item.id)" }, properties: { "id": "item.toggleId", "class.collapsed": "item.collapsed", "attr.aria-controls": "item.collapseId", "attr.aria-expanded": "!item.collapsed" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionToggle, decorators: [{ type: Directive, args: [{ selector: '[ngbAccordionToggle]', standalone: true, host: { '[id]': 'item.toggleId', '[class.collapsed]': 'item.collapsed', '[attr.aria-controls]': 'item.collapseId', '[attr.aria-expanded]': '!item.collapsed', '(click)': '!item.disabled && accordion.toggle(item.id)', }, }] }], ctorParameters: function () { return [{ type: NgbAccordionItem, decorators: [{ type: Inject, args: [forwardRef(() => NgbAccordionItem)] }] }, { type: NgbAccordionDirective, decorators: [{ type: Inject, args: [forwardRef(() => NgbAccordionDirective)] }] }]; } }); /** * A directive to put on a button element inside an accordion item's header. * * If you want a custom markup for the header, you can also use the [`NgbAccordionToggle` directive](#/components/accordion/api#NgbAccordionToggle). * * @since 14.1.0 */ class NgbAccordionButton { constructor(item) { this.item = item; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionButton, deps: [{ token: forwardRef(() => NgbAccordionItem) }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbAccordionButton, isStandalone: true, selector: "button[ngbAccordionButton]", host: { attributes: { "type": "button" }, properties: { "disabled": "item.disabled", "class.accordion-button": "true" } }, hostDirectives: [{ directive: NgbAccordionToggle }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionButton, decorators: [{ type: Directive, args: [{ selector: 'button[ngbAccordionButton]', standalone: true, host: { '[disabled]': 'item.disabled', '[class.accordion-button]': 'true', type: 'button', }, hostDirectives: [ { directive: NgbAccordionToggle, }, ], }] }], ctorParameters: function () { return [{ type: NgbAccordionItem, decorators: [{ type: Inject, args: [forwardRef(() => NgbAccordionItem)] }] }]; } }); /** * A directive that wraps an accordion item's header. * * @since 14.1.0 */ class NgbAccordionHeader { constructor(item) { this.item = item; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionHeader, deps: [{ token: forwardRef(() => NgbAccordionItem) }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.6", type: NgbAccordionHeader, isStandalone: true, selector: "[ngbAccordionHeader]", host: { attributes: { "role": "heading" }, properties: { "class.accordion-header": "true", "class.collapsed": "item.collapsed" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: NgbAccordionHeader, decorators: [{ type: Directive, args: [{ selector: '[ngbAccordionHeader]', standalone: true, host: { role: 'heading', '[class.accordion-header]': 'true', '[class.collapsed]': 'item.collapsed', }, }] }], ctorParameters: function () { return [{ type: NgbAccordionItem, decorators: [{ type: Inject, args: [forwardRef(() => NgbAccordionItem)] }] }]; } }); /** * A dire