UNPKG

igniteui-angular

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

1,267 lines (1,262 loc) 61.2 kB
import { NgClass, NgTemplateOutlet } from '@angular/common'; import * as i0 from '@angular/core'; import { inject, ChangeDetectorRef, EventEmitter, Directive, ElementRef, booleanAttribute, Output, HostBinding, Input, Component, Injectable, IterableDiffers, DOCUMENT, TemplateRef, HostListener, ViewChildren, ViewChild, ContentChildren, ContentChild, NgModule } from '@angular/core'; import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser'; import { Subject, merge } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { IgxAngularAnimationService, HammerGesturesManager, PlatformUtil, ɵIgxDirectionality as _IgxDirectionality, getCurrentResourceStrings, CarouselResourceStringsEN, first, last } from 'igniteui-angular/core'; import { useAnimation } from '@angular/animations'; import { fadeIn, slideInLeft } from 'igniteui-angular/animations'; import { IgxIconComponent } from 'igniteui-angular/icon'; import { IgxButtonDirective } from 'igniteui-angular/directives'; const CarouselAnimationType = { none: 'none', slide: 'slide', fade: 'fade' }; const CarouselIndicatorsOrientation = { /** * @deprecated in version 19.1.0. Use `end` instead. */ bottom: 'bottom', /** * @deprecated in version 19.1.0. Use `start` instead. */ top: 'top', start: 'start', end: 'end' }; var CarouselAnimationDirection; (function (CarouselAnimationDirection) { CarouselAnimationDirection[CarouselAnimationDirection["NONE"] = 0] = "NONE"; CarouselAnimationDirection[CarouselAnimationDirection["NEXT"] = 1] = "NEXT"; CarouselAnimationDirection[CarouselAnimationDirection["PREV"] = 2] = "PREV"; })(CarouselAnimationDirection || (CarouselAnimationDirection = {})); /** @hidden */ class IgxCarouselComponentBase { constructor() { this.animationService = inject(IgxAngularAnimationService); this.cdr = inject(ChangeDetectorRef); /** @hidden */ this.animationType = CarouselAnimationType.slide; /** @hidden @internal */ this.enterAnimationDone = new EventEmitter(); /** @hidden @internal */ this.leaveAnimationDone = new EventEmitter(); /** @hidden */ this.defaultAnimationDuration = 320; /** @hidden */ this.animationPosition = 0; /** @hidden */ this.newDuration = 0; /** @hidden */ this.vertical = false; } ngOnDestroy() { if (this.enterAnimationPlayer) { this.enterAnimationPlayer.destroy(); this.enterAnimationPlayer = null; } if (this.leaveAnimationPlayer) { this.leaveAnimationPlayer.destroy(); this.leaveAnimationPlayer = null; } } /** @hidden */ triggerAnimations() { if (this.animationType !== CarouselAnimationType.none) { if (this.animationStarted(this.leaveAnimationPlayer) || this.animationStarted(this.enterAnimationPlayer)) { requestAnimationFrame(() => { this.resetAnimations(); this.playAnimations(); }); } else { this.playAnimations(); } } } /** @hidden */ animationStarted(animation) { return animation && animation.hasStarted(); } /** @hidden */ playAnimations() { this.playLeaveAnimation(); this.playEnterAnimation(); } resetAnimations() { if (this.animationStarted(this.leaveAnimationPlayer)) { this.leaveAnimationPlayer.reset(); this.leaveAnimationDone.emit(); } if (this.animationStarted(this.enterAnimationPlayer)) { this.enterAnimationPlayer.reset(); this.enterAnimationDone.emit(); this.cdr.markForCheck(); } } getAnimation() { let duration; if (this.newDuration) { duration = this.animationPosition ? this.animationPosition * this.newDuration : this.newDuration; } else { duration = this.animationPosition ? this.animationPosition * this.defaultAnimationDuration : this.defaultAnimationDuration; } const trans = this.animationPosition ? this.animationPosition * 100 : 100; switch (this.animationType) { case CarouselAnimationType.slide: return { enterAnimation: useAnimation(slideInLeft, { params: { delay: '0s', duration: `${duration}ms`, endOpacity: 1, startOpacity: 1, fromPosition: `${this.vertical ? 'translateY' : 'translateX'}(${this.currentItem.direction === 1 ? trans : -trans}%)`, toPosition: `${this.vertical ? 'translateY(0%)' : 'translateX(0%)'}` } }), leaveAnimation: useAnimation(slideInLeft, { params: { delay: '0s', duration: `${duration}ms`, endOpacity: 1, startOpacity: 1, fromPosition: `${this.vertical ? 'translateY(0%)' : 'translateX(0%)'}`, toPosition: `${this.vertical ? 'translateY' : 'translateX'}(${this.currentItem.direction === 1 ? -trans : trans}%)`, } }) }; case CarouselAnimationType.fade: return { enterAnimation: useAnimation(fadeIn, { params: { duration: `${duration}ms`, startOpacity: `${this.animationPosition}` } }), leaveAnimation: null }; } return { enterAnimation: null, leaveAnimation: null }; } playEnterAnimation() { const animation = this.getAnimation().enterAnimation; if (!animation) { return; } this.enterAnimationPlayer = this.animationService.buildAnimation(animation, this.getCurrentElement()); this.enterAnimationPlayer.animationEnd.subscribe(() => { // TODO: animation may never end. Find better way to clean up the player if (this.enterAnimationPlayer) { this.enterAnimationPlayer.destroy(); this.enterAnimationPlayer = null; } this.animationPosition = 0; this.newDuration = 0; this.previousItem.previous = false; this.enterAnimationDone.emit(); this.cdr.markForCheck(); }); this.previousItem.previous = true; this.enterAnimationPlayer.play(); } playLeaveAnimation() { const animation = this.getAnimation().leaveAnimation; if (!animation) { return; } this.leaveAnimationPlayer = this.animationService.buildAnimation(animation, this.getPreviousElement()); this.leaveAnimationPlayer.animationEnd.subscribe(() => { // TODO: animation may never end. Find better way to clean up the player if (this.leaveAnimationPlayer) { this.leaveAnimationPlayer.destroy(); this.leaveAnimationPlayer = null; } this.animationPosition = 0; this.newDuration = 0; this.leaveAnimationDone.emit(); }); this.leaveAnimationPlayer.play(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselComponentBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxCarouselComponentBase, isStandalone: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselComponentBase, decorators: [{ type: Directive }] }); class IgxCarouselIndicatorDirective { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxCarouselIndicatorDirective, isStandalone: true, selector: "[igxCarouselIndicator]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselIndicatorDirective, decorators: [{ type: Directive, args: [{ selector: '[igxCarouselIndicator]', standalone: true }] }] }); class IgxCarouselNextButtonDirective { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselNextButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxCarouselNextButtonDirective, isStandalone: true, selector: "[igxCarouselNextButton]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselNextButtonDirective, decorators: [{ type: Directive, args: [{ selector: '[igxCarouselNextButton]', standalone: true }] }] }); class IgxCarouselPrevButtonDirective { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselPrevButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxCarouselPrevButtonDirective, isStandalone: true, selector: "[igxCarouselPrevButton]", ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselPrevButtonDirective, decorators: [{ type: Directive, args: [{ selector: '[igxCarouselPrevButton]', standalone: true }] }] }); /** * A slide component that usually holds an image and/or a caption text. * IgxSlideComponent is usually a child component of an IgxCarouselComponent. * * ``` * <igx-slide [input bindings] > * <ng-content></ng-content> * </igx-slide> * ``` * * @export */ class IgxSlideComponent { constructor() { this.elementRef = inject(ElementRef); /** * Returns the `role` of the slide component. * By default is set to `tabpanel` * * @memberof IgxSlideComponent */ this.tab = 'tabpanel'; /** * Returns the class of the slide component. * ```typescript * let class = this.slide.cssClass; * ``` * * @memberof IgxSlideComponent */ this.cssClass = 'igx-slide'; this.previous = false; /** * @hidden */ this.activeChange = new EventEmitter(); this._active = false; this._destroy$ = new Subject(); } /** * Returns the `tabIndex` of the slide component. * ```typescript * let tabIndex = this.carousel.tabIndex; * ``` * * @memberof IgxSlideComponent * @deprecated in version 19.2.0. */ get tabIndex() { return this.active ? 0 : null; } /** * Gets/sets the `active` state of the slide. * ```html * <igx-carousel> * <igx-slide [active] ="false"></igx-slide> * <igx-carousel> * ``` * * Two-way data binding. * ```html * <igx-carousel> * <igx-slide [(active)] ="model.isActive"></igx-slide> * <igx-carousel> * ``` * * @memberof IgxSlideComponent */ get active() { return this._active; } set active(value) { this._active = value; this.activeChange.emit(this._active); } /** * Returns a reference to the carousel element in the DOM. * ```typescript * let nativeElement = this.slide.nativeElement; * ``` * * @memberof IgxSlideComponent */ get nativeElement() { return this.elementRef.nativeElement; } /** * @hidden */ get isDestroyed() { return this._destroy$; } ngAfterContentChecked() { this.id = `panel-${this.index}`; this.ariaLabelledBy = `tab-${this.index}-${this.total}`; } /** * @hidden */ ngOnDestroy() { this._destroy$.next(true); this._destroy$.complete(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxSlideComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "21.0.2", type: IgxSlideComponent, isStandalone: true, selector: "igx-slide", inputs: { index: "index", direction: "direction", total: "total", active: ["active", "active", booleanAttribute], previous: ["previous", "previous", booleanAttribute] }, outputs: { activeChange: "activeChange" }, host: { properties: { "attr.tabindex": "this.tabIndex", "attr.id": "this.id", "attr.role": "this.tab", "attr.aria-labelledby": "this.ariaLabelledBy", "class.igx-slide": "this.cssClass", "class.igx-slide--current": "this.active", "class.igx-slide--previous": "this.previous" } }, ngImport: i0, template: "<ng-content></ng-content>\n" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxSlideComponent, decorators: [{ type: Component, args: [{ selector: 'igx-slide', standalone: true, template: "<ng-content></ng-content>\n" }] }], propDecorators: { index: [{ type: Input }], direction: [{ type: Input }], total: [{ type: Input }], tabIndex: [{ type: HostBinding, args: ['attr.tabindex'] }], id: [{ type: HostBinding, args: ['attr.id'] }], tab: [{ type: HostBinding, args: ['attr.role'] }], ariaLabelledBy: [{ type: HostBinding, args: ['attr.aria-labelledby'] }], cssClass: [{ type: HostBinding, args: ['class.igx-slide'] }], active: [{ type: HostBinding, args: ['class.igx-slide--current'] }, { type: Input, args: [{ transform: booleanAttribute }] }], previous: [{ type: HostBinding, args: ['class.igx-slide--previous'] }, { type: Input, args: [{ transform: booleanAttribute }] }], activeChange: [{ type: Output }] } }); let NEXT_ID = 0; class CarouselHammerConfig extends HammerGestureConfig { constructor() { super(...arguments); this.overrides = { pan: { direction: HammerGesturesManager.Hammer?.DIRECTION_HORIZONTAL } }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: CarouselHammerConfig, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: CarouselHammerConfig }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: CarouselHammerConfig, decorators: [{ type: Injectable }] }); /** * **Ignite UI for Angular Carousel** - * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/carousel.html) * * The Ignite UI Carousel is used to browse or navigate through a collection of slides. Slides can contain custom * content such as images or cards and be used for things such as on-boarding tutorials or page-based interfaces. * It can be used as a separate fullscreen element or inside another component. * * Example: * ```html * <igx-carousel> * <igx-slide> * <h3>First Slide Header</h3> * <p>First slide Content</p> * <igx-slide> * <igx-slide> * <h3>Second Slide Header</h3> * <p>Second Slide Content</p> * </igx-carousel> * ``` */ class IgxCarouselComponent extends IgxCarouselComponentBase { /** @hidden */ get labelId() { return this.showIndicatorsLabel ? `${this.id}-label` : null; } /** @hidden */ get isVertical() { return this.vertical; } /** * Gets the `touch-action` style of the `list item`. * ```typescript * let touchAction = this.listItem.touchAction; * ``` */ get touchAction() { return this.gesturesSupport ? 'pan-y' : 'auto'; } /** * An accessor that sets the resource strings. * By default it uses EN resources. */ set resourceStrings(value) { this._resourceStrings = Object.assign({}, this._resourceStrings, value); } /** * An accessor that returns the resource strings. */ get resourceStrings() { return this._resourceStrings; } /** @hidden */ get getIndicatorTemplate() { if (this.indicatorTemplate) { return this.indicatorTemplate; } return this.defaultIndicator; } /** @hidden */ get getNextButtonTemplate() { if (this.nextButtonTemplate) { return this.nextButtonTemplate; } return this.defaultNextButton; } /** @hidden */ get getPrevButtonTemplate() { if (this.prevButtonTemplate) { return this.prevButtonTemplate; } return this.defaultPrevButton; } /** @hidden */ get indicatorsClass() { return { ['igx-carousel-indicators--focused']: this._hasKeyboardFocusOnIndicators, [`igx-carousel-indicators--${this.getIndicatorsClass()}`]: true }; } /** @hidden */ get showIndicators() { return this.indicators && this.total <= this.maximumIndicatorsCount && this.total > 0; } /** @hidden */ get showIndicatorsLabel() { return this.indicators && this.total > this.maximumIndicatorsCount; } /** @hidden */ get getCarouselLabel() { return `${this.current + 1} ${this.resourceStrings.igx_carousel_of} ${this.total}`; } /** * Returns the total number of `slides` in the carousel. * ```typescript * let slideCount = this.carousel.total; * ``` * * @memberOf IgxCarouselComponent */ get total() { return this.slides?.length; } /** * The index of the slide being currently shown. * ```typescript * let currentSlideNumber = this.carousel.current; * ``` * * @memberOf IgxCarouselComponent */ get current() { return !this.currentItem ? 0 : this.currentItem.index; } /** * Returns a boolean indicating if the carousel is playing. * ```typescript * let isPlaying = this.carousel.isPlaying; * ``` * * @memberOf IgxCarouselComponent */ get isPlaying() { return this.playing; } /** * Returns а boolean indicating if the carousel is destroyed. * ```typescript * let isDestroyed = this.carousel.isDestroyed; * ``` * * @memberOf IgxCarouselComponent */ get isDestroyed() { return this.destroyed; } /** * Returns a reference to the carousel element in the DOM. * ```typescript * let nativeElement = this.carousel.nativeElement; * ``` * * @memberof IgxCarouselComponent */ get nativeElement() { return this.element.nativeElement; } /** * Returns the time `interval` in milliseconds before the slide changes. * ```typescript * let timeInterval = this.carousel.interval; * ``` * * @memberof IgxCarouselComponent */ get interval() { return this._interval; } /** * Sets the time `interval` in milliseconds before the slide changes. * If not set, the carousel will not change `slides` automatically. * ```html * <igx-carousel [interval]="1000"></igx-carousel> * ``` * * @memberof IgxCarouselComponent */ set interval(value) { this._interval = +value; this.restartInterval(); } constructor() { super(); this.element = inject(ElementRef); this.iterableDiffers = inject(IterableDiffers); this.platformUtil = inject(PlatformUtil); this.dir = inject(_IgxDirectionality); this.document = inject(DOCUMENT); /** * Sets the `id` of the carousel. * If not set, the `id` of the first carousel component will be `"igx-carousel-0"`. * ```html * <igx-carousel id="my-first-carousel"></igx-carousel> * ``` * * @memberof IgxCarouselComponent */ this.id = `igx-carousel-${NEXT_ID++}`; /** * Returns the `role` attribute of the carousel. * ```typescript * let carouselRole = this.carousel.role; * ``` * * @memberof IgxCarouselComponent */ this.role = 'region'; /** @hidden */ this.roleDescription = 'carousel'; /** * Returns the class of the carousel component. * ```typescript * let class = this.carousel.cssClass; * ``` * * @memberof IgxCarouselComponent */ this.cssClass = 'igx-carousel'; /** * Sets whether the carousel should `loop` back to the first slide after reaching the last slide. * Default value is `true`. * ```html * <igx-carousel [loop]="false"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.loop = true; /** * Sets whether the carousel will `pause` the slide transitions on user interactions. * Default value is `true`. * ```html * <igx-carousel [pause]="false"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.pause = true; /** * Controls whether the carousel should render the left/right `navigation` buttons. * Default value is `true`. * ```html * <igx-carousel [navigation]="false"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.navigation = true; /** * Controls whether the carousel should render the indicators. * Default value is `true`. * ```html * <igx-carousel [indicators]="false"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.indicators = true; /** * Controls whether the carousel has vertical alignment. * Default value is `false`. * ```html * <igx-carousel [vertical]="true"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.vertical = false; /** * Controls whether the carousel should support gestures. * Default value is `true`. * ```html * <igx-carousel [gesturesSupport]="false"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.gesturesSupport = true; /** * Controls the maximum indexes that can be shown. * Default value is `10`. * ```html * <igx-carousel [maximumIndicatorsCount]="5"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.maximumIndicatorsCount = 10; /** * Gets/sets the display mode of carousel indicators. It can be `start` or `end`. * Default value is `end`. * ```html * <igx-carousel indicatorsOrientation="start"> * <igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.indicatorsOrientation = CarouselIndicatorsOrientation.end; /** * Gets/sets the animation type of carousel. * Default value is `slide`. * ```html * <igx-carousel animationType="none"> * <igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.animationType = CarouselAnimationType.slide; /** * The custom template, if any, that should be used when rendering carousel indicators * * ```typescript * // Set in typescript * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate; * myComponent.carousel.indicatorTemplate = myCustomTemplate; * ``` * ```html * <!-- Set in markup --> * <igx-carousel #carousel> * ... * <ng-template igxCarouselIndicator let-slide> * <igx-icon *ngIf="slide.active">brightness_7</igx-icon> * <igx-icon *ngIf="!slide.active">brightness_5</igx-icon> * </ng-template> * </igx-carousel> * ``` */ this.indicatorTemplate = null; /** * The custom template, if any, that should be used when rendering carousel next button * * ```typescript * // Set in typescript * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate; * myComponent.carousel.nextButtonTemplate = myCustomTemplate; * ``` * ```html * <!-- Set in markup --> * <igx-carousel #carousel> * ... * <ng-template igxCarouselNextButton let-disabled> * <button type="button" igxButton="fab" igxRipple="white" [disabled]="disabled"> * <igx-icon name="add"></igx-icon> * </button> * </ng-template> * </igx-carousel> * ``` */ this.nextButtonTemplate = null; /** * The custom template, if any, that should be used when rendering carousel previous button * * ```typescript * // Set in typescript * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate; * myComponent.carousel.prevButtonTemplate = myCustomTemplate; * ``` * ```html * <!-- Set in markup --> * <igx-carousel #carousel> * ... * <ng-template igxCarouselPrevButton let-disabled> * <button type="button" igxButton="fab" igxRipple="white" [disabled]="disabled"> * <igx-icon name="remove"></igx-icon> * </button> * </ng-template> * </igx-carousel> * ``` */ this.prevButtonTemplate = null; /** * An event that is emitted after a slide transition has happened. * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments. * ```html * <igx-carousel (slideChanged)="slideChanged($event)"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.slideChanged = new EventEmitter(); /** * An event that is emitted after a slide has been added to the carousel. * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments. * ```html * <igx-carousel (slideAdded)="slideAdded($event)"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.slideAdded = new EventEmitter(); /** * An event that is emitted after a slide has been removed from the carousel. * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments. * ```html * <igx-carousel (slideRemoved)="slideRemoved($event)"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.slideRemoved = new EventEmitter(); /** * An event that is emitted after the carousel has been paused. * Provides a reference to the `IgxCarouselComponent` as an event argument. * ```html * <igx-carousel (carouselPaused)="carouselPaused($event)"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.carouselPaused = new EventEmitter(); /** * An event that is emitted after the carousel has resumed transitioning between `slides`. * Provides a reference to the `IgxCarouselComponent` as an event argument. * ```html * <igx-carousel (carouselPlaying)="carouselPlaying($event)"></igx-carousel> * ``` * * @memberOf IgxCarouselComponent */ this.carouselPlaying = new EventEmitter(); this._resourceStrings = getCurrentResourceStrings(CarouselResourceStringsEN); this.destroy$ = new Subject(); this.differ = null; this._hasKeyboardFocusOnIndicators = false; this.differ = this.iterableDiffers.find([]).create(null); } /** @hidden */ onTap(event) { // play pause only when tap on slide if (event.target && event.target.classList.contains('igx-slide')) { if (this.isPlaying) { if (this.pause) { this.stoppedByInteraction = true; } this.stop(); } else if (this.stoppedByInteraction) { this.play(); } } } /** @hidden */ onMouseEnter() { if (this.pause && this.isPlaying) { this.stoppedByInteraction = true; } this.stop(); } /** @hidden */ onMouseLeave() { if (this.stoppedByInteraction) { this.play(); } } /** @hidden */ onPanLeft(event) { if (!this.vertical) { this.pan(event); } } /** @hidden */ onPanRight(event) { if (!this.vertical) { this.pan(event); } } /** @hidden */ onPanUp(event) { if (this.vertical) { this.pan(event); } } /** @hidden */ onPanDown(event) { if (this.vertical) { this.pan(event); } } /** * @hidden */ onPanEnd(event) { if (!this.gesturesSupport) { return; } event.preventDefault(); const slideSize = this.vertical ? this.currentItem.nativeElement.offsetHeight : this.currentItem.nativeElement.offsetWidth; const panOffset = (slideSize / 1000); const eventDelta = this.vertical ? event.deltaY : event.deltaX; const delta = Math.abs(eventDelta) + panOffset < slideSize ? Math.abs(eventDelta) : slideSize - panOffset; const velocity = Math.abs(event.velocity); this.resetSlideStyles(this.currentItem); if (this.incomingSlide) { this.resetSlideStyles(this.incomingSlide); if (slideSize / 2 < delta || velocity > 1) { this.incomingSlide.direction = eventDelta < 0 ? CarouselAnimationDirection.NEXT : CarouselAnimationDirection.PREV; this.incomingSlide.previous = false; this.animationPosition = this.animationType === CarouselAnimationType.fade ? delta / slideSize : (slideSize - delta) / slideSize; if (velocity > 1) { this.newDuration = this.defaultAnimationDuration / velocity; } this.incomingSlide.active = true; } else { this.currentItem.direction = eventDelta > 0 ? CarouselAnimationDirection.NEXT : CarouselAnimationDirection.PREV; this.previousItem = this.incomingSlide; this.previousItem.previous = true; this.animationPosition = this.animationType === CarouselAnimationType.fade ? Math.abs((slideSize - delta) / slideSize) : delta / slideSize; this.playAnimations(); } } if (this.stoppedByInteraction) { this.play(); } } /** @hidden */ ngAfterContentInit() { this.slides.changes .pipe(takeUntil(this.destroy$)) .subscribe((change) => this.initSlides(change)); this.initSlides(this.slides); } /** @hidden */ ngOnDestroy() { super.ngOnDestroy(); this.destroy$.next(true); this.destroy$.complete(); this.destroyed = true; if (this.lastInterval) { clearInterval(this.lastInterval); } } /** @hidden */ handleKeydownPrev(event) { if (this.platformUtil.isActivationKey(event)) { event.preventDefault(); this.prev(); } } /** @hidden */ handleKeydownNext(event) { if (this.platformUtil.isActivationKey(event)) { event.preventDefault(); this.next(); } } /** @hidden */ handleKeyUp(event) { if (event.key === this.platformUtil.KEYMAP.TAB) { this._hasKeyboardFocusOnIndicators = true; } } /** @hidden */ handleFocusOut(event) { const target = event.relatedTarget; if (!target || !target.classList.contains('igx-carousel-indicators__indicator')) { this._hasKeyboardFocusOnIndicators = false; } } /** @hidden */ handleClick() { this._hasKeyboardFocusOnIndicators = false; } /** @hidden */ handleKeydown(event) { const { key } = event; const slides = this.slides.toArray(); switch (key) { case this.platformUtil.KEYMAP.ARROW_LEFT: this.dir.rtl ? this.next() : this.prev(); break; case this.platformUtil.KEYMAP.ARROW_RIGHT: this.dir.rtl ? this.prev() : this.next(); break; case this.platformUtil.KEYMAP.HOME: event.preventDefault(); this.select(this.dir.rtl ? last(slides) : first(slides)); break; case this.platformUtil.KEYMAP.END: event.preventDefault(); this.select(this.dir.rtl ? first(slides) : last(slides)); break; } this.indicatorsElements[this.current].nativeElement.focus(); } /** * Returns the slide corresponding to the provided `index` or null. * ```typescript * let slide1 = this.carousel.get(1); * ``` * * @memberOf IgxCarouselComponent */ get(index) { return this.slides.find((slide) => slide.index === index); } /** * Adds a new slide to the carousel. * ```typescript * this.carousel.add(newSlide); * ``` * * @memberOf IgxCarouselComponent */ add(slide) { const newSlides = this.slides.toArray(); newSlides.push(slide); this.slides.reset(newSlides); this.slides.notifyOnChanges(); } /** * Removes a slide from the carousel. * ```typescript * this.carousel.remove(slide); * ``` * * @memberOf IgxCarouselComponent */ remove(slide) { if (slide && slide === this.get(slide.index)) { // check if the requested slide for delete is present in the carousel const newSlides = this.slides.toArray(); newSlides.splice(slide.index, 1); this.slides.reset(newSlides); this.slides.notifyOnChanges(); } } select(slideOrIndex, direction = CarouselAnimationDirection.NONE) { const slide = typeof slideOrIndex === 'number' ? this.get(slideOrIndex) : slideOrIndex; if (slide && slide !== this.currentItem) { slide.direction = direction; slide.active = true; } } /** * Transitions to the next slide in the carousel. * ```typescript * this.carousel.next(); * ``` * * @memberOf IgxCarouselComponent */ next() { const index = this.getNextIndex(); if (index === 0 && !this.loop) { this.stop(); return; } return this.select(this.get(index), CarouselAnimationDirection.NEXT); } /** * Transitions to the previous slide in the carousel. * ```typescript * this.carousel.prev(); * ``` * * @memberOf IgxCarouselComponent */ prev() { const index = this.getPrevIndex(); if (!this.loop && index === this.total - 1) { this.stop(); return; } return this.select(this.get(index), CarouselAnimationDirection.PREV); } /** * Resumes playing of the carousel if in paused state. * No operation otherwise. * ```typescript * this.carousel.play(); * } * ``` * * @memberOf IgxCarouselComponent */ play() { if (!this.playing) { this.playing = true; this.carouselPlaying.emit(this); this.restartInterval(); this.stoppedByInteraction = false; } } /** * Stops slide transitions if the `pause` option is set to `true`. * No operation otherwise. * ```typescript * this.carousel.stop(); * } * ``` * * @memberOf IgxCarouselComponent */ stop() { if (this.pause) { this.playing = false; this.carouselPaused.emit(this); this.resetInterval(); } } getPreviousElement() { return this.previousItem.nativeElement; } getCurrentElement() { return this.currentItem.nativeElement; } resetInterval() { if (this.lastInterval) { clearInterval(this.lastInterval); this.lastInterval = null; } } restartInterval() { this.resetInterval(); if (!isNaN(this.interval) && this.interval > 0 && this.platformUtil.isBrowser) { this.lastInterval = setInterval(() => { const tick = +this.interval; if (this.playing && this.total && !isNaN(tick) && tick > 0) { this.next(); } else { this.stop(); } }, this.interval); } } /** @hidden */ get nextButtonDisabled() { return !this.loop && this.current === (this.total - 1); } /** @hidden */ get prevButtonDisabled() { return !this.loop && this.current === 0; } get indicatorsElements() { return this._indicators.toArray(); } getIndicatorsClass() { switch (this.indicatorsOrientation) { case CarouselIndicatorsOrientation.top: return CarouselIndicatorsOrientation.start; case CarouselIndicatorsOrientation.bottom: return CarouselIndicatorsOrientation.end; default: return this.indicatorsOrientation; } } getNextIndex() { return (this.current + 1) % this.total; } getPrevIndex() { return this.current - 1 < 0 ? this.total - 1 : this.current - 1; } resetSlideStyles(slide) { slide.nativeElement.style.transform = ''; slide.nativeElement.style.opacity = ''; } pan(event) { const slideSize = this.vertical ? this.currentItem.nativeElement.offsetHeight : this.currentItem.nativeElement.offsetWidth; const panOffset = (slideSize / 1000); const delta = this.vertical ? event.deltaY : event.deltaX; const index = delta < 0 ? this.getNextIndex() : this.getPrevIndex(); const offset = delta < 0 ? slideSize + delta : -slideSize + delta; if (!this.gesturesSupport || event.isFinal || Math.abs(delta) + panOffset >= slideSize) { return; } if (!this.loop && ((this.current === 0 && delta > 0) || (this.current === this.total - 1 && delta < 0))) { this.incomingSlide = null; return; } event.preventDefault(); if (this.isPlaying) { this.stoppedByInteraction = true; this.stop(); } if (this.previousItem && this.previousItem.previous) { this.previousItem.previous = false; } this.finishAnimations(); if (this.incomingSlide) { if (index !== this.incomingSlide.index) { this.resetSlideStyles(this.incomingSlide); this.incomingSlide.previous = false; this.incomingSlide = this.get(index); } } else { this.incomingSlide = this.get(index); } this.incomingSlide.previous = true; if (this.animationType === CarouselAnimationType.fade) { this.currentItem.nativeElement.style.opacity = `${Math.abs(offset) / slideSize}`; } else { this.currentItem.nativeElement.style.transform = this.vertical ? `translateY(${delta}px)` : `translateX(${delta}px)`; this.incomingSlide.nativeElement.style.transform = this.vertical ? `translateY(${offset}px)` : `translateX(${offset}px)`; } } unsubscriber(slide) { return merge(this.destroy$, slide.isDestroyed); } onSlideActivated(slide) { if (slide.active && slide !== this.currentItem) { if (slide.direction === CarouselAnimationDirection.NONE) { const newIndex = slide.index; slide.direction = newIndex > this.current ? CarouselAnimationDirection.NEXT : CarouselAnimationDirection.PREV; } if (this.currentItem) { if (this.previousItem && this.previousItem.previous) { this.previousItem.previous = false; } this.currentItem.direction = slide.direction; this.currentItem.active = false; this.previousItem = this.currentItem; this.currentItem = slide; this.triggerAnimations(); } else { this.currentItem = slide; } this.slideChanged.emit({ carousel: this, slide }); this.restartInterval(); this.cdr.markForCheck(); } } finishAnimations() { if (this.animationStarted(this.leaveAnimationPlayer)) { this.leaveAnimationPlayer.finish(); } if (this.animationStarted(this.enterAnimationPlayer)) { this.enterAnimationPlayer.finish(); } } initSlides(change) { const diff = this.differ.diff(change.toArray()); if (diff) { this.slides.reduce((any, c, ind) => c.index = ind, 0); // reset slides indexes diff.forEachAddedItem((record) => { const slide = record.item; slide.total = this.total; this.slideAdded.emit({ carousel: this, slide }); if (slide.active) { this.currentItem = slide; } slide.activeChange.pipe(takeUntil(this.unsubscriber(slide))).subscribe(() => this.onSlideActivated(slide)); }); diff.forEachRemovedItem((record) => { const slide = record.item; this.slideRemoved.emit({ carousel: this, slide }); if (slide.active) { slide.active = false; this.currentItem = this.get(slide.index < this.total ? slide.index : this.total - 1); } }); this.updateSlidesSelection(); } } updateSlidesSelection() { if (this.platformUtil.isBrowser) { requestAnimationFrame(() => { if (this.currentItem) { this.currentItem.active = true; const activeSlides = this.slides.filter(slide => slide.active && slide.index !== this.currentItem.index); activeSlides.forEach(slide => slide.active = false); } else if (this.total) { this.slides.first.active = true; } this.play(); }); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: IgxCarouselComponent, isStandalone: true, selector: "igx-carousel", inputs: { id: "id", loop: ["loop", "loop", booleanAttribute], pause: ["pause", "pause", booleanAttribute], navigation: ["navigation", "navigation", booleanAttribute], indicators: ["indicators", "indicators", booleanAttribute], vertical: ["vertical", "vertical", booleanAttribute], gesturesSupport: ["gesturesSupport", "gesturesSupport", booleanAttribute], maximumIndicatorsCount: "maximumIndicatorsCount", indicatorsOrientation: "indicatorsOrientation", animationType: "animationType", resourceStrings: "resourceStrings", interval: "interval" }, outputs: { slideChanged: "slideChanged", slideAdded: "slideAdded", slideRemoved: "slideRemoved", carouselPaused: "carouselPaused", carouselPlaying: "carouselPlaying" }, host: { listeners: { "tap": "onTap($event)", "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "panleft": "onPanLeft($event)", "panright": "onPanRight($event)", "panup": "onPanUp($event)", "pandown": "onPanDown($event)", "panend": "onPanEnd($event)" }, properties: { "attr.id": "this.id", "attr.role": "this.role", "attr.aria-roledescription": "this.roleDescription", "attr.aria-labelledby": "this.labelId", "class.igx-carousel--vertical": "this.isVertical", "class.igx-carousel": "this.cssClass", "style.touch-action": "this.touchAction" } }, providers: [ { provide: HAMMER_GESTURE_CONFIG, useClass: CarouselHammerConfig } ], queries: [{ propertyName: "indicatorTemplate", first: true, predicate: IgxCarouselIndicatorDirective, descendants: true, read: TemplateRef }, { propertyName: "nextButtonTemplate", first: true, predicate: IgxCarouselNextButtonDirective, descendants: true, read: TemplateRef }, { propertyName: "prevButtonTemplate", first: true, predicate: IgxCarouselPrevButtonDirective, descendants: true, read: TemplateRef }, { propertyName: "slides", predicate: IgxSlideComponent }], viewQueries: [{ propertyName: "defaultIndicator", first: true, predicate: ["defaultIndicator"], descendants: true, read: TemplateRef, static: true }, { propertyName: "defaultNextButton", first: true, predicate: ["defaultNextButton"], descendants: true, read: TemplateRef, static: true }, { propertyName: "defaultPrevButton", first: true, predicate: ["defaultPrevButton"], descendants: true, read: TemplateRef, static: true }, { propertyName: "_indicators", predicate: ["indicators"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "<ng-template #defaultIndicator let-slide>\n <div class=\"igx-nav-dot\"\n [class.igx-nav-dot--active]=\"slide.active\">\n </div>\n</ng-template>\n\n<ng-template #defaultNextButton>\n <igx-icon aria-hidden=\"true\" family=\"default\" name=\"carousel_next\"\n class=\"igx-nav-arrow\">\n </igx-icon>\n</ng-template>\n\n<ng-template #defaultPrevButton>\n <igx-icon aria-hidden=\"true\" family=\"default\" name=\"carousel_prev\"\n class=\"igx-nav-arrow\">\n </igx-icon>\n</ng-template>\n\n@if (navigation && slides.length) {\n <button\n igxButton\n class=\"igx-carousel__arrow--prev\"\n [attr.aria-label]=\"resourceStrings.igx_carousel_previous_slide\"\n [disabled]=\"prevButtonDisabled\"\n (click)=\"prev()\"\n (keydown)=\"handleKeydownPrev($event)\">\n <ng-container *ngTemplateOutlet=\"getPrevButtonTemplate; context: {$implicit: prevButtonDisabled};\"></ng-container>\n </button>\n}\n\n@if (navigation && slides.length) {\n <button\n igxButton\n class=\"igx-carousel__arrow--next\"\n [attr.aria-label]=\"resourceStrings.igx_carousel_next_slide\"\n [disabled]=\"nextButtonDisabled\"\n (click)=\"next()\"\n (keydown)=\"handleKeydownNext($event)\">\n <ng-container *ngTemplateOutlet=\"getNextButtonTemplate; context: {$implicit: nextButtonDisabled};\"></ng-container>\n </button>\n}\n\n@if (showIndicators) {\n <div [ngClass]=\"indicatorsClass\" [attr.role]=\"'tablist'\" (keyup)=\"handleKeyUp($event)\" (focusout)=\"handleFocusOut($event)\" (click)=\"handleClick()\" (keydown)=\"handleKeydown($event)\">\n @for (slide of slides; track slide) {\n <div #indicators\n class=\"igx-carousel-indicators__indicator\"\n (click)=\"select(slide)\"\n [id]=\"'tab-'+ slide.index + '-' + total\"\n [attr.role]=\"'tab'\"\n