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

IgniteUI / igniteui-angular / 13287444581

12 Feb 2025 02:18PM UTC coverage: 10.56% (-81.0%) from 91.606%
13287444581

Pull #15359

github

web-flow
Merge a24969adb into 32cfe83f6
Pull Request #15359: fix(time-picker): exclude from SSR toggle events #15135

933 of 15233 branches covered (6.12%)

3037 of 28759 relevant lines covered (10.56%)

352.42 hits per line

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

10.16
/projects/igniteui-angular/src/lib/progressbar/progressbar.component.ts
1
import { NgClass, NgTemplateOutlet, NgIf } from '@angular/common';
2
import {
3
    Component,
4
    ElementRef,
5
    EventEmitter,
6
    HostBinding,
7
    Input,
8
    Output,
9
    Renderer2,
10
    ViewChild,
11
    ContentChild,
12
    AfterViewInit,
13
    AfterContentInit,
14
    Directive,
15
    booleanAttribute
16
} from '@angular/core';
17
import {
18
    IgxProgressBarTextTemplateDirective,
19
    IgxProgressBarGradientDirective,
20
} from './progressbar.common';
21
import { IBaseEventArgs, mkenum } from '../core/utils';
22
import { IgxDirectionality } from '../services/direction/directionality';
23
const ONE_PERCENT = 0.01;
2✔
24
const MIN_VALUE = 0;
2✔
25

26
export const IgxTextAlign = /*@__PURE__*/mkenum({
2✔
27
    START: 'start',
28
    CENTER: 'center',
29
    END: 'end'
30
});
31
export type IgxTextAlign = (typeof IgxTextAlign)[keyof typeof IgxTextAlign];
32

33
export const IgxProgressType = /*@__PURE__*/mkenum({
2✔
34
    ERROR: 'error',
35
    INFO: 'info',
36
    WARNING: 'warning',
37
    SUCCESS: 'success'
38
});
39
export type IgxProgressType = (typeof IgxProgressType)[keyof typeof IgxProgressType];
40

41
export interface IChangeProgressEventArgs extends IBaseEventArgs {
42
    previousValue: number;
43
    currentValue: number;
44
}
45

46
/**
47
 * @hidden
48
 */
49
@Directive()
50
export abstract class BaseProgressDirective {
2✔
51
    /**
52
     * An event, which is triggered after a progress is changed.
53
     * ```typescript
54
     * public progressChange(event) {
55
     *     alert("Progress made!");
56
     * }
57
     *  //...
58
     * ```
59
     * ```html
60
     * <igx-circular-bar [value]="currentValue" (progressChanged)="progressChange($event)"></igx-circular-bar>
61
     * <igx-linear-bar [value]="currentValue" (progressChanged)="progressChange($event)"></igx-linear-bar>
62
     * ```
63
     */
64
    @Output()
65
    public progressChanged = new EventEmitter<IChangeProgressEventArgs>();
×
66

67
    /**
68
     * Sets/Gets progressbar in indeterminate. By default it is set to false.
69
     * ```html
70
     * <igx-linear-bar [indeterminate]="true"></igx-linear-bar>
71
     * <igx-circular-bar [indeterminate]="true"></igx-circular-bar>
72
     * ```
73
     */
74
    @Input({ transform: booleanAttribute })
75
    public indeterminate = false;
×
76

77
    /**
78
     * Sets/Gets progressbar animation duration. By default it is 2000ms.
79
     * ```html
80
     * <igx-linear-bar [indeterminate]="true"></igx-linear-bar>
81
     * ```
82
     */
83
    @Input()
84
    public animationDuration = 2000;
×
85
    public _interval;
86

87
    protected _initValue = 0;
×
88
    protected _contentInit = false;
×
89
    protected _max = 100;
×
90
    protected _value = MIN_VALUE;
×
91
    protected _newVal = MIN_VALUE;
×
92
    protected _animate = true;
×
93
    protected _step;
94
    protected _animation;
95
    protected _valueInPercent;
96
    protected _internalState = {
×
97
        oldVal: 0,
98
        newVal: 0
99
    };
100

101
    constructor() { }
102

103
    /**
104
     * Returns the value which update the progress indicator of the `progress bar`.
105
     * ```typescript
106
     * @ViewChild("MyProgressBar")
107
     * public progressBar: IgxLinearProgressBarComponent | IgxCircularBarComponent;
108
     * public stepValue(event) {
109
     *     let step = this.progressBar.step;
110
     *     alert(step);
111
     * }
112
     * ```
113
     */
114
    @Input()
115
    public get step(): number {
116
        if (this._step) {
×
117
            return this._step;
×
118
        }
119
        return this._max * ONE_PERCENT;
×
120
    }
121

122
    /**
123
     * Sets the value by which progress indicator is updated. By default it is 1.
124
     * ```html
125
     * <igx-linear-bar [max]="200" [value]="0" [step]="1"></igx-linear-bar>
126
     * <igx-circular-bar [max]="200" [value]="0" [step]="1"></igx-circular-bar>
127
     * ```
128
     */
129
    public set step(val: number) {
130
        const step = Number(val);
×
131
        if (step > this.max) {
×
132
            return;
×
133
        }
134

135
        this._step = step;
×
136
    }
137

138
    /**
139
     * Animating the progress. By default it is set to true.
140
     * ```html
141
     * <igx-linear-bar [animate]="false" [max]="200" [value]="50"></igx-linear-bar>
142
     * <igx-circular-bar [animate]="false" [max]="200" [value]="50"></igx-circular-bar>
143
     * ```
144
     */
145
    @Input({ transform: booleanAttribute })
146
    public set animate(animate: boolean) {
147
        this._animate = animate;
×
148
        if (animate) {
×
149
            this.animationDuration = 2000;
×
150
        } else {
151
            this.animationDuration = 0;
×
152
        }
153
    }
154

155
    /**
156
     * Returns whether the `progress bar` has animation true/false.
157
     * ```typescript
158
     * @ViewChild("MyProgressBar")
159
     * public progressBar: IgxLinearProgressBarComponent | IgxCircularBarComponent;
160
     * public animationStatus(event) {
161
     *     let animationStatus = this.progressBar.animate;
162
     *     alert(animationStatus);
163
     * }
164
     * ```
165
     */
166
    public get animate(): boolean {
167
        return this._animate;
×
168
    }
169

170
    /**
171
     * Set maximum value that can be passed. By default it is set to 100.
172
     * ```html
173
     * <igx-linear-bar [max]="200" [value]="0"></igx-linear-bar>
174
     * <igx-circular-bar [max]="200" [value]="0"></igx-circular-bar>
175
     * ```
176
     */
177
    @HostBinding('attr.aria-valuemax')
178
    @Input()
179
    public set max(maxNum: number) {
180
        if (maxNum < MIN_VALUE || this._max === maxNum ||
×
181
            (this._animation && this._animation.playState !== 'finished')) {
182
            return;
×
183
        }
184

185
        this._internalState.newVal = Math.round(toValue(toPercent(this.value, maxNum), maxNum));
×
186
        this._value = this._internalState.oldVal = Math.round(toValue(this.valueInPercent, maxNum));
×
187
        this._max = maxNum;
×
188
        this.triggerProgressTransition(this._internalState.oldVal, this._internalState.newVal, true);
×
189
    }
190

191
    /**
192
     * Returns the the maximum progress value of the `progress bar`.
193
     * ```typescript
194
     * @ViewChild("MyProgressBar")
195
     * public progressBar: IgxLinearProgressBarComponent | IgxCircularBarComponent;
196
     * public maxValue(event) {
197
     *     let max = this.progressBar.max;
198
     *     alert(max);
199
     * }
200
     * ```
201
     */
202
    public get max() {
203
        return this._max;
×
204
    }
205

206
    /**
207
     * Returns the `IgxLinearProgressBarComponent`/`IgxCircularProgressBarComponent` value in percentage.
208
     * ```typescript
209
     * @ViewChild("MyProgressBar")
210
     * public progressBar: IgxLinearProgressBarComponent; // IgxCircularProgressBarComponent
211
     * public valuePercent(event){
212
     *     let percentValue = this.progressBar.valueInPercent;
213
     *     alert(percentValue);
214
     * }
215
     * ```
216
     */
217
    public get valueInPercent(): number {
218
        const val = toPercent(this._value, this._max);
×
219
        return val;
×
220
    }
221

222
    /**
223
     * Returns value that indicates the current `IgxLinearProgressBarComponent` position.
224
     * ```typescript
225
     * @ViewChild("MyProgressBar")
226
     * public progressBar: IgxLinearProgressBarComponent;
227
     * public getValue(event) {
228
     *     let value = this.progressBar.value;
229
     *     alert(value);
230
     * }
231
     * ```
232
     */
233
    @HostBinding('attr.aria-valuenow')
234
    @Input()
235
    public get value(): number {
236
        return this._value;
×
237
    }
238

239
    /**
240
     * Set value that indicates the current `IgxLinearProgressBarComponent` position.
241
     * ```html
242
     * <igx-linear-bar [striped]="false" [max]="200" [value]="50"></igx-linear-bar>
243
     * ```
244
     */
245
    public set value(val) {
246
        if (this._animation && this._animation.playState !== 'finished' || val < 0) {
×
247
            return;
×
248
        }
249

250
        const valInRange = valueInRange(val, this.max);
×
251

252
        if (isNaN(valInRange) || this._value === val || this.indeterminate) {
×
253
            return;
×
254
        }
255

256
        if (this._contentInit) {
×
257
            this.triggerProgressTransition(this._value, valInRange);
×
258
        } else {
259
            this._initValue = valInRange;
×
260
        }
261
    }
262

263
    protected triggerProgressTransition(oldVal, newVal, maxUpdate = false) {
×
264
        if (oldVal === newVal) {
×
265
            return;
×
266
        }
267

268
        const changedValues = {
×
269
            currentValue: newVal,
270
            previousValue: oldVal
271
        };
272

273
        const stepDirection = this.directionFlow(oldVal, newVal);
×
274
        if (this._animate) {
×
275
            const newToPercent = toPercent(newVal, this.max);
×
276
            const oldToPercent = toPercent(oldVal, this.max);
×
277
            const duration = this.animationDuration / Math.abs(newToPercent - oldToPercent) / (this._step ? this._step : 1);
×
278
            this.runAnimation(newVal);
×
279
            this._interval = setInterval(() => this.increase(newVal, stepDirection), duration);
×
280
        } else {
281
            this.updateProgress(newVal);
×
282
        }
283

284
        if (maxUpdate) {
×
285
            return;
×
286
        }
287
        this.progressChanged.emit(changedValues);
×
288
    }
289

290
    /**
291
     * @hidden
292
     */
293
    protected increase(newValue: number, step: number) {
294
        const targetValue = toPercent(newValue, this._max);
×
295
        this._value = valueInRange(this._value, this._max) + step;
×
296
        if ((step > 0 && this.valueInPercent >= targetValue) || (step < 0 && this.valueInPercent <= targetValue)) {
×
297
            if (this._value !== newValue) {
×
298
                this._value = newValue;
×
299
            }
300
            return clearInterval(this._interval);
×
301
        }
302
    }
303

304
    /**
305
     * @hidden
306
     */
307
    protected directionFlow(currentValue: number, prevValue: number): number {
308
        return currentValue < prevValue ? this.step : -this.step;
×
309
    }
310

311
    protected abstract runAnimation(value: number);
312

313
    /**
314
     * @hidden
315
     * @param step
316
     */
317
    private updateProgress(val: number) {
318
        this._value = valueInRange(val, this._max);
×
319
        // this.valueInPercent = toPercent(val, this._max);
320
        this.runAnimation(val);
×
321
    }
322
}
323
let NEXT_LINEAR_ID = 0;
2✔
324
let NEXT_CIRCULAR_ID = 0;
2✔
325
let NEXT_GRADIENT_ID = 0;
2✔
326
@Component({
327
    selector: 'igx-linear-bar',
328
    templateUrl: 'templates/linear-bar.component.html',
329
    imports: [NgClass]
330
})
331
export class IgxLinearProgressBarComponent extends BaseProgressDirective implements AfterContentInit {
2✔
332
    @HostBinding('attr.aria-valuemin')
333
    public valueMin = 0;
×
334

335
    @HostBinding('class.igx-linear-bar')
336
    public cssClass = 'igx-linear-bar';
×
337

338
    /**
339
     * Set `IgxLinearProgressBarComponent` to have striped style. By default it is set to false.
340
     * ```html
341
     * <igx-linear-bar [striped]="true" [max]="200" [value]="50"></igx-linear-bar>
342
     * ```
343
     */
344
    @HostBinding('class.igx-linear-bar--striped')
345
    @Input({ transform: booleanAttribute })
346
    public striped = false;
×
347

348
    /**
349
     * @hidden
350
     * ```
351
     */
352
    @HostBinding('class.igx-linear-bar--indeterminate')
353
    public get isIndeterminate() {
354
        return this.indeterminate;
×
355
    }
356

357
    /**
358
     * Sets the value of the `role` attribute. If not provided it will be automatically set to `progressbar`.
359
     * ```html
360
     * <igx-linear-bar role="progressbar"></igx-linear-bar>
361
     * ```
362
     */
363
    @HostBinding('attr.role')
364
    @Input()
365
    public role = 'progressbar';
×
366

367
    /**
368
     * Sets the value of `id` attribute. If not provided it will be automatically generated.
369
     * ```html
370
     * <igx-linear-bar [id]="'igx-linear-bar-55'" [striped]="true" [max]="200" [value]="50"></igx-linear-bar>
371
     * ```
372
     */
373
    @HostBinding('attr.id')
374
    @Input()
375
    public id = `igx-linear-bar-${NEXT_LINEAR_ID++}`;
×
376

377
    /**
378
     * Set the position that defines where the text is aligned.
379
     * Possible options - `IgxTextAlign.START` (default), `IgxTextAlign.CENTER`, `IgxTextAlign.END`.
380
     * ```typescript
381
     * public positionCenter: IgxTextAlign;
382
     * public ngOnInit() {
383
     *     this.positionCenter = IgxTextAlign.CENTER;
384
     * }
385
     *  //...
386
     * ```
387
     *  ```html
388
     * <igx-linear-bar type="warning" [text]="'Custom text'" [textAlign]="positionCenter" [striped]="true"></igx-linear-bar>
389
     * ```
390
     */
391
    @Input()
392
    public textAlign: IgxTextAlign = IgxTextAlign.START;
×
393

394
    /**
395
     * Set the text to be visible. By default it is set to true.
396
     * ```html
397
     *  <igx-linear-bar type="default" [textVisibility]="false"></igx-linear-bar>
398
     * ```
399
     */
400
    @Input({ transform: booleanAttribute })
401
    public textVisibility = true;
×
402

403
    /**
404
     * Set the position that defines if the text should be aligned above the progress line. By default is set to false.
405
     * ```html
406
     *  <igx-linear-bar type="error" [textTop]="true"></igx-linear-bar>
407
     * ```
408
     */
409
    @Input({ transform: booleanAttribute })
410
    public textTop = false;
×
411

412
    /**
413
     * Set a custom text that is displayed according to the defined position.
414
     *  ```html
415
     * <igx-linear-bar type="warning" [text]="'Custom text'" [textAlign]="positionCenter" [striped]="true"></igx-linear-bar>
416
     * ```
417
     */
418
    @Input()
419
    public text: string;
420

421
    /**
422
     * Set type of the `IgxLinearProgressBarComponent`. Possible options - `default`, `success`, `info`, `warning`, and `error`.
423
     * ```html
424
     * <igx-linear-bar [striped]="false" [max]="100" [value]="0" type="error"></igx-linear-bar>
425
     * ```
426
     */
427
    @Input()
428
    public type = 'default';
×
429

430
    @ViewChild('indicator', { static: true })
431
    private _progressIndicator: ElementRef;
432

433
    private animationState = {
×
434
        width: '0%'
435
    };
436

437
    /**
438
     * @hidden
439
     */
440
    @HostBinding('class.igx-linear-bar--danger')
441
    public get error() {
442
        return this.type === IgxProgressType.ERROR;
×
443
    }
444

445
    /**
446
     * @hidden
447
     */
448
    @HostBinding('class.igx-linear-bar--info')
449
    public get info() {
450
        return this.type === IgxProgressType.INFO;
×
451
    }
452

453
    /**
454
     * @hidden
455
     */
456
    @HostBinding('class.igx-linear-bar--warning')
457
    public get warning() {
458
        return this.type === IgxProgressType.WARNING;
×
459
    }
460

461
    /**
462
     * @hidden
463
     */
464
    @HostBinding('class.igx-linear-bar--success')
465
    public get success() {
466
        return this.type === IgxProgressType.SUCCESS;
×
467
    }
468

469
    public ngAfterContentInit() {
470
        this.triggerProgressTransition(MIN_VALUE, this._initValue);
×
471
        this._contentInit = true;
×
472
    }
473

474
    public runAnimation(value: number) {
475
        if (this._animation && this._animation.playState !== 'finished') {
×
476
            return;
×
477
        }
478

479
        const valueInPercent = this.max <= 0 ? 0 : toPercent(value, this.max);
×
480

481
        const FRAMES = [];
×
482
        FRAMES[0] = {
×
483
            ...this.animationState
484
        };
485

486
        this.animationState.width = valueInPercent + '%';
×
487
        FRAMES[1] = {
×
488
            ...this.animationState
489
        };
490

491
        this._animation = this._progressIndicator.nativeElement.animate(FRAMES, {
×
492
            easing: 'ease-out',
493
            fill: 'forwards',
494
            duration: this.animationDuration
495
        });
496
    }
497
}
498

499
@Component({
500
    selector: 'igx-circular-bar',
501
    templateUrl: 'templates/circular-bar.component.html',
502
    imports: [NgTemplateOutlet, NgIf]
503
})
504
export class IgxCircularProgressBarComponent extends BaseProgressDirective implements AfterViewInit, AfterContentInit {
2✔
505

506
    /** @hidden */
507
    @HostBinding('class.igx-circular-bar')
508
    public cssClass = 'igx-circular-bar';
×
509

510
    /**
511
     * Sets the value of `id` attribute. If not provided it will be automatically generated.
512
     * ```html
513
     * <igx-circular-bar [id]="'igx-circular-bar-55'" [value]="50"></igx-circular-bar>
514
     * ```
515
     */
516
    @HostBinding('attr.id')
517
    @Input()
518
    public id = `igx-circular-bar-${NEXT_CIRCULAR_ID++}`;
×
519

520
    /**
521
     * @hidden
522
     */
523
    @HostBinding('class.igx-circular-bar--indeterminate')
524
    @Input()
525
    public get isIndeterminate() {
526
        return this.indeterminate;
×
527
    }
528

529
    /**
530
     * Sets the text visibility. By default it is set to true.
531
     * ```html
532
     * <igx-circular-bar [textVisibility]="false"></igx-circular-bar>
533
     * ```
534
     */
535
    @Input({ transform: booleanAttribute })
536
    public textVisibility = true;
×
537

538
    /**
539
     * Sets/gets the text to be displayed inside the `igxCircularBar`.
540
     * ```html
541
     * <igx-circular-bar text="Progress"></igx-circular-bar>
542
     * ```
543
     * ```typescript
544
     * let text = this.circularBar.text;
545
     * ```
546
     */
547
    @Input()
548
    public text: string;
549

550
    @ContentChild(IgxProgressBarTextTemplateDirective, { read: IgxProgressBarTextTemplateDirective })
551
    public textTemplate: IgxProgressBarTextTemplateDirective;
552

553
    @ContentChild(IgxProgressBarGradientDirective, { read: IgxProgressBarGradientDirective })
554
    public gradientTemplate: IgxProgressBarGradientDirective;
555

556
    @ViewChild('circle', { static: true })
557
    private _svgCircle: ElementRef;
558

559
    /**
560
     * @hidden
561
     */
562
    public gradientId = `igx-circular-gradient-${NEXT_GRADIENT_ID++}`;
×
563

564
    /**
565
     * @hidden
566
     */
567
    public get context(): any {
568
        return {
×
569
            $implicit: { value: this.value, valueInPercent: this.valueInPercent, max: this.max }
570
        };
571
    }
572

573
    private _circleRadius = 46;
×
574
    private _circumference = 2 * Math.PI * this._circleRadius;
×
575

576
    private readonly STROKE_OPACITY_DVIDER = 100;
×
577
    private readonly STROKE_OPACITY_ADDITION = .2;
×
578

579
    private animationState = {
×
580
        strokeDashoffset: 289,
581
        strokeOpacity: 1
582
    };
583

584
    constructor(private renderer: Renderer2, private _directionality: IgxDirectionality) {
×
585
        super();
×
586
    }
587

588
    public ngAfterContentInit() {
589
        this.triggerProgressTransition(MIN_VALUE, this._initValue);
×
590
        this._contentInit = true;
×
591
    }
592

593
    public ngAfterViewInit() {
594
        this.renderer.setStyle(
×
595
            this._svgCircle.nativeElement,
596
            'stroke',
597
            `url(#${this.gradientId})`
598
        );
599
    }
600

601
    /**
602
     * @hidden
603
     */
604
    public get textContent(): string {
605
        return this.text;
×
606
    }
607

608
    public runAnimation(value: number) {
609
        if (this._animation && this._animation.playState !== 'finished') {
×
610
            return;
×
611
        }
612

613
        const valueInPercent = this.max <= 0 ? 0 : toPercent(value, this.max);
×
614

615
        const FRAMES = [];
×
616
        FRAMES[0] = { ...this.animationState };
×
617

618
        this.animationState.strokeDashoffset = this.getProgress(valueInPercent);
×
619
        this.animationState.strokeOpacity = toPercent(value, this.max) / this.STROKE_OPACITY_DVIDER + this.STROKE_OPACITY_ADDITION;
×
620

621
        FRAMES[1] = {
×
622
            ...this.animationState
623
        };
624

625
        this._animation = this._svgCircle.nativeElement.animate(FRAMES, {
×
626
            easing: 'ease-out',
627
            fill: 'forwards',
628
            duration: this.animationDuration
629
        });
630
    }
631

632
    private getProgress(percentage: number) {
633
        return this._directionality.rtl ?
×
634
            this._circumference + (percentage * this._circumference / 100) :
635
            this._circumference - (percentage * this._circumference / 100);
636
    }
637
}
638

639
export const valueInRange = (value: number, max: number, min = 0): number => Math.max(Math.min(value, max), min);
2!
640

641
export const toPercent = (value: number, max: number) => !max ? 0 : Math.floor(100 * value / max);
2!
642

643
export const toValue = (value: number, max: number) => max * value / 100;
2✔
644

645

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