import { BigNumber } from 'bignumber.js'; import { Component, forwardRef, Input, Output, AfterViewInit, ViewChild, ElementRef, OnDestroy, SimpleChanges, EventEmitter, OnInit, OnChanges } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NumberSpinnerComponent } from './number-spinner.component'; import { PopoverDirective } from '@farris/ui-popover'; @Component({ selector: 'farris-number-range', templateUrl: './number-range.component.html', styleUrls: ['./number.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NumberRangeComponent), multi: true } ] }) export class NumberRangeComponent extends NumberSpinnerComponent implements AfterViewInit, OnDestroy, OnInit, OnChanges { @Input() beginPlaceHolder = ''; @Input() endPlaceHolder = ''; @ViewChild('inputPrev') inputPrev: ElementRef; @ViewChild('inputNext') inputNext: ElementRef; @ViewChild('prevWrapper') prevWrapper: ElementRef; @ViewChild('nextWrapper') nextWrapper: ElementRef; @ViewChild('popover') popover: PopoverDirective; @Input() separator = '~'; @Input() set beginValue(value) { this.prevValue = this.format(value); this._beginValue = this.isEmpty(value) ? '' : value; } @Input() set endValue(value) { this.nextValue = this.format(value); this._endValue = this.isEmpty(value) ? '' : value; } private __value = ''; @Input() get value(): any { return this.__value; } set value(v: any) { if (v !== this.__value) { this.__value = v === '~' ? '' : v; } } @Output() beginValueChange = new EventEmitter(); @Output() endValueChange = new EventEmitter(); prevValue: any = ''; nextValue: any = ''; private _beginValue = ''; private _endValue = ''; get preNumValue() { const v = this.getRealValue(this.parser ? this.prevValue : this._beginValue); if (this.canNull && this.isEmpty(v)) { return ''; } else { return v || 0; } } get nextNumValue() { const v = this.getRealValue(this.parser ? this.nextValue : this._endValue); if (this.canNull && this.isEmpty(v)) { return ''; } else { return v || 0; } } private documentClick; private focusState = { isFocus: false, target: '' // begin | end }; ngOnInit(): void { super.ngOnInit(); this.showButton = false; this.initPlaceHolderInfo(); } ngAfterViewInit() { this.documentClick = this.render.listen(document, 'click', e => { this.render.setStyle(this.prevWrapper.nativeElement, 'flex', 1); this.render.setStyle(this.nextWrapper.nativeElement, 'flex', 1); }); } ngOnChanges(changes: SimpleChanges): void { super.ngOnChanges(changes); if (changes.value && !changes.value.isFirstChange()) { this.setValue(changes.value.currentValue); } } ngOnDestroy() { if (this.documentClick) { this.documentClick(); } } private initPlaceHolderInfo() { if (!this.beginPlaceHolder) { this.beginPlaceHolder = this.localeService.getValue('numberSpinner.range.begin') || this.placeholder; } if (!this.endPlaceHolder) { this.endPlaceHolder = this.localeService.getValue('numberSpinner.range.end') || this.placeholder; } } private getRangeValue() { return this.preNumValue + this.separator + this.nextNumValue; } private getEndValue() { return this.getRealValue(this.nextValue); } private getBeginValue() { return this.getRealValue(this.prevValue); } getRealValue(val: any) { if (this.parser) { return this.parser(val); } let _n = this.validInterval(new BigNumber(val)); if (_n.isNaN()) { const minBigNum = new BigNumber('' + this.min); if (this.canNull || minBigNum.isNaN()) { return null; } else { _n = minBigNum; } } return this._getRealValue(_n) as any; } onModelChange(val, type?: string) { if (type === 'begin') { this._beginValue = val; } else if (type === 'end') { this._endValue = val; } this.value = this.getRangeValue(); this.onChangeCallback(this.value); this.onTouchedCallback(); } onClick($event) { $event.stopPropagation(); } onBlur(e: any, type: string) { if (!this.readonly && !this.disabled) { const tmpValue = e.value; this.focusState = { isFocus: false, target: type }; if (type === 'begin') { const nextVal = this.getEndValue(); const minValue = this.isEmpty(tmpValue) ? '' : this.isEmpty(nextVal) ? tmpValue : BigNumber.min(tmpValue, nextVal).toFixed(); // this.prevValue = this.format(minValue); this.beginValue = this.validInterval(new BigNumber(minValue)).toFixed(); // this.beginValue = this.getRealValue(this.prevValue); this.inputPrev.nativeElement.value = this.prevValue; this.onModelChange(minValue, type); } else { const beginVal = this.getBeginValue(); const maxValue = this.isEmpty(tmpValue) ? '' : this.isEmpty(beginVal) ? tmpValue : BigNumber.max(tmpValue, beginVal).toFixed(); // this.nextValue = this.format(maxValue); // this.endValue = this.getRealValue(this.nextValue); this.endValue = this.validInterval(new BigNumber(maxValue)).toFixed(); this.inputNext.nativeElement.value = this.nextValue; this.onModelChange(maxValue, type); } } this.valueChange.emit(this.value); if (type === 'begin') { this.beginValueChange.emit(this.preNumValue); } else { this.endValueChange.emit(this.nextNumValue); } this.blur.emit(); } onFocus(type?: string) { if (!this.readonly && !this.disabled) { this.focusState = { isFocus: true, target: type }; if (type === 'begin') { this.inputPrev.nativeElement.value = this._beginValue; this.inputPrev.nativeElement.setSelectionRange(0, this._beginValue.length); this.expendWidth('begin', 3); } else { this.inputNext.nativeElement.value = this._endValue; this.inputNext.nativeElement.setSelectionRange(0, this._endValue.length); this.expendWidth('end', 3); } } this.focus.emit(type); } expendWidth(which: string, num: number) { let target: Element; let other: Element; if (which === 'begin') { target = this.prevWrapper.nativeElement; other = this.nextWrapper.nativeElement; } else { target = this.nextWrapper.nativeElement; other = this.prevWrapper.nativeElement; } const { width } = target.getBoundingClientRect(); if (width < 150) { this.render.setStyle(target, 'flex', num); this.render.setStyle(other, 'flex', 1); } } onMouseEnter(event) { this.isActiveTip = this.isShowPopover(); if (this.nextValue && this.prevValue) { this.popValue = this.prevValue + '~' + this.nextValue; } else { this.popValue = ''; } } isShowPopover() { const { width } = this.el.nativeElement.getBoundingClientRect(); if (width && width < 200) { return true; } return false; } onKeyDownRange($event: KeyboardEvent, typ: 'begin'|'end') { if ($event.key === 'ArrowDown') { $event.preventDefault(); this.down($event, typ); } if ($event.key === 'ArrowUp') { $event.preventDefault(); this.up($event, typ); } $event.stopPropagation(); } writeValue(val: any): void { this.setValue(val); } setValue(val: any) { if (typeof val === 'string') { const valArr = val.split(this.separator); this._beginValue = valArr[0] ? valArr[0] : ''; this._endValue = valArr[1] ? valArr[1] : ''; } else { this._beginValue = ''; this._beginValue = ''; } this.prevValue = this.format(this._beginValue); this.nextValue = this.format(this._endValue); } //#region 上下操作按钮 或 上下方向键 up(e: Event, type: string) { e.stopPropagation(); this._compute(type, 'up', (e.target as any).value); } down(e: Event, type: string) { e.stopPropagation(); this._compute(type, 'down', (e.target as any).value); } private _compute(type: string, dir: 'up' | 'down', tagVal) { const m = dir === 'up' ? 'plus' : 'minus'; if (type === 'begin' && this.isDisableOfBtn(dir, this.prevValue)) { const bv = tagVal || 0; const ev = this.getRealValue(this.nextValue); const r = new BigNumber(bv)[m](Number(this.step)); let v = r.toFixed(); const minValue = BigNumber.min(v, ev); if (!minValue.eq(v) && !this.isEmpty(ev)) { v = ev; } this._beginValue = v; // this.prevValue = this.format(v); this.inputPrev.nativeElement.value = v; this.onModelChange(v, type); this.beginValueChange.emit(v); } else if (type === 'end' && this.isDisableOfBtn(dir, this.nextValue)) { const bv = this.getRealValue(this.prevValue); const ev = tagVal || 0; const r = new BigNumber(ev)[m](Number(this.step)); let v = r.toFixed(); const maxValue = BigNumber.max(bv, v); if (!maxValue.eq(v) && !this.isEmpty(bv)) { v = bv; } this._endValue = v; // this.nextValue = this.format(v); this.inputNext.nativeElement.value = v; this.onModelChange(v, type); this.endValueChange.emit(v); } this.valueChange.emit(this.value); this.expendWidth(type, 3); } //#endregion }