fileOverview import { Component , Vue , Prop , Watch } from 'vue-property-decorator' ; import $ from 'jquery' ; import { ValueType , parseToken } from '@/data/parser' ; import { isNumericalType } from '@/data/util' ; export interface FormInputEdit { startIndex : number ; endIndex : number ; type : 'insert' | 'delete' | 'replace' | 'undo' ; value : string ; } @ Component export default class FormInput extends Vue { @ Prop ( { default : 1000 } ) private maxlength ! : number ; @ Prop ( { type : [ String , Number ] , default : '' } ) private value ! : string | null ; @ Prop ( { type : [ String , Number ] , default : ValueType . STRING } ) private type ! : ValueType ; @ Prop ( { default : false } ) private disabled ! : boolean ; private text : string = '' ; private prevText : string = '' ; private prevEditText : string = '' ; private selectionRange : [ number , number ] = [ 0 , 0 ] ; private prevSelectionRange : [ number , number ] = [ 0 , 0 ] ; public getSelectionRange ( ) { return this . selectionRange . concat ( ) as [ number , number ] ; } public getPrevSelectionRange ( ) { return this . prevSelectionRange . concat ( ) as [ number , number ] ; } /**
     * Gets the last edit in the input.
     */ public getLastEdit ( ) { const caret = this . selectionRange [ 0 ] ; const l = this . prevSelectionRange [ 0 ] ; const r = this . prevSelectionRange [ 1 ] ; const edit = { startIndex : l , endIndex : r , type : 'insert' , value : '' } ; if ( caret > l ) { edit . type = r === l ? 'insert' : 'replace' ; edit . value = this . text . slice ( l , caret ) ; } else { if ( l === r && l === 0 ) { edit . type = 'undo' ; edit . value = '' ; } else { edit . type = 'delete' ; edit . value = l === r ? this . prevEditText [ l - 1 ] : this . prevEditText . slice ( l , r ) ; } } if ( ! edit . value ) { edit . value = '' ; } return edit ; } @ Watch ( 'value' ) private onValueChange ( value ) { this . text = value !== null ? value . toString ( ) : '' ; } private created ( ) { this . text = this . prevText = this . value !== null ? this . value : '' ; } private onSelect ( ) { this . updateSelectionRange ( ) ; } private onKeydown ( evt ) { this . prevEditText = this . text ; this . updateSelectionRange ( ) ; } private typed ( value ) { if ( value === '' ) { return null ; } const typedValue = parseToken ( value , this . type ) ; if ( isNumericalType ( this . type ) && isNaN ( typedValue as number ) ) { return null ; } return typedValue ; } private onInput ( ) { if ( this . typed ( this . text ) !== this . typed ( this . prevEditText ) ) { this . $emit ( 'input' , this . typed ( this . text ) , this . typed ( this . prevText ) ) ; this . updateSelectionRange ( ) ; } } private save ( ) { if ( this . typed ( this . text ) !== this . typed ( this . prevText ) ) { this . $emit ( 'change' , this . typed ( this . text ) , this . typed ( this . prevText ) ) ; this . prevText = this . text ; } } private updateSelectionRange ( ) { if ( ! $ ( this . $refs . input ) . length ) { return ; } this . prevSelectionRange = this . selectionRange ; this . selectionRange = [ ( $ ( this . $refs . input ) [ 0 ] as HTMLInputElement ) . selectionStart as number , ( $ ( this . $refs . input ) [ 0 ] as HTMLInputElement ) . selectionEnd as number , ] ; } private destroyed ( ) { this . $emit ( 'destroyed' ) ; this . save ( ) ; } }