UNPKG

@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
/**----------------------------------------------------------------------------------------- * 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`&mdash;The tick marks are located to the top side of the horizontal track or to the left side of a vertical track. * * `after`&mdash;The tick marks are located to the bottom side of the horizontal track or to the right side of the vertical track. * * `both`&mdash; (Default) The tick marks are located on both sides of the track. * * `none`&mdash;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">&nbsp;</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">&nbsp;</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