• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 6653488239

26 Oct 2023 11:33AM UTC coverage: 92.101% (-0.1%) from 92.206%
6653488239

push

github

web-flow
Merge pull request #13451 from IgniteUI/bundle-test-extended

refactor(i18n, util): tree shaking i18n

15273 of 17962 branches covered (0.0%)

45 of 45 new or added lines in 24 files covered. (100.0%)

26410 of 28675 relevant lines covered (92.1%)

30213.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.86
/projects/igniteui-angular/src/lib/carousel/carousel.component.ts
1
import { NgIf, NgClass, NgFor, NgTemplateOutlet } from '@angular/common';
2
import {
3
    AfterContentInit,
4
    ChangeDetectorRef,
5
    Component,
6
    ContentChild,
7
    ContentChildren,
8
    ElementRef,
9
    EventEmitter,
10
    HostBinding,
11
    HostListener,
12
    Inject,
13
    Injectable,
14
    Input,
15
    IterableChangeRecord,
16
    IterableDiffer,
17
    IterableDiffers,
2✔
18
    OnDestroy,
2✔
19
    Output,
20
    QueryList,
21
    TemplateRef,
22
    ViewChild
2✔
23
} from '@angular/core';
24
import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';
×
25
import { merge, Subject } from 'rxjs';
×
26
import { takeUntil } from 'rxjs/operators';
27
import { CarouselResourceStringsEN, ICarouselResourceStrings } from '../core/i18n/carousel-resources';
28
import { IBaseEventArgs, mkenum, PlatformUtil } from '../core/utils';
29

30
import { IgxAngularAnimationService } from '../services/animation/angular-animation-service';
2✔
31
import { AnimationService } from '../services/animation/animation';
32
import { Direction, HorizontalAnimationType, IgxCarouselComponentBase } from './carousel-base';
33
import { IgxCarouselIndicatorDirective, IgxCarouselNextButtonDirective, IgxCarouselPrevButtonDirective } from './carousel.directives';
34
import { IgxSlideComponent } from './slide.component';
35
import { IgxIconComponent } from '../icon/icon.component';
36
import { getCurrentResourceStrings } from '../core/i18n/resources';
37

38
let NEXT_ID = 0;
39

40
export const CarouselIndicatorsOrientation = mkenum({
41
    bottom: 'bottom',
42
    top: 'top'
43
});
44
export type CarouselIndicatorsOrientation = (typeof CarouselIndicatorsOrientation)[keyof typeof CarouselIndicatorsOrientation];
45

46
@Injectable()
47
export class CarouselHammerConfig extends HammerGestureConfig {
48
    public override overrides = {
49
        pan: { direction: Hammer.DIRECTION_HORIZONTAL }
50
    };
51
}
52
/**
53
 * **Ignite UI for Angular Carousel** -
54
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/carousel.html)
55
 *
2✔
56
 * The Ignite UI Carousel is used to browse or navigate through a collection of slides. Slides can contain custom
57
 * content such as images or cards and be used for things such as on-boarding tutorials or page-based interfaces.
286✔
58
 * It can be used as a separate fullscreen element or inside another component.
59
 *
60
 * Example:
282✔
61
 * ```html
62
 * <igx-carousel>
63
 *   <igx-slide>
×
64
 *     <h3>First Slide Header</h3>
65
 *     <p>First slide Content</p>
66
 *   <igx-slide>
67
 *   <igx-slide>
68
 *     <h3>Second Slide Header</h3>
69
 *     <p>Second Slide Content</p>
2,728✔
70
 * </igx-carousel>
71
 * ```
72
 */
73
@Component({
1,086✔
74
    providers: [
24✔
75
        {
76
            provide: HAMMER_GESTURE_CONFIG,
1,062✔
77
            useClass: CarouselHammerConfig
78
        }
79
    ],
80
    selector: 'igx-carousel',
276✔
81
    templateUrl: 'carousel.component.html',
6✔
82
    styles: [`
83
    :host {
270✔
84
        display: block;
85
        outline-style: none;
86
    }`],
87
    standalone: true,
276✔
88
    imports: [IgxIconComponent, NgIf, NgClass, NgFor, NgTemplateOutlet]
6✔
89
})
90

270✔
91
export class IgxCarouselComponent extends IgxCarouselComponentBase implements OnDestroy, AfterContentInit {
92

93
    /**
94
     * Sets the `id` of the carousel.
280✔
95
     * If not set, the `id` of the first carousel component will be `"igx-carousel-0"`.
96
     * ```html
97
     * <igx-carousel id="my-first-carousel"></igx-carousel>
98
     * ```
282✔
99
     *
100
     * @memberof IgxCarouselComponent
101
     */
102
    @HostBinding('attr.id')
568✔
103
    @Input()
104
    public id = `igx-carousel-${NEXT_ID++}`;
105
    /**
106
     * Returns the `role` attribute of the carousel.
4✔
107
     * ```typescript
108
     * let carouselRole =  this.carousel.role;
109
     * ```
110
     *
111
     * @memberof IgxCarouselComponent
112
     */
113
    @HostBinding('attr.role') public role = 'region';
114

115
    /** @hidden */
116
    @HostBinding('attr.aria-roledescription')
117
    public roleDescription = 'carousel';
10,197✔
118

119
    /** @hidden */
120
    @HostBinding('attr.aria-labelledby')
121
    public get labelId() {
122
        return this.showIndicatorsLabel ? `${this.id}-label` : null;
123
    }
124

125
    /**
126
     * Returns the class of the carousel component.
127
     * ```typescript
128
     * let class =  this.carousel.cssClass;
3,521✔
129
     * ```
130
     *
131
     * @memberof IgxCarouselComponent
132
     */
133
    @HostBinding('class.igx-carousel')
134
    public cssClass = 'igx-carousel';
135

136
    /**
137
     * Gets the `touch-action` style of the `list item`.
138
     * ```typescript
139
     * let touchAction = this.listItem.touchAction;
25✔
140
     * ```
141
     */
142
    @HostBinding('style.touch-action')
143
    public get touchAction() {
144
        return this.gesturesSupport ? 'pan-y' : 'auto';
145
    }
146

147
    /**
148
     * Sets whether the carousel should `loop` back to the first slide after reaching the last slide.
149
     * Default value is `true`.
150
     * ```html
1✔
151
     * <igx-carousel [loop]="false"></igx-carousel>
152
     * ```
153
     *
154
     * @memberOf IgxCarouselComponent
155
     */
156
    @Input() public loop = true;
157

158
    /**
159
     * Sets whether the carousel will `pause` the slide transitions on user interactions.
160
     * Default value is `true`.
161
     * ```html
24✔
162
     *  <igx-carousel [pause]="false"></igx-carousel>
163
     * ```
164
     *
3,870✔
165
     * @memberOf IgxCarouselComponent
166
     */
167
    @Input() public pause = true;
168

169
    /**
170
     * Controls whether the carousel should render the left/right `navigation` buttons.
171
     * Default value is `true`.
172
     * ```html
173
     * <igx-carousel [navigation]="false"></igx-carousel>
174
     * ```
175
     *
176
     * @memberOf IgxCarouselComponent
22✔
177
     */
22✔
178
    @Input() public navigation = true;
179

180
    /**
32✔
181
     * Controls whether the carousel should support keyboard navigation.
32✔
182
     * Default value is `true`.
32✔
183
     * ```html
32✔
184
     * <igx-carousel [keyboardSupport]="false"></igx-carousel>
32✔
185
     * ```
32✔
186
     *
32✔
187
     * @memberOf IgxCarouselComponent
32✔
188
     */
32✔
189
    @Input() public keyboardSupport = true;
32✔
190

32✔
191
    /**
32✔
192
     * Controls whether the carousel should support gestures.
32✔
193
     * Default value is `true`.
32✔
194
     * ```html
32✔
195
     * <igx-carousel [gesturesSupport]="false"></igx-carousel>
32✔
196
     * ```
32✔
197
     *
32✔
198
     * @memberOf IgxCarouselComponent
32✔
199
     */
32✔
200
    @Input() public gesturesSupport = true;
32✔
201

32✔
202
    /**
32✔
203
     * Controls the maximum indexes that can be shown.
32✔
204
     * Default value is `5`.
32✔
205
     * ```html
32✔
206
     * <igx-carousel [maximumIndicatorsCount]="10"></igx-carousel>
32✔
207
     * ```
32✔
208
     *
209
     * @memberOf IgxCarouselComponent
210
     */
6✔
211
    @Input() public maximumIndicatorsCount = 5;
5✔
212

5✔
213
    /**
5✔
214
     * Gets/sets the display mode of carousel indicators. It can be top or bottom.
215
     * Default value is `bottom`.
216
     * ```html
217
     * <igx-carousel indicatorsOrientation='top'>
3✔
218
     * <igx-carousel>
2✔
219
     * ```
2✔
220
     *
2✔
221
     * @memberOf IgxSlideComponent
222
     */
223
    @Input() public indicatorsOrientation: CarouselIndicatorsOrientation = CarouselIndicatorsOrientation.bottom;
224

225
    /**
4!
226
     * Gets/sets the animation type of carousel.
4✔
227
     * Default value is `slide`.
1!
228
     * ```html
1✔
229
     * <igx-carousel animationType='none'>
230
     * <igx-carousel>
1✔
231
     * ```
232
     *
3✔
233
     * @memberOf IgxSlideComponent
1✔
234
     */
235
    @Input() public override animationType: HorizontalAnimationType = HorizontalAnimationType.slide;
236

237
    /**
238
     * The custom template, if any, that should be used when rendering carousel indicators
2✔
239
     *
1✔
240
     * ```typescript
1✔
241
     * // Set in typescript
1✔
242
     * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate;
243
     * myComponent.carousel.indicatorTemplate = myCustomTemplate;
244
     * ```
245
     * ```html
2✔
246
     * <!-- Set in markup -->
1✔
247
     *  <igx-carousel #carousel>
1✔
248
     *      ...
1✔
249
     *      <ng-template igxCarouselIndicator let-slide>
250
     *         <igx-icon *ngIf="slide.active">brightness_7</igx-icon>
251
     *         <igx-icon *ngIf="!slide.active">brightness_5</igx-icon>
252
     *      </ng-template>
2✔
253
     *  </igx-carousel>
1✔
254
     * ```
255
     */
2✔
256
    @ContentChild(IgxCarouselIndicatorDirective, { read: TemplateRef, static: false })
257
    public indicatorTemplate: TemplateRef<any> = null;
258

2✔
259
    /**
1✔
260
     * The custom template, if any, that should be used when rendering carousel next button
261
     *
262
     * ```typescript
263
     * // Set in typescript
5✔
264
     * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate;
265
     * myComponent.carousel.nextButtonTemplate = myCustomTemplate;
266
     * ```
5✔
267
     * ```html
268
     * <!-- Set in markup -->
269
     *  <igx-carousel #carousel>
10✔
270
     *      ...
2✔
271
     *      <ng-template igxCarouselNextButton let-disabled>
272
     *          <button type="button" igxButton="fab" igxRipple="white" [disabled]="disabled">
8✔
273
     *              <igx-icon>add</igx-icon>
8✔
274
     *          </button>
8✔
275
     *      </ng-template>
8!
276
     *  </igx-carousel>
8✔
277
     * ```
8✔
278
     */
8✔
279
    @ContentChild(IgxCarouselNextButtonDirective, { read: TemplateRef, static: false })
6✔
280
    public nextButtonTemplate: TemplateRef<any> = null;
6✔
281

4✔
282
    /**
4✔
283
     * The custom template, if any, that should be used when rendering carousel previous button
4!
284
     *
285
     * ```typescript
4✔
286
     * // Set in typescript
2✔
287
     * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate;
288
     * myComponent.carousel.nextButtonTemplate = myCustomTemplate;
4✔
289
     * ```
290
     * ```html
291
     * <!-- Set in markup -->
2✔
292
     *  <igx-carousel #carousel>
2✔
293
     *      ...
2✔
294
     *      <ng-template igxCarouselPrevButton let-disabled>
2!
295
     *          <button type="button" igxButton="fab" igxRipple="white" [disabled]="disabled">
296
     *              <igx-icon>remove</igx-icon>
2✔
297
     *          </button>
298
     *      </ng-template>
299
     *  </igx-carousel>
8!
300
     * ```
×
301
     */
302
    @ContentChild(IgxCarouselPrevButtonDirective, { read: TemplateRef, static: false })
303
    public prevButtonTemplate: TemplateRef<any> = null;
304

305
    /**
32✔
306
     * The collection of `slides` currently in the carousel.
307
     * ```typescript
13✔
308
     * let slides: QueryList<IgxSlideComponent> = this.carousel.slides;
32✔
309
     * ```
310
     *
311
     * @memberOf IgxCarouselComponent
312
     */
33✔
313
    @ContentChildren(IgxSlideComponent)
33✔
314
    public slides: QueryList<IgxSlideComponent>;
33✔
315

33✔
316
    /**
18✔
317
     * An event that is emitted after a slide transition has happened.
318
     * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments.
319
     * ```html
320
     * <igx-carousel (slideChanged)="slideChanged($event)"></igx-carousel>
321
     * ```
322
     *
323
     * @memberOf IgxCarouselComponent
324
     */
325
    @Output() public slideChanged = new EventEmitter<ISlideEventArgs>();
326

327
    /**
328
     * An event that is emitted after a slide has been added to the carousel.
8,222✔
329
     * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments.
330
     * ```html
331
     * <igx-carousel (slideAdded)="slideAdded($event)"></igx-carousel>
332
     * ```
333
     *
334
     * @memberOf IgxCarouselComponent
335
     */
336
    @Output() public slideAdded = new EventEmitter<ISlideEventArgs>();
337

338
    /**
339
     * An event that is emitted after a slide has been removed from the carousel.
3✔
340
     * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments.
3✔
341
     * ```html
3✔
342
     * <igx-carousel (slideRemoved)="slideRemoved($event)"></igx-carousel>
3✔
343
     * ```
344
     *
345
     * @memberOf IgxCarouselComponent
346
     */
347
    @Output() public slideRemoved = new EventEmitter<ISlideEventArgs>();
348

349
    /**
350
     * An event that is emitted after the carousel has been paused.
351
     * Provides a reference to the `IgxCarouselComponent` as an event argument.
352
     * ```html
353
     * <igx-carousel (carouselPaused)="carouselPaused($event)"></igx-carousel>
4!
354
     * ```
4✔
355
     *
4✔
356
     * @memberOf IgxCarouselComponent
4✔
357
     */
4✔
358
    @Output() public carouselPaused = new EventEmitter<IgxCarouselComponent>();
359

360
    /**
361
     * An event that is emitted after the carousel has resumed transitioning between `slides`.
362
     * Provides a reference to the `IgxCarouselComponent` as an event argument.
363
     * ```html
364
     * <igx-carousel (carouselPlaying)="carouselPlaying($event)"></igx-carousel>
365
     * ```
366
     *
367
     * @memberOf IgxCarouselComponent
368
     */
8✔
369
    @Output() public carouselPlaying = new EventEmitter<IgxCarouselComponent>();
3,368!
370

3,368✔
371
    @ViewChild('defaultIndicator', { read: TemplateRef, static: true })
3,368✔
372
    private defaultIndicator: TemplateRef<any>;
373

374
    @ViewChild('defaultNextButton', { read: TemplateRef, static: true })
375
    private defaultNextButton: TemplateRef<any>;
376

377
    @ViewChild('defaultPrevButton', { read: TemplateRef, static: true })
378
    private defaultPrevButton: TemplateRef<any>;
379

380
    /**
381
     * @hidden
382
     * @internal
383
     */
3,351✔
384
    public stoppedByInteraction: boolean;
3,351✔
385
    protected override currentItem: IgxSlideComponent;
1✔
386
    protected override previousItem: IgxSlideComponent;
1✔
387
    private _interval: number;
388
    private _resourceStrings = getCurrentResourceStrings(CarouselResourceStringsEN);
3,350✔
389
    private lastInterval: any;
390
    private playing: boolean;
391
    private destroyed: boolean;
392
    private destroy$ = new Subject<any>();
393
    private differ: IterableDiffer<IgxSlideComponent> | null = null;
394
    private incomingSlide: IgxSlideComponent;
395

396
    /**
397
     * An accessor that sets the resource strings.
398
     * By default it uses EN resources.
399
     */
11✔
400
    @Input()
11✔
401
    public set resourceStrings(value: ICarouselResourceStrings) {
1✔
402
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
1✔
403
    }
404

10✔
405
    /**
406
     * An accessor that returns the resource strings.
407
     */
408
    public get resourceStrings(): ICarouselResourceStrings {
409
        return this._resourceStrings;
410
    }
411

412
    /** @hidden */
413
    public get getIndicatorTemplate(): TemplateRef<any> {
414
        if (this.indicatorTemplate) {
415
            return this.indicatorTemplate;
416
        }
417
        return this.defaultIndicator;
50✔
418
    }
36✔
419

36✔
420
    /** @hidden */
36✔
421
    public get getNextButtonTemplate(): TemplateRef<any> {
36✔
422
        if (this.nextButtonTemplate) {
423
            return this.nextButtonTemplate;
424
        }
425
        return this.defaultNextButton;
426
    }
427

428
    /** @hidden */
429
    public get getPrevButtonTemplate(): TemplateRef<any> {
430
        if (this.prevButtonTemplate) {
431
            return this.prevButtonTemplate;
432
        }
433
        return this.defaultPrevButton;
434
    }
435

9!
436
    /** @hidden */
9✔
437
    public get indicatorsOrientationClass() {
9✔
438
        return `igx-carousel-indicators--${this.indicatorsOrientation}`;
9✔
439
    }
440

441
    /** @hidden */
442
    public get showIndicators(): boolean {
2✔
443
        return this.total <= this.maximumIndicatorsCount && this.total > 0;
444
    }
445

3✔
446
    /** @hidden */
447
    public get showIndicatorsLabel(): boolean {
448
        return this.total > this.maximumIndicatorsCount;
109✔
449
    }
56✔
450

56✔
451
    /** @hidden */
452
    public get getCarouselLabel() {
453
        return `${this.current + 1} ${this.resourceStrings.igx_carousel_of} ${this.total}`;
454
    }
100✔
455

100✔
456
    /**
76✔
457
     * Returns the total number of `slides` in the carousel.
3,334✔
458
     * ```typescript
3,334!
459
     * let slideCount =  this.carousel.total;
3,334✔
460
     * ```
461
     *
462
     * @memberOf IgxCarouselComponent
×
463
     */
464
    public get total(): number {
465
        return this.slides?.length;
466
    }
467

468
    /**
469
     * The index of the slide being currently shown.
276✔
470
     * ```typescript
471
     * let currentSlideNumber =  this.carousel.current;
472
     * ```
473
     *
276✔
474
     * @memberOf IgxCarouselComponent
475
     */
476
    public get current(): number {
3,356✔
477
        return !this.currentItem ? 0 : this.currentItem.index;
478
    }
479

16✔
480
    /**
481
     * Returns a boolean indicating if the carousel is playing.
482
     * ```typescript
16✔
483
     * let isPlaying =  this.carousel.isPlaying;
16✔
484
     * ```
485
     *
486
     * @memberOf IgxCarouselComponent
10✔
487
     */
10✔
488
    public get isPlaying(): boolean {
10✔
489
        return this.playing;
10✔
490
    }
10✔
491

10✔
492
    /**
2✔
493
     * Returns а boolean indicating if the carousel is destroyed.
494
     * ```typescript
8✔
495
     * let isDestroyed =  this.carousel.isDestroyed;
2✔
496
     * ```
2✔
497
     *
498
     * @memberOf IgxCarouselComponent
6✔
499
     */
6!
500
    public get isDestroyed(): boolean {
×
501
        return this.destroyed;
×
502
    }
503
    /**
6✔
504
     * Returns a reference to the carousel element in the DOM.
2✔
505
     * ```typescript
506
     * let nativeElement =  this.carousel.nativeElement;
6✔
507
     * ```
6✔
508
     *
4✔
509
     * @memberof IgxCarouselComponent
2✔
510
     */
2✔
511
    public get nativeElement(): any {
2✔
512
        return this.element.nativeElement;
513
    }
514

515
    /**
2✔
516
     * Returns the time `interval` in milliseconds before the slide changes.
517
     * ```typescript
6✔
518
     * let timeInterval = this.carousel.interval;
6!
519
     * ```
×
520
     *
521
     * @memberof IgxCarouselComponent
522
     */
6✔
523
    @Input()
6✔
524
    public get interval(): number {
525
        return this._interval;
526
    }
527

135✔
528
    /**
529
     * Sets the time `interval` in milliseconds before the slide changes.
530
     * If not set, the carousel will not change `slides` automatically.
83✔
531
     * ```html
42✔
532
     * <igx-carousel [interval]="1000"></igx-carousel>
8✔
533
     * ```
8✔
534
     *
535
     * @memberof IgxCarouselComponent
42✔
536
     */
32!
537
    public set interval(value: number) {
×
538
        this._interval = +value;
539
        this.restartInterval();
32✔
540
    }
32✔
541

32✔
542
    constructor(
32✔
543
        cdr: ChangeDetectorRef,
32✔
544
        private element: ElementRef,
545
        private iterableDiffers: IterableDiffers,
546
        @Inject(IgxAngularAnimationService) animationService: AnimationService,
10✔
547
        private platformUtil: PlatformUtil) {
548
        super(animationService, cdr);
42✔
549
        this.differ = this.iterableDiffers.find([]).create(null);
42✔
550
    }
551

552

553
    /** @hidden */
6!
554
    @HostListener('keydown.arrowright', ['$event'])
×
555
    public onKeydownArrowRight(event) {
556
        if (this.keyboardSupport) {
6!
557
            event.preventDefault();
×
558
            this.next();
559
            this.focusSlideElement();
560
        }
561
    }
45✔
562

45!
563
    /** @hidden */
170✔
564
    @HostListener('keydown.arrowleft', ['$event'])
45✔
565
    public onKeydownArrowLeft(event) {
135✔
566
        if (this.keyboardSupport) {
135✔
567
            event.preventDefault();
135✔
568
            this.prev();
135✔
569
            this.focusSlideElement();
13✔
570
        }
571
    }
135✔
572

573
    /** @hidden */
45✔
574
    @HostListener('tap', ['$event'])
10✔
575
    public onTap(event) {
10✔
576
        // play pause only when tap on slide
10✔
577
        if (event.target && event.target.classList.contains('igx-slide')) {
3✔
578
            if (this.isPlaying) {
3✔
579
                if (this.pause) {
580
                    this.stoppedByInteraction = true;
581
                }
45✔
582
                this.stop();
583
            } else if (this.stoppedByInteraction) {
584
                this.play();
585
            }
45!
586
        }
45✔
587
    }
45✔
588

26✔
589
    /** @hidden */
102✔
590
    @HostListener('keydown.home', ['$event'])
26✔
591
    public onKeydownHome(event) {
592
        if (this.keyboardSupport && this.slides.length > 0) {
19✔
593
            event.preventDefault();
18✔
594
            this.slides.first.active = true;
595
            this.focusSlideElement();
45✔
596
        }
597
    }
598

599
    /** @hidden */
600
    @HostListener('keydown.end', ['$event'])
9!
601
    public onKeydownEnd(event) {
×
602
        if (this.keyboardSupport && this.slides.length > 0) {
603
            event.preventDefault();
604
            this.slides.last.active = true;
×
605
            this.focusSlideElement();
606
        }
607
    }
608

35✔
609
    /** @hidden */
610
    @HostListener('mouseenter')
611
    public onMouseEnter() {
2✔
612
        if (this.pause && this.isPlaying) {
613
            this.stoppedByInteraction = true;
614
        }
615
        this.stop();
616
    }
617

618
    /** @hidden */
2✔
619
    @HostListener('mouseleave')
620
    public onMouseLeave() {
621
        if (this.stoppedByInteraction) {
622
            this.play();
623
        }
624
    }
625

626
    /** @hidden */
627
    @HostListener('panleft', ['$event'])
628
    public onPanLeft(event) {
629
        this.pan(event);
630
    }
631

632
    /** @hidden */
633
    @HostListener('panright', ['$event'])
634
    public onPanRight(event) {
635
        this.pan(event);
636
    }
637

638
    /**
639
     * @hidden
640
     */
641
    @HostListener('panend', ['$event'])
642
    public onPanEnd(event) {
643
        if (!this.gesturesSupport) {
644
            return;
645
        }
646
        event.preventDefault();
647

648
        const slideWidth = this.currentItem.nativeElement.offsetWidth;
649
        const panOffset = (slideWidth / 1000);
650
        const deltaX = Math.abs(event.deltaX) + panOffset < slideWidth ? Math.abs(event.deltaX) : slideWidth - panOffset;
651
        const velocity = Math.abs(event.velocity);
652
        this.resetSlideStyles(this.currentItem);
653
        if (this.incomingSlide) {
654
            this.resetSlideStyles(this.incomingSlide);
655
            if (slideWidth / 2 < deltaX || velocity > 1) {
656
                this.incomingSlide.direction = event.deltaX < 0 ? Direction.NEXT : Direction.PREV;
657
                this.incomingSlide.previous = false;
658

659
                this.animationPosition = this.animationType === HorizontalAnimationType.fade ?
2✔
660
                    deltaX / slideWidth : (slideWidth - deltaX) / slideWidth;
661

662
                if (velocity > 1) {
663
                    this.newDuration = this.defaultAnimationDuration / velocity;
664
                }
665
                this.incomingSlide.active = true;
666
            } else {
667
                this.currentItem.direction = event.deltaX > 0 ? Direction.NEXT : Direction.PREV;
668
                this.previousItem = this.incomingSlide;
669
                this.previousItem.previous = true;
670
                this.animationPosition = this.animationType === HorizontalAnimationType.fade ?
671
                    Math.abs((slideWidth - deltaX) / slideWidth) : deltaX / slideWidth;
672
                this.playAnimations();
673
            }
674
        }
675

676
        if (this.stoppedByInteraction) {
677
            this.play();
678
        }
679
    }
680

681
    /** @hidden */
682
    public ngAfterContentInit() {
683
        this.slides.changes
684
            .pipe(takeUntil(this.destroy$))
685
            .subscribe((change: QueryList<IgxSlideComponent>) => this.initSlides(change));
686

687
        this.initSlides(this.slides);
688
    }
689

690
    /** @hidden */
691
    public ngOnDestroy() {
692
        this.destroy$.next(true);
693
        this.destroy$.complete();
694
        this.destroyed = true;
695
        if (this.lastInterval) {
696
            clearInterval(this.lastInterval);
697
        }
698
    }
699

700
    /**
701
     * Returns the slide corresponding to the provided `index` or null.
702
     * ```typescript
703
     * let slide1 =  this.carousel.get(1);
704
     * ```
705
     *
706
     * @memberOf IgxCarouselComponent
707
     */
708
    public get(index: number): IgxSlideComponent {
709
        return this.slides.find((slide) => slide.index === index);
710
    }
711

712
    /**
713
     * Adds a new slide to the carousel.
714
     * ```typescript
715
     * this.carousel.add(newSlide);
716
     * ```
717
     *
718
     * @memberOf IgxCarouselComponent
719
     */
720
    public add(slide: IgxSlideComponent) {
721
        const newSlides = this.slides.toArray();
722
        newSlides.push(slide);
723
        this.slides.reset(newSlides);
724
        this.slides.notifyOnChanges();
725
    }
726

727
    /**
728
     * Removes a slide from the carousel.
729
     * ```typescript
730
     * this.carousel.remove(slide);
731
     * ```
732
     *
733
     * @memberOf IgxCarouselComponent
734
     */
735
    public remove(slide: IgxSlideComponent) {
736
        if (slide && slide === this.get(slide.index)) { // check if the requested slide for delete is present in the carousel
737
            const newSlides = this.slides.toArray();
738
            newSlides.splice(slide.index, 1);
739
            this.slides.reset(newSlides);
740
            this.slides.notifyOnChanges();
741
        }
742
    }
743

744
    /**
745
     * Kicks in a transition for a given slide with a given `direction`.
746
     * ```typescript
747
     * this.carousel.select(this.carousel.get(2), Direction.NEXT);
748
     * ```
749
     *
750
     * @memberOf IgxCarouselComponent
751
     */
752
    public select(slide: IgxSlideComponent, direction: Direction = Direction.NONE) {
753
        if (slide && slide !== this.currentItem) {
754
            slide.direction = direction;
755
            slide.active = true;
756
        }
757
    }
758

759
    /**
760
     * Transitions to the next slide in the carousel.
761
     * ```typescript
762
     * this.carousel.next();
763
     * ```
764
     *
765
     * @memberOf IgxCarouselComponent
766
     */
767
    public next() {
768
        const index = this.getNextIndex();
769

770
        if (index === 0 && !this.loop) {
771
            this.stop();
772
            return;
773
        }
774
        return this.select(this.get(index), Direction.NEXT);
775
    }
776

777
    /**
778
     * Transitions to the previous slide in the carousel.
779
     * ```typescript
780
     * this.carousel.prev();
781
     * ```
782
     *
783
     * @memberOf IgxCarouselComponent
784
     */
785
    public prev() {
786
        const index = this.getPrevIndex();
787

788
        if (!this.loop && index === this.total - 1) {
789
            this.stop();
790
            return;
791
        }
792
        return this.select(this.get(index), Direction.PREV);
793
    }
794

795
    /**
796
     * Resumes playing of the carousel if in paused state.
797
     * No operation otherwise.
798
     * ```typescript
799
     * this.carousel.play();
800
     * }
801
     * ```
802
     *
803
     * @memberOf IgxCarouselComponent
804
     */
805
    public play() {
806
        if (!this.playing) {
807
            this.playing = true;
808
            this.carouselPlaying.emit(this);
809
            this.restartInterval();
810
            this.stoppedByInteraction = false;
811
        }
812
    }
813

814
    /**
815
     * Stops slide transitions if the `pause` option is set to `true`.
816
     * No operation otherwise.
817
     * ```typescript
818
     *  this.carousel.stop();
819
     * }
820
     * ```
821
     *
822
     * @memberOf IgxCarouselComponent
823
     */
824
    public stop() {
825
        if (this.pause) {
826
            this.playing = false;
827
            this.carouselPaused.emit(this);
828
            this.resetInterval();
829
        }
830
    }
831

832
    protected getPreviousElement(): HTMLElement {
833
        return this.previousItem.nativeElement;
834
    }
835

836
    protected getCurrentElement(): HTMLElement {
837

838
        return this.currentItem.nativeElement;
839
    }
840

841
    private resetInterval() {
842
        if (this.lastInterval) {
843
            clearInterval(this.lastInterval);
844
            this.lastInterval = null;
845
        }
846
    }
847

848
    private restartInterval() {
849
        this.resetInterval();
850

851
        if (!isNaN(this.interval) && this.interval > 0 && this.platformUtil.isBrowser) {
852
            this.lastInterval = setInterval(() => {
853
                const tick = +this.interval;
854
                if (this.playing && this.total && !isNaN(tick) && tick > 0) {
855
                    this.next();
856
                } else {
857
                    this.stop();
858
                }
859
            }, this.interval);
860
        }
861
    }
862

863
    /** @hidden */
864
    public get nextButtonDisabled() {
865
        return !this.loop && this.current === (this.total - 1);
866
    }
867

868
    /** @hidden */
869
    public get prevButtonDisabled() {
870
        return !this.loop && this.current === 0;
871
    }
872

873
    private getNextIndex(): number {
874
        return (this.current + 1) % this.total;
875
    }
876

877
    private getPrevIndex(): number {
878
        return this.current - 1 < 0 ? this.total - 1 : this.current - 1;
879
    }
880

881
    private resetSlideStyles(slide: IgxSlideComponent) {
882
        slide.nativeElement.style.transform = '';
883
        slide.nativeElement.style.opacity = '';
884
    }
885

886
    private pan(event) {
887
        const slideWidth = this.currentItem.nativeElement.offsetWidth;
888
        const panOffset = (slideWidth / 1000);
889
        const deltaX = event.deltaX;
890
        const index = deltaX < 0 ? this.getNextIndex() : this.getPrevIndex();
891
        const offset = deltaX < 0 ? slideWidth + deltaX : -slideWidth + deltaX;
892

893
        if (!this.gesturesSupport || event.isFinal || Math.abs(deltaX) + panOffset >= slideWidth) {
894
            return;
895
        }
896

897
        if (!this.loop && ((this.current === 0 && deltaX > 0) || (this.current === this.total - 1 && deltaX < 0))) {
898
            this.incomingSlide = null;
899
            return;
900
        }
901

902
        event.preventDefault();
903
        if (this.isPlaying) {
904
            this.stoppedByInteraction = true;
905
            this.stop();
906
        }
907

908
        if (this.previousItem && this.previousItem.previous) {
909
            this.previousItem.previous = false;
910
        }
911
        this.finishAnimations();
912

913
        if (this.incomingSlide) {
914
            if (index !== this.incomingSlide.index) {
915
                this.resetSlideStyles(this.incomingSlide);
916
                this.incomingSlide.previous = false;
917
                this.incomingSlide = this.get(index);
918
            }
919
        } else {
920
            this.incomingSlide = this.get(index);
921
        }
922
        this.incomingSlide.previous = true;
923

924
        if (this.animationType === HorizontalAnimationType.fade) {
925
            this.currentItem.nativeElement.style.opacity = `${Math.abs(offset) / slideWidth}`;
926
        } else {
927
            this.currentItem.nativeElement.style.transform = `translateX(${deltaX}px)`;
928
            this.incomingSlide.nativeElement.style.transform = `translateX(${offset}px)`;
929
        }
930
    }
931

932
    private unsubscriber(slide: IgxSlideComponent) {
933
        return merge(this.destroy$, slide.isDestroyed);
934
    }
935

936
    private onSlideActivated(slide: IgxSlideComponent) {
937
        if (slide.active && slide !== this.currentItem) {
938
            if (slide.direction === Direction.NONE) {
939
                const newIndex = slide.index;
940
                slide.direction = newIndex > this.current ? Direction.NEXT : Direction.PREV;
941
            }
942

943
            if (this.currentItem) {
944
                if (this.previousItem && this.previousItem.previous) {
945
                    this.previousItem.previous = false;
946
                }
947
                this.currentItem.direction = slide.direction;
948
                this.currentItem.active = false;
949

950
                this.previousItem = this.currentItem;
951
                this.currentItem = slide;
952
                this.triggerAnimations();
953
            } else {
954
                this.currentItem = slide;
955
            }
956
            this.slideChanged.emit({ carousel: this, slide });
957
            this.restartInterval();
958
        }
959
    }
960

961

962
    private finishAnimations() {
963
        if (this.animationStarted(this.leaveAnimationPlayer)) {
964
            this.leaveAnimationPlayer.finish();
965
        }
966

967
        if (this.animationStarted(this.enterAnimationPlayer)) {
968
            this.enterAnimationPlayer.finish();
969
        }
970
    }
971

972
    private initSlides(change: QueryList<IgxSlideComponent>) {
973
        const diff = this.differ.diff(change.toArray());
974
        if (diff) {
975
            this.slides.reduce((any, c, ind) => c.index = ind, 0); // reset slides indexes
976
            diff.forEachAddedItem((record: IterableChangeRecord<IgxSlideComponent>) => {
977
                const slide = record.item;
978
                slide.total = this.total;
979
                this.slideAdded.emit({ carousel: this, slide });
980
                if (slide.active) {
981
                    this.currentItem = slide;
982
                }
983
                slide.activeChange.pipe(takeUntil(this.unsubscriber(slide))).subscribe(() => this.onSlideActivated(slide));
984
            });
985

986
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxSlideComponent>) => {
987
                const slide = record.item;
988
                this.slideRemoved.emit({ carousel: this, slide });
989
                if (slide.active) {
990
                    slide.active = false;
991
                    this.currentItem = this.get(slide.index < this.total ? slide.index : this.total - 1);
992
                }
993
            });
994

995
            this.updateSlidesSelection();
996
        }
997
    }
998

999
    private updateSlidesSelection() {
1000
        if (this.platformUtil.isBrowser) {
1001
            requestAnimationFrame(() => {
1002
                if (this.currentItem) {
1003
                    this.currentItem.active = true;
1004
                    const activeSlides = this.slides.filter(slide => slide.active && slide.index !== this.currentItem.index);
1005
                    activeSlides.forEach(slide => slide.active = false);
1006
                } else if (this.total) {
1007
                    this.slides.first.active = true;
1008
                }
1009
                this.play();
1010
            });
1011
        }
1012
    }
1013
    private focusSlideElement() {
1014
        if (this.leaveAnimationPlayer) {
1015
            this.leaveAnimationPlayer.animationEnd
1016
                .pipe(takeUntil(this.destroy$))
1017
                .subscribe(() => {
1018
                this.slides.find(s => s.active).nativeElement.focus();
1019
            });
1020
        } else {
1021
            requestAnimationFrame(() => this.slides.find(s => s.active).nativeElement.focus());
1022
        }
1023
    }
1024

1025
}
1026

1027
export interface ISlideEventArgs extends IBaseEventArgs {
1028
    carousel: IgxCarouselComponent;
1029
    slide: IgxSlideComponent;
1030
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc