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

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

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

21
let NEXT_ID = 0;
2✔
22

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

169

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

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

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

UNCOV
187
        this.positionHandlersAndUpdateTrack();
×
188

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

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

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

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

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

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

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

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

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

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

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

UNCOV
322
        return this._minValue;
×
323
    }
324

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

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

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

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

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

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

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

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

UNCOV
417
        return this.minValue;
×
418
    }
419

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

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

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

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

UNCOV
456
        return this.maxValue;
×
457
    }
458

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
803
        return this.lowerBound;
×
804
    }
805

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

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

UNCOV
842
        return this.upperBound;
×
843
    }
844

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1081
            this.value = newVal;
×
1082

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1180
        return value;
×
1181
    }
1182

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1298
    }
1299

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1414
/**
1415
 * @hidden
1416
 */
1417

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

© 2025 Coveralls, Inc