import {NgModule,Component,ElementRef,OnInit,Input,Output,EventEmitter,forwardRef,ViewChild} from '@angular/core'; import {CommonModule} from '@angular/common'; import {InputTextModule} from '../inputtext/inputtext'; import {DomHandler} from '../dom/domhandler'; import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms'; export const SPINNER_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Spinner), multi: true }; @Component({ selector: 'p-spinner', template: ` `, host: { '[class.ui-inputwrapper-filled]': 'filled', '[class.ui-inputwrapper-focus]': 'focus' }, providers: [DomHandler,SPINNER_VALUE_ACCESSOR] }) export class Spinner implements OnInit,ControlValueAccessor { @Output() onChange: EventEmitter = new EventEmitter(); @Output() onFocus: EventEmitter = new EventEmitter(); @Output() onBlur: EventEmitter = new EventEmitter(); @Input() step: number = 1; @Input() min: number; @Input() max: number; @Input() maxlength: number; @Input() size: number; @Input() placeholder: string; @Input() inputId: string; @Input() disabled: boolean; @Input() readonly: boolean; @Input() decimalSeparator: string = '.'; @Input() thousandSeparator: string = ','; @Input() tabindex: number; @Input() formatInput: boolean = true; @Input() type: string = 'text'; @Input() required: boolean; @Input() name: string; @Input() inputStyle: string; @Input() inputStyleClass: string; value: number; valueAsString: string = ''; onModelChange: Function = () => {}; onModelTouched: Function = () => {}; keyPattern: RegExp = /[0-9\+\-]/; public precision: number; public timer: any; public focus: boolean; public filled: boolean; @ViewChild('inputfield') inputfieldViewChild: ElementRef; constructor(public el: ElementRef, public domHandler: DomHandler) {} ngOnInit() { if(Math.floor(this.step) === 0) { this.precision = this.step.toString().split(/[,]|[.]/)[1].length; } } repeat(event: Event, interval: number, dir: number) { let i = interval||500; this.clearTimer(); this.timer = setTimeout(() => { this.repeat(event, 40, dir); }, i); this.spin(event, dir); } spin(event: Event, dir: number) { let step = this.step * dir; let currentValue = this.value||0; let newValue: number = null; if(this.precision) this.value = parseFloat(this.toFixed(currentValue + step, this.precision)); else this.value = currentValue + step; if(this.maxlength !== undefined && this.value.toString().length > this.maxlength) { this.value = currentValue; } if(this.min !== undefined && this.value < this.min) { this.value = this.min; } if(this.max !== undefined && this.value > this.max) { this.value = this.max; } this.formatValue(); this.onModelChange(this.value); this.onChange.emit(event); } toFixed(value: number, precision: number) { let power = Math.pow(10, precision||0); return String(Math.round(value * power) / power); } onUpButtonMousedown(event: Event) { if(!this.disabled) { this.inputfieldViewChild.nativeElement.focus(); this.repeat(event, null, 1); this.updateFilledState(); event.preventDefault(); } } onUpButtonMouseup(event: Event) { if(!this.disabled) { this.clearTimer(); } } onUpButtonMouseleave(event: Event) { if(!this.disabled) { this.clearTimer(); } } onDownButtonMousedown(event: Event) { if(!this.disabled) { this.inputfieldViewChild.nativeElement.focus(); this.repeat(event, null, -1); this.updateFilledState(); event.preventDefault(); } } onDownButtonMouseup(event: Event) { if(!this.disabled) { this.clearTimer(); } } onDownButtonMouseleave(event: Event) { if(!this.disabled) { this.clearTimer(); } } onInputKeydown(event: KeyboardEvent) { if(event.which == 38) { this.spin(event, 1); event.preventDefault(); } else if(event.which == 40) { this.spin(event, -1); event.preventDefault(); } } onInputKeyPress(event: KeyboardEvent) { let inputChar = String.fromCharCode(event.charCode); if(!this.keyPattern.test(inputChar) && inputChar != this.decimalSeparator && event.keyCode != 9 && event.keyCode != 8 && event.keyCode != 37 && event.keyCode != 39 && event.keyCode != 46) { event.preventDefault(); } } onInputKeyup(event: KeyboardEvent) { let inputValue = ( event.target).value; if (event.key !== this.decimalSeparator && event.key !== this.thousandSeparator) { this.value = this.parseValue(inputValue); this.formatValue(); } this.onModelChange(this.value); this.updateFilledState(); } onInputBlur(event) { this.focus = false; this.restrictValue(); this.onModelTouched(); this.onBlur.emit(event); } onInputFocus(event) { this.focus = true; this.onFocus.emit(event); } parseValue(val: string): number { let value: number; if(this.formatInput) { val = val.split(this.thousandSeparator).join(''); } if(val.trim() === '') { value = null; } else { if(this.precision) { value = parseFloat(val.replace(',','.')); } else { value = parseInt(val); } if(isNaN(value)) { value = null; } } return value; } restrictValue() { let restricted: boolean; if(this.max !== undefined && this.value > this.max) { this.value = this.max; restricted = true; } if(this.min !== undefined && this.value < this.min) { this.value = this.min; restricted = true; } if(restricted) { this.onModelChange(this.value); this.formatValue(); } } formatValue(): void { if(this.value !== null && this.value !== undefined) { let textValue = String(this.value).replace('.', this.decimalSeparator); if(this.formatInput) { let parts = textValue.split(this.decimalSeparator); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, this.thousandSeparator); textValue = parts.join(this.decimalSeparator); } this.valueAsString = textValue; } else { this.valueAsString = ''; } this.inputfieldViewChild.nativeElement.value = this.valueAsString; } handleChange(event: Event) { this.onChange.emit(event); } clearTimer() { if(this.timer) { clearInterval(this.timer); } } writeValue(value: any) : void { this.value = value; this.formatValue(); this.updateFilledState(); } registerOnChange(fn: Function): void { this.onModelChange = fn; } registerOnTouched(fn: Function): void { this.onModelTouched = fn; } setDisabledState(val: boolean): void { this.disabled = val; } updateFilledState() { this.filled = (this.value !== undefined && this.value != null); } } @NgModule({ imports: [CommonModule,InputTextModule], exports: [Spinner], declarations: [Spinner] }) export class SpinnerModule { }