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

IgniteUI / igniteui-angular / 20960087204

13 Jan 2026 02:19PM UTC coverage: 12.713% (-78.8%) from 91.5%
20960087204

Pull #16746

github

web-flow
Merge 9afce6e5d into a967f087e
Pull Request #16746: fix(csv): export summaries - master

1008 of 16803 branches covered (6.0%)

19 of 23 new or added lines in 2 files covered. (82.61%)

24693 existing lines in 336 files now uncovered.

3985 of 31345 relevant lines covered (12.71%)

2.49 hits per line

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

1.09
/projects/igniteui-angular/carousel/src/carousel/carousel.component.ts
1
import { NgClass, NgTemplateOutlet } from '@angular/common';
2
import { AfterContentInit, Component, ContentChild, ContentChildren, ElementRef, EventEmitter, HostBinding, HostListener, Injectable, Input, IterableChangeRecord, IterableDiffer, IterableDiffers, OnDestroy, Output, QueryList, TemplateRef, ViewChild, ViewChildren, booleanAttribute, DOCUMENT, inject } from '@angular/core';
3
import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';
4
import { merge, Subject } from 'rxjs';
5
import { takeUntil } from 'rxjs/operators';
6
import { CarouselResourceStringsEN, ICarouselResourceStrings, ɵIgxDirectionality } from 'igniteui-angular/core';
7
import { first, IBaseEventArgs, last, PlatformUtil } from 'igniteui-angular/core';
8
import { CarouselAnimationDirection, IgxCarouselComponentBase } from './carousel-base';
9
import { IgxCarouselIndicatorDirective, IgxCarouselNextButtonDirective, IgxCarouselPrevButtonDirective } from './carousel.directives';
10
import { IgxSlideComponent } from './slide.component';
11
import { IgxIconComponent } from 'igniteui-angular/icon';
12
import { IgxButtonDirective } from 'igniteui-angular/directives';
13
import { getCurrentResourceStrings, onResourceChangeHandle } from 'igniteui-angular/core';
14
import { HammerGesturesManager } from 'igniteui-angular/core';
15
import { CarouselAnimationType, CarouselIndicatorsOrientation } from './enums';
16

17
let NEXT_ID = 0;
3✔
18

19

20
@Injectable()
21
export class CarouselHammerConfig extends HammerGestureConfig {
3✔
22
    public override overrides = {
×
23
        pan: { direction: HammerGesturesManager.Hammer?.DIRECTION_HORIZONTAL }
24
    };
25
}
26
/**
27
 * **Ignite UI for Angular Carousel** -
28
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/carousel.html)
29
 *
30
 * The Ignite UI Carousel is used to browse or navigate through a collection of slides. Slides can contain custom
31
 * content such as images or cards and be used for things such as on-boarding tutorials or page-based interfaces.
32
 * It can be used as a separate fullscreen element or inside another component.
33
 *
34
 * Example:
35
 * ```html
36
 * <igx-carousel>
37
 *   <igx-slide>
38
 *     <h3>First Slide Header</h3>
39
 *     <p>First slide Content</p>
40
 *   <igx-slide>
41
 *   <igx-slide>
42
 *     <h3>Second Slide Header</h3>
43
 *     <p>Second Slide Content</p>
44
 * </igx-carousel>
45
 * ```
46
 */
47
@Component({
48
    providers: [
49
        {
50
            provide: HAMMER_GESTURE_CONFIG,
51
            useClass: CarouselHammerConfig
52
        }
53
    ],
54
    selector: 'igx-carousel',
55
    templateUrl: 'carousel.component.html',
56
    styles: [`
57
    :host {
58
        display: block;
59
        outline-style: none;
60
    }`],
61
    imports: [IgxButtonDirective, IgxIconComponent, NgClass, NgTemplateOutlet]
62
})
63
export class IgxCarouselComponent extends IgxCarouselComponentBase implements OnDestroy, AfterContentInit {
3✔
UNCOV
64
    private element = inject(ElementRef);
×
UNCOV
65
    private iterableDiffers = inject(IterableDiffers);
×
UNCOV
66
    private platformUtil = inject(PlatformUtil);
×
UNCOV
67
    private dir = inject(ɵIgxDirectionality);
×
UNCOV
68
    private document = inject(DOCUMENT);
×
69

70

71
    /**
72
     * Sets the `id` of the carousel.
73
     * If not set, the `id` of the first carousel component will be `"igx-carousel-0"`.
74
     * ```html
75
     * <igx-carousel id="my-first-carousel"></igx-carousel>
76
     * ```
77
     *
78
     * @memberof IgxCarouselComponent
79
     */
80
    @HostBinding('attr.id')
81
    @Input()
UNCOV
82
    public id = `igx-carousel-${NEXT_ID++}`;
×
83
    /**
84
     * Returns the `role` attribute of the carousel.
85
     * ```typescript
86
     * let carouselRole =  this.carousel.role;
87
     * ```
88
     *
89
     * @memberof IgxCarouselComponent
90
     */
UNCOV
91
    @HostBinding('attr.role') public role = 'region';
×
92

93
    /** @hidden */
94
    @HostBinding('attr.aria-roledescription')
UNCOV
95
    public roleDescription = 'carousel';
×
96

97
    /** @hidden */
98
    @HostBinding('attr.aria-labelledby')
99
    public get labelId() {
UNCOV
100
        return this.showIndicatorsLabel ? `${this.id}-label` : null;
×
101
    }
102

103
    /** @hidden */
104
    @HostBinding('class.igx-carousel--vertical')
105
        public get isVertical(): boolean {
UNCOV
106
                return this.vertical;
×
107
        }
108

109
    /**
110
     * Returns the class of the carousel component.
111
     * ```typescript
112
     * let class =  this.carousel.cssClass;
113
     * ```
114
     *
115
     * @memberof IgxCarouselComponent
116
     */
117
    @HostBinding('class.igx-carousel')
UNCOV
118
    public cssClass = 'igx-carousel';
×
119

120
    /**
121
     * Gets the `touch-action` style of the `list item`.
122
     * ```typescript
123
     * let touchAction = this.listItem.touchAction;
124
     * ```
125
     */
126
    @HostBinding('style.touch-action')
127
    public get touchAction() {
UNCOV
128
        return this.gesturesSupport ? 'pan-y' : 'auto';
×
129
    }
130

131
    /**
132
     * Sets whether the carousel should `loop` back to the first slide after reaching the last slide.
133
     * Default value is `true`.
134
     * ```html
135
     * <igx-carousel [loop]="false"></igx-carousel>
136
     * ```
137
     *
138
     * @memberOf IgxCarouselComponent
139
     */
UNCOV
140
    @Input({ transform: booleanAttribute }) public loop = true;
×
141

142
    /**
143
     * Sets whether the carousel will `pause` the slide transitions on user interactions.
144
     * Default value is `true`.
145
     * ```html
146
     *  <igx-carousel [pause]="false"></igx-carousel>
147
     * ```
148
     *
149
     * @memberOf IgxCarouselComponent
150
     */
UNCOV
151
    @Input({ transform: booleanAttribute }) public pause = true;
×
152

153
    /**
154
     * Controls whether the carousel should render the left/right `navigation` buttons.
155
     * Default value is `true`.
156
     * ```html
157
     * <igx-carousel [navigation]="false"></igx-carousel>
158
     * ```
159
     *
160
     * @memberOf IgxCarouselComponent
161
     */
UNCOV
162
    @Input({ transform: booleanAttribute }) public navigation = true;
×
163

164
    /**
165
     * Controls whether the carousel should render the indicators.
166
     * Default value is `true`.
167
     * ```html
168
     * <igx-carousel [indicators]="false"></igx-carousel>
169
     * ```
170
     *
171
     * @memberOf IgxCarouselComponent
172
     */
UNCOV
173
    @Input({ transform: booleanAttribute }) public indicators = true;
×
174

175

176
    /**
177
     * Controls whether the carousel has vertical alignment.
178
     * Default value is `false`.
179
     * ```html
180
     * <igx-carousel [vertical]="true"></igx-carousel>
181
     * ```
182
     *
183
     * @memberOf IgxCarouselComponent
184
     */
UNCOV
185
    @Input({ transform: booleanAttribute }) public override vertical = false;
×
186

187
    /**
188
     * Controls whether the carousel should support gestures.
189
     * Default value is `true`.
190
     * ```html
191
     * <igx-carousel [gesturesSupport]="false"></igx-carousel>
192
     * ```
193
     *
194
     * @memberOf IgxCarouselComponent
195
     */
UNCOV
196
    @Input({ transform: booleanAttribute }) public gesturesSupport = true;
×
197

198
    /**
199
     * Controls the maximum indexes that can be shown.
200
     * Default value is `10`.
201
     * ```html
202
     * <igx-carousel [maximumIndicatorsCount]="5"></igx-carousel>
203
     * ```
204
     *
205
     * @memberOf IgxCarouselComponent
206
     */
UNCOV
207
    @Input() public maximumIndicatorsCount = 10;
×
208

209
    /**
210
     * Gets/sets the display mode of carousel indicators. It can be `start` or `end`.
211
     * Default value is `end`.
212
     * ```html
213
     * <igx-carousel indicatorsOrientation="start">
214
     * <igx-carousel>
215
     * ```
216
     *
217
     * @memberOf IgxCarouselComponent
218
     */
UNCOV
219
    @Input() public indicatorsOrientation: CarouselIndicatorsOrientation = CarouselIndicatorsOrientation.end;
×
220

221
    /**
222
     * Gets/sets the animation type of carousel.
223
     * Default value is `slide`.
224
     * ```html
225
     * <igx-carousel animationType="none">
226
     * <igx-carousel>
227
     * ```
228
     *
229
     * @memberOf IgxCarouselComponent
230
     */
UNCOV
231
    @Input() public override animationType: CarouselAnimationType = CarouselAnimationType.slide;
×
232

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

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

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

301
    /**
302
     * The collection of `slides` currently in the carousel.
303
     * ```typescript
304
     * let slides: QueryList<IgxSlideComponent> = this.carousel.slides;
305
     * ```
306
     *
307
     * @memberOf IgxCarouselComponent
308
     */
309
    @ContentChildren(IgxSlideComponent)
310
    public slides: QueryList<IgxSlideComponent>;
311

312
    /**
313
     * An event that is emitted after a slide transition has happened.
314
     * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments.
315
     * ```html
316
     * <igx-carousel (slideChanged)="slideChanged($event)"></igx-carousel>
317
     * ```
318
     *
319
     * @memberOf IgxCarouselComponent
320
     */
UNCOV
321
    @Output() public slideChanged = new EventEmitter<ISlideEventArgs>();
×
322

323
    /**
324
     * An event that is emitted after a slide has been added to the carousel.
325
     * Provides references to the `IgxCarouselComponent` and `IgxSlideComponent` as event arguments.
326
     * ```html
327
     * <igx-carousel (slideAdded)="slideAdded($event)"></igx-carousel>
328
     * ```
329
     *
330
     * @memberOf IgxCarouselComponent
331
     */
UNCOV
332
    @Output() public slideAdded = new EventEmitter<ISlideEventArgs>();
×
333

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

345
    /**
346
     * An event that is emitted after the carousel has been paused.
347
     * Provides a reference to the `IgxCarouselComponent` as an event argument.
348
     * ```html
349
     * <igx-carousel (carouselPaused)="carouselPaused($event)"></igx-carousel>
350
     * ```
351
     *
352
     * @memberOf IgxCarouselComponent
353
     */
UNCOV
354
    @Output() public carouselPaused = new EventEmitter<IgxCarouselComponent>();
×
355

356
    /**
357
     * An event that is emitted after the carousel has resumed transitioning between `slides`.
358
     * Provides a reference to the `IgxCarouselComponent` as an event argument.
359
     * ```html
360
     * <igx-carousel (carouselPlaying)="carouselPlaying($event)"></igx-carousel>
361
     * ```
362
     *
363
     * @memberOf IgxCarouselComponent
364
     */
UNCOV
365
    @Output() public carouselPlaying = new EventEmitter<IgxCarouselComponent>();
×
366

367
    @ViewChild('defaultIndicator', { read: TemplateRef, static: true })
368
    private defaultIndicator: TemplateRef<any>;
369

370
    @ViewChild('defaultNextButton', { read: TemplateRef, static: true })
371
    private defaultNextButton: TemplateRef<any>;
372

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

376
    @ViewChildren('indicators', { read: ElementRef })
377
    private _indicators: QueryList<ElementRef<HTMLDivElement>>;
378

379
    /**
380
     * @hidden
381
     * @internal
382
     */
383
    public stoppedByInteraction: boolean;
384
    protected override currentItem: IgxSlideComponent;
385
    protected override previousItem: IgxSlideComponent;
386
    private _interval: number;
UNCOV
387
    private _resourceStrings: ICarouselResourceStrings = null;
×
UNCOV
388
    private _defaultResourceStrings = getCurrentResourceStrings(CarouselResourceStringsEN);
×
389
    private lastInterval: any;
390
    private playing: boolean;
391
    private destroyed: boolean;
UNCOV
392
    private destroy$ = new Subject<any>();
×
UNCOV
393
    private differ: IterableDiffer<IgxSlideComponent> | null = null;
×
394
    private incomingSlide: IgxSlideComponent;
UNCOV
395
    private _hasKeyboardFocusOnIndicators = false;
×
396

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

406
    /**
407
     * An accessor that returns the resource strings.
408
     */
409
    public get resourceStrings(): ICarouselResourceStrings {
UNCOV
410
        return this._resourceStrings || this._defaultResourceStrings;
×
411
    }
412

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

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

UNCOV
427
        return this.defaultNextButton
×
428
    }
429

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

UNCOV
436
        return this.defaultPrevButton
×
437
    }
438

439
    /** @hidden */
440
    public get indicatorsClass() {
UNCOV
441
        return {
×
442
            ['igx-carousel-indicators--focused']: this._hasKeyboardFocusOnIndicators,
443
            [`igx-carousel-indicators--${this.getIndicatorsClass()}`]: true
444
        };
445
    }
446

447
    /** @hidden */
448
    public get showIndicators(): boolean {
UNCOV
449
        return this.indicators && this.total <= this.maximumIndicatorsCount && this.total > 0;
×
450
    }
451

452
    /** @hidden */
453
    public get showIndicatorsLabel(): boolean {
UNCOV
454
        return this.indicators && this.total > this.maximumIndicatorsCount;
×
455
    }
456

457
    /** @hidden */
458
    public get getCarouselLabel() {
UNCOV
459
        return `${this.current + 1} ${this.resourceStrings.igx_carousel_of} ${this.total}`;
×
460
    }
461

462
    /**
463
     * Returns the total number of `slides` in the carousel.
464
     * ```typescript
465
     * let slideCount =  this.carousel.total;
466
     * ```
467
     *
468
     * @memberOf IgxCarouselComponent
469
     */
470
    public get total(): number {
UNCOV
471
        return this.slides?.length;
×
472
    }
473

474
    /**
475
     * The index of the slide being currently shown.
476
     * ```typescript
477
     * let currentSlideNumber =  this.carousel.current;
478
     * ```
479
     *
480
     * @memberOf IgxCarouselComponent
481
     */
482
    public get current(): number {
UNCOV
483
        return !this.currentItem ? 0 : this.currentItem.index;
×
484
    }
485

486
    /**
487
     * Returns a boolean indicating if the carousel is playing.
488
     * ```typescript
489
     * let isPlaying =  this.carousel.isPlaying;
490
     * ```
491
     *
492
     * @memberOf IgxCarouselComponent
493
     */
494
    public get isPlaying(): boolean {
UNCOV
495
        return this.playing;
×
496
    }
497

498
    /**
499
     * Returns а boolean indicating if the carousel is destroyed.
500
     * ```typescript
501
     * let isDestroyed =  this.carousel.isDestroyed;
502
     * ```
503
     *
504
     * @memberOf IgxCarouselComponent
505
     */
506
    public get isDestroyed(): boolean {
UNCOV
507
        return this.destroyed;
×
508
    }
509
    /**
510
     * Returns a reference to the carousel element in the DOM.
511
     * ```typescript
512
     * let nativeElement =  this.carousel.nativeElement;
513
     * ```
514
     *
515
     * @memberof IgxCarouselComponent
516
     */
517
    public get nativeElement(): any {
UNCOV
518
        return this.element.nativeElement;
×
519
    }
520

521
    /**
522
     * Returns the time `interval` in milliseconds before the slide changes.
523
     * ```typescript
524
     * let timeInterval = this.carousel.interval;
525
     * ```
526
     *
527
     * @memberof IgxCarouselComponent
528
     */
529
    @Input()
530
    public get interval(): number {
UNCOV
531
        return this._interval;
×
532
    }
533

534
    /**
535
     * Sets the time `interval` in milliseconds before the slide changes.
536
     * If not set, the carousel will not change `slides` automatically.
537
     * ```html
538
     * <igx-carousel [interval]="1000"></igx-carousel>
539
     * ```
540
     *
541
     * @memberof IgxCarouselComponent
542
     */
543
    public set interval(value: number) {
UNCOV
544
        this._interval = +value;
×
UNCOV
545
        this.restartInterval();
×
546
    }
547

548
    constructor() {
UNCOV
549
        super();
×
UNCOV
550
        this.differ = this.iterableDiffers.find([]).create(null);
×
UNCOV
551
        onResourceChangeHandle(this.destroy$, () => {
×
552
            this._defaultResourceStrings = getCurrentResourceStrings(CarouselResourceStringsEN, false);
×
553
        }, this);
554
    }
555

556
    /** @hidden */
557
    @HostListener('tap', ['$event'])
558
    public onTap(event) {
559
        // play pause only when tap on slide
UNCOV
560
        if (event.target && event.target.classList.contains('igx-slide')) {
×
UNCOV
561
            if (this.isPlaying) {
×
UNCOV
562
                if (this.pause) {
×
UNCOV
563
                    this.stoppedByInteraction = true;
×
564
                }
UNCOV
565
                this.stop();
×
UNCOV
566
            } else if (this.stoppedByInteraction) {
×
UNCOV
567
                this.play();
×
568
            }
569
        }
570
    }
571

572
    /** @hidden */
573
    @HostListener('mouseenter')
574
    public onMouseEnter() {
UNCOV
575
        if (this.pause && this.isPlaying) {
×
UNCOV
576
            this.stoppedByInteraction = true;
×
577
        }
UNCOV
578
        this.stop();
×
579
    }
580

581
    /** @hidden */
582
    @HostListener('mouseleave')
583
    public onMouseLeave() {
UNCOV
584
        if (this.stoppedByInteraction) {
×
UNCOV
585
            this.play();
×
586
        }
587
    }
588

589
    /** @hidden */
590
    @HostListener('panleft', ['$event'])
591
    public onPanLeft(event) {
UNCOV
592
        if (!this.vertical) {
×
UNCOV
593
            this.pan(event);
×
594
        }
595
    }
596

597
    /** @hidden */
598
    @HostListener('panright', ['$event'])
599
    public onPanRight(event) {
UNCOV
600
        if (!this.vertical) {
×
UNCOV
601
            this.pan(event);
×
602
        }
603
    }
604

605
    /** @hidden */
606
    @HostListener('panup', ['$event'])
607
    public onPanUp(event) {
UNCOV
608
        if (this.vertical) {
×
UNCOV
609
            this.pan(event);
×
610
        }
611
    }
612

613
    /** @hidden */
614
    @HostListener('pandown', ['$event'])
615
    public onPanDown(event) {
UNCOV
616
        if (this.vertical) {
×
UNCOV
617
            this.pan(event);
×
618
        }
619
    }
620

621
    /**
622
     * @hidden
623
     */
624
    @HostListener('panend', ['$event'])
625
    public onPanEnd(event) {
UNCOV
626
        if (!this.gesturesSupport) {
×
UNCOV
627
            return;
×
628
        }
UNCOV
629
        event.preventDefault();
×
630

UNCOV
631
        const slideSize = this.vertical
×
632
            ? this.currentItem.nativeElement.offsetHeight
633
            : this.currentItem.nativeElement.offsetWidth;
UNCOV
634
        const panOffset = (slideSize / 1000);
×
UNCOV
635
        const eventDelta = this.vertical ? event.deltaY : event.deltaX;
×
UNCOV
636
        const delta = Math.abs(eventDelta) + panOffset < slideSize ? Math.abs(eventDelta) : slideSize - panOffset;
×
UNCOV
637
        const velocity = Math.abs(event.velocity);
×
UNCOV
638
        this.resetSlideStyles(this.currentItem);
×
UNCOV
639
        if (this.incomingSlide) {
×
UNCOV
640
            this.resetSlideStyles(this.incomingSlide);
×
UNCOV
641
            if (slideSize / 2 < delta || velocity > 1) {
×
UNCOV
642
                this.incomingSlide.direction = eventDelta < 0 ? CarouselAnimationDirection.NEXT : CarouselAnimationDirection.PREV;
×
UNCOV
643
                this.incomingSlide.previous = false;
×
644

UNCOV
645
                this.animationPosition = this.animationType === CarouselAnimationType.fade ?
×
646
                    delta / slideSize : (slideSize - delta) / slideSize;
647

UNCOV
648
                if (velocity > 1) {
×
UNCOV
649
                    this.newDuration = this.defaultAnimationDuration / velocity;
×
650
                }
UNCOV
651
                this.incomingSlide.active = true;
×
652
            } else {
UNCOV
653
                this.currentItem.direction = eventDelta > 0 ? CarouselAnimationDirection.NEXT : CarouselAnimationDirection.PREV;
×
UNCOV
654
                this.previousItem = this.incomingSlide;
×
UNCOV
655
                this.previousItem.previous = true;
×
UNCOV
656
                this.animationPosition = this.animationType === CarouselAnimationType.fade ?
×
657
                    Math.abs((slideSize - delta) / slideSize) : delta / slideSize;
UNCOV
658
                this.playAnimations();
×
659
            }
660
        }
661

UNCOV
662
        if (this.stoppedByInteraction) {
×
663
            this.play();
×
664
        }
665
    }
666

667
    /** @hidden */
668
    public ngAfterContentInit() {
UNCOV
669
        this.slides.changes
×
670
            .pipe(takeUntil(this.destroy$))
UNCOV
671
            .subscribe((change: QueryList<IgxSlideComponent>) => this.initSlides(change));
×
672

UNCOV
673
        this.initSlides(this.slides);
×
674
    }
675

676
    /** @hidden */
677
    public override ngOnDestroy() {
UNCOV
678
        super.ngOnDestroy();
×
UNCOV
679
        this.destroy$.next(true);
×
UNCOV
680
        this.destroy$.complete();
×
UNCOV
681
        this.destroyed = true;
×
UNCOV
682
        if (this.lastInterval) {
×
UNCOV
683
            clearInterval(this.lastInterval);
×
684
        }
685
    }
686

687
    /** @hidden */
688
    public handleKeydownPrev(event: KeyboardEvent): void {
UNCOV
689
        if (this.platformUtil.isActivationKey(event)) {
×
UNCOV
690
            event.preventDefault();
×
UNCOV
691
            this.prev();
×
692
        }
693
    }
694

695
    /** @hidden */
696
    public handleKeydownNext(event: KeyboardEvent): void {
UNCOV
697
        if (this.platformUtil.isActivationKey(event)) {
×
UNCOV
698
            event.preventDefault();
×
UNCOV
699
            this.next();
×
700
        }
701
    }
702

703
    /** @hidden */
704
    public handleKeyUp(event: KeyboardEvent): void {
UNCOV
705
        if (event.key === this.platformUtil.KEYMAP.TAB) {
×
UNCOV
706
            this._hasKeyboardFocusOnIndicators = true;
×
707
        }
708
    }
709

710
    /** @hidden */
711
    public handleFocusOut(event: FocusEvent): void {
UNCOV
712
        const target = event.relatedTarget as HTMLElement;
×
713

UNCOV
714
        if (!target || !target.classList.contains('igx-carousel-indicators__indicator')) {
×
UNCOV
715
            this._hasKeyboardFocusOnIndicators = false;
×
716
        }
717
    }
718

719
    /** @hidden */
720
    public handleClick(): void {
UNCOV
721
        this._hasKeyboardFocusOnIndicators = false;
×
722
    }
723

724
    /** @hidden */
725
    public handleKeydown(event: KeyboardEvent): void {
UNCOV
726
        const { key } = event;
×
UNCOV
727
        const slides = this.slides.toArray();
×
728

UNCOV
729
        switch (key) {
×
730
            case this.platformUtil.KEYMAP.ARROW_LEFT:
UNCOV
731
                this.dir.rtl ? this.next() : this.prev();
×
UNCOV
732
                break;
×
733
            case this.platformUtil.KEYMAP.ARROW_RIGHT:
UNCOV
734
                this.dir.rtl ? this.prev() : this.next();
×
UNCOV
735
                break;
×
736
            case this.platformUtil.KEYMAP.HOME:
UNCOV
737
                event.preventDefault();
×
UNCOV
738
                this.select(this.dir.rtl ? last(slides) : first(slides));
×
UNCOV
739
                break;
×
740
            case this.platformUtil.KEYMAP.END:
UNCOV
741
                event.preventDefault();
×
UNCOV
742
                this.select(this.dir.rtl ? first(slides) : last(slides));
×
UNCOV
743
                break;
×
744
        }
745

UNCOV
746
        this.indicatorsElements[this.current].nativeElement.focus();
×
747
    }
748

749
    /**
750
     * Returns the slide corresponding to the provided `index` or null.
751
     * ```typescript
752
     * let slide1 =  this.carousel.get(1);
753
     * ```
754
     *
755
     * @memberOf IgxCarouselComponent
756
     */
757
    public get(index: number): IgxSlideComponent {
UNCOV
758
        return this.slides.find((slide) => slide.index === index);
×
759
    }
760

761
    /**
762
     * Adds a new slide to the carousel.
763
     * ```typescript
764
     * this.carousel.add(newSlide);
765
     * ```
766
     *
767
     * @memberOf IgxCarouselComponent
768
     */
769
    public add(slide: IgxSlideComponent) {
UNCOV
770
        const newSlides = this.slides.toArray();
×
UNCOV
771
        newSlides.push(slide);
×
UNCOV
772
        this.slides.reset(newSlides);
×
UNCOV
773
        this.slides.notifyOnChanges();
×
774
    }
775

776
    /**
777
     * Removes a slide from the carousel.
778
     * ```typescript
779
     * this.carousel.remove(slide);
780
     * ```
781
     *
782
     * @memberOf IgxCarouselComponent
783
     */
784
    public remove(slide: IgxSlideComponent) {
UNCOV
785
        if (slide && slide === this.get(slide.index)) { // check if the requested slide for delete is present in the carousel
×
UNCOV
786
            const newSlides = this.slides.toArray();
×
UNCOV
787
            newSlides.splice(slide.index, 1);
×
UNCOV
788
            this.slides.reset(newSlides);
×
UNCOV
789
            this.slides.notifyOnChanges();
×
790
        }
791
    }
792

793
    /**
794
     * Switches to the passed-in slide with a given `direction`.
795
     * ```typescript
796
     * const slide = this.carousel.get(2);
797
     * this.carousel.select(slide, CarouselAnimationDirection.NEXT);
798
     * ```
799
     *
800
     * @memberOf IgxCarouselComponent
801
     */
802
    public select(slide: IgxSlideComponent, direction?: CarouselAnimationDirection): void;
803
    /**
804
     * Switches to slide by index with a given `direction`.
805
     * ```typescript
806
     * this.carousel.select(2, CarouselAnimationDirection.NEXT);
807
     * ```
808
     *
809
     * @memberOf IgxCarouselComponent
810
     */
811
    public select(index: number, direction?: CarouselAnimationDirection): void;
812
    public select(slideOrIndex: IgxSlideComponent | number, direction: CarouselAnimationDirection = CarouselAnimationDirection.NONE): void {
×
UNCOV
813
        const slide = typeof slideOrIndex === 'number'
×
814
            ? this.get(slideOrIndex)
815
            : slideOrIndex;
816

UNCOV
817
        if (slide && slide !== this.currentItem) {
×
UNCOV
818
            slide.direction = direction;
×
UNCOV
819
            slide.active = true;
×
820
        }
821
    }
822

823
    /**
824
     * Transitions to the next slide in the carousel.
825
     * ```typescript
826
     * this.carousel.next();
827
     * ```
828
     *
829
     * @memberOf IgxCarouselComponent
830
     */
831
    public next() {
UNCOV
832
        const index = this.getNextIndex();
×
833

UNCOV
834
        if (index === 0 && !this.loop) {
×
UNCOV
835
            this.stop();
×
UNCOV
836
            return;
×
837
        }
UNCOV
838
        return this.select(this.get(index), CarouselAnimationDirection.NEXT);
×
839
    }
840

841
    /**
842
     * Transitions to the previous slide in the carousel.
843
     * ```typescript
844
     * this.carousel.prev();
845
     * ```
846
     *
847
     * @memberOf IgxCarouselComponent
848
     */
849
    public prev() {
UNCOV
850
        const index = this.getPrevIndex();
×
851

UNCOV
852
        if (!this.loop && index === this.total - 1) {
×
UNCOV
853
            this.stop();
×
UNCOV
854
            return;
×
855
        }
UNCOV
856
        return this.select(this.get(index), CarouselAnimationDirection.PREV);
×
857
    }
858

859
    /**
860
     * Resumes playing of the carousel if in paused state.
861
     * No operation otherwise.
862
     * ```typescript
863
     * this.carousel.play();
864
     * }
865
     * ```
866
     *
867
     * @memberOf IgxCarouselComponent
868
     */
869
    public play() {
UNCOV
870
        if (!this.playing) {
×
UNCOV
871
            this.playing = true;
×
UNCOV
872
            this.carouselPlaying.emit(this);
×
UNCOV
873
            this.restartInterval();
×
UNCOV
874
            this.stoppedByInteraction = false;
×
875
        }
876
    }
877

878
    /**
879
     * Stops slide transitions if the `pause` option is set to `true`.
880
     * No operation otherwise.
881
     * ```typescript
882
     *  this.carousel.stop();
883
     * }
884
     * ```
885
     *
886
     * @memberOf IgxCarouselComponent
887
     */
888
    public stop() {
UNCOV
889
        if (this.pause) {
×
UNCOV
890
            this.playing = false;
×
UNCOV
891
            this.carouselPaused.emit(this);
×
UNCOV
892
            this.resetInterval();
×
893
        }
894
    }
895

896
    protected getPreviousElement(): HTMLElement {
UNCOV
897
        return this.previousItem.nativeElement;
×
898
    }
899

900
    protected getCurrentElement(): HTMLElement {
UNCOV
901
        return this.currentItem.nativeElement;
×
902
    }
903

904
    private resetInterval() {
UNCOV
905
        if (this.lastInterval) {
×
UNCOV
906
            clearInterval(this.lastInterval);
×
UNCOV
907
            this.lastInterval = null;
×
908
        }
909
    }
910

911
    private restartInterval() {
UNCOV
912
        this.resetInterval();
×
913

UNCOV
914
        if (!isNaN(this.interval) && this.interval > 0 && this.platformUtil.isBrowser) {
×
UNCOV
915
            this.lastInterval = setInterval(() => {
×
UNCOV
916
                const tick = +this.interval;
×
UNCOV
917
                if (this.playing && this.total && !isNaN(tick) && tick > 0) {
×
UNCOV
918
                    this.next();
×
919
                } else {
920
                    this.stop();
×
921
                }
922
            }, this.interval);
923
        }
924
    }
925

926
    /** @hidden */
927
    public get nextButtonDisabled() {
UNCOV
928
        return !this.loop && this.current === (this.total - 1);
×
929
    }
930

931
    /** @hidden */
932
    public get prevButtonDisabled() {
UNCOV
933
        return !this.loop && this.current === 0;
×
934
    }
935

936
    private get indicatorsElements() {
UNCOV
937
        return this._indicators.toArray();
×
938
    }
939

940
    private getIndicatorsClass(): string {
UNCOV
941
        switch (this.indicatorsOrientation) {
×
942
            case CarouselIndicatorsOrientation.top:
943
                return CarouselIndicatorsOrientation.start;
×
944
            case CarouselIndicatorsOrientation.bottom:
945
                return CarouselIndicatorsOrientation.end;
×
946
            default:
UNCOV
947
                return this.indicatorsOrientation;
×
948
        }
949
    }
950

951
    private getNextIndex(): number {
UNCOV
952
        return (this.current + 1) % this.total;
×
953
    }
954

955
    private getPrevIndex(): number {
UNCOV
956
        return this.current - 1 < 0 ? this.total - 1 : this.current - 1;
×
957
    }
958

959
    private resetSlideStyles(slide: IgxSlideComponent) {
UNCOV
960
        slide.nativeElement.style.transform = '';
×
UNCOV
961
        slide.nativeElement.style.opacity = '';
×
962
    }
963

964
    private pan(event) {
UNCOV
965
        const slideSize = this.vertical
×
966
            ? this.currentItem.nativeElement.offsetHeight
967
            : this.currentItem.nativeElement.offsetWidth;
UNCOV
968
        const panOffset = (slideSize / 1000);
×
UNCOV
969
        const delta = this.vertical ? event.deltaY : event.deltaX;
×
UNCOV
970
        const index = delta < 0 ? this.getNextIndex() : this.getPrevIndex();
×
UNCOV
971
        const offset = delta < 0 ? slideSize + delta : -slideSize + delta;
×
972

UNCOV
973
        if (!this.gesturesSupport || event.isFinal || Math.abs(delta) + panOffset >= slideSize) {
×
UNCOV
974
            return;
×
975
        }
976

UNCOV
977
        if (!this.loop && ((this.current === 0 && delta > 0) || (this.current === this.total - 1 && delta < 0))) {
×
UNCOV
978
            this.incomingSlide = null;
×
UNCOV
979
            return;
×
980
        }
981

UNCOV
982
        event.preventDefault();
×
UNCOV
983
        if (this.isPlaying) {
×
984
            this.stoppedByInteraction = true;
×
985
            this.stop();
×
986
        }
987

UNCOV
988
        if (this.previousItem && this.previousItem.previous) {
×
UNCOV
989
            this.previousItem.previous = false;
×
990
        }
UNCOV
991
        this.finishAnimations();
×
992

UNCOV
993
        if (this.incomingSlide) {
×
UNCOV
994
            if (index !== this.incomingSlide.index) {
×
UNCOV
995
                this.resetSlideStyles(this.incomingSlide);
×
UNCOV
996
                this.incomingSlide.previous = false;
×
UNCOV
997
                this.incomingSlide = this.get(index);
×
998
            }
999
        } else {
UNCOV
1000
            this.incomingSlide = this.get(index);
×
1001
        }
UNCOV
1002
        this.incomingSlide.previous = true;
×
1003

UNCOV
1004
        if (this.animationType === CarouselAnimationType.fade) {
×
1005
            this.currentItem.nativeElement.style.opacity = `${Math.abs(offset) / slideSize}`;
×
1006
        } else {
UNCOV
1007
            this.currentItem.nativeElement.style.transform = this.vertical
×
1008
                ? `translateY(${delta}px)`
1009
                : `translateX(${delta}px)`;
UNCOV
1010
            this.incomingSlide.nativeElement.style.transform = this.vertical
×
1011
                ? `translateY(${offset}px)`
1012
                : `translateX(${offset}px)`;
1013
        }
1014
    }
1015

1016
    private unsubscriber(slide: IgxSlideComponent) {
UNCOV
1017
        return merge(this.destroy$, slide.isDestroyed);
×
1018
    }
1019

1020
    private onSlideActivated(slide: IgxSlideComponent) {
UNCOV
1021
        if (slide.active && slide !== this.currentItem) {
×
UNCOV
1022
            if (slide.direction === CarouselAnimationDirection.NONE) {
×
UNCOV
1023
                const newIndex = slide.index;
×
UNCOV
1024
                slide.direction = newIndex > this.current ? CarouselAnimationDirection.NEXT : CarouselAnimationDirection.PREV;
×
1025
            }
1026

UNCOV
1027
            if (this.currentItem) {
×
UNCOV
1028
                if (this.previousItem && this.previousItem.previous) {
×
1029
                    this.previousItem.previous = false;
×
1030
                }
UNCOV
1031
                this.currentItem.direction = slide.direction;
×
UNCOV
1032
                this.currentItem.active = false;
×
1033

UNCOV
1034
                this.previousItem = this.currentItem;
×
UNCOV
1035
                this.currentItem = slide;
×
UNCOV
1036
                this.triggerAnimations();
×
1037
            } else {
UNCOV
1038
                this.currentItem = slide;
×
1039
            }
UNCOV
1040
            this.slideChanged.emit({ carousel: this, slide });
×
UNCOV
1041
            this.restartInterval();
×
UNCOV
1042
            this.cdr.markForCheck();
×
1043
        }
1044
    }
1045

1046

1047
    private finishAnimations() {
UNCOV
1048
        if (this.animationStarted(this.leaveAnimationPlayer)) {
×
1049
            this.leaveAnimationPlayer.finish();
×
1050
        }
1051

UNCOV
1052
        if (this.animationStarted(this.enterAnimationPlayer)) {
×
1053
            this.enterAnimationPlayer.finish();
×
1054
        }
1055
    }
1056

1057
    private initSlides(change: QueryList<IgxSlideComponent>) {
UNCOV
1058
        const diff = this.differ.diff(change.toArray());
×
UNCOV
1059
        if (diff) {
×
UNCOV
1060
            this.slides.reduce((any, c, ind) => c.index = ind, 0); // reset slides indexes
×
UNCOV
1061
            diff.forEachAddedItem((record: IterableChangeRecord<IgxSlideComponent>) => {
×
UNCOV
1062
                const slide = record.item;
×
UNCOV
1063
                slide.total = this.total;
×
UNCOV
1064
                this.slideAdded.emit({ carousel: this, slide });
×
UNCOV
1065
                if (slide.active) {
×
UNCOV
1066
                    this.currentItem = slide;
×
1067
                }
UNCOV
1068
                slide.activeChange.pipe(takeUntil(this.unsubscriber(slide))).subscribe(() => this.onSlideActivated(slide));
×
1069
            });
1070

UNCOV
1071
            diff.forEachRemovedItem((record: IterableChangeRecord<IgxSlideComponent>) => {
×
UNCOV
1072
                const slide = record.item;
×
UNCOV
1073
                this.slideRemoved.emit({ carousel: this, slide });
×
UNCOV
1074
                if (slide.active) {
×
UNCOV
1075
                    slide.active = false;
×
UNCOV
1076
                    this.currentItem = this.get(slide.index < this.total ? slide.index : this.total - 1);
×
1077
                }
1078
            });
1079

UNCOV
1080
            this.updateSlidesSelection();
×
1081
        }
1082
    }
1083

1084
    private updateSlidesSelection() {
UNCOV
1085
        if (this.platformUtil.isBrowser) {
×
UNCOV
1086
            requestAnimationFrame(() => {
×
UNCOV
1087
                if (this.currentItem) {
×
UNCOV
1088
                    this.currentItem.active = true;
×
UNCOV
1089
                    const activeSlides = this.slides.filter(slide => slide.active && slide.index !== this.currentItem.index);
×
UNCOV
1090
                    activeSlides.forEach(slide => slide.active = false);
×
UNCOV
1091
                } else if (this.total) {
×
UNCOV
1092
                    this.slides.first.active = true;
×
1093
                }
UNCOV
1094
                this.play();
×
1095
            });
1096
        }
1097
    }
1098
}
1099

1100
export interface ISlideEventArgs extends IBaseEventArgs {
1101
    carousel: IgxCarouselComponent;
1102
    slide: IgxSlideComponent;
1103
}
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