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

276 lines (275 loc) 10.2 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { Directive, ElementRef, EventEmitter, HostBinding, Renderer2, Optional, Inject, Input, NgZone, forwardRef, Output, ChangeDetectorRef, Injector } from '@angular/core'; import { NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; import { hasObservers, KendoInput, isDocumentAvailable } from '@progress/kendo-angular-common'; import { requiresZoneOnBlur } from '../common/utils'; import { RTL } from '@progress/kendo-angular-l10n'; import { fromEvent } from 'rxjs'; import { debounceTime, take } from 'rxjs/operators'; import * as i0 from "@angular/core"; /** * Represents the [Kendo UI TextArea directive for the Inputs components for Angular]({% slug overview_textarea %}). * Provides floating labels to `textarea` elements. * * @example * ```ts-no-run * <textarea kendoTextArea></textarea> * ``` */ export class TextAreaDirective { renderer; element; zone; changeDetector; injector; elementClasses = true; autofillClass = true; direction; /** * Fires each time the textarea value is changed. */ valueChange = new EventEmitter(); /** * Specifies if the `textarea` element will resize its height automatically * ([see example](slug:textarea_sizing#toc-auto-resizing)). * * @default false */ autoSize = false; /** * Specifies the textarea value. */ value; /** * @hidden */ onFocus = new EventEmitter(); /** * @hidden */ onBlur = new EventEmitter(); /** * @hidden */ onValueChange = new EventEmitter(); /** * @hidden */ autoFillStart = new EventEmitter(); /** * @hidden */ autoFillEnd = new EventEmitter(); get id() { return this.element.nativeElement.id; } set id(id) { this.renderer.setAttribute(this.element.nativeElement, 'id', id); } listeners = []; inputSubscription; initialHeight; control; resizeSubscription; constructor(renderer, element, zone, changeDetector, injector, rtl) { this.renderer = renderer; this.element = element; this.zone = zone; this.changeDetector = changeDetector; this.injector = injector; this.direction = rtl ? 'rtl' : 'ltr'; } /** * @hidden */ writeValue(value) { this.elementValue = value; this.resize(); } /** * @hidden */ registerOnChange(fn) { this.ngChange = fn; } /** * @hidden */ registerOnTouched(fn) { this.ngTouched = fn; } /** * @hidden */ setDisabledState(isDisabled) { this.setElementProperty('disabled', isDisabled); } ngOnInit() { const element = this.element.nativeElement; this.zone.runOutsideAngular(() => { this.listeners = [ this.renderer.listen(element, 'focus', this.handleFocus.bind(this)), this.renderer.listen(element, 'blur', this.handleBlur.bind(this)), this.renderer.listen(element, 'animationstart', (e) => { if (e.animationName === 'autoFillStart') { this.autoFillStart.emit(); } else if (e.animationName === 'autoFillEnd') { this.autoFillEnd.emit(); } }) ]; if (isDocumentAvailable() && this.autoSize) { this.resizeSubscription = fromEvent(window, 'resize') .pipe((debounceTime(50))) .subscribe(() => this.resize()); } this.inputSubscription = fromEvent(element, 'input') .subscribe(this.handleInput.bind(this)); }); this.control = this.injector.get(NgControl, null); } ngOnChanges(changes) { const element = this.element.nativeElement; if (changes.value) { this.elementValue = this.value; } if (changes.autoSize) { if (this.autoSize) { this.initialHeight = element.offsetHeight; this.renderer.setStyle(element, 'resize', 'none'); } else { this.renderer.setStyle(element, 'overflow-y', 'auto'); this.renderer.setStyle(element, 'resize', 'both'); element.style.height = `${this.initialHeight}px`; } } this.zone.onStable.pipe(take(1)).subscribe(() => this.resize()); } ngOnDestroy() { this.listeners.forEach(listener => listener()); if (this.inputSubscription) { this.inputSubscription.unsubscribe(); } if (this.resizeSubscription) { this.resizeSubscription.unsubscribe(); } } ngChange = (_) => { }; ngTouched = () => { }; get elementValue() { if (this.element) { return this.element.nativeElement.value; } return ''; } set elementValue(value) { this.setElementProperty('value', (value === undefined || value === null) ? '' : value); } setElementProperty(name, value) { if (this.element) { this.renderer.setProperty(this.element.nativeElement, name, value); } } resize() { if (!this.autoSize) { return; } const element = this.element.nativeElement; this.renderer.setStyle(element, 'overflow-y', 'hidden'); element.style.height = `${this.initialHeight}px`; const scrollHeight = element.scrollHeight; if (scrollHeight > this.initialHeight) { element.style.height = `${scrollHeight}px`; } } handleInput() { const value = this.elementValue; this.value = value; if (this.control || hasObservers(this.onValueChange) || hasObservers(this.valueChange)) { this.zone.run(() => { this.ngChange(value); this.onValueChange.emit(value); this.valueChange.emit(value); this.changeDetector.markForCheck(); }); } this.resize(); } handleFocus() { if (hasObservers(this.onFocus)) { this.zone.run(() => { this.onFocus.emit(); }); } } handleBlur() { if (hasObservers(this.onBlur) || requiresZoneOnBlur(this.control)) { this.zone.run(() => { this.ngTouched(); this.onBlur.emit(); this.changeDetector.markForCheck(); }); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TextAreaDirective, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i0.Injector }, { token: RTL, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: TextAreaDirective, isStandalone: true, selector: "textarea[kendoTextArea]", inputs: { autoSize: "autoSize", value: "value" }, outputs: { valueChange: "valueChange" }, host: { properties: { "class.k-textarea": "this.elementClasses", "class.k-input": "this.elementClasses", "class.k-input-md": "this.elementClasses", "class.k-rounded-md": "this.elementClasses", "class.k-input-solid": "this.elementClasses", "class.k-autofill": "this.autofillClass", "attr.dir": "this.direction" } }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextAreaDirective), multi: true }, { provide: KendoInput, useExisting: forwardRef(() => TextAreaDirective) }], usesOnChanges: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TextAreaDirective, decorators: [{ type: Directive, args: [{ providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextAreaDirective), multi: true }, { provide: KendoInput, useExisting: forwardRef(() => TextAreaDirective) }], selector: 'textarea[kendoTextArea]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i0.Injector }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [RTL] }] }]; }, propDecorators: { elementClasses: [{ type: HostBinding, args: ['class.k-textarea'] }, { type: HostBinding, args: ['class.k-input'] }, { type: HostBinding, args: ['class.k-input-md'] }, { type: HostBinding, args: ['class.k-rounded-md'] }, { type: HostBinding, args: ['class.k-input-solid'] }], autofillClass: [{ type: HostBinding, args: ['class.k-autofill'] }], direction: [{ type: HostBinding, args: ['attr.dir'] }], valueChange: [{ type: Output }], autoSize: [{ type: Input }], value: [{ type: Input }] } });