@progress/kendo-angular-inputs
Version:
Kendo UI for Angular Inputs Package - Everything you need to build professional form functionality (Checkbox, ColorGradient, ColorPalette, ColorPicker, FlatColorPicker, FormField, MaskedTextBox, NumericTextBox, RadioButton, RangeSlider, Slider, Switch, Te
1,396 lines (1,381 loc) • 774 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 i0 from '@angular/core';
import { InjectionToken, isDevMode, Directive, Optional, EventEmitter, ElementRef, Component, Input, HostBinding, Output, ViewChild, ContentChild, ViewChildren, forwardRef, Inject, Injectable, HostListener, ContentChildren, ViewContainerRef, ChangeDetectionStrategy, NgModule } from '@angular/core';
import { NgControl, NG_VALUE_ACCESSOR, NG_VALIDATORS, RadioControlValueAccessor } from '@angular/forms';
import { Subscription, fromEvent, interval, merge, BehaviorSubject, Subject } from 'rxjs';
import { take, tap, filter, concatMap, startWith, takeUntil, skip, debounceTime, throttleTime } from 'rxjs/operators';
import * as i1 from '@progress/kendo-angular-l10n';
import { ComponentMessages, LocalizationService, L10N_PREFIX, RTL } from '@progress/kendo-angular-l10n';
import * as i7 from '@progress/kendo-angular-common';
import { Keys, guid, anyChanged, isDocumentAvailable, hasObservers, KendoInput, EventsOutsideAngularDirective, DraggableDirective, ResizeSensorComponent, isObjectPresent, removeHTMLAttributes, parseAttributes, isControlRequired, setHTMLAttributes, SuffixTemplateDirective, PrefixTemplateDirective, isChanged, isPresent as isPresent$1, isSafari, PreventableEvent, findFocusableChild, parseCSSClassNames, closest as closest$1, SeparatorComponent, ResizeBatchService, KENDO_ADORNMENTS } from '@progress/kendo-angular-common';
export { PrefixTemplateDirective, SeparatorComponent, SuffixTemplateDirective } from '@progress/kendo-angular-common';
import { validatePackage } from '@progress/kendo-licensing';
import { caretAltUpIcon, caretAltDownIcon, caretAltLeftIcon, caretAltRightIcon, checkIcon, exclamationCircleIcon, xIcon, caretAltExpandIcon, xCircleIcon, dropletSlashIcon, dropletSliderIcon, paletteIcon, starIcon, starOutlineIcon, hyperlinkOpenIcon } from '@progress/kendo-svg-icons';
import { NgFor, NgClass, NgSwitch, NgSwitchCase, NgTemplateOutlet, NgIf, NgStyle } from '@angular/common';
import { ButtonComponent } from '@progress/kendo-angular-buttons';
import { browser, mobileOS } from '@progress/kendo-common';
import * as i1$1 from '@progress/kendo-angular-intl';
import { IconWrapperComponent, IconsService } from '@progress/kendo-angular-icons';
import { parseColor as parseColor$1, Color, namedColors } from '@progress/kendo-drawing';
import * as i1$2 from '@progress/kendo-angular-popup';
import { PopupService } from '@progress/kendo-angular-popup';
import * as i3 from '@progress/kendo-angular-utils';
import { AdaptiveService } from '@progress/kendo-angular-utils';
import { ActionSheetComponent, ActionSheetTemplateDirective } from '@progress/kendo-angular-navigation';
import { SignaturePad } from '@progress/kendo-inputs-common';
import { DialogComponent, DialogContainerService, DialogService, WindowService, WindowContainerService } from '@progress/kendo-angular-dialog';
/**
* @hidden
*
* Checks if the value is `null` or `undefined`. Falsy values like '', 0, false, NaN, etc. are regarded as present.
*/
const isPresent = (value) => value !== null && value !== undefined;
/**
* @hidden
*/
const areSame = (value1, value2) => value1 === value2 || (value1 === null && value2 === undefined) || (value1 === undefined && value2 === null);
/**
* @hidden
*/
const requiresZoneOnBlur = (ngControl) => ngControl &&
(!ngControl.touched || (ngControl.control && ngControl.control.updateOn === 'blur'));
/**
* @hidden
*
* Fits the contender number into the specified bounds. If the number is NaN or null, the min is returned.
*
* @param contender Represents the number you want to fit into specified bounds.
* @param min The inclusive lower bound number.
* @param max The inclusive upper bound number.
*/
const fitIntoBounds = (contender, min, max) => {
if (!isPresent(contender) || isNaN(contender)) {
return min;
}
return contender <= min ? min : contender >= max ? max : contender;
};
/**
* @hidden
*/
const SIZE_MAP = {
small: 'sm',
medium: 'md',
large: 'lg'
};
/**
* @hidden
*/
const ROUNDED_MAP = {
small: 'sm',
medium: 'md',
large: 'lg',
full: 'full'
};
/**
* @hidden
*/
const isNone = (style) => style === 'none';
/**
* @hidden
*
* Returns the styling classes to be added and removed
*/
const getStylingClasses = (componentType, stylingOption, previousValue, newValue) => {
switch (stylingOption) {
case 'size':
return {
toRemove: `k-${componentType}-${SIZE_MAP[previousValue]}`,
toAdd: newValue !== 'none' ? `k-${componentType}-${SIZE_MAP[newValue]}` : ''
};
case 'rounded':
return {
toRemove: `k-rounded-${ROUNDED_MAP[previousValue]}`,
toAdd: newValue !== 'none' ? `k-rounded-${ROUNDED_MAP[newValue]}` : ''
};
case 'fillMode':
return {
toRemove: `k-${componentType}-${previousValue}`,
toAdd: newValue !== 'none' ? `k-${componentType}-${newValue}` : ''
};
default:
break;
}
};
/**
* @hidden
*
* Used to differentiate between the Radio and CheckBox components in their base class.
*/
const COMPONENT_TYPE = new InjectionToken('TYPE_TOKEN');
/**
* @hidden
*/
const replaceMessagePlaceholder = (message, name, value) => message.replace(new RegExp(`{\\s*${name}\\s*}`, 'g'), value);
/**
* @hidden
*/
const MAX_PRECISION = 20;
/**
* @hidden
*/
const limitPrecision = (precision) => Math.min(precision, MAX_PRECISION);
/**
* @hidden
*/
const fractionLength = (value) => {
return (String(value).split('.')[1] || "").length;
};
const maxFractionLength = (value1, value2) => {
return Math.max(fractionLength(value1), fractionLength(value2));
};
/**
* @hidden
*/
const toFixedPrecision = (value, precision) => {
const maxPrecision = limitPrecision(precision);
return parseFloat(value.toFixed(maxPrecision));
};
/**
* @hidden
*/
const add = (value1, value2) => {
const maxPrecision = maxFractionLength(value1, value2);
return toFixedPrecision(value1 + value2, maxPrecision);
};
/**
* @hidden
*/
const subtract = (value1, value2) => {
return add(value1, -value2);
};
/**
* @hidden
*/
const multiply = (value1, value2) => {
const maxPrecision = fractionLength(value1) + fractionLength(value2);
return toFixedPrecision(value1 * value2, maxPrecision);
};
/**
* @hidden
*/
const divide = (dividend, divisor) => {
if (divisor === 0) {
return NaN;
}
const power = maxFractionLength(dividend, divisor);
const correctionValue = Math.pow(10, power);
return ((correctionValue * dividend) / (correctionValue * divisor));
};
/**
* @hidden
*/
const remainder = (dividend, divisor) => {
return Math.abs(subtract(dividend, multiply(divisor, Math.floor(divide(dividend, divisor)))));
};
/**
* @hidden
*/
const calculateFixedTrackSize = ({ max, min, smallStep, fixedTickWidth }) => ((max - min) / smallStep) * fixedTickWidth;
/**
* @hidden
*/
const calculateTicksCount = (min = 0, max = 0, smallStep = 1) => {
if (smallStep <= 0) {
throw new Error('Invalid argument: smallStep must be a positive number');
}
const adjustedRange = Math.abs(subtract(max, min));
const adjustedRatio = Math.floor(divide(adjustedRange, smallStep));
const result = add(adjustedRatio, 1);
return result;
};
/**
* @hidden
*/
const calculateValueFromTick = (index, { max, min, smallStep, reverse, vertical }) => {
const value = add(min, multiply(index, smallStep));
return vertical || reverse ? Math.abs(subtract(value, max)) : value;
};
/**
* @hidden
*/
const calculateHandlePosition = ({ trackWidth, min, max, value }) => {
const step = trackWidth / Math.abs(max - min);
const pos = isPresent(value) ? step * (value - min) : min;
return Math.floor(pos);
};
/**
* @hidden
*/
const decreaseValueToStep = (value, { max, min, smallStep, largeStep }, large = false) => {
const step = large && largeStep ? multiply(smallStep, largeStep) : smallStep;
const stepValue = subtract(value, min);
let result;
const stepRemainder = remainder(stepValue, step);
if (stepRemainder === 0) {
result = subtract(stepValue, step);
}
else {
result = subtract(stepValue, stepRemainder);
}
return limitValue(add(result, min), min, max);
};
/**
* @hidden
*/
const increaseValueToStep = (value, { max, min, smallStep, largeStep }, large = false) => {
const step = large && largeStep ? multiply(smallStep, largeStep) : smallStep;
const stepValue = subtract(value, min);
const stepRemainder = remainder(stepValue, step);
const result = add(subtract(stepValue, stepRemainder), step);
return limitValue(add(result, min), min, max);
};
/**
* @hidden
*/
const isStartHandle = (dragHandle) => dragHandle.id.indexOf('k-start-handle') > -1;
/**
* @hidden
*/
const snapValue = (value, options) => {
const { smallStep, min, max } = options;
const limited = limitValue(value, min, max);
if (value !== limited) {
return limited;
}
const left = decreaseValueToStep(value, options);
const right = increaseValueToStep(value, options);
if ((value - min) % smallStep === 0) {
return value;
}
if (right - value <= (right - left) / 2) {
return right;
}
return left;
};
/**
* @hidden
*/
const trimValue = (max, min, value) => {
if (value > max) {
return max;
}
if (value < min) {
return min;
}
return value;
};
/**
* @hidden
*/
const trimValueRange = (max, min, value) => {
return value ? [trimValue(max, min, value[0]), trimValue(max, min, value[1])] : [min, min];
};
/**
* @hidden
*/
const identity = (value) => value;
/**
* @hidden
*/
const isSameRange = (value1, value2) => areSame(value1[0], value2[0]) && areSame(value1[1], value2[1]);
/**
* @hidden
*/
const elementOffset = (element) => {
const box = element.getBoundingClientRect();
const documentElement = document.documentElement;
return {
left: box.left + (window.pageXOffset || documentElement.scrollLeft) - (documentElement.clientLeft || 0),
top: box.top + (window.pageYOffset || documentElement.scrollTop) - (documentElement.clientTop || 0)
};
};
/**
* @hidden
*/
const limitValue = (value, min, max) => {
return Math.max(Math.min(value, max), min);
};
/**
* @hidden
*/
const eventValue = (eventArgs, scaleElement, options) => {
const { min, max, vertical, rtl } = options;
const trackOffset = elementOffset(scaleElement);
const offset = vertical ? eventArgs.pageY - trackOffset.top : eventArgs.pageX - trackOffset.left;
const scale = (max - min) / (vertical ? scaleElement.clientHeight : scaleElement.clientWidth);
const offsetValue = offset * scale;
let value = rtl || vertical ? max - offsetValue : min + offsetValue;
const stepFractionLength = fractionLength(options.smallStep);
value = toFixedPrecision(value, stepFractionLength + 1);
return snapValue(value, options);
};
/**
* @hidden
*/
const isButton = (element) => {
return element.className.indexOf('k-button-increase') >= 0 || element.className.indexOf('k-button-decrease') >= 0;
};
/**
* @hidden
*/
const increment = (options) => {
return increaseValueToStep(options.value, options);
};
/**
* @hidden
*/
const decrement = (options) => {
return decreaseValueToStep(options.value, options);
};
/**
* @hidden
*/
const incrementLarge = (options) => {
return increaseValueToStep(options.value, options, true);
};
/**
* @hidden
*/
const decrementLarge = (options) => {
return decreaseValueToStep(options.value, options, true);
};
/**
* @hidden
*/
const validateValue = (value) => {
if (isDevMode && value && value[0] > value[1]) {
throw new Error('[RangeSlider] The start value should not be greater than the end value.');
}
};
/**
* @hidden
*/
var slidersUtil = {
calculateFixedTrackSize,
calculateValueFromTick,
calculateTicksCount,
calculateHandlePosition,
decreaseValueToStep,
decrement,
decrementLarge,
eventValue,
identity,
increment,
incrementLarge,
isButton,
isSameRange,
isStartHandle,
increaseValueToStep,
trimValue,
trimValueRange,
snapValue,
validateValue
};
/**
* @hidden
*/
class SliderModelBase {
props;
wrapper;
track;
renderer;
button;
tickSizes;
constructor(props, wrapper, track, renderer, button) {
this.props = props;
this.wrapper = wrapper;
this.track = track;
this.renderer = renderer;
this.button = button;
this.props = props;
this.wrapper = wrapper;
this.track = track;
this.tickSizes = this.getTickSizes();
}
resizeTrack() {
const orientation = this.props.vertical ? 'height' : 'width';
const altOrientation = this.props.vertical ? 'width' : 'height';
const trackWidth = this.trackWidth();
this.track.parentElement.style[orientation] = `${trackWidth}px`;
this.track.parentElement.style[altOrientation] = '';
}
resizeTicks(ticksContainer, ticks) {
const dimension = this.props.vertical ? "height" : "width";
[...ticks].map((tick, index) => tick.style[dimension] = `${this.tickSizes[index]}px`);
if (this.props.vertical) {
this.adjustPadding(ticksContainer);
}
}
resizeWrapper() {
const dimension = this.props.vertical ? "height" : "width";
const fixedTrackWidth = calculateFixedTrackSize(this.props);
const wrapperParentEl = this.wrapper.parentElement;
if (fixedTrackWidth) {
wrapperParentEl.style[dimension] = "auto";
}
}
trackWidth() {
if (this.props.fixedTickWidth) {
return calculateFixedTrackSize(this.props);
}
const wrapperWidth = this.elementSize(this.wrapper.parentElement);
const trackOffset = this.getTrackOffset();
return wrapperWidth - trackOffset;
}
getTickSizes() {
const { min, max, smallStep } = this.props;
const count = calculateTicksCount(min, max, smallStep);
const trackSize = this.trackWidth();
const distStep = trackSize / subtract(max, min);
const result = [];
let usedSpace = 0;
let endPoint = 0;
for (let i = 0; i < count; i++) {
if (i === 0 || i === count - 1) {
endPoint += (smallStep / 2) * distStep;
}
else {
endPoint += smallStep * distStep;
}
// ensure that the sum of the tick sizes does not exceed the track width
endPoint = +endPoint.toFixed(2) - 0.01;
const size = Math.round(endPoint - usedSpace);
result.push(size);
usedSpace += size;
}
if (usedSpace >= trackSize) {
result[result.length - 1] -= 1;
}
return result;
}
adjustPadding(ticksContainer) {
const totalTickSize = this.tickSizes.reduce((prev, curr) => prev + curr, 0);
const trackWidth = this.trackWidth();
const reminder = trackWidth - totalTickSize;
if (reminder !== 0) {
const padding = reminder + this.elementOffset(this.track);
ticksContainer.style.paddingTop = `${padding}px`;
}
}
elementOffset(element) {
const { vertical } = this.props;
const style = getComputedStyle(element);
return parseInt(vertical ? style.bottom : style.left, 10);
}
elementSize(element) {
const { vertical } = this.props;
return vertical ? element.clientHeight : element.clientWidth;
}
getTrackOffset() {
const showButtons = this.props.buttons && isPresent(this.button);
if (!showButtons) {
return 0;
}
const BUTTONS_COUNT = 2;
const buttonStyles = this.button.nativeElement.getBoundingClientRect();
const wrapperGap = parseInt(window.getComputedStyle(this.wrapper.parentElement).gap);
const buttonSize = this.props.vertical ? buttonStyles?.height : buttonStyles?.width;
return (buttonSize + wrapperGap) * BUTTONS_COUNT;
}
}
/**
* @hidden
*/
class SliderModel extends SliderModelBase {
handlePosition;
positionHandle(dragHandle) {
const { max, min, reverse, vertical } = this.props;
const position = vertical ? 'bottom' : reverse ? 'right' : 'left';
const trackWidth = this.trackWidth();
const value = trimValue(max, min, this.props.value);
this.handlePosition = calculateHandlePosition({
min,
max,
reverse,
value,
trackWidth
});
this.renderer.setStyle(dragHandle, position, `${this.handlePosition}px`);
}
positionSelection(selection) {
const { vertical } = this.props;
const dimension = vertical ? 'height' : 'width';
const size = this.handlePosition;
this.renderer.setStyle(selection, dimension, `${size}px`);
}
}
const UNTOUCHED = 'ng-untouched';
const toClassList = (classNames) => String(classNames).trim().split(' ');
/**
* @hidden
*/
const hasClass = (element, className) => Boolean(toClassList(element.className).find((name) => name === className));
/**
* @hidden
*/
function invokeElementMethod(element, name, ...args) {
if (element && element.nativeElement) {
// eslint-disable-next-line prefer-spread
return element.nativeElement[name].apply(element.nativeElement, args);
}
}
/**
* @hidden
*/
const isUntouched = (element) => element && element.nativeElement && hasClass(element.nativeElement, UNTOUCHED);
/**
* @hidden
*/
const containsFocus = (hostElement, contender) => hostElement && contender && (hostElement === contender || hostElement.contains(contender));
/**
* @hidden
*/
const closest = (node, predicate) => {
while (node && !predicate(node)) {
node = node.parentNode;
}
return node;
};
/**
* @hidden
*/
const packageMetadata = {
name: '@progress/kendo-angular-inputs',
productName: 'Kendo UI for Angular',
productCode: 'KENDOUIANGULAR',
productCodes: ['KENDOUIANGULAR'],
publishDate: 1743579576,
version: '18.4.0',
licensingDocsUrl: 'https://www.telerik.com/kendo-angular-ui/my-license/'
};
/**
* Represents the template for the labels of the Slider.
* To define the labels template, nest an `<ng-template>` tag with the `kendoSliderLabelTemplate` directive inside
* the `<kendo-slider>` tag. The template context is passed to the `label` value.
*
* @example
* ```ts-no-run
*
* _@Component({
* selector: 'my-app',
* template: `
* <kendo-slider [largeStep]="2">
* <ng-template kendoSliderLabelTemplate let-value="value">
* <b>{{value}}</b>
* </ng-template>
* </kendo-slider>
* `
* })
*
* class AppComponent {
* }
*
* ```
*/
class LabelTemplateDirective {
templateRef;
constructor(templateRef) {
this.templateRef = templateRef;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LabelTemplateDirective, deps: [{ token: i0.TemplateRef, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: LabelTemplateDirective, isStandalone: true, selector: "[kendoSliderLabelTemplate]", ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LabelTemplateDirective, decorators: [{
type: Directive,
args: [{
selector: '[kendoSliderLabelTemplate]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i0.TemplateRef, decorators: [{
type: Optional
}] }]; } });
/**
* @hidden
*/
class SliderBase {
localizationService;
injector;
renderer;
ngZone;
changeDetector;
hostElement;
/**
* Defines the title of the ticks ([see example]({% slug ticks_slider %}#toc-titles)). The default title
* for each tick is its Slider value. If you use a callback function, the function accepts an argument
* that holds the value of the component and returns a string with the new title.
*/
title = identity;
/**
* Denotes the location of the tick marks in the Slider ([see example]({% slug ticks_slider %}#toc-placement)).
*
* The available options are:
* * `before`—The tick marks are located to the top side of the horizontal track or to the left side of a vertical track.
* * `after`—The tick marks are located to the bottom side of the horizontal track or to the right side of the vertical track.
* * `both`— (Default) The tick marks are located on both sides of the track.
* * `none`—The tick marks are not visible. The actual elements are not added to the DOM tree.
*/
tickPlacement = 'both';
/**
* If `vertical` is set to `true`, the orientation of the Slider changes from horizontal to vertical
* ([see example]({% slug orientation_slider %})).
*/
vertical = false;
/**
* The minimum value of the Slider ([see example]({% slug predefinedsteps_slider %}#toc-small-steps)).
* The attribute accepts both integers and floating-point numbers.
*/
min = 0;
/**
* The maximum value of the Slider ([see example]({% slug predefinedsteps_slider %}#toc-small-steps)).
* The attribute accepts both integers and floating-point numbers.
*/
max = 10;
/**
* The step value of the Slider ([see example]({% slug predefinedsteps_slider %}#toc-small-steps)).
* Accepts positive values only. Can be an integer or a floating-point number.
*/
smallStep = 1;
/**
* Specifies that every n<sup>th</sup> tick will be large and will have a label
* ([see example]({% slug predefinedsteps_slider %}#toc-large-steps)).
* Accepts positive integer values only.
*/
largeStep = null;
/**
* Sets the width between each two ticks along the track ([see example]({% slug ticks_slider %}#toc-width)). The value
* has to be set in pixels. If no `fixedTickWidth` is provided, the Slider automatically adjusts the tick width to
* accommodate the elements within the size of the component.
*/
fixedTickWidth;
/**
* Determines whether the Slider is disabled ([see example]({% slug disabledstate_slider %})). To learn how to disable the component in reactive forms, refer to the article on [Forms Support](slug:formssupport_slider#toc-managing-the-slider-disabled-state-in-reactive-forms).
*/
disabled = false;
/**
* Determines whether the Slider is in its read-only state ([see example]({% slug readonly_slider %})).
*
* @default false
*/
readonly = false;
/**
* Specifies the [tabindex](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) of the Slider.
*/
tabindex = 0;
/**
* Fires each time the user focuses the component.
*/
onFocus = new EventEmitter();
/**
* Fires each time the component is blurred.
*/
onBlur = new EventEmitter();
/**
* Fires each time the user selects a new value.
*/
valueChange = new EventEmitter();
direction;
get horizontalClass() {
return !this.vertical;
}
get verticalClass() {
return this.vertical;
}
sliderClass = true;
get disabledClass() {
return this.disabled;
}
wrapper;
track;
sliderSelection;
ticksContainer;
ticks;
labelTemplate;
subscriptions = new Subscription();
isFocused;
isDragged;
control;
constructor(localizationService, injector, renderer, ngZone, changeDetector, hostElement) {
this.localizationService = localizationService;
this.injector = injector;
this.renderer = renderer;
this.ngZone = ngZone;
this.changeDetector = changeDetector;
this.hostElement = hostElement;
validatePackage(packageMetadata);
this.direction = localizationService.rtl ? 'rtl' : 'ltr';
}
/**
* @hidden
* Called when the status of the component changes to or from `disabled`.
* Depending on the value, it enables or disables the appropriate DOM element.
*
* @param isDisabled
*/
setDisabledState(isDisabled) {
this.changeDetector.markForCheck();
this.disabled = isDisabled;
}
ngOnInit() {
this.subscriptions.add(this.localizationService
.changes
.subscribe(({ rtl }) => {
this.direction = rtl ? 'rtl' : 'ltr';
this.sizeComponent();
}));
if (this.hostElement) {
this.renderer.removeAttribute(this.hostElement.nativeElement, "tabindex");
}
this.control = this.injector.get(NgControl, null);
}
/**
* @hidden
*/
get isDisabled() {
return this.disabled || this.readonly;
}
/**
* @hidden
*/
ifEnabled = (callback, event) => {
if (!this.isDisabled) {
callback.call(this, event);
}
};
/**
* @hidden
* Used by the FloatingLabel to determine if the component is empty.
*/
isEmpty() {
return false;
}
get reverse() {
return this.localizationService.rtl && !this.vertical;
}
get keyBinding() {
const reverse = this.reverse;
return {
[Keys.ArrowLeft]: reverse ? increment : decrement,
[Keys.ArrowRight]: reverse ? decrement : increment,
[Keys.ArrowDown]: decrement,
[Keys.ArrowUp]: increment,
[Keys.PageUp]: incrementLarge,
[Keys.PageDown]: decrementLarge,
[Keys.Home]: ({ min }) => min,
[Keys.End]: ({ max }) => max
};
}
resetStyles(elements) {
elements.forEach(el => {
if (el) {
if (this.vertical) {
this.renderer.removeStyle(el, 'width');
this.renderer.removeStyle(el, 'left');
this.renderer.removeStyle(el, 'right');
}
else {
this.renderer.removeStyle(el, 'height');
this.renderer.removeStyle(el, 'bottom');
}
this.renderer.removeStyle(el, 'padding-top');
}
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SliderBase, deps: [{ token: i1.LocalizationService }, { token: i0.Injector }, { token: i0.Renderer2 }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: SliderBase, selector: "kendo-slider-base", inputs: { title: "title", tickPlacement: "tickPlacement", vertical: "vertical", min: "min", max: "max", smallStep: "smallStep", largeStep: "largeStep", fixedTickWidth: "fixedTickWidth", disabled: "disabled", readonly: "readonly", tabindex: "tabindex" }, outputs: { onFocus: "focus", onBlur: "blur", valueChange: "valueChange" }, host: { properties: { "class.k-readonly": "this.readonly", "attr.dir": "this.direction", "class.k-slider-horizontal": "this.horizontalClass", "class.k-slider-vertical": "this.verticalClass", "class.k-slider": "this.sliderClass", "class.k-disabled": "this.disabledClass" } }, queries: [{ propertyName: "labelTemplate", first: true, predicate: LabelTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "wrapper", first: true, predicate: ["wrap"], descendants: true, static: true }, { propertyName: "track", first: true, predicate: ["track"], descendants: true, static: true }, { propertyName: "sliderSelection", first: true, predicate: ["sliderSelection"], descendants: true, static: true }, { propertyName: "ticksContainer", first: true, predicate: ["ticks"], descendants: true, read: ElementRef }, { propertyName: "ticks", first: true, predicate: ["ticks"], descendants: true }], ngImport: i0, template: ``, isInline: true });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SliderBase, decorators: [{
type: Component,
args: [{
selector: 'kendo-slider-base',
template: ``
}]
}], ctorParameters: function () { return [{ type: i1.LocalizationService }, { type: i0.Injector }, { type: i0.Renderer2 }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { title: [{
type: Input
}], tickPlacement: [{
type: Input
}], vertical: [{
type: Input
}], min: [{
type: Input
}], max: [{
type: Input
}], smallStep: [{
type: Input
}], largeStep: [{
type: Input
}], fixedTickWidth: [{
type: Input
}], disabled: [{
type: Input
}], readonly: [{
type: Input
}, {
type: HostBinding,
args: ['class.k-readonly']
}], tabindex: [{
type: Input
}], onFocus: [{
type: Output,
args: ['focus']
}], onBlur: [{
type: Output,
args: ['blur']
}], valueChange: [{
type: Output
}], direction: [{
type: HostBinding,
args: ['attr.dir']
}], horizontalClass: [{
type: HostBinding,
args: ['class.k-slider-horizontal']
}], verticalClass: [{
type: HostBinding,
args: ['class.k-slider-vertical']
}], sliderClass: [{
type: HostBinding,
args: ['class.k-slider']
}], disabledClass: [{
type: HostBinding,
args: ['class.k-disabled']
}], wrapper: [{
type: ViewChild,
args: ['wrap', { static: true }]
}], track: [{
type: ViewChild,
args: ['track', { static: true }]
}], sliderSelection: [{
type: ViewChild,
args: ['sliderSelection', { static: true }]
}], ticksContainer: [{
type: ViewChild,
args: ['ticks', { read: ElementRef, static: false }]
}], ticks: [{
type: ViewChild,
args: ['ticks', { static: false }]
}], labelTemplate: [{
type: ContentChild,
args: [LabelTemplateDirective, { static: false }]
}] } });
/* eslint-disable @angular-eslint/component-selector */
/**
* @hidden
*/
class SliderTick {
value;
classes = {
'k-tick': true
};
large;
constructor(value) {
this.value = value;
}
}
/**
* @hidden
*/
class SliderTicksComponent {
wrapperClasses = 'k-reset k-slider-items';
tickTitle;
vertical;
step;
largeStep;
min;
max;
labelTemplate;
tickElements;
ticks = [];
ngOnChanges(_) {
this.createTicks();
}
createTicks() {
const count = calculateTicksCount(this.min, this.max, this.step);
const largeStep = this.largeStep;
const tickValueProps = {
max: this.max,
min: this.min,
smallStep: this.step
};
const result = [];
for (let i = 0; i < count; i++) {
result.push(new SliderTick(calculateValueFromTick(i, tickValueProps)));
if (largeStep && i % largeStep === 0) {
result[i].large = true;
result[i].classes['k-tick-large'] = true;
}
}
if (result.length > 0) {
Object.assign(result[0].classes, this.endTickClasses(true));
Object.assign(result[result.length - 1].classes, this.endTickClasses(false));
}
this.ticks = result;
}
endTickClasses(first) {
return {
'k-first': (first && !this.vertical) || (!first && this.vertical),
'k-last': (!first && !this.vertical) || (first && this.vertical)
};
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SliderTicksComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: SliderTicksComponent, isStandalone: true, selector: "[kendoSliderTicks]", inputs: { tickTitle: "tickTitle", vertical: "vertical", step: "step", largeStep: "largeStep", min: "min", max: "max", labelTemplate: "labelTemplate" }, host: { properties: { "class": "this.wrapperClasses" } }, viewQueries: [{ propertyName: "tickElements", predicate: ["tickElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
<li #tickElement *ngFor="let tick of ticks;"
[ngClass]="tick.classes"
title="{{ tickTitle(tick.value) }}"
role="presentation"
>
<ng-container [ngSwitch]="tick.large">
<span class="k-label" *ngSwitchCase="true">
<ng-container [ngTemplateOutlet]="labelTemplate || defaultLabel" [ngTemplateOutletContext]="tick">
</ng-container>
</span>
<ng-container *ngSwitchCase="false"> </ng-container>
</ng-container>
</li>
<ng-template #defaultLabel let-value="value">
{{ tickTitle(value) }}
</ng-template>
`, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SliderTicksComponent, decorators: [{
type: Component,
args: [{
selector: '[kendoSliderTicks]',
template: `
<li #tickElement *ngFor="let tick of ticks;"
[ngClass]="tick.classes"
title="{{ tickTitle(tick.value) }}"
role="presentation"
>
<ng-container [ngSwitch]="tick.large">
<span class="k-label" *ngSwitchCase="true">
<ng-container [ngTemplateOutlet]="labelTemplate || defaultLabel" [ngTemplateOutletContext]="tick">
</ng-container>
</span>
<ng-container *ngSwitchCase="false"> </ng-container>
</ng-container>
</li>
<ng-template #defaultLabel let-value="value">
{{ tickTitle(value) }}
</ng-template>
`,
standalone: true,
imports: [NgFor, NgClass, NgSwitch, NgSwitchCase, NgTemplateOutlet]
}]
}], propDecorators: { wrapperClasses: [{
type: HostBinding,
args: ['class']
}], tickTitle: [{
type: Input
}], vertical: [{
type: Input
}], step: [{
type: Input
}], largeStep: [{
type: Input
}], min: [{
type: Input
}], max: [{
type: Input
}], labelTemplate: [{
type: Input
}], tickElements: [{
type: ViewChildren,
args: ['tickElement']
}] } });
/**
* @hidden
*/
class SliderMessages extends ComponentMessages {
/**
* The title of the **Decrease** button of the Slider.
*/
decrement;
/**
* The title of the **Increase** button of the Slider.
*/
increment;
/**
* The title of the drag handle of the Slider.
*/
dragHandle;
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SliderMessages, deps: null, target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SliderMessages, selector: "kendo-slider-messages-base", inputs: { decrement: "decrement", increment: "increment", dragHandle: "dragHandle" }, usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SliderMessages, decorators: [{
type: Directive,
args: [{
// eslint-disable-next-line @angular-eslint/directive-selector
selector: 'kendo-slider-messages-base'
}]
}], propDecorators: { decrement: [{
type: Input
}], increment: [{
type: Input
}], dragHandle: [{
type: Input
}] } });
/**
* @hidden
*/
class LocalizedSliderMessagesDirective extends SliderMessages {
service;
constructor(service) {
super();
this.service = service;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedSliderMessagesDirective, deps: [{ token: i1.LocalizationService }], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: LocalizedSliderMessagesDirective, isStandalone: true, selector: "[kendoSliderLocalizedMessages]", providers: [
{
provide: SliderMessages,
useExisting: forwardRef(() => LocalizedSliderMessagesDirective)
}
], usesInheritance: true, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: LocalizedSliderMessagesDirective, decorators: [{
type: Directive,
args: [{
providers: [
{
provide: SliderMessages,
useExisting: forwardRef(() => LocalizedSliderMessagesDirective)
}
],
selector: '[kendoSliderLocalizedMessages]',
standalone: true
}]
}], ctorParameters: function () { return [{ type: i1.LocalizationService }]; } });
/* eslint-disable @typescript-eslint/no-explicit-any */
const PRESSED$1 = 'k-pressed';
/**
* Represents the [Kendo UI Slider component for Angular]({% slug overview_slider %}).
*/
class SliderComponent extends SliderBase {
localization;
injector;
renderer;
ngZone;
changeDetector;
hostElement;
/**
* @hidden
*/
focusableId = `k-${guid()}`;
/**
* Changes the `title` attribute of the drag handle so that it can be localized.
*/
dragHandleTitle;
/**
* Sets the title of the **Increase** button of the Slider ([see example]({% slug sidebuttons_slider %}#toc-titles)).
*/
incrementTitle;
/**
* Determines if the animation will be played on value change.
* Regardless of this setting, no animation will be played during the initial rendering.
*/
animate = true;
/**
* Sets the title of the **Decrease** button of the Slider ([see example]({% slug sidebuttons_slider %}#toc-titles)).
*/
decrementTitle;
/**
* Renders the arrow side buttons of the Slider ([see example]({% slug sidebuttons_slider %}#toc-hidden-state)).
* When `showButtons` is set to `false`, the buttons are not displayed.
*/
showButtons = true;
/**
* The current value of the Slider when it is initially displayed.
* The component can use either NgModel or the `value` binding but not both of them at the same time.
*/
value = this.min;
/**
* @hidden
*/
set tabIndex(tabIndex) {
this.tabindex = tabIndex;
}
get tabIndex() {
return this.tabindex;
}
/**
* @hidden
*/
get currentValue() {
return isPresent(this.value) ? this.value.toString() : '';
}
/**
* @hidden
*/
arrowUpIcon = caretAltUpIcon;
/**
* @hidden
*/
arrowDownIcon = caretAltDownIcon;
/**
* @hidden
*/
arrowLeftIcon = caretAltLeftIcon;
/**
* @hidden
*/
arrowRightIcon = caretAltRightIcon;
draghandle;
decreaseButton;
increaseButton;
focusChangedProgrammatically = false;
isInvalid;
constructor(localization, injector, renderer, ngZone, changeDetector, hostElement) {
super(localization, injector, renderer, ngZone, changeDetector, hostElement);
this.localization = localization;
this.injector = injector;
this.renderer = renderer;
this.ngZone = ngZone;
this.changeDetector = changeDetector;
this.hostElement = hostElement;
}
/**
* Focuses the Slider.
*
* @example
* ```ts-no-run
* _@Component({
* selector: 'my-app',
* template: `
* <button (click)="slider.focus()">Focus</button>
* <kendo-slider #slider></kendo-slider>
* `
* })
* class AppComponent { }
* ```
*/
focus() {
if (!this.disabled) {
this.focusChangedProgrammatically = true;
invokeElementMethod(this.draghandle, 'focus');
this.focusChangedProgrammatically = false;
}
}
/**
* Blurs the Slider.
*/
blur() {
this.focusChangedProgrammatically = true;
invokeElementMethod(this.draghandle, 'blur');
this.handleBlur();
this.focusChangedProgrammatically = false;
}
ngOnChanges(changes) {
if (anyChanged(['value', 'fixedTickWidth', 'tickPlacement'], changes, true)) {
this.ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
this.sizeComponent(false);
});
}
}
ngAfterViewInit() {
if (!isDocumentAvailable()) {
return;
}
if (this.showButtons) {
this.setValueChangeInterval(this.increaseButton.nativeElement, () => this.increaseValue());
this.setValueChangeInterval(this.decreaseButton.nativeElement, () => this.decreaseValue());
}
this.sizeComponent(false);
if (this.ticks) {
this.ticks.tickElements
.changes
.subscribe(() => this.sizeComponent(false));
}
this.attachElementEventHandlers();
this.isSliderInvalid();
}
ngOnDestroy() {
if (this.subscriptions) {
this.subscriptions.unsubscribe();
}
}
/**
* @hidden
*/
get incrementMessage() {
return this.incrementTitle || this.localizationService.get('increment');
}
/**
* @hidden
*/
get decrementMessage() {
return this.decrementTitle || this.localizationService.get('decrement');
}
/**
* @hidden
*/
get dragHandleMessage() {
return this.dragHandleTitle || this.localizationService.get('dragHandle');
}
/**
* @hidden
*/
onWrapClick = (args) => {
const target = args.target;
if (!this.isDisabled && !(target.closest('.k-button'))) {
const value = eventValue(args, this.track.nativeElement, this.getProps());
this.changeValue(value);
}
invokeElementMethod(this.draghandle, 'focus');
};
/**
* @hidden
*/
handleDragPress(args) {
if (args.originalEvent) {
args.originalEvent.preventDefault();
}
this.renderer.removeClass(this.hostElement.nativeElement, 'k-slider-transitions');
}
/**
* @hidden
*/
onHandleDrag(args) {
this.dragging = true;
this.changeValue(eventValue(args, this.track.nativeElement, this.getProps()));
}
/**
* @hidden
*/
onKeyDown = (e) => {
const options = this.getProps();
const { max, min } = options;
const handler = this.keyBinding[e.keyCode];
if (this.isDisabled || !handler) {
return;
}
const value = handler(options);
this.changeValue(trimValue(max, min, value));
e.preventDefault();
};
/**
* @hidden
*/
onHandleRelease() {
this.dragging = false; //needed for animation
this.renderer.addClass(this.hostElement.nativeElement, 'k-slider-transitions');
}
//ngModel binding
/**
* @hidden
*/
writeValue(value) {
this.changeDetector.markForCheck();
this.value = value;
this.sizeComponent(this.animate);
}
/**
* @hidden
*/
registerOnChange(fn) {
this.ngChange = fn;
}
/**
* @hidden
*/
registerOnTouched(fn) {
this.ngTouched = fn;
}
/**
* @hidden
*/
changeValue(value) {
if (!areSame(this.value, value)) {
this.ngZone.run(() => {
this.value = value;
this.ngChange(value);
this.valueChange.emit(value);
this.sizeComponent(this.animate);
this.changeDetector.markForCheck();
});
}
this.isSliderInvalid();
}
/**
* @hidden
*/
sizeComponent(animate) {
if (!isDocumentAvailable()) {
return;
}
const wrapper = this.wrapper.nativeElement;
const track = this.track.nativeElement;
const selectionEl = this.sliderSelection.nativeElement;
const dragHandleEl = this.draghandle.nativeElement;
const ticks = this.ticks ? this.ticksContainer.nativeElement : null;
if (!animate) {
this.renderer.removeClass(this.hostElement.nativeElement, 'k-slider-transitions');
}
this.resetStyles([track, selectionEl, dragHandleEl, ticks, this.hostElement.nativeElement]);
const props = this.getProps();
const model = new SliderModel(props, wrapper, track, this.renderer, this.increaseButton);
model.resizeTrack();
if (this.ticks) { //for case when tickPlacement: none
model.resizeTicks(this.ticksContainer.nativeElement, this.ticks.tickElements.map(element => element.nativeElement));
}
model.positionHandle(dragHandleEl);
model.positionSelection(selectionEl);
if (!animate) {
this.hostElement.nativeElement.getBoundingClientRect();
this.renderer.addClass(this.hostElement.nativeElement, 'k-slider-transitions');
}
if (this.fixedTickWidth) {
model.resizeWrapper();
}
}
set focused(value) {
if (this.isFocused !== value && this.hostElement) {
this.isFocused = value;
}
}
set dragging(value) {
if (this.isDragged !== value && this.sliderSelection && this.draghandle) {
const sliderSelection = this.sliderSelection.nativeElement;
const draghandle = this.draghandle.nativeElement;
if (value) {
this.renderer.addClass(sliderSelection, PRESSED$1);
this.renderer.addClass(draghandle, PRESSED$1);
}
else {
this.renderer.removeClass(sliderSelection, PRESSED$1);
this.renderer.removeClass(draghandle, PRESSED$1);
}
this.isDragged = value;
}
}
setValueChangeInterval(element, callback) {
this.ngZone.runOutsideAngular(() => {
const pointerdown = fromEvent(element, 'pointerdown');
const pointerup = fromEvent(element, 'pointerup');
const pointerout = fromEvent(element, 'pointerout');
const subscription = pointerdown.pipe(tap((e) => e.preventDefault()), filter((e) => e.button === 0 && !this.isDisabled), concatMap(() => interval(150).pipe(startWith(-1), takeUntil(merge(pointerup, pointerout))))).subscribe(() => {
if (!this.isFocused) {
invokeElementMethod(this.draghandle, 'focus');
}
callback();
});
this.subscriptions.add(subscription);
});
}
ngChange = (_) => { };
ngTouched = () => { };
decreaseValue = () => {
this.changeValue(decreaseValueToStep(this.value, this.getProps()));
};
increaseValue = () => {
this.changeValue(increaseValueToStep(this.value, this.getProps()));
};
getProps() {
return {
buttons: this.showButtons,
disabled: this.disabled,
fixedTickWidth: this.fixedTickWidth,
largeStep: this.largeStep,
max: this.max,
min: this.min,
readonly