@progress/kendo-angular-progressbar
Version:
Kendo UI Angular component starter template
1,274 lines (1,264 loc) • 64.3 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
import * as i1 from '@progress/kendo-angular-l10n';
import { ComponentMessages, LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n';
import * as i0 from '@angular/core';
import { isDevMode, Component, HostBinding, Input, Directive, forwardRef, EventEmitter, Output, ViewChild, ContentChild, NgModule } from '@angular/core';
import { validatePackage } from '@progress/kendo-licensing';
import { hasObservers, isDocumentAvailable, isChanged, ResizeSensorComponent, ResizeBatchService } from '@progress/kendo-angular-common';
import { NgStyle, NgClass, NgIf, NgFor, NgTemplateOutlet } from '@angular/common';
import { Subscription } from 'rxjs';
import { take } from 'rxjs/operators';
/**
* @hidden
*/
const packageMetadata = {
name: '@progress/kendo-angular-progressbar',
productName: 'Kendo UI for Angular',
productCode: 'KENDOUIANGULAR',
productCodes: ['KENDOUIANGULAR'],
publishDate: 1743579485,
version: '18.4.0',
licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
};
/**
* @hidden
*/
const MIN_MAX_ERROR_MESSAGE = `The max value should be greater than the min.`;
/**
* @hidden
*/
const LABEL_DECIMALS = 3;
/**
* @hidden
*/
const MIN_RATIO = 0.0001;
/**
* @hidden
*/
const formatValue = (value, min, max, label) => {
const defaultFormattedValue = truncateNumber(value);
if (typeof label !== 'boolean') {
if (typeof label.format === 'string') {
switch (label.format) {
case 'value':
return defaultFormattedValue;
case 'percent':
return `${Math.floor(calculatePercentage(value, min, max))}%`;
default:
return defaultFormattedValue;
}
}
else if (typeof label.format === 'function') {
return label.format(value);
}
else {
return defaultFormattedValue;
}
}
return defaultFormattedValue;
};
/**
* @hidden
*/
const validateRange = (min, max) => {
if (isDevMode && min > max) {
throw new Error(MIN_MAX_ERROR_MESSAGE);
}
};
/**
* @hidden
*/
const adjustValueToRange = (min, max, value) => Math.max(Math.min(value, max), min);
/**
* @hidden
*/
const calculatePercentage = (value, min, max) => {
const decimalValue = Math.abs((value - min) / (max - min));
return decimalValue * 100;
};
/**
* @hidden
*/
const truncateNumber = (value) => {
const numberParts = value.toString().split('.');
return numberParts.length === 1 ? `${numberParts[0]}` : `${numberParts[0]}.${numberParts[1].substr(0, LABEL_DECIMALS)}`;
};
/**
* @hidden
*/
const calculateRatio = (min, max, value) => Math.max((value - min) / (max - min), MIN_RATIO);
/**
* @hidden
*/
const extractValueFromChanges = (changes, type, value) => changes[type] && changes[type].currentValue !== undefined ? changes[type].currentValue : value;
/**
* @hidden
*/
const runAnimation = (changes, animation, previousValue, displayValue) => animation && typeof requestAnimationFrame !== 'undefined' && changes['value'] && previousValue !== displayValue;
/**
* @hidden
*/
const stopCurrentAnimation = (changes) => {
const isAnimationChanged = Boolean(changes['animation']);
const hasAnimation = isAnimationChanged && changes['animation'].currentValue;
return isAnimationChanged && !hasAnimation;
};
/**
* @hidden
*/
const setProgressBarStyles = (props, renderer) => {
props.forEach(prop => {
renderer[prop.method](prop.el, prop.attr, `${prop.attrValue}`);
});
};
/**
* @hidden
*/
const removeProgressBarStyles = (props, renderer) => {
props.forEach(prop => {
renderer[prop.method](prop.el, prop.attr);
});
};
/**
* @hidden
*/
const hasElementSize = (element) => {
return !!(element.style.width && element.style.height);
};
/**
* @hidden
*/
class ProgressBarBase {
elem;
renderer;
localization;
hostClasses = true;
get isHorizontal() {
return this.orientation === 'horizontal';
}
get isVertical() {
return this.orientation === 'vertical';
}
get disabledClass() {
return this.disabled;
}
get reverseClass() {
return this.reverse;
}
get indeterminateClass() {
return this.indeterminate;
}
get dirAttribute() {
return this.direction;
}
roleAttribute = 'progressbar';
get ariaMinAttribute() {
return String(this.min);
}
get ariaMaxAttribute() {
return String(this.max);
}
get ariaValueAttribute() {
return this.indeterminate ? undefined : String(this.displayValue);
}
/**
* The maximum value of the ProgressBar.
* Defaults to `100`.
*/
max = 100;
/**
* The minimum value of the ProgressBar.
* Defaults to `0`.
*/
min = 0;
/**
* The value of the ProgressBar.
* Has to be between `min` and `max`.
* By default, the value is equal to the `min` value.
*/
/**
* The value of the ProgressBar.
* Has to be between `min` and `max`.
* Defaults to `0`.
*/
value = 0;
/**
* @hidden
*/
get isCompleted() {
return this.value === this.max;
}
/**
* @hidden
*/
get statusWidth() {
return this.orientation === 'horizontal' ? this._progressRatio * 100 : 100;
}
/**
* @hidden
*/
get statusHeight() {
return this.orientation === 'vertical' ? this._progressRatio * 100 : 100;
}
/**
* @hidden
*/
get statusWrapperWidth() {
return this.orientation === 'horizontal' ? 100 / this._progressRatio : 100;
}
/**
* @hidden
*/
get statusWrapperHeight() {
return this.orientation === 'vertical' ? 100 / this._progressRatio : 100;
}
get _progressRatio() {
return calculateRatio(this.min, this.max, this.displayValue);
}
/**
* Defines the orientation of the ProgressBar
* ([see example]({% slug progressbar_orientation %})).
* Defaults to `horizontal`.
*/
orientation = 'horizontal';
/**
* If set to `true`, the ProgressBar will be disabled
* ([see example]({% slug progressbar_disabled %})).
* It will still allow you to change its value.
* Defaults to `false`.
*/
disabled = false;
/**
* If set to `true`, the ProgressBar will be reversed
* ([see example]({% slug progressbar_direction %})).
* Defaults to `false`.
*/
reverse = false;
/**
* Sets the `indeterminate` state of the ProgressBar.
* Defaults to `false`.
*/
indeterminate = false;
direction;
localizationChangeSubscription;
displayValue = 0;
previousValue = 0;
/**
* @hidden
*/
constructor(elem, renderer, localization) {
this.elem = elem;
this.renderer = renderer;
this.localization = localization;
validatePackage(packageMetadata);
this.localizationChangeSubscription = localization.changes.subscribe(({ rtl }) => {
this.direction = rtl ? 'rtl' : 'ltr';
});
}
ngAfterViewInit() {
const elem = this.elem.nativeElement;
const label = this.localization.get('progressBarLabel');
this.renderer.setAttribute(elem, 'aria-label', label);
}
ngOnChanges(changes) {
const min = extractValueFromChanges(changes, 'min', this.min);
const max = extractValueFromChanges(changes, 'max', this.max);
const value = extractValueFromChanges(changes, 'value', this.value);
if (changes['min'] || changes['max'] || changes['value']) {
if (changes['min'] || changes['max']) {
validateRange(min, max);
}
if (changes['value']) {
if (value == null || Number.isNaN(value)) {
this.value = min;
}
const previousValue = this.displayValue;
this.displayValue = adjustValueToRange(this.min, this.max, value);
this.previousValue = previousValue;
}
this.min = min;
this.max = max;
this.displayValue = adjustValueToRange(this.min, this.max, value);
}
}
ngOnDestroy() {
if (this.localizationChangeSubscription) {
this.localizationChangeSubscription.unsubscribe();
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ProgressBarBase, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ProgressBarBase, selector: "ng-component", inputs: { max: "max", min: "min", value: "value", orientation: "orientation", disabled: "disabled", reverse: "reverse", indeterminate: "indeterminate" }, host: { properties: { "class.k-progressbar": "this.hostClasses", "class.k-progressbar-horizontal": "this.isHorizontal", "class.k-progressbar-vertical": "this.isVertical", "class.k-disabled": "this.disabledClass", "class.k-progressbar-reverse": "this.reverseClass", "class.k-progressbar-indeterminate": "this.indeterminateClass", "attr.dir": "this.dirAttribute", "attr.role": "this.roleAttribute", "attr.aria-valuemin": "this.ariaMinAttribute", "attr.aria-valuemax": "this.ariaMaxAttribute", "attr.aria-valuenow": "this.ariaValueAttribute" } }, usesOnChanges: true, ngImport: i0, template: '', isInline: true });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ProgressBarBase, decorators: [{
type: Component,
args: [{
template: ''
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1.LocalizationService }]; }, propDecorators: { hostClasses: [{
type: HostBinding,
args: ['class.k-progressbar']
}], isHorizontal: [{
type: HostBinding,
args: ['class.k-progressbar-horizontal']
}], isVertical: [{
type: HostBinding,
args: ['class.k-progressbar-vertical']
}], disabledClass: [{
type: HostBinding,
args: ['class.k-disabled']
}], reverseClass: [{
type: HostBinding,
args: ['class.k-progressbar-reverse']
}], indeterminateClass: [{
type: HostBinding,
args: ['class.k-progressbar-indeterminate']
}], dirAttribute: [{
type: HostBinding,
args: ['attr.dir']
}], roleAttribute: [{
type: HostBinding,
args: ['attr.role']
}], ariaMinAttribute: [{
type: HostBinding,
args: ['attr.aria-valuemin']
}], ariaMaxAttribute: [{
type: HostBinding,
args: ['attr.aria-valuemax']
}], ariaValueAttribute: [{
type: HostBinding,
args: ['attr.aria-valuenow']
}], max: [{
type: Input
}], min: [{
type: Input
}], value: [{
type: Input
}], orientation: [{
type: Input
}], disabled: [{
type: Input
}], reverse: [{
type: Input
}], indeterminate: [{
type: Input
}] } });
/**
* @hidden
*/
class ProgressBarMessages extends ComponentMessages {
/**
* The aria-label attribute for the ProgressBar component.
*/
progressBarLabel;
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ProgressBarMessages, deps: null, target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ProgressBarMessages, inputs: { progressBarLabel: "progressBarLabel" }, usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ProgressBarMessages, decorators: [{
type: Directive,
args: [{}]
}], propDecorators: { progressBarLabel: [{
type: Input
}] } });
/**
* @hidden
*/
class LocalizedProgressBarMessagesDirective extends ProgressBarMessages {
service;
constructor(service) {
super();
this.service = service;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedProgressBarMessagesDirective, deps: [{ token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: LocalizedProgressBarMessagesDirective, isStandalone: true, selector: "[kendoProgressBarLocalizedMessages]", providers: [
{
provide: ProgressBarMessages,
useExisting: forwardRef(() => LocalizedProgressBarMessagesDirective)
}
], usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedProgressBarMessagesDirective, decorators: [{
type: Directive,
args: [{
providers: [
{
provide: ProgressBarMessages,
useExisting: forwardRef(() => LocalizedProgressBarMessagesDirective)
}
],
selector: `[kendoProgressBarLocalizedMessages]`,
standalone: true
}]
}], ctorParameters: function () { return [{ type: i1.LocalizationService }]; } });
/**
* Represents the [Kendo UI ProgressBar component for Angular]({% slug overview_progressbar %}).
*
* @example
* ```ts-preview
* _@Component({
* selector: 'my-app',
* template: `
* <kendo-progressbar [value]="value">
* </kendo-progressbar>
* `
* })
* class AppComponent {
* public value = 50;
* }
* ```
*/
class ProgressBarComponent extends ProgressBarBase {
localization;
elem;
renderer;
zone;
/**
* Determines whether the status label will be visible.
* Defaults to `true`—the label will be visible and displayed with the default
* `LabelSettings` having its position set to `end` and its format set to `value`.
*/
label = true;
/**
* The CSS styles that will be rendered on the inner element which represents the full portion of the progress bar
* ([see example]({% slug progressbar_appearance %})).
* Supports the type of values that are supported by [`ngStyle`](link:site.data.urls.angular['ngstyleapi']).
*/
progressCssStyle;
/**
* The CSS classes that will be rendered on the inner element which represents the full portion of the progress bar
* ([see example]({% slug progressbar_appearance %})).
* Supports the type of values that are supported by [`ngClass`](link:site.data.urls.angular['ngclassapi']).
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
progressCssClass;
/**
* The CSS styles that will be rendered on the inner element which represents the empty portion of the progress bar
* ([see example]({% slug progressbar_appearance %})).
* Supports the type of values that are supported by [`ngStyle`](link:site.data.urls.angular['ngstyleapi']).
*/
emptyCssStyle;
/**
* The CSS classes that will be rendered on the inner element which represents the empty portion of the progress bar
* ([see example]({% slug progressbar_appearance %})).
* Supports the type of values that are supported by [`ngClass`](link:site.data.urls.angular['ngclassapi']).
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
emptyCssClass;
/**
* The animation configuration of the ProgressBar.
* Defaults to `false`.
*/
animation = false;
/**
* Fires when the animation which indicates the latest value change is completed.
*/
animationEnd = new EventEmitter();
/**
* @hidden
*/
get showLabel() {
if (typeof this.label === 'boolean') {
return this.label;
}
else {
if (this.label && !this.label.hasOwnProperty('visible')) {
this.label.visible = true;
}
return this.label.visible;
}
}
/**
* @hidden
*/
get labelPosition() {
if (typeof this.label === 'boolean') {
return 'end';
}
else {
if (this.label && !this.label.hasOwnProperty('position')) {
this.label.position = 'end';
}
return this.label.position;
}
}
/**
* @hidden
*/
get isPositionStart() {
return this.labelPosition === 'start';
}
/**
* @hidden
*/
get isPositionCenter() {
return this.labelPosition === 'center';
}
/**
* @hidden
*/
get isPositionEnd() {
return this.labelPosition === 'end';
}
/**
* @hidden
*/
get formattedLabelValue() {
return formatValue(this.displayValue, this.min, this.max, this.label);
}
progressStatusElement;
progressStatusWrapperElement;
animationFrame;
cancelCurrentAnimation;
isAnimationInProgress;
/**
* @hidden
*/
constructor(localization, elem, renderer, zone) {
super(elem, renderer, localization);
this.localization = localization;
this.elem = elem;
this.renderer = renderer;
this.zone = zone;
}
/**
* @hidden
*/
ngOnChanges(changes) {
super.ngOnChanges(changes);
if (this.isAnimationInProgress && stopCurrentAnimation(changes)) {
this.cancelCurrentAnimation = true;
}
if (runAnimation(changes, this.animation, this.previousValue, this.displayValue) && !changes['value'].firstChange) {
this.startAnimation(this.previousValue);
}
}
/**
* @hidden
*/
ngOnDestroy() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
}
/**
* @hidden
*/
startAnimation(previousValue) {
this.isAnimationInProgress = true;
const element = this.progressStatusElement.nativeElement;
const wrapperElement = this.progressStatusWrapperElement.nativeElement;
const animationOptions = this.getAnimationOptions(previousValue);
this.zone.runOutsideAngular(() => {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame);
}
const animate = () => {
const elapsed = new Date().getTime() - animationOptions.startTime;
const position = Math.min(elapsed / animationOptions.duration, 1);
const size = animationOptions.startSize + animationOptions.deltaSize * position;
const wrapperSize = (100 / size) * 100;
this.renderValueChange(element, wrapperElement, animationOptions.property, size, wrapperSize);
if (position < 1) {
if (this.cancelCurrentAnimation) {
this.resetProgress(element, wrapperElement, animationOptions.property);
return;
}
this.animationFrame = requestAnimationFrame(animate);
}
else {
this.stopAnimation(previousValue);
}
};
animate();
});
}
/**
* @hidden
*/
get animationDuration() {
if (typeof this.animation === 'boolean') {
return 400;
}
else {
if (this.animation && !this.animation.hasOwnProperty('duration')) {
this.animation.duration = 400;
}
return this.animation.duration;
}
}
stopAnimation(value) {
if (hasObservers(this.animationEnd)) {
this.zone.run(() => {
this.animationEnd.emit({
from: value,
to: this.displayValue
});
});
}
this.zone.run(() => {
this.isAnimationInProgress = false;
});
}
getAnimationOptions(value) {
const isHorizontal = this.orientation === 'horizontal';
const previousRatio = calculateRatio(this.min, this.max, value);
const previousStatusWidth = isHorizontal ? previousRatio * 100 : 100;
const previousStatusHeight = !isHorizontal ? previousRatio * 100 : 100;
const property = isHorizontal ? 'width' : 'height';
const startTime = new Date().getTime();
const startSize = isHorizontal ? previousStatusWidth : previousStatusHeight;
const deltaSize = isHorizontal ? this.statusWidth - previousStatusWidth : this.statusHeight - previousStatusHeight;
const duration = this.animationDuration * Math.abs((deltaSize / 100));
return {
property,
startTime,
startSize,
deltaSize,
duration
};
}
renderValueChange(element, wrapperElement, property, size, wrapperSize) {
this.renderer.setStyle(element, property, size + '%');
this.renderer.setStyle(wrapperElement, property, wrapperSize + '%');
}
resetProgress(element, wrapperElement, property) {
const size = calculateRatio(this.min, this.max, this.value);
const newSize = size * 100;
const newWrapperSize = 100 / size;
this.renderValueChange(element, wrapperElement, property, newSize, newWrapperSize);
this.zone.run(() => {
this.cancelCurrentAnimation = false;
this.isAnimationInProgress = false;
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ProgressBarComponent, deps: [{ token: i1.LocalizationService }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ProgressBarComponent, isStandalone: true, selector: "kendo-progressbar", inputs: { label: "label", progressCssStyle: "progressCssStyle", progressCssClass: "progressCssClass", emptyCssStyle: "emptyCssStyle", emptyCssClass: "emptyCssClass", animation: "animation" }, outputs: { animationEnd: "animationEnd" }, providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.progressbar'
}
], viewQueries: [{ propertyName: "progressStatusElement", first: true, predicate: ["progressStatus"], descendants: true }, { propertyName: "progressStatusWrapperElement", first: true, predicate: ["progressStatusWrap"], descendants: true }], exportAs: ["kendoProgressBar"], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
<ng-container kendoProgressBarLocalizedMessages
i18n-progressBarLabel="kendo.progressbar.progressBarLabel|The aria-label attribute for the ProgressBar component."
progressBarLabel="Progressbar"
>
</ng-container>
<span class="k-progress-status-wrap"
[class.k-progress-start]="isPositionStart"
[class.k-progress-center]="isPositionCenter"
[class.k-progress-end]="isPositionEnd"
[ngStyle]="emptyCssStyle"
[ngClass]="emptyCssClass">
<span *ngIf="showLabel" class="k-progress-status">{{formattedLabelValue}}</span>
</span>
<div
#progressStatus
class="k-selected k-progressbar-value"
[class.k-complete]="isCompleted"
[ngStyle]="progressCssStyle"
[ngClass]="progressCssClass"
[style.width.%]="statusWidth"
[style.height.%]="statusHeight"
>
<span
#progressStatusWrap
class="k-progress-status-wrap"
[style.width.%]="statusWrapperWidth"
[style.height.%]="statusWrapperHeight"
[class.k-progress-start]="isPositionStart"
[class.k-progress-center]="isPositionCenter"
[class.k-progress-end]="isPositionEnd"
>
<span *ngIf="showLabel" class="k-progress-status">{{formattedLabelValue}}</span>
</span>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: LocalizedProgressBarMessagesDirective, selector: "[kendoProgressBarLocalizedMessages]" }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ProgressBarComponent, decorators: [{
type: Component,
args: [{
exportAs: 'kendoProgressBar',
selector: 'kendo-progressbar',
template: `
<ng-container kendoProgressBarLocalizedMessages
i18n-progressBarLabel="kendo.progressbar.progressBarLabel|The aria-label attribute for the ProgressBar component."
progressBarLabel="Progressbar"
>
</ng-container>
<span class="k-progress-status-wrap"
[class.k-progress-start]="isPositionStart"
[class.k-progress-center]="isPositionCenter"
[class.k-progress-end]="isPositionEnd"
[ngStyle]="emptyCssStyle"
[ngClass]="emptyCssClass">
<span *ngIf="showLabel" class="k-progress-status">{{formattedLabelValue}}</span>
</span>
<div
#progressStatus
class="k-selected k-progressbar-value"
[class.k-complete]="isCompleted"
[ngStyle]="progressCssStyle"
[ngClass]="progressCssClass"
[style.width.%]="statusWidth"
[style.height.%]="statusHeight"
>
<span
#progressStatusWrap
class="k-progress-status-wrap"
[style.width.%]="statusWrapperWidth"
[style.height.%]="statusWrapperHeight"
[class.k-progress-start]="isPositionStart"
[class.k-progress-center]="isPositionCenter"
[class.k-progress-end]="isPositionEnd"
>
<span *ngIf="showLabel" class="k-progress-status">{{formattedLabelValue}}</span>
</span>
</div>
`,
providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.progressbar'
}
],
standalone: true,
imports: [LocalizedProgressBarMessagesDirective, NgStyle, NgClass, NgIf]
}]
}], ctorParameters: function () { return [{ type: i1.LocalizationService }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.NgZone }]; }, propDecorators: { label: [{
type: Input
}], progressCssStyle: [{
type: Input
}], progressCssClass: [{
type: Input
}], emptyCssStyle: [{
type: Input
}], emptyCssClass: [{
type: Input
}], animation: [{
type: Input
}], animationEnd: [{
type: Output
}], progressStatusElement: [{
type: ViewChild,
args: ['progressStatus', { static: false }]
}], progressStatusWrapperElement: [{
type: ViewChild,
args: ['progressStatusWrap', { static: false }]
}] } });
/**
* Represents the [Kendo UI ChunkProgressBar component for Angular]({% slug overview_chunkprogressbar %}).
*
* @example
* ```ts-preview
* _@Component({
* selector: 'my-app',
* template: `
* <kendo-chunkprogressbar [value]="value">
* </kendo-chunkprogressbar>
* `
* })
* class AppComponent {
* public value = 40;
* }
* ```
*/
class ChunkProgressBarComponent extends ProgressBarBase {
localization;
elem;
renderer;
chunkClass = true;
/**
* Sets the number of chunks into which the ChunkProgressBar will be split.
* Defaults to `5`.
*/
chunkCount = 5;
/**
* @hidden
*/
get chunks() {
const count = this.chunkCount;
const chunks = Array(count).fill(false);
const completedChunks = Math.floor(this._progressRatio * count);
for (let i = 0; i < completedChunks; i++) {
chunks[i] = true;
}
return chunks;
}
/**
* The CSS styles that will be rendered on the full chunk elements ([see example]({% slug chunkprogressbar_appearance %})).
* Supports the type of values that are supported by [`ngStyle`](link:site.data.urls.angular['ngstyleapi']).
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
progressCssStyle;
/**
* The CSS classes that will be rendered on the full chunk elements ([see example]({% slug chunkprogressbar_appearance %})).
* Supports the type of values that are supported by [`ngClass`](link:site.data.urls.angular['ngclassapi']).
*/
progressCssClass;
/**
* The CSS styles that will be rendered on the empty chunk elements ([see example]({% slug chunkprogressbar_appearance %})).
* Supports the type of values that are supported by [`ngStyle`](link:site.data.urls.angular['ngstyleapi']).
*/
emptyCssStyle;
/**
* The CSS classes that will be rendered on the empty chunk elements ([see example]({% slug chunkprogressbar_appearance %})).
* Supports the type of values that are supported by [`ngClass`](link:site.data.urls.angular['ngclassapi']).
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
emptyCssClass;
/**
* @hidden
*/
get chunkSizePercentage() {
return 100 / this.chunkCount;
}
/**
* @hidden
*/
get orientationStyles() {
if (this.orientation === 'horizontal') {
this._orientationStyles.width = `${this.chunkSizePercentage}%`;
this._orientationStyles.height = null;
}
else {
this._orientationStyles.height = `${this.chunkSizePercentage}%`;
this._orientationStyles.width = null;
}
return this._orientationStyles;
}
_orientationStyles = {
width: `${this.chunkSizePercentage}%`,
height: null
};
/**
* @hidden
*/
constructor(localization, elem, renderer) {
super(elem, renderer, localization);
this.localization = localization;
this.elem = elem;
this.renderer = renderer;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ChunkProgressBarComponent, deps: [{ token: i1.LocalizationService }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ChunkProgressBarComponent, isStandalone: true, selector: "kendo-chunkprogressbar", inputs: { chunkCount: "chunkCount", progressCssStyle: "progressCssStyle", progressCssClass: "progressCssClass", emptyCssStyle: "emptyCssStyle", emptyCssClass: "emptyCssClass" }, host: { properties: { "class.k-chunk-progressbar": "this.chunkClass" } }, providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.chunkprogressbar'
}
], exportAs: ["kendoChunkProgressBar"], usesInheritance: true, ngImport: i0, template: `
<ng-container kendoProgressBarLocalizedMessages
i18n-progressBarLabel="kendo.chunkprogressbar.progressBarLabel|The aria-label attribute for the ChunkProgressBar component."
progressBarLabel="Chunk progressbar"
>
</ng-container>
<ul class="k-reset k-progressbar-chunks">
<li class="k-progressbar-chunk" *ngFor="let chunk of chunks; let i = index;"
[class.k-first]="i === 0"
[class.k-last]="i === chunkCount - 1"
[class.k-selected]="chunk"
[ngClass]="chunk ? progressCssClass : emptyCssClass"
[ngStyle]="chunk ? progressCssStyle : emptyCssStyle"
[style.width]="orientationStyles.width"
[style.height]="orientationStyles.height"
>
</li>
</ul>
`, isInline: true, dependencies: [{ kind: "directive", type: LocalizedProgressBarMessagesDirective, selector: "[kendoProgressBarLocalizedMessages]" }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ChunkProgressBarComponent, decorators: [{
type: Component,
args: [{
exportAs: 'kendoChunkProgressBar',
selector: 'kendo-chunkprogressbar',
template: `
<ng-container kendoProgressBarLocalizedMessages
i18n-progressBarLabel="kendo.chunkprogressbar.progressBarLabel|The aria-label attribute for the ChunkProgressBar component."
progressBarLabel="Chunk progressbar"
>
</ng-container>
<ul class="k-reset k-progressbar-chunks">
<li class="k-progressbar-chunk" *ngFor="let chunk of chunks; let i = index;"
[class.k-first]="i === 0"
[class.k-last]="i === chunkCount - 1"
[class.k-selected]="chunk"
[ngClass]="chunk ? progressCssClass : emptyCssClass"
[ngStyle]="chunk ? progressCssStyle : emptyCssStyle"
[style.width]="orientationStyles.width"
[style.height]="orientationStyles.height"
>
</li>
</ul>
`,
providers: [
LocalizationService,
{
provide: L10N_PREFIX,
useValue: 'kendo.chunkprogressbar'
}
],
standalone: true,
imports: [LocalizedProgressBarMessagesDirective, NgFor, NgClass, NgStyle]
}]
}], ctorParameters: function () { return [{ type: i1.LocalizationService }, { type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { chunkClass: [{
type: HostBinding,
args: ['class.k-chunk-progressbar']
}], chunkCount: [{
type: Input
}], progressCssStyle: [{
type: Input
}], progressCssClass: [{
type: Input
}], emptyCssStyle: [{
type: Input
}], emptyCssClass: [{
type: Input
}] } });
/**
* A directive that selects a template within the `<kendo-circularprogessbar>` component which will be used for the center template.
* ([see example]({% slug center_template_circularprogressbar %})).
*/
class CircularProgressbarCenterTemplateDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CircularProgressbarCenterTemplateDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: CircularProgressbarCenterTemplateDirective, isStandalone: true, selector: "[kendoCircularProgressbarCenterTemplate]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CircularProgressbarCenterTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoCircularProgressbarCenterTemplate]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef }]; } });
const DEFAULT_SURFACE_SIZE = 200;
/**
* Represents the [Kendo UI Circular ProgressBar component for Angular]({% slug overview_circularprogressbar %}).
*
* @example
* ```ts-preview
* _@Component({
* selector: 'my-app',
* template: `
* <kendo-circularprogressbar [value]="value"></kendo-circularprogressbar>
* `
* })
* class AppComponent {
* public value: number = 50;
* }
* ```
*/
class CircularProgressBarComponent {
renderer;
cdr;
localization;
element;
zone;
hostClasses = true;
get ariaMinAttribute() {
return String(this.min);
}
get ariaMaxAttribute() {
return String(this.max);
}
get ariaValueAttribute() {
return this.indeterminate ? undefined : String(this.value);
}
roleAttribute = 'progressbar';
/**
* Sets the default value of the Circular Progressbar between `min` and `max`.
*
* @default 0
*/
set value(value) {
if (value > this.max) {
this.handleErrors('value > max');
}
if (value < this.min) {
this.handleErrors('value < min');
}
this.previousValue = this.value;
this._value = value;
}
get value() {
return this._value;
}
/**
* The maximum value which the Circular Progressbar can accept.
*
* @default 100
*/
set max(max) {
if (max < this.min) {
this.handleErrors('max < min');
}
this._max = max;
}
get max() {
return this._max;
}
/**
* The minimum value which the Circular Progressbar can accept.
*
* @default 0
*/
set min(min) {
if (min > this.max) {
this.handleErrors('max < min');
}
this._min = min;
}
get min() {
return this._min;
}
/**
* Indicates whether an animation will be played on value changes.
*
* @default false
*/
animation = false;
/**
* The opacity of the value arc.
* @default 1
*/
opacity = 1;
/**
* Puts the Circular ProgressBar in indeterminate state.
* @default false
*/
set indeterminate(indeterminate) {
this._indeterminate = indeterminate;
}
get indeterminate() {
return this._indeterminate;
}
/**
* Configures the pointer color. Could be set to a single color string or customized per progress stages.
*/
progressColor;
/**
* Fires when the animation which indicates the latest value change is completed.
*/
animationEnd = new EventEmitter();
progress;
scale;
labelElement;
surface;
centerTemplate;
centerTemplateContext = {};
_indeterminate = false;
_max = 100;
_min = 0;
_value = 0;
previousValue = 0;
internalValue = 0;
rtl;
subscriptions = new Subscription();
constructor(renderer, cdr, localization, element, zone) {
this.renderer = renderer;
this.cdr = cdr;
this.localization = localization;
this.element = element;
this.zone = zone;
validatePackage(packageMetadata);
this.subscriptions.add(this.localization.changes.subscribe(this.rtlChange.bind(this)));
}
ngAfterViewInit() {
if (!isDocumentAvailable()) {
return;
}
const elem = this.element.nativeElement;
const ariaLabel = this.localization.get('progressBarLabel');
this.renderer.setAttribute(elem, 'aria-label', ariaLabel);
this.initProgressArc();
}
ngOnChanges(changes) {
const skipFirstChange = true;
if (isChanged('value', changes, skipFirstChange) && this.progress) {
if (this.animation) {
this.progressbarAnimation();
}
else {
const value = this.value - this.min;
this.internalValue = changes['value'].currentValue;
this.calculateProgress(value);
}
}
if (changes['opacity'] && this.progress) {
setProgressBarStyles([{ method: 'setAttribute', el: this.progress.nativeElement, attr: 'opacity', attrValue: this.opacity.toString() }], this.renderer);
}
if (changes['indeterminate'] && !changes['indeterminate'].firstChange) {
this.indeterminateState();
}
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
/**
* @hidden
*/
onResize() {
this.setStyles();
const value = this.animation ? this.internalValue : this.value;
this.calculateProgress(value);
this.updateCenterTemplate(value);
}
initProgressArc() {
this.setStyles();
if (this.indeterminate) {
this.indeterminateState();
}
else {
if (!this.animation) {
const value = this.value - this.min;
this.calculateProgress(value);
}
else {
this.progressbarAnimation();
}
}
}
calculateProgress(value) {
if (this.progressColor) {
this.updateProgressColor(value);
}
// needed when we have *ngIf inside the template to render different content depending on some condition
this.zone.onStable.pipe(take(1)).subscribe(() => {
this.updateCenterTemplate(value + this.min);
});
const progressArc = this.progress.nativeElement;
const radius = this.progress.nativeElement.r.baseVal.value;
const circumference = Math.PI * (radius * 2);
const dir = this.rtl ? circumference * -1 : circumference;
const strokeDashOffest = circumference - dir * (value / (this.max - this.min));
const progressCalculations = [
{ method: 'setStyle', el: progressArc, attr: 'strokeDasharray', attrValue: circumference.toString() },
{ method: 'setStyle', el: progressArc, attr: 'strokeDashoffset', attrValue: strokeDashOffest.toString() }
];
setProgressBarStyles(progressCalculations, this.renderer);
}
progressbarAnimation() {
const forwardProgress = {
isOngoing: this.internalValue > this.value - this.min,
isPositive: this.value >= this.previousValue
};
const backwardProgress = {
isOngoing: this.internalValue < this.value - this.min,
isNegative: this.value <= this.previousValue
};
if (forwardProgress.isOngoing && forwardProgress.isPositive ||
backwardProgress.isOngoing && backwardProgress.isNegative) {
return;
}
this.calculateProgress(this.internalValue);
const from = this.internalValue;
if (hasObservers(this.animationEnd)) {
this.animationEnd.emit({
from: from,
to: this.internalValue
});
}
// eslint-disable-next-line no-unused-expressions
forwardProgress.isPositive ? this.internalValue += 1 : this.internalValue -= 1;
requestAnimationFrame(this.progressbarAnimation.bind(this));
}
setStyles() {
const progressArc = this.progress.nativeElement;
const scale = this.scale.nativeElement;
const surface = this.surface.nativeElement;
const element = this.element.nativeElement;
let elWidth = element.getBoundingClientRect().width;
if (!hasElementSize(element)) {
const surfaceSize = [
{ method: 'setStyle', el: surface, attr: 'width', attrValue: `${DEFAULT_SURFACE_SIZE}px` },
{ method: 'setStyle', el: surface, attr: 'height', attrValue: `${DEFAULT_SURFACE_SIZE}px` }
];
elWidth = DEFAULT_SURFACE_SIZE;
setProgressBarStyles(surfaceSize, this.renderer);
}
const attributesArray = [
{ method: 'setAttribute', el: progressArc, attr: 'r', attrValue: String((elWidth / 2) - 10) },
{ method: 'setAttribute', el: progressArc, attr: 'cx', attrValue: String((elWidth / 2)) },
{ method: 'setAttribute', el: progressArc, attr: 'cy', attrValue: String((elWidth / 2)) },
{ method: 'setAttribute', el: progressArc, attr: 'opacity', attrValue: String(this.opacity) },
{ method: 'setAttribute', el: scale, attr: 'r', attrValue: String((elWidth / 2) - 10) },
{ method: 'setAttribute', el: scale, attr: 'cx', attrValue: String(elWidth / 2) },
{ method: 'setAttribute', el: scale, attr: 'cy', attrValue: String(elWidth / 2) }
];
setProgressBarStyles(attributesArray, this.renderer);
}
indeterminateState() {
const progressArc = this.progress.nativeElement;
if (this.indeterminate) {
// the indeterminate state wont work as the `k-circular-progressbar-arc` has a transform: rotate(-90deg) which is
// interfering with the svg animation as the animateTransform brings its own transform: rotate()
// This will be like this until the themes release a new version, bringing a new class `k-circular-progressbar-indeterminate-arc`
// containing only the necassery CSS styles and we will switch between them when the state of the progressbar is switched.
this.calculateProgress(this.value - this.min);
const rotate = this.rtl ? { from: 360, to: 0 } : { from: 0, to: 360 };
let color;
if (!this.progressColor) {
color = getComputedStyle(progressArc).stroke;
}
const indeterminateStyles = [
{ method: 'setStyle', el: progressArc, attr: 'transform-origin', attrValue: 'center' },
{ method: 'setStyle', el: progressArc, attr: 'fill', attrValue: 'none' },
{ method: 'setStyle', el: progressArc, attr: 'stroke-linecap', attrValue: 'round' },
{ method: 'setStyle', el: progressArc, attr: 'stroke', attrValue: color ? color : this.currentColor }
];
setProgressBarStyles(indeterminateStyles, this.renderer);
this.renderer.removeClass(progressArc, 'k-circular-progressbar-arc');
progressArc.innerHTML = `<animateTransform attributeName="transform" type="rotate" from="${rotate.from} 0 0" to="${rotate.to} 0 0" dur="1s" repeatCount="indefinite" />`;
}
else {
this.renderer.addClass(progressArc, 'k-circular-progressbar-arc');
const removeIndeterminateStyles = [
{ method: 'removeStyle', el: progressArc, attr: 'transform-origin' },
{ method: 'removeStyle', el: progressArc, attr: 'fill' },
{ method: 'removeStyle', el: progressArc, attr: 'stroke-linecap' }
];
removeProgressBarStyles(removeIndeterminateStyles, this.renderer);
progressArc.innerHTML = '';
if (this.animation) {
this.progressbarAnimation();
}
}
}
updateCenterTemplate(value) {
if (!this.centerTemplate) {
return;
}
this.centerTemplateContext.value = value;
this.centerTemplateContext.color = this.currentColor;
this.cdr.detectChanges();
this.positionLabel();
}
positionLabel() {
const labelEl = this.labelElement.nativeElement;
const element = this.element.nativeElement;
const surface = this.surface.nativeElement;
let elWidth;
let elHeight;
if (!hasElementSize(element)) {
const surfaceSize = surface.getBoundingClientRect();
elWidth = surfaceSize.width;
elHeight = surfaceSize.height;
}
else {
const elementSize = element.getBoundingClientRect();
elWidth = elementSize.width;
elHeight = elementSize.height;
}
const left = (elWidth / 2) - (labelEl.offsetWidth / 2);
const top = (elHeight / 2) - (labelEl.offsetHeight / 2);
const labelCalculations = [
{ method: 'setStyle', el: labelEl, attr: 'left', attrValue: `${left}px` },
{ method: 'setStyle', el: labelEl, attr: 'top', attrValue: `${top}px` }
];
setProgressBa