import { Component , ElementRef , OnInit , Input , AfterViewInit , OnDestroy , ContentChild , ViewChild } from '@angular/core' ; import { FormControl , FormGroup } from '@angular/forms' ; import { TranslateService } from '@ngx-translate/core' ; import { Subject } from 'rxjs/Subject' ; import { Subscription } from 'rxjs/Subscription' ; import { DropDownComponent } from './../../drop-down/drop-down.component' ; import { TextboxComponent } from './../textbox/textbox.component' ; import { PortalResources } from './../../shared/models/portal-resources' ; export class CustomFormGroup extends FormGroup { public msShowTextbox : Subject < boolean > ; public msFocusedControl : string ; public msStartInEditMode : boolean ; public msExistenceState : 'original' | 'new' | 'deleted' = 'original' ; public msStayInEditMode : boolean ; } export class CustomFormControl extends FormControl { public _msRunValidation : boolean ; } @ Component ( { selector : 'click-to-edit' , templateUrl : './click-to-edit.component.html' , styleUrls : [ './click-to-edit.component.scss' ] , } ) export class ClickToEditComponent implements OnInit , AfterViewInit , OnDestroy { public showTextbox = false ; public group : CustomFormGroup ; @ Input ( 'group' ) set origGroup ( group ) { this . group = group as CustomFormGroup ; } @ Input ( ) name : string ; @ Input ( ) placeholder : string ; @ Input ( ) hiddenText : boolean ; @ Input ( ) maskText : string ; @ Input ( ) alwaysShow : boolean ; @ ViewChild ( 'container' ) container : ElementRef ; @ ViewChild ( 'target' ) target : ElementRef ; @ ContentChild ( TextboxComponent ) textbox : TextboxComponent ; @ ContentChild ( DropDownComponent ) dropdown : DropDownComponent < any > ; public control : CustomFormControl ; private _sub : Subscription ; private _targetFocusState : 'focused' | 'blurring' | 'blurred' ; private _focusFunc = ( e ) => { this . _targetFocusListener ( e ) ; } ; private _blurFunc = ( e ) => { this . _targetBlurListener ( e ) ; } ; constructor ( translateService ) { this . maskText = translateService . instant ( PortalResources . hiddenValueClickToShow ) ; } ngOnInit ( ) { this . _targetFocusState = 'blurred' ; this . control = this . group . controls [ this . name ] as CustomFormControl ; const group = this . group as CustomFormGroup ; if ( ! group . msShowTextbox ) { group . msShowTextbox = new Subject < boolean > ( ) ; } this . _sub = group . msShowTextbox . subscribe ( showTextbox => { this . showTextbox = showTextbox || this . alwaysShow || ( group . msStartInEditMode && group . pristine ) ; if ( this . showTextbox && ( this . group as CustomFormGroup ) . msFocusedControl === this . name ) { setTimeout ( ( ) => { this . _focusChild ( ) ; } ) ; } } ) ; if ( group . msStartInEditMode || this . alwaysShow ) { this . showTextbox = true ; } } ngAfterViewInit ( ) { if ( this . target && this . target . nativeElement ) { this . target . nativeElement . addEventListener ( 'focus' , this . _focusFunc , true ) ; this . target . nativeElement . addEventListener ( 'blur' , this . _blurFunc , true ) ; } } ngOnDestroy ( ) { if ( this . _sub ) { this . _sub . unsubscribe ( ) ; this . _sub = null ; } if ( this . target && this . target . nativeElement ) { this . target . nativeElement . removeEventListener ( 'focus' , this . _focusFunc , true ) ; this . target . nativeElement . removeEventListener ( 'blur' , this . _blurFunc , true ) ; } } private _focusChild ( ) { if ( ! this . target ) { return ; } if ( ( this . target . nativeElement as HTMLElement ) . contains ( document . activeElement ) ) { return ; } if ( this . textbox ) { this . textbox . focus ( ) ; } else if ( this . dropdown ) { this . dropdown . focus ( ) ; } else { return ; } this . _targetFocusState = 'focused' ; } onMouseDown ( event ) { if ( ! this . showTextbox && ! ! this . control && ! this . control . disabled ) { event . preventDefault ( ) ; event . stopPropagation ( ) ; this . _updateShowTextbox ( true ) ; this . _simulateMouseEvents ( this . container . nativeElement , [ 'mousedown' , 'mouseup' , 'click' ] ) ; } } private _onTargetFocus ( ) { this . _updateShowTextbox ( true ) ; } private _onTargetBlur ( ) { if ( ! this . group . pristine ) { for ( let name in this . group . controls ) { const control = this . group . controls [ name ] as CustomFormControl ; control . _msRunValidation = true ; control . updateValueAndValidity ( ) ; } } if ( this . group . valid ) { setTimeout ( ( ) => { this . _updateShowTextbox ( false ) ; } , 0 ) ; } } protected _updateShowTextbox ( show ) { const alwaysShowSuffix = '#ALWAYSSHOW' ; const group = this . group as CustomFormGroup ; const name = this . name + ( this . alwaysShow ? alwaysShowSuffix : '' ) ; if ( show ) { group . msFocusedControl = name ; } else if ( group . msFocusedControl === name ) { group . msFocusedControl = '' ; } if ( ! group . msFocusedControl || ( group . msFocusedControl === name && ! this . alwaysShow ) ) { group . msShowTextbox . next ( show ) ; } } private _targetBlurListener ( event ) { this . _targetFocusState = 'blurring' ; setTimeout ( ( ) => { if ( this . _targetFocusState !== 'focused' ) { this . _targetFocusState = 'blurred' ; this . _onTargetBlur ( ) ; } } ) ; } private _targetFocusListener ( event ) { if ( this . _targetFocusState === 'blurred' ) { this . _onTargetFocus ( ) ; } this . _targetFocusState = 'focused' ; } private _simulateMouseEvents ( target , eventTypes ) { if ( ! eventTypes || eventTypes . length === 0 ) { return ; } let newEvent ; if ( typeof Event === 'function' ) { newEvent = new MouseEvent ( eventTypes [ 0 ] , { bubbles : true , cancelable : true } ) ; } else { newEvent = document . createEvent ( 'MouseEvents' ) ; newEvent . initEvent ( eventTypes [ 0 ] , true , true ) ; } target . dispatchEvent ( newEvent ) ; setTimeout ( ( ) => { eventTypes . splice ( 0 , 1 ) ; this . _simulateMouseEvents ( target , eventTypes ) ; } ) ; } }