ng-ytl-zorro-antd
Version:
An enterprise-class UI components based on Ant Design and Angular
282 lines (251 loc) • 8.28 kB
text/typescript
import { TAB } from '@angular/cdk/keycodes';
import {
forwardRef,
Component,
ElementRef,
EventEmitter,
HostBinding,
Input,
Output,
Renderer2,
ViewChild,
ViewEncapsulation,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { toBoolean } from '../util/convert';
export class NzInputNumberComponent implements ControlValueAccessor {
private _disabled = false;
_el: HTMLElement;
_value: number;
_size = 'default';
_prefixCls = 'ant-input-number';
_step = 1;
_precisionStep = 0;
_precisionFactor = 1;
_displayValue;
_disabledUp = false;
_disabledDown = false;
_focused = false;
_mouseInside = false;
_allowClear = false;
// ngModel Access
onChange: (value: number) => void = () => null;
onTouched: () => void = () => null;
_inputNumber: ElementRef;
nzPlaceHolder = '';
nzMin: number = -Infinity;
nzMax: number = Infinity;
nzFormatter = (value) => value;
nzParser = (value) => value;
set nzDisabled(value: boolean) {
this._disabled = toBoolean(value);
}
get nzDisabled(): boolean {
return this._disabled;
}
set nzAllowClear(value: boolean) {
this._allowClear = toBoolean(value);
}
get nzAllowClear(): boolean {
return this._allowClear;
}
set nzSize(value: string) {
this._renderer.removeClass(this._el, `${this._prefixCls}-${this._size}`);
this._size = { large: 'lg', small: 'sm' }[ value ];
this._renderer.addClass(this._el, `${this._prefixCls}-${this._size}`);
}
get nzSize(): string {
return this._size;
}
set nzStep(value: number) {
this._step = value;
const stepString = value.toString();
if (stepString.indexOf('e-') >= 0) {
this._precisionStep = parseInt(stepString.slice(stepString.indexOf('e-')), 10);
}
if (stepString.indexOf('.') >= 0) {
this._precisionStep = stepString.length - stepString.indexOf('.') - 1;
}
this._precisionFactor = Math.pow(10, this._precisionStep);
}
get nzStep(): number {
return this._step;
}
// TODO: should reconsider the payload
nzBlur: EventEmitter<FocusEvent | KeyboardEvent> = new EventEmitter();
nzFocus: EventEmitter<FocusEvent> = new EventEmitter();
_numberUp($event: MouseEvent): void {
$event.preventDefault();
$event.stopPropagation();
this._inputNumber.nativeElement.focus();
if (this.nzValue === undefined) {
this.nzValue = this.nzMin || 0;
}
if (!this._disabledUp) {
this.nzValue = this.toPrecisionAsStep((this._precisionFactor * this.nzValue + this._precisionFactor * this.nzStep) / this._precisionFactor);
}
}
_numberDown($event: MouseEvent): void {
$event.preventDefault();
$event.stopPropagation();
this._inputNumber.nativeElement.focus();
if (this.nzValue === undefined) {
this.nzValue = this.nzMin || 0;
}
if (!this._disabledDown) {
this.nzValue = this.toPrecisionAsStep((this._precisionFactor * this.nzValue - this._precisionFactor * this.nzStep) / this._precisionFactor);
}
}
get nzValue(): number {
return this._value;
}
set nzValue(value: number) {
this._updateValue(value);
}
_emitBlur($event: FocusEvent): void {
// avoid unnecessary events
if (this._focused && !this._mouseInside) {
this._checkValue();
this._focused = false;
this.nzBlur.emit($event);
}
this.onTouched();
}
_emitFocus($event: FocusEvent): void {
// avoid unnecessary events
if (!this._focused) {
this._focused = true;
this.nzFocus.emit($event);
}
}
_emitKeyDown($event: KeyboardEvent): void {
if ($event.keyCode === TAB && this._focused) {
this._checkValue();
this._focused = false;
this.nzBlur.emit($event);
}
}
_isEmpty(value?: number | string | undefined | null): boolean {
return value === undefined
|| value === null
|| (typeof value === 'string' && this._displayValue.trim() === '')
|| (typeof value === 'string' && this.nzFormatter('') === value);
}
_checkValue(): void {
if (this.nzAllowClear && this._isEmpty(this._displayValue)) {
this.nzValue = undefined;
return;
}
const numberValue = +this.nzParser(this._displayValue);
if (this._isNumber(numberValue)) {
this.nzValue = numberValue;
} else {
this._displayValue = this.nzFormatter(this._value);
this._inputNumber.nativeElement.value = this.nzFormatter(this._value);
}
}
_getBoundValue(value: number): number {
if (this.nzAllowClear && (value === undefined)) {
return value;
} else if (value > this.nzMax) {
return this.nzMax;
} else if (value < this.nzMin) {
return this.nzMin;
} else {
return value;
}
}
_isNumber(value: number): boolean {
return !isNaN(value) && isFinite(value);
}
// TODO: normalize value type on @Input
toPrecisionAsStep(num: number): number {
const input = num as number | string;
if (isNaN(num) || input === '') {
return num;
}
return Number(Number(num).toFixed(this._precisionStep));
}
constructor(private _elementRef: ElementRef, private _renderer: Renderer2) {
this._el = this._elementRef.nativeElement;
this._renderer.addClass(this._el, `${this._prefixCls}`);
}
writeValue(value: number): void {
this._updateValue(value, false);
}
registerOnChange(fn: (_: number) => void): void {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.nzDisabled = isDisabled;
}
private _updateValue(value: number, emitChange: boolean = true): void {
const cacheValue = this._value;
this._value = this._getBoundValue(value);
this._displayValue = this.nzFormatter(this._isNumber(this._value) ? this._value : '');
this._inputNumber.nativeElement.value = this._displayValue;
if (emitChange && (value !== cacheValue)) {
this.onChange(this._value);
}
this._disabledUp = (this.nzValue !== undefined) && !((this.nzValue + this.nzStep) <= this.nzMax);
this._disabledDown = (this.nzValue !== undefined) && !((this.nzValue - this.nzStep) >= this.nzMin);
}
}