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

IgniteUI / igniteui-angular / 16053471080

03 Jul 2025 02:41PM UTC coverage: 4.981% (-86.4%) from 91.409%
16053471080

Pull #16021

github

web-flow
Merge 7c49966eb into 7e40671a1
Pull Request #16021: fix(radio-group): dynamically added radio buttons do not initialize

178 of 15753 branches covered (1.13%)

13 of 14 new or added lines in 2 files covered. (92.86%)

25644 existing lines in 324 files now uncovered.

1478 of 29670 relevant lines covered (4.98%)

0.51 hits per line

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

0.56
/projects/igniteui-angular/src/lib/slider/slider.component.ts
1
import {
2
    AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter,
3
    HostBinding, HostListener, Input, NgZone, OnChanges, OnDestroy, OnInit, Output, QueryList, Renderer2, SimpleChanges, TemplateRef, ViewChild, ViewChildren, booleanAttribute
4
} from '@angular/core';
5
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
6
import { animationFrameScheduler, fromEvent, interval, merge, noop, Observable, Subject, timer } from 'rxjs';
7
import { takeUntil, throttle, throttleTime } from 'rxjs/operators';
8
import { EditorProvider } from '../core/edit-provider';
9
import { resizeObservable } from '../core/utils';
10
import { IgxDirectionality } from '../services/direction/directionality';
11
import { IgxThumbLabelComponent } from './label/thumb-label.component';
12
import {
13
    IgxSliderType, IgxThumbFromTemplateDirective,
14
    IgxThumbToTemplateDirective, IgxTickLabelTemplateDirective, IRangeSliderValue, ISliderValueChangeEventArgs, SliderHandle, TickLabelsOrientation, TicksOrientation
15
} from './slider.common';
16
import { IgxSliderThumbComponent } from './thumb/thumb-slider.component';
17
import { IgxTickLabelsPipe } from './ticks/tick.pipe';
18
import { IgxTicksComponent } from './ticks/ticks.component';
19

20
let NEXT_ID = 0;
3✔
21

22
/**
23
 * **Ignite UI for Angular Slider** -
24
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/slider/slider)
25
 *
26
 * The Ignite UI Slider allows selection in a given range by moving the thumb along the track. The track
27
 * can be defined as continuous or stepped, and you can choose between single and range slider types.
28
 *
29
 * Example:
30
 * ```html
31
 * <igx-slider id="slider"
32
 *            [minValue]="0" [maxValue]="100"
33
 *            [continuous]=true [(ngModel)]="volume">
34
 * </igx-slider>
35
 * ```
36
 */
37
@Component({
38
    providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: IgxSliderComponent, multi: true }],
39
    selector: 'igx-slider',
40
    templateUrl: 'slider.component.html',
41
    imports: [IgxTicksComponent, IgxThumbLabelComponent, IgxSliderThumbComponent, IgxTickLabelsPipe]
42
})
43
export class IgxSliderComponent implements
3✔
44
    ControlValueAccessor,
45
    EditorProvider,
46
    OnInit,
47
    AfterViewInit,
48
    AfterContentInit,
49
    OnChanges,
50
    OnDestroy {
51
    /**
52
     * @hidden
53
     */
54
    public get thumbFrom(): IgxSliderThumbComponent {
UNCOV
55
        return this.thumbs.find(thumb => thumb.type === SliderHandle.FROM);
×
56
    }
57

58
    /**
59
     * @hidden
60
     */
61
    public get thumbTo(): IgxSliderThumbComponent {
UNCOV
62
        return this.thumbs.find(thumb => thumb.type === SliderHandle.TO);
×
63
    }
64

65
    private get labelFrom(): IgxThumbLabelComponent {
UNCOV
66
        return this.labelRefs.find(label => label.type === SliderHandle.FROM);
×
67
    }
68

69
    private get labelTo(): IgxThumbLabelComponent {
UNCOV
70
        return this.labelRefs.find(label => label.type === SliderHandle.TO);
×
71
    }
72

73
    /**
74
     * @hidden
75
     */
76
    @ViewChild('track', { static: true })
77
    public trackRef: ElementRef;
78

79
    /**
80
     * @hidden
81
     */
82
    @ContentChild(IgxThumbFromTemplateDirective, { read: TemplateRef })
83
    public thumbFromTemplateRef: TemplateRef<any>;
84

85
    /**
86
     * @hidden
87
     */
88
    @ContentChild(IgxThumbToTemplateDirective, { read: TemplateRef })
89
    public thumbToTemplateRef: TemplateRef<any>;
90

91
    /**
92
     * @hidden
93
     */
94
    @ContentChild(IgxTickLabelTemplateDirective, { read: TemplateRef, static: false })
95
    public tickLabelTemplateRef: TemplateRef<any>;
96

97
    /**
98
     * @hidden
99
     */
100
    @HostBinding('class.igx-slider')
UNCOV
101
    public slierClass = true;
×
102

103
    /**
104
     * Sets the value of the `id` attribute.
105
     * If not provided it will be automatically generated.
106
     * ```html
107
     * <igx-slider [id]="'igx-slider-32'" [(ngModel)]="task.percentCompleted" [step]="5" [lowerBound]="20">
108
     * ```
109
     */
110
    @HostBinding('attr.id')
111
    @Input()
UNCOV
112
    public id = `igx-slider-${NEXT_ID++}`;
×
113

114
    /**
115
     * Sets the duration visibility of thumbs labels. The default value is 750 milliseconds.
116
     * ```html
117
     * <igx-slider #slider [thumbLabelVisibilityDuration]="3000" [(ngModel)]="task.percentCompleted" [step]="5">
118
     * ```
119
     */
120
    @Input()
UNCOV
121
    public thumbLabelVisibilityDuration = 750;
×
122

123
    /**
124
     * @hidden
125
     */
126
    @HostBinding('class.igx-slider--disabled')
127
    public get disabledClass() {
UNCOV
128
        return this.disabled;
×
129
    }
130

131
    /**
132
     * Gets the type of the `IgxSliderComponent`.
133
     * The slider can be IgxSliderType.SLIDER(default) or IgxSliderType.RANGE.
134
     * ```typescript
135
     * @ViewChild("slider2")
136
     * public slider: IgxSliderComponent;
137
     * ngAfterViewInit(){
138
     *     let type = this.slider.type;
139
     * }
140
     */
141
    @Input()
142
    public get type() {
UNCOV
143
        return this._type as IgxSliderType;
×
144
    }
145

146
    /**
147
     * Sets the type of the `IgxSliderComponent`.
148
     * The slider can be IgxSliderType.SLIDER(default) or IgxSliderType.RANGE.
149
     * ```typescript
150
     * sliderType: IgxSliderType = IgxSliderType.RANGE;
151
     * ```
152
     * ```html
153
     * <igx-slider #slider2 [type]="sliderType" [(ngModel)]="rangeValue" [minValue]="0" [maxValue]="100">
154
     * ```
155
     */
156
    public set type(type: IgxSliderType) {
UNCOV
157
        this._type = type;
×
158

UNCOV
159
        if (type === IgxSliderType.SLIDER) {
×
UNCOV
160
            this.lowerValue = 0;
×
161
        }
162

UNCOV
163
        if (this._hasViewInit) {
×
UNCOV
164
            this.updateTrack();
×
165
        }
166
    }
167

168

169
    /**
170
     * Enables `labelView`, by accepting a collection of primitive values with more than one element.
171
     * Each element will be equally spread over the slider and it will serve as a thumb label.
172
     * Once the property is set, it will precendence over {@link maxValue}, {@link minValue}, {@link step}.
173
     * This means that the manipulation for those properties won't be allowed.
174
     */
175
    @Input()
176
    public get labels() {
UNCOV
177
        return this._labels;
×
178
    }
179

180
    public set labels(labels: Array<number | string | boolean | null | undefined>) {
UNCOV
181
        this._labels = labels;
×
182

UNCOV
183
        this._pMax = this.valueToFraction(this.upperBound, 0, 1);
×
UNCOV
184
        this._pMin = this.valueToFraction(this.lowerBound, 0, 1);
×
185

UNCOV
186
        this.positionHandlersAndUpdateTrack();
×
187

UNCOV
188
        if (this._hasViewInit) {
×
UNCOV
189
            this.stepDistance = this.calculateStepDistance();
×
UNCOV
190
            this.setTickInterval();
×
191
        }
192
    }
193

194
    /**
195
     * Returns the template context corresponding
196
     * to {@link IgxThumbFromTemplateDirective} and {@link IgxThumbToTemplateDirective} templates.
197
     *
198
     * ```typescript
199
     * return {
200
     *  $implicit // returns the value of the label,
201
     *  labels // returns the labels collection the user has passed.
202
     * }
203
     * ```
204
     */
205
    public get context(): any {
UNCOV
206
        return {
×
207
            $implicit: this.value,
208
            labels: this.labels
209
        };
210
    }
211

212
    /**
213
     * Sets the incremental/decremental step of the value when dragging the thumb.
214
     * The default step is 1, and step should not be less or equal than 0.
215
     * ```html
216
     * <igx-slider #slider [(ngModel)]="task.percentCompleted" [step]="5">
217
     * ```
218
     */
219
    @Input()
220
    public set step(step: number) {
UNCOV
221
        this._step = step;
×
222

UNCOV
223
        if (this._hasViewInit) {
×
UNCOV
224
            this.stepDistance = this.calculateStepDistance();
×
UNCOV
225
            this.normalizeByStep(this._value);
×
UNCOV
226
            this.setValue(this._value, true);
×
UNCOV
227
            this.positionHandlersAndUpdateTrack();
×
UNCOV
228
            this.setTickInterval();
×
229
        }
230
    }
231

232
    /**
233
     * Returns the incremental/decremental dragging step of the {@link IgxSliderComponent}.
234
     * ```typescript
235
     * @ViewChild("slider2")
236
     * public slider: IgxSliderComponent;
237
     * ngAfterViewInit(){
238
     *     let step = this.slider.step;
239
     * }
240
     * ```
241
     */
242
    public get step() {
UNCOV
243
        return this.labelsViewEnabled ? 1 : this._step;
×
244
    }
245

246
    /**
247
     * Returns if the {@link IgxSliderComponent} is disabled.
248
     * ```typescript
249
     * @ViewChild("slider2")
250
     * public slider: IgxSliderComponent;
251
     * ngAfterViewInit(){
252
     *     let isDisabled = this.slider.disabled;
253
     * }
254
     * ```
255
     */
256
    @Input({ transform: booleanAttribute })
257
    public get disabled(): boolean {
UNCOV
258
        return this._disabled;
×
259
    }
260

261
    /**
262
     * Disables the component.
263
     * ```html
264
     * <igx-slider #slider [disabled]="true" [(ngModel)]="task.percentCompleted" [step]="5" [lowerBound]="20">
265
     * ```
266
     */
267
    public set disabled(disable: boolean) {
UNCOV
268
        this._disabled = disable;
×
269

UNCOV
270
        if (this._hasViewInit) {
×
UNCOV
271
            this.changeThumbFocusableState(disable);
×
272
        }
273
    }
274

275
    /**
276
     * Returns if the {@link IgxSliderComponent} is set as continuous.
277
     * ```typescript
278
     * @ViewChild("slider2")
279
     * public slider: IgxSliderComponent;
280
     * ngAfterViewInit(){
281
     *     let continuous = this.slider.continuous;
282
     * }
283
     * ```
284
     */
285
    @Input({ transform: booleanAttribute })
286
    public get continuous(): boolean {
UNCOV
287
        return this._continuous;
×
288
    }
289

290
    /**
291
     * Sets the {@link IgxSliderComponent} as continuous.
292
     * By default is considered that the {@link IgxSliderComponent} is discrete.
293
     * Discrete {@link IgxSliderComponent} slider has step indicators over the track and visible thumb labels during interaction.
294
     * Continuous {@link IgxSliderComponent} does not have ticks and does not show bubble labels for values.
295
     * ```html
296
     * <igx-slider #slider [continuous]="'true'" [(ngModel)]="task.percentCompleted" [step]="5" [lowerBound]="20">
297
     * ```
298
     */
299
    public set continuous(continuous: boolean) {
UNCOV
300
        this._continuous = continuous;
×
UNCOV
301
        if (this._hasViewInit) {
×
UNCOV
302
            this.setTickInterval();
×
303
        }
304
    }
305

306
    /**
307
     * Returns the minimal displayed track value of the `IgxSliderComponent`.
308
     * ```typescript
309
     *  @ViewChild("slider2")
310
     * public slider: IgxSliderComponent;
311
     * ngAfterViewInit(){
312
     *     let sliderMin = this.slider.minValue;
313
     * }
314
     * ```
315
     */
316
    public get minValue(): number {
UNCOV
317
        if (this.labelsViewEnabled) {
×
UNCOV
318
            return 0;
×
319
        }
320

UNCOV
321
        return this._minValue;
×
322
    }
323

324
    /**
325
     * Sets the minimal displayed track value for the `IgxSliderComponent`.
326
     * The default minimal value is 0.
327
     * ```html
328
     * <igx-slider [type]="sliderType" [minValue]="56" [maxValue]="100">
329
     * ```
330
     */
331
    @Input()
332
    public set minValue(value: number) {
UNCOV
333
        if (value >= this.maxValue) {
×
UNCOV
334
            return;
×
335
        } else {
UNCOV
336
            this._minValue = value;
×
337
        }
338

UNCOV
339
        if (value > this._upperBound) {
×
UNCOV
340
            this.updateUpperBoundAndMaxTravelZone();
×
UNCOV
341
            this.lowerBound = value;
×
342
        }
343

344
        // Refresh min travel zone limit.
UNCOV
345
        this._pMin = 0;
×
346
        // Recalculate step distance.
UNCOV
347
        this.positionHandlersAndUpdateTrack();
×
UNCOV
348
        if (this._hasViewInit) {
×
UNCOV
349
            this.stepDistance = this.calculateStepDistance();
×
UNCOV
350
            this.setTickInterval();
×
351
        }
352
    }
353

354
    /**
355
     * Returns the maximum displayed track value for the {@link IgxSliderComponent}.
356
     * ```typescript
357
     * @ViewChild("slider")
358
     * public slider: IgxSliderComponent;
359
     * ngAfterViewInit(){
360
     *     let sliderMax = this.slider.maxValue;
361
     * }
362
     *  ```
363
     */
364
    public get maxValue(): number {
UNCOV
365
        return this.labelsViewEnabled ?
×
366
            this.labels.length - 1 :
367
            this._maxValue;
368
    }
369

370
    /**
371
     * Sets the maximal displayed track value for the `IgxSliderComponent`.
372
     * The default maximum value is 100.
373
     * ```html
374
     * <igx-slider [type]="sliderType" [minValue]="56" [maxValue]="256">
375
     * ```
376
     */
377
    @Input()
378
    public set maxValue(value: number) {
UNCOV
379
        if (value <= this._minValue) {
×
UNCOV
380
            return;
×
381
        } else {
UNCOV
382
            this._maxValue = value;
×
383
        }
384

UNCOV
385
        if (value < this._lowerBound) {
×
UNCOV
386
            this.updateLowerBoundAndMinTravelZone();
×
UNCOV
387
            this.upperBound = value;
×
388
        }
389

390
        // refresh max travel zone limits.
UNCOV
391
        this._pMax = 1;
×
392
        // recalculate step distance.
UNCOV
393
        this.positionHandlersAndUpdateTrack();
×
UNCOV
394
        if (this._hasViewInit) {
×
UNCOV
395
            this.stepDistance = this.calculateStepDistance();
×
UNCOV
396
            this.setTickInterval();
×
397
        }
398
    }
399

400
    /**
401
     * Returns the lower boundary of settable values of the `IgxSliderComponent`.
402
     * If not set, will return `minValue`.
403
     * ```typescript
404
     * @ViewChild("slider")
405
     * public slider: IgxSliderComponent;
406
     * ngAfterViewInit(){
407
     *     let sliderLowBound = this.slider.lowerBound;
408
     * }
409
     * ```
410
     */
411
    public get lowerBound(): number {
UNCOV
412
        if (!Number.isNaN(this._lowerBound) && this._lowerBound !== undefined) {
×
UNCOV
413
            return this.valueInRange(this._lowerBound, this.minValue, this.maxValue);
×
414
        }
415

UNCOV
416
        return this.minValue;
×
417
    }
418

419
    /**
420
     * Sets the lower boundary of settable values of the `IgxSliderComponent`.
421
     * If not set is the same as min value.
422
     * ```html
423
     * <igx-slider [step]="5" [lowerBound]="20">
424
     * ```
425
     */
426
    @Input()
427
    public set lowerBound(value: number) {
UNCOV
428
        if (value >= this.upperBound || (this.labelsViewEnabled && value < 0)) {
×
UNCOV
429
            return;
×
430
        }
431

UNCOV
432
        this._lowerBound = this.valueInRange(value, this.minValue, this.maxValue);
×
433

434
        // Refresh min travel zone.
UNCOV
435
        this._pMin = this.valueToFraction(this._lowerBound, 0, 1);
×
UNCOV
436
        this.positionHandlersAndUpdateTrack();
×
437
    }
438

439
    /**
440
     * Returns the upper boundary of settable values of the `IgxSliderComponent`.
441
     * If not set, will return `maxValue`
442
     * ```typescript
443
     * @ViewChild("slider")
444
     * public slider: IgxSliderComponent;
445
     * ngAfterViewInit(){
446
     *    let sliderUpBound = this.slider.upperBound;
447
     * }
448
     * ```
449
     */
450
    public get upperBound(): number {
UNCOV
451
        if (!Number.isNaN(this._upperBound) && this._upperBound !== undefined) {
×
UNCOV
452
            return this.valueInRange(this._upperBound, this.minValue, this.maxValue);
×
453
        }
454

UNCOV
455
        return this.maxValue;
×
456
    }
457

458
    /**
459
     * Sets the upper boundary of the `IgxSliderComponent`.
460
     * If not set is the same as max value.
461
     * ```html
462
     * <igx-slider [step]="5" [upperBound]="20">
463
     * ```
464
     */
465
    @Input()
466
    public set upperBound(value: number) {
UNCOV
467
        if (value <= this.lowerBound || (this.labelsViewEnabled && value > this.labels.length - 1)) {
×
UNCOV
468
            return;
×
469
        }
470

UNCOV
471
        this._upperBound = this.valueInRange(value, this.minValue, this.maxValue);
×
472
        // Refresh time travel zone.
UNCOV
473
        this._pMax = this.valueToFraction(this._upperBound, 0, 1);
×
UNCOV
474
        this.positionHandlersAndUpdateTrack();
×
475
    }
476

477
    /**
478
     * Returns the slider value. If the slider is of type {@link IgxSliderType.SLIDER} the returned value is number.
479
     * If the slider type is {@link IgxSliderType.RANGE}.
480
     * The returned value represents an object of {@link lowerValue} and {@link upperValue}.
481
     * ```typescript
482
     * @ViewChild("slider2")
483
     * public slider: IgxSliderComponent;
484
     * public sliderValue(event){
485
     *     let sliderVal = this.slider.value;
486
     * }
487
     * ```
488
     */
489
    public get value(): number | IRangeSliderValue {
UNCOV
490
        if (this.isRange) {
×
UNCOV
491
            return {
×
492
                lower: this.valueInRange(this.lowerValue, this.lowerBound, this.upperBound),
493
                upper: this.valueInRange(this.upperValue, this.lowerBound, this.upperBound)
494
            };
495
        } else {
UNCOV
496
            return this.valueInRange(this.upperValue, this.lowerBound, this.upperBound);
×
497
        }
498
    }
499

500
    /**
501
     * Sets the slider value.
502
     * If the slider is of type {@link IgxSliderType.SLIDER}.
503
     * The argument is number. By default the {@link value} gets the {@link lowerBound}.
504
     * If the slider type is {@link IgxSliderType.RANGE} the argument
505
     * represents an object of {@link lowerValue} and {@link upperValue} properties.
506
     * By default the object is associated with the {@link lowerBound} and {@link upperBound} property values.
507
     * ```typescript
508
     * rangeValue = {
509
     *   lower: 30,
510
     *   upper: 60
511
     * };
512
     * ```
513
     * ```html
514
     * <igx-slider [type]="sliderType" [(ngModel)]="rangeValue" [minValue]="56" [maxValue]="256">
515
     * ```
516
     */
517
    @Input()
518
    public set value(value: number | IRangeSliderValue) {
UNCOV
519
        this.normalizeByStep(value);
×
520

UNCOV
521
        if (this._hasViewInit) {
×
UNCOV
522
            this.setValue(this._value, true);
×
UNCOV
523
            this.positionHandlersAndUpdateTrack();
×
524
        }
525
    }
526

527
    /**
528
     * Returns the number of the presented primary ticks.
529
     * ```typescript
530
     * const primaryTicks = this.slider.primaryTicks;
531
     * ```
532
     */
533
    @Input()
534
    public get primaryTicks() {
UNCOV
535
        if (this.labelsViewEnabled) {
×
UNCOV
536
            return this._primaryTicks = this.labels.length;
×
537
        }
UNCOV
538
        return this._primaryTicks;
×
539
    }
540

541
    /**
542
     * Sets the number of primary ticks. If {@link @labels} is enabled, this property won't function.
543
     * Insted enable ticks by {@link showTicks} property.
544
     * ```typescript
545
     * this.slider.primaryTicks = 5;
546
     * ```
547
     */
548
    public set primaryTicks(val: number) {
UNCOV
549
        if (val <= 1) {
×
UNCOV
550
            return;
×
551
        }
552

UNCOV
553
        this._primaryTicks = val;
×
554
    }
555

556
    /**
557
     * Returns the number of the presented secondary ticks.
558
     * ```typescript
559
     * const secondaryTicks = this.slider.secondaryTicks;
560
     * ```
561
     */
562
    @Input()
563
    public get secondaryTicks() {
UNCOV
564
        return this._secondaryTicks;
×
565
    }
566

567
    /**
568
     * Sets the number of secondary ticks. The property functions even when {@link labels} is enabled,
569
     * but all secondary ticks won't present any tick labels.
570
     * ```typescript
571
     * this.slider.secondaryTicks = 5;
572
     * ```
573
     */
574
    public set secondaryTicks(val: number) {
UNCOV
575
        if (val < 1) {
×
UNCOV
576
            return;
×
577
        }
578

UNCOV
579
        this._secondaryTicks = val;
×
580
    }
581

582
    /**
583
     * Show/hide slider ticks
584
     * ```html
585
     * <igx-slier [showTicks]="true" [primaryTicks]="5"></igx-slier>
586
     * ```
587
     */
588
    @Input({ transform: booleanAttribute })
UNCOV
589
    public showTicks = false;
×
590

591
    /**
592
     * show/hide primary tick labels
593
     * ```html
594
     * <igx-slider [primaryTicks]="5" [primaryTickLabels]="false"></igx-slider>
595
     * ```
596
     */
597
    @Input({ transform: booleanAttribute })
UNCOV
598
    public primaryTickLabels = true;
×
599

600
    /**
601
     * show/hide secondary tick labels
602
     * ```html
603
     * <igx-slider [secondaryTicks]="5" [secondaryTickLabels]="false"></igx-slider>
604
     * ```
605
     */
606
    @Input({ transform: booleanAttribute })
UNCOV
607
    public secondaryTickLabels = true;
×
608

609
    /**
610
     * Changes ticks orientation:
611
     * bottom - The default orienation, below the slider track.
612
     * top - Above the slider track
613
     * mirror - combines top and bottom orientation.
614
     * ```html
615
     * <igx-slider [primaryTicks]="5" [ticksOrientation]="ticksOrientation"></igx-slider>
616
     * ```
617
     */
618
    @Input()
UNCOV
619
    public ticksOrientation: TicksOrientation = TicksOrientation.Bottom;
×
620

621
    /**
622
     * Changes tick labels rotation:
623
     * horizontal - The default rotation
624
     * toptobottom - Rotates tick labels vertically to 90deg
625
     * bottomtotop - Rotate tick labels vertically to -90deg
626
     * ```html
627
     * <igx-slider [primaryTicks]="5" [secondaryTicks]="3" [tickLabelsOrientation]="tickLabelsOrientaiton"></igx-slider>
628
     * ```
629
     */
630
    @Input()
UNCOV
631
    public tickLabelsOrientation: TickLabelsOrientation = TickLabelsOrientation.Horizontal;
×
632

633
    /**
634
     * @hidden
635
     */
636
    public get deactivateThumbLabel() {
UNCOV
637
        return ((this.primaryTicks && this.primaryTickLabels) || (this.secondaryTicks && this.secondaryTickLabels)) &&
×
638
            (this.ticksOrientation === TicksOrientation.Top || this.ticksOrientation === TicksOrientation.Mirror);
639
    }
640

641
    /**
642
     * This event is emitted every time the value is changed.
643
     * ```typescript
644
     * public change(event){
645
     *    alert("The value has been changed!");
646
     * }
647
     * ```
648
     * ```html
649
     * <igx-slider (valueChange)="change($event)" #slider [(ngModel)]="task.percentCompleted" [step]="5">
650
     * ```
651
     */
652
    @Output()
UNCOV
653
    public valueChange = new EventEmitter<ISliderValueChangeEventArgs>();
×
654

655
    /**
656
     * This event is emitted every time the lower value of a range slider is changed.
657
     * ```typescript
658
     * public change(value){
659
     *    alert(`The lower value has been changed to ${value}`);
660
     * }
661
     * ```
662
     * ```html
663
     * <igx-slider [(lowerValue)]="model.lowervalue" (lowerValueChange)="change($event)" [step]="5">
664
     * ```
665
     */
666
    @Output()
UNCOV
667
    public lowerValueChange = new EventEmitter<number>();
×
668

669
    /**
670
     * This event is emitted every time the upper value of a range slider is changed.
671
     * ```typescript
672
     * public change(value){
673
     *    alert(`The upper value has been changed to ${value}`);
674
     * }
675
     * ```
676
     * ```html
677
     * <igx-slider [(upperValue)]="model.uppervalue" (upperValueChange)="change($event)" [step]="5">
678
     * ```
679
     */
680
    @Output()
UNCOV
681
    public upperValueChange = new EventEmitter<number>();
×
682

683
    /**
684
     * This event is emitted at the end of every slide interaction.
685
     * ```typescript
686
     * public change(event){
687
     *    alert("The value has been changed!");
688
     * }
689
     * ```
690
     * ```html
691
     * <igx-slider (dragFinished)="change($event)" #slider [(ngModel)]="task.percentCompleted" [step]="5">
692
     * ```
693
     */
694
    @Output()
UNCOV
695
    public dragFinished = new EventEmitter<number | IRangeSliderValue>();
×
696

697
    /**
698
     * @hidden
699
     */
700
    @ViewChild('ticks', { static: true })
701
    private ticks: ElementRef;
702

703
    /**
704
     * @hidden
705
     */
706
    @ViewChildren(IgxSliderThumbComponent)
UNCOV
707
    private thumbs: QueryList<IgxSliderThumbComponent> = new QueryList<IgxSliderThumbComponent>();
×
708

709
    /**
710
     * @hidden
711
     */
712
    @ViewChildren(IgxThumbLabelComponent)
UNCOV
713
    private labelRefs: QueryList<IgxThumbLabelComponent> = new QueryList<IgxThumbLabelComponent>();
×
714

715
    /**
716
     * @hidden
717
     */
UNCOV
718
    public onPan: Subject<number> = new Subject<number>();
×
719

720
    /**
721
     * @hidden
722
     */
723
    public stepDistance: number;
724

725
    // Limit handle travel zone
UNCOV
726
    private _pMin = 0;
×
UNCOV
727
    private _pMax = 1;
×
728

729
    // From/upperValue in percent values
UNCOV
730
    private _hasViewInit = false;
×
UNCOV
731
    private _minValue = 0;
×
UNCOV
732
    private _maxValue = 100;
×
733
    private _lowerBound: number;
734
    private _upperBound: number;
735
    private _lowerValue: number;
736
    private _upperValue: number;
UNCOV
737
    private _continuous = false;
×
UNCOV
738
    private _disabled = false;
×
UNCOV
739
    private _step = 1;
×
UNCOV
740
    private _value: number | IRangeSliderValue = 0;
×
741

742
    // ticks
UNCOV
743
    private _primaryTicks = 0;
×
UNCOV
744
    private _secondaryTicks = 0;
×
UNCOV
745
    private _sliding = false;
×
746

UNCOV
747
    private _labels = new Array<number | string | boolean | null | undefined>();
×
UNCOV
748
    private _type: IgxSliderType = IgxSliderType.SLIDER;
×
749

UNCOV
750
    private _destroyer$ = new Subject<boolean>();
×
UNCOV
751
    private _indicatorsDestroyer$ = new Subject<boolean>();
×
752
    private _indicatorsTimer: Observable<any>;
753

UNCOV
754
    private _onChangeCallback: (_: any) => void = noop;
×
UNCOV
755
    private _onTouchedCallback: () => void = noop;
×
756

UNCOV
757
    constructor(private renderer: Renderer2,
×
UNCOV
758
        private _el: ElementRef,
×
UNCOV
759
        private _cdr: ChangeDetectorRef,
×
UNCOV
760
        private _ngZone: NgZone,
×
UNCOV
761
        private _dir: IgxDirectionality) {
×
UNCOV
762
        this.stepDistance = this._step;
×
763
    }
764

765
    /**
766
     * @hidden
767
     */
768
    @HostListener('focus')
769
    public onFocus() {
770
        this.toggleSliderIndicators();
×
771
    }
772

773
    /**
774
     * Returns whether the `IgxSliderComponent` type is RANGE.
775
     * ```typescript
776
     *  @ViewChild("slider")
777
     * public slider: IgxSliderComponent;
778
     * ngAfterViewInit(){
779
     *     let sliderRange = this.slider.isRange;
780
     * }
781
     * ```
782
     */
783
    public get isRange(): boolean {
UNCOV
784
        return this.type === IgxSliderType.RANGE;
×
785
    }
786

787
    /**
788
     * Returns the lower value of a RANGE `IgxSliderComponent`.
789
     * ```typescript
790
     * @ViewChild("slider")
791
     * public slider: IgxSliderComponent;
792
     * public lowValue(event){
793
     *    let sliderLowValue = this.slider.lowerValue;
794
     * }
795
     * ```
796
     */
797
    public get lowerValue(): number {
UNCOV
798
        if (!Number.isNaN(this._lowerValue) && this._lowerValue !== undefined && this._lowerValue >= this.lowerBound) {
×
UNCOV
799
            return this._lowerValue;
×
800
        }
801

UNCOV
802
        return this.lowerBound;
×
803
    }
804

805
    /**
806
     * Sets the lower value of a RANGE `IgxSliderComponent`.
807
     * ```typescript
808
     * @ViewChild("slider")
809
     * public slider: IgxSliderComponent;
810
     * public lowValue(event){
811
     *    this.slider.lowerValue = value;
812
     * }
813
     * ```
814
     */
815
    @Input()
816
    public set lowerValue(value: number) {
UNCOV
817
        const adjustedValue = this.valueInRange(value, this.lowerBound, this.upperBound);
×
UNCOV
818
        if (this._lowerValue !== adjustedValue) {
×
UNCOV
819
            this._lowerValue = adjustedValue;
×
UNCOV
820
            this.lowerValueChange.emit(this._lowerValue);
×
UNCOV
821
            this.value = { lower: this._lowerValue, upper: this._upperValue };
×
822
        }
823
    }
824

825
    /**
826
     * Returns the upper value of a RANGE `IgxSliderComponent`.
827
     * Returns `value` of a SLIDER `IgxSliderComponent`
828
     * ```typescript
829
     *  @ViewChild("slider2")
830
     * public slider: IgxSliderComponent;
831
     * public upperValue(event){
832
     *     let upperValue = this.slider.upperValue;
833
     * }
834
     * ```
835
     */
836
    public get upperValue() {
UNCOV
837
        if (!Number.isNaN(this._upperValue) && this._upperValue !== undefined && this._upperValue <= this.upperBound) {
×
UNCOV
838
            return this._upperValue;
×
839
        }
840

UNCOV
841
        return this.upperBound;
×
842
    }
843

844
    /**
845
     * Sets the upper value of a RANGE `IgxSliderComponent`.
846
     * ```typescript
847
     *  @ViewChild("slider2")
848
     * public slider: IgxSliderComponent;
849
     * public upperValue(event){
850
     *     this.slider.upperValue = value;
851
     * }
852
     * ```
853
     */
854
    @Input()
855
    public set upperValue(value: number) {
UNCOV
856
        const adjustedValue = this.valueInRange(value, this.lowerBound, this.upperBound);
×
UNCOV
857
        if (this._upperValue !== adjustedValue) {
×
UNCOV
858
            this._upperValue = adjustedValue;
×
UNCOV
859
            this.upperValueChange.emit(this._upperValue);
×
UNCOV
860
            this.value = { lower: this._lowerValue, upper: this._upperValue };
×
861
        }
862
    }
863

864
    /**
865
     * Returns the value corresponding the lower label.
866
     * ```typescript
867
     * @ViewChild("slider")
868
     * public slider: IgxSliderComponent;
869
     * let label = this.slider.lowerLabel;
870
     * ```
871
     */
872
    public get lowerLabel() {
UNCOV
873
        return this.labelsViewEnabled ? this.labels[this.lowerValue] : this.lowerValue;
×
874
    }
875

876
    /**
877
     * Returns the value corresponding the upper label.
878
     * ```typescript
879
     * @ViewChild("slider")
880
     * public slider: IgxSliderComponent;
881
     * let label = this.slider.upperLabel;
882
     * ```
883
     */
884
    public get upperLabel() {
UNCOV
885
        return this.labelsViewEnabled ? this.labels[this.upperValue] : this.upperValue;
×
886
    }
887

888
    /**
889
     * Returns if label view is enabled.
890
     * If the {@link labels} is set, the view is automatically activated.
891
     * ```typescript
892
     * @ViewChild("slider")
893
     * public slider: IgxSliderComponent;
894
     * let labelView = this.slider.labelsViewEnabled;
895
     * ```
896
     */
897
    public get labelsViewEnabled(): boolean {
UNCOV
898
        return !!(this.labels && this.labels.length > 1);
×
899
    }
900

901
    /**
902
     * @hidden
903
     */
904
    public get showTopTicks() {
UNCOV
905
        return this.ticksOrientation === TicksOrientation.Top ||
×
906
            this.ticksOrientation === TicksOrientation.Mirror;
907
    }
908

909
    /**
910
     * @hidden
911
     */
912
    public get showBottomTicks() {
UNCOV
913
        return this.ticksOrientation === TicksOrientation.Bottom ||
×
914
            this.ticksOrientation === TicksOrientation.Mirror;
915
    }
916

917
    /**
918
     * @hidden
919
     */
920
    public ngOnChanges(changes: SimpleChanges) {
UNCOV
921
        if (changes.minValue && changes.maxValue &&
×
922
            changes.minValue.currentValue < changes.maxValue.currentValue) {
UNCOV
923
            this._maxValue = changes.maxValue.currentValue;
×
UNCOV
924
            this._minValue = changes.minValue.currentValue;
×
925
        }
926

UNCOV
927
        if (changes.step && changes.step.isFirstChange()) {
×
928
            this.normalizeByStep(this._value);
×
929
        }
930
    }
931

932
    /**
933
     * @hidden
934
     */
935
    public ngOnInit() {
936
        /**
937
         * if {@link SliderType.SLIDER} than the initial value shold be the lowest one.
938
         */
UNCOV
939
        if (!this.isRange) {
×
UNCOV
940
            this._upperValue = this.lowerBound;
×
941
        }
942

943
        // Set track travel zone
UNCOV
944
        this._pMin = this.valueToFraction(this.lowerBound) || 0;
×
UNCOV
945
        this._pMax = this.valueToFraction(this.upperBound) || 1;
×
946
    }
947

948
    public ngAfterContentInit() {
UNCOV
949
        this.setValue(this._value, false);
×
950
    }
951

952
    /**
953
     * @hidden
954
     */
955
    public ngAfterViewInit() {
UNCOV
956
        this._hasViewInit = true;
×
UNCOV
957
        this.stepDistance = this.calculateStepDistance();
×
UNCOV
958
        this.positionHandlersAndUpdateTrack();
×
UNCOV
959
        this.setTickInterval();
×
UNCOV
960
        this.changeThumbFocusableState(this.disabled);
×
961

UNCOV
962
        this.subscribeToEvents(this.thumbFrom);
×
UNCOV
963
        this.subscribeToEvents(this.thumbTo);
×
964

UNCOV
965
        this.thumbs.changes.pipe(takeUntil(this._destroyer$)).subscribe(change => {
×
UNCOV
966
            const thumbFrom = change.find((thumb: IgxSliderThumbComponent) => thumb.type === SliderHandle.FROM);
×
UNCOV
967
            this.positionHandler(thumbFrom, null, this.lowerValue);
×
UNCOV
968
            this.subscribeToEvents(thumbFrom);
×
UNCOV
969
            this.changeThumbFocusableState(this.disabled);
×
970
        });
971

UNCOV
972
        this.labelRefs.changes.pipe(takeUntil(this._destroyer$)).subscribe(() => {
×
UNCOV
973
            const labelFrom = this.labelRefs.find((label: IgxThumbLabelComponent) => label.type === SliderHandle.FROM);
×
UNCOV
974
            this.positionHandler(null, labelFrom, this.lowerValue);
×
975
        });
976

UNCOV
977
        this._ngZone.runOutsideAngular(() => {
×
UNCOV
978
            resizeObservable(this._el.nativeElement).pipe(
×
979
                throttleTime(40),
UNCOV
980
                takeUntil(this._destroyer$)).subscribe(() => this._ngZone.run(() => {
×
UNCOV
981
                    this.stepDistance = this.calculateStepDistance();
×
982
                }));
UNCOV
983
            fromEvent(this._el.nativeElement, 'pointermove').pipe(
×
984
                throttle(() => interval(0, animationFrameScheduler)),
×
985
                takeUntil(this._destroyer$)).subscribe(($event: PointerEvent) => this._ngZone.run(() => {
×
986
                    this.onPointerMove($event);
×
987
                }));
988
        });
989
    }
990

991
    /**
992
     * @hidden
993
     */
994
    public ngOnDestroy() {
UNCOV
995
        this._destroyer$.next(true);
×
UNCOV
996
        this._destroyer$.complete();
×
997

UNCOV
998
        this._indicatorsDestroyer$.next(true);
×
UNCOV
999
        this._indicatorsDestroyer$.complete();
×
1000
    }
1001

1002
    /**
1003
     * @hidden
1004
     */
1005
    public writeValue(value: IRangeSliderValue | number): void {
UNCOV
1006
        if (this.isNullishButNotZero(value)) {
×
UNCOV
1007
            return;
×
1008
        }
1009

UNCOV
1010
        this.normalizeByStep(value);
×
UNCOV
1011
        this.setValue(this._value, false);
×
UNCOV
1012
        this.positionHandlersAndUpdateTrack();
×
1013
    }
1014

1015
    /**
1016
     * @hidden
1017
     */
1018
    public registerOnChange(fn: any): void {
UNCOV
1019
        this._onChangeCallback = fn;
×
1020
    }
1021

1022
    /**
1023
     * @hidden
1024
     */
1025
    public registerOnTouched(fn: any): void {
UNCOV
1026
        this._onTouchedCallback = fn;
×
1027
    }
1028

1029
    /** @hidden */
1030
    public getEditElement() {
UNCOV
1031
        return this.isRange ? this.thumbFrom.nativeElement : this.thumbTo.nativeElement;
×
1032
    }
1033

1034
    /**
1035
     *
1036
     * @hidden
1037
     */
1038
    public update(mouseX) {
UNCOV
1039
        if (this.disabled) {
×
UNCOV
1040
            return;
×
1041
        }
1042

1043
        // Update To/From Values
UNCOV
1044
        this.onPan.next(mouseX);
×
1045

1046
        // Finally do positionHandlersAndUpdateTrack the DOM
1047
        // based on data values
UNCOV
1048
        this.positionHandlersAndUpdateTrack();
×
1049
    }
1050

1051
    /**
1052
     * @hidden
1053
     */
1054
    public thumbChanged(value: number, thumbType: string) {
UNCOV
1055
        const oldValue = this.value;
×
1056

UNCOV
1057
        if (this.isRange) {
×
UNCOV
1058
            if (thumbType === SliderHandle.FROM) {
×
UNCOV
1059
                if (this.lowerValue + value > this.upperValue) {
×
1060
                    this.upperValue = this.lowerValue + value;
×
1061
                }
UNCOV
1062
                this.lowerValue += value;
×
1063
            } else {
UNCOV
1064
                if (this.upperValue + value < this.lowerValue) {
×
1065
                    this.lowerValue = this.upperValue + value;
×
1066
                }
UNCOV
1067
                this.upperValue += value;
×
1068
            }
1069

UNCOV
1070
            const newVal: IRangeSliderValue = {
×
1071
                lower: this.lowerValue,
1072
                upper: this.upperValue
1073
            }
1074

1075
            // Swap the thumbs if a collision appears.
1076
            // if (newVal.lower == newVal.upper) {
1077
            //     this.toggleThumb();
1078
            // }
1079

UNCOV
1080
            this.value = newVal;
×
1081

1082
        } else {
UNCOV
1083
            const newVal = (this.value as number) + value;
×
UNCOV
1084
            if (newVal >= this.lowerBound && newVal <= this.upperBound) {
×
UNCOV
1085
                this.value = newVal;
×
1086
            }
1087
        }
1088

UNCOV
1089
        if (this.hasValueChanged(oldValue)) {
×
UNCOV
1090
            this.emitValueChange(oldValue);
×
1091
        }
1092
    }
1093

1094
    /**
1095
     * @hidden
1096
     */
1097
    public onThumbChange() {
UNCOV
1098
        this.toggleSliderIndicators();
×
1099
    }
1100

1101
    /**
1102
     * @hidden
1103
     */
1104
    public onHoverChange(state: boolean) {
1105
        return state ? this.showSliderIndicators() : this.hideSliderIndicators();
×
1106
    }
1107

1108
    public setValue(value: number | IRangeSliderValue, triggerChange: boolean) {
1109
        let res;
UNCOV
1110
        if (!this.isRange) {
×
UNCOV
1111
            value = value as number;
×
UNCOV
1112
            if (!isNaN(value)) {
×
UNCOV
1113
                this._upperValue = value - value % this.step;
×
UNCOV
1114
                res = this.upperValue;
×
1115
            }
1116
        } else {
UNCOV
1117
            value = this.validateInitialValue(value as IRangeSliderValue);
×
UNCOV
1118
            this._upperValue = value.upper;
×
UNCOV
1119
            this._lowerValue = value.lower;
×
UNCOV
1120
            res = { lower: this.lowerValue, upper: this.upperValue };
×
1121
        }
1122

UNCOV
1123
        if (triggerChange) {
×
UNCOV
1124
            this._onChangeCallback(res);
×
1125
        }
1126
    }
1127

1128
    @HostListener('pointerdown', ['$event'])
1129
    private onPointerDown($event: PointerEvent) {
UNCOV
1130
        this.findClosestThumb($event);
×
1131

UNCOV
1132
        if (!this.thumbTo.isActive && this.thumbFrom === undefined) {
×
1133
            return;
×
1134
        }
1135

UNCOV
1136
        this._sliding = true;
×
UNCOV
1137
        const activeThumb = this.thumbTo.isActive ? this.thumbTo : this.thumbFrom;
×
UNCOV
1138
        activeThumb.nativeElement.setPointerCapture($event.pointerId);
×
UNCOV
1139
        this.showSliderIndicators();
×
1140

UNCOV
1141
        $event.preventDefault();
×
1142
    }
1143

1144
    private onPointerMove($event: PointerEvent) {
UNCOV
1145
        if (this._sliding) {
×
UNCOV
1146
            this.update($event.clientX);
×
1147
        }
1148
    }
1149

1150
    @HostListener('pointerup', ['$event'])
1151
    private onPointerUp($event: PointerEvent) {
UNCOV
1152
        if (!this.thumbTo.isActive && this.thumbFrom === undefined) {
×
1153
            return;
×
1154
        }
1155

UNCOV
1156
        const activeThumb = this.thumbTo.isActive ? this.thumbTo : this.thumbFrom;
×
UNCOV
1157
        activeThumb.nativeElement.releasePointerCapture($event.pointerId);
×
1158

UNCOV
1159
        this._sliding = false;
×
UNCOV
1160
        this.hideSliderIndicators();
×
UNCOV
1161
        this.dragFinished.emit(this.value);
×
1162
    }
1163

1164
    private validateInitialValue(value: IRangeSliderValue) {
UNCOV
1165
        if (value.upper < value.lower) {
×
UNCOV
1166
            const temp = value.upper;
×
UNCOV
1167
            value.upper = value.lower;
×
UNCOV
1168
            value.lower = temp;
×
1169
        }
1170

UNCOV
1171
        if (value.lower < this.lowerBound) {
×
UNCOV
1172
            value.lower = this.lowerBound;
×
1173
        }
1174

UNCOV
1175
        if (value.upper > this.upperBound) {
×
UNCOV
1176
            value.upper = this.upperBound;
×
1177
        }
1178

UNCOV
1179
        return value;
×
1180
    }
1181

1182
    private findClosestThumb(event: PointerEvent) {
UNCOV
1183
        if (this.isRange) {
×
UNCOV
1184
            this.closestHandle(event);
×
1185
        } else {
UNCOV
1186
            this.thumbTo.nativeElement.focus();
×
1187
        }
1188

UNCOV
1189
        this.update(event.clientX);
×
1190
    }
1191

1192
    private updateLowerBoundAndMinTravelZone() {
UNCOV
1193
        this.lowerBound = this.minValue;
×
UNCOV
1194
        this._pMin = 0;
×
1195
    }
1196

1197
    private updateUpperBoundAndMaxTravelZone() {
UNCOV
1198
        this.upperBound = this.maxValue;
×
UNCOV
1199
        this._pMax = 1;
×
1200
    }
1201

1202
    private calculateStepDistance() {
UNCOV
1203
        return this._el.nativeElement.getBoundingClientRect().width / (this.maxValue - this.minValue) * this.step;
×
1204
    }
1205

1206
    // private toggleThumb() {
1207
    //     return this.thumbFrom.isActive ?
1208
    //         this.thumbTo.nativeElement.focus() :
1209
    //         this.thumbFrom.nativeElement.focus();
1210
    // }
1211

1212
    private valueInRange(value, min = 0, max = 100) {
×
UNCOV
1213
        return Math.max(Math.min(value, max), min);
×
1214
    }
1215

1216
    private positionHandler(thumbHandle: ElementRef, labelHandle: ElementRef, position: number) {
UNCOV
1217
        const percent = `${this.valueToFraction(position) * 100}%`;
×
UNCOV
1218
        const dir = this._dir.rtl ? 'right' : 'left';
×
1219

UNCOV
1220
        if (thumbHandle) {
×
UNCOV
1221
            thumbHandle.nativeElement.style[dir] = percent;
×
1222
        }
1223

UNCOV
1224
        if (labelHandle) {
×
UNCOV
1225
            labelHandle.nativeElement.style[dir] = percent;
×
1226
        }
1227
    }
1228

1229
    private positionHandlersAndUpdateTrack() {
UNCOV
1230
        if (!this.isRange) {
×
UNCOV
1231
            this.positionHandler(this.thumbTo, this.labelTo, this.value as number);
×
1232
        } else {
UNCOV
1233
            this.positionHandler(this.thumbTo, this.labelTo, (this.value as IRangeSliderValue).upper);
×
UNCOV
1234
            this.positionHandler(this.thumbFrom, this.labelFrom, (this.value as IRangeSliderValue).lower);
×
1235
        }
1236

UNCOV
1237
        if (this._hasViewInit) {
×
UNCOV
1238
            this.updateTrack();
×
1239
        }
1240
    }
1241

1242
    private closestHandle(event: PointerEvent) {
UNCOV
1243
        const fromOffset = this.thumbFrom.nativeElement.offsetLeft + this.thumbFrom.nativeElement.offsetWidth / 2;
×
UNCOV
1244
        const toOffset = this.thumbTo.nativeElement.offsetLeft + this.thumbTo.nativeElement.offsetWidth / 2;
×
UNCOV
1245
        const xPointer = event.clientX - this._el.nativeElement.getBoundingClientRect().left;
×
UNCOV
1246
        const match = this.closestTo(xPointer, [fromOffset, toOffset]);
×
1247

UNCOV
1248
        if (fromOffset === toOffset && toOffset < xPointer) {
×
1249
            this.thumbTo.nativeElement.focus();
×
UNCOV
1250
        } else if (fromOffset === toOffset && toOffset > xPointer) {
×
1251
            this.thumbFrom.nativeElement.focus();
×
UNCOV
1252
        } else if (match === fromOffset) {
×
UNCOV
1253
            this.thumbFrom.nativeElement.focus();
×
1254
        } else {
1255
            this.thumbTo.nativeElement.focus();
×
1256
        }
1257
    }
1258

1259
    private setTickInterval() {
1260
        let tickInterval;
UNCOV
1261
        const trackProgress = 100;
×
1262

UNCOV
1263
        if (this.labelsViewEnabled) {
×
1264
            // Calc ticks depending on the labels length;
UNCOV
1265
            tickInterval = ((trackProgress / (this.labels.length - 1) * 10)) / 10;
×
1266
        } else {
UNCOV
1267
            const trackRange = this.maxValue - this.minValue;
×
UNCOV
1268
            tickInterval = this.step > 1 ?
×
1269
                (trackProgress / ((trackRange / this.step)) * 10) / 10
1270
                : null;
1271
        }
1272

UNCOV
1273
        this.renderer.setStyle(this.ticks.nativeElement, 'stroke-dasharray', `0, ${tickInterval * Math.sqrt(2)}%`);
×
UNCOV
1274
        this.renderer.setStyle(this.ticks.nativeElement, 'visibility', this.continuous || tickInterval === null ? 'hidden' : 'visible');
×
1275
    }
1276

1277
    private showSliderIndicators() {
UNCOV
1278
        if (this.disabled) {
×
UNCOV
1279
            return;
×
1280
        }
1281

UNCOV
1282
        if (this._indicatorsTimer) {
×
UNCOV
1283
            this._indicatorsDestroyer$.next(true);
×
UNCOV
1284
            this._indicatorsTimer = null;
×
1285
        }
1286

UNCOV
1287
        this.thumbTo.showThumbIndicators();
×
UNCOV
1288
        this.labelTo.active = true;
×
UNCOV
1289
        if (this.thumbFrom) {
×
UNCOV
1290
            this.thumbFrom.showThumbIndicators();
×
1291
        }
1292

UNCOV
1293
        if (this.labelFrom) {
×
UNCOV
1294
            this.labelFrom.active = true;
×
1295
        }
1296

1297
    }
1298

1299
    private hideSliderIndicators() {
UNCOV
1300
        if (this.disabled) {
×
1301
            return;
×
1302
        }
1303

UNCOV
1304
        this._indicatorsTimer = timer(this.thumbLabelVisibilityDuration);
×
UNCOV
1305
        this._indicatorsTimer.pipe(takeUntil(this._indicatorsDestroyer$)).subscribe(() => {
×
UNCOV
1306
            this.thumbTo.hideThumbIndicators();
×
UNCOV
1307
            this.labelTo.active = false;
×
UNCOV
1308
            if (this.thumbFrom) {
×
UNCOV
1309
                this.thumbFrom.hideThumbIndicators();
×
1310
            }
1311

UNCOV
1312
            if (this.labelFrom) {
×
UNCOV
1313
                this.labelFrom.active = false;
×
1314
            }
1315
        });
1316
    }
1317

1318
    private toggleSliderIndicators() {
UNCOV
1319
        this.showSliderIndicators();
×
UNCOV
1320
        this.hideSliderIndicators();
×
1321
    }
1322

1323
    private changeThumbFocusableState(state: boolean) {
UNCOV
1324
        const value = state ? -1 : 0;
×
1325

UNCOV
1326
        if (this.isRange) {
×
UNCOV
1327
            this.thumbFrom.tabindex = value;
×
1328
        }
1329

UNCOV
1330
        this.thumbTo.tabindex = value;
×
1331

UNCOV
1332
        this._cdr.detectChanges();
×
1333
    }
1334

1335
    private closestTo(goal: number, positions: number[]): number {
UNCOV
1336
        return positions.reduce((previous, current) => (Math.abs(goal - current) < Math.abs(goal - previous) ? current : previous));
×
1337
    }
1338

1339
    private valueToFraction(value: number, pMin = this._pMin, pMax = this._pMax) {
×
UNCOV
1340
        return this.valueInRange((value - this.minValue) / (this.maxValue - this.minValue), pMin, pMax);
×
1341
    }
1342

1343
    private isNullishButNotZero(value: any): boolean {
UNCOV
1344
        return !value && value !== 0;
×
1345
    }
1346

1347
    /**
1348
     * @hidden
1349
     * Normalizе the value when two-way data bind is used and {@link this.step} is set.
1350
     * @param value
1351
     */
1352
    private normalizeByStep(value: IRangeSliderValue | number) {
UNCOV
1353
        if (this.isRange) {
×
UNCOV
1354
            this._value = {
×
1355
                lower: Math.floor((value as IRangeSliderValue).lower / this.step) * this.step,
1356
                upper: Math.floor((value as IRangeSliderValue).upper / this.step) * this.step
1357
            };
1358
        } else {
UNCOV
1359
            this._value = Math.floor((value as number) / this.step) * this.step;
×
1360
        }
1361
    }
1362

1363
    private updateTrack() {
UNCOV
1364
        const fromPosition = this.valueToFraction(this.lowerValue);
×
UNCOV
1365
        const toPosition = this.valueToFraction(this.upperValue);
×
UNCOV
1366
        const positionGap = toPosition - fromPosition;
×
1367

UNCOV
1368
        let trackLeftIndention = fromPosition;
×
UNCOV
1369
        if (this.isRange) {
×
UNCOV
1370
            if (positionGap) {
×
UNCOV
1371
                trackLeftIndention = Math.round((1 / positionGap * fromPosition) * 100);
×
1372
            }
1373

UNCOV
1374
            trackLeftIndention = this._dir.rtl ? -trackLeftIndention : trackLeftIndention;
×
UNCOV
1375
            this.renderer.setStyle(this.trackRef.nativeElement, 'transform', `scaleX(${positionGap}) translateX(${trackLeftIndention}%)`);
×
1376
        } else {
UNCOV
1377
            this.renderer.setStyle(this.trackRef.nativeElement, 'transform', `scaleX(${toPosition})`);
×
1378
        }
1379
    }
1380

1381
    private subscribeToEvents(thumb: IgxSliderThumbComponent) {
UNCOV
1382
        if (!thumb) {
×
UNCOV
1383
            return;
×
1384
        }
1385

UNCOV
1386
        thumb.thumbValueChange
×
1387
            .pipe(takeUntil(this.unsubscriber(thumb)))
UNCOV
1388
            .subscribe(value => this.thumbChanged(value, thumb.type));
×
1389

UNCOV
1390
        thumb.thumbBlur
×
1391
            .pipe(takeUntil(this.unsubscriber(thumb)))
UNCOV
1392
            .subscribe(() => this._onTouchedCallback());
×
1393
    }
1394

1395
    private unsubscriber(thumb: IgxSliderThumbComponent) {
UNCOV
1396
        return merge(this._destroyer$, thumb.destroy);
×
1397
    }
1398

1399
    private hasValueChanged(oldValue) {
UNCOV
1400
        const isSliderWithDifferentValue: boolean = !this.isRange && oldValue !== this.value;
×
UNCOV
1401
        const isRangeWithOneDifferentValue: boolean = this.isRange &&
×
1402
            ((oldValue as IRangeSliderValue).lower !== (this.value as IRangeSliderValue).lower ||
1403
                (oldValue as IRangeSliderValue).upper !== (this.value as IRangeSliderValue).upper);
1404

UNCOV
1405
        return isSliderWithDifferentValue || isRangeWithOneDifferentValue;
×
1406
    }
1407

1408
    private emitValueChange(oldValue: number | IRangeSliderValue) {
UNCOV
1409
        this.valueChange.emit({ oldValue, value: this.value });
×
1410
    }
1411
}
1412

1413
/**
1414
 * @hidden
1415
 */
1416

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