import { Component , Input , Output , EventEmitter , TemplateRef , ElementRef , HostListener , HostBinding , ViewChild , ChangeDetectorRef , Renderer2 } from '@angular/core' ; import { TagModel } from '../../core/tag-model' ; import { TagRipple } from '../tag/tag-ripple.component' ; const navigator = typeof window !== 'undefined' ? window . navigator : { userAgent : 'Chrome' , vendor : 'Google Inc' } ; const isChrome = /Chrome/ . test ( navigator . userAgent ) && /Google Inc/ . test ( navigator . vendor ) ; @ Component ( { selector : 'tag' , templateUrl : './tag.template.html' , styleUrls : [ './tag-component.style.scss' ] } ) export class TagComponent { name @ Input ( ) public model : TagModel ; name @ Input ( ) public removable : boolean ; name @ Input ( ) public editable : boolean ; name @ Input ( ) public template : TemplateRef < any > ; name @ Input ( ) public displayBy : string ; name @ Input ( ) public identifyBy : string ; name @ Input ( ) public index : number ; name @ Input ( ) public hasRipple : boolean ; name @ Input ( ) public disabled = false ; name @ Input ( ) public canAddTag : ( tag : TagModel ) => boolean ; name @ Output ( ) public onSelect : EventEmitter < TagModel > = new EventEmitter < TagModel > ( ) ; name @ Output ( ) public onRemove : EventEmitter < TagModel > = new EventEmitter < TagModel > ( ) ; name @ Output ( ) public onBlur : EventEmitter < TagModel > = new EventEmitter < TagModel > ( ) ; name @ Output ( ) public onKeyDown : EventEmitter < any > = new EventEmitter < any > ( ) ; name @ Output ( ) public onTagEdited : EventEmitter < TagModel > = new EventEmitter < TagModel > ( ) ; name public get readonly ( ) : boolean { return typeof this . model !== 'string' && this . model . readonly === true ; } name public editing = false ; name @ HostBinding ( 'class.moving' ) public moving : boolean ; name public rippleState = 'none' ; name @ ViewChild ( TagRipple ) public ripple : TagRipple ; constructor ( public element , public renderer , private cdRef ) { } name public select ( $event ? ) { if ( this . readonly || this . disabled ) { return ; } if ( $event ) { $event . stopPropagation ( ) ; } this . focus ( ) ; this . onSelect . emit ( this . model ) ; } name public remove ( $event ) { $event . stopPropagation ( ) ; this . onRemove . emit ( this ) ; } name public focus ( ) { this . element . nativeElement . focus ( ) ; } public move ( ) { this . moving = true ; } name param event @ HostListener ( 'keydown' , [ '$event' ] ) public keydown ( event ) { if ( this . editing ) { if ( event . keyCode === 13 ) { return this . disableEditMode ( event ) ; } } else { this . onKeyDown . emit ( { event , model : this . model } ) ; } } name public blink ( ) { const classList = this . element . nativeElement . classList ; classList . add ( 'blink' ) ; setTimeout ( ( ) => classList . remove ( 'blink' ) , 50 ) ; } name public toggleEditMode ( ) { if ( this . editable ) { return this . editing ? undefined : this . activateEditMode ( ) ; } } name param event public onBlurred ( event ) { if ( ! this . editable ) { return ; } this . disableEditMode ( ) ; const value = event . target . innerText ; const result = typeof this . model === 'string' ? value : { ... this . model , [ this . displayBy ] : value } ; this . onBlur . emit ( result ) ; } name param item public getDisplayValue ( item ) { return typeof item === 'string' ? item : item [ this . displayBy ] ; } desc name public get isRippleVisible ( ) : boolean { return ! this . readonly && ! this . editing && isChrome && this . hasRipple ; } name param $event public disableEditMode ( $event ? ) { const classList = this . element . nativeElement . classList ; const input = this . getContentEditableText ( ) ; this . editing = false ; classList . remove ( 'tag--editing' ) ; if ( ! input ) { this . setContentEditableText ( this . model ) ; return ; } this . storeNewValue ( input ) ; this . cdRef . detectChanges ( ) ; if ( $event ) { $event . preventDefault ( ) ; } } name public isDeleteIconVisible ( ) { return ( ! this . readonly && ! this . disabled && this . removable && ! this . editing ) ; } name private getContentEditableText ( ) { const input = this . getContentEditable ( ) ; return input ? input . innerText . trim ( ) : '' ; } name param model private setContentEditableText ( model ) { const input = this . getContentEditable ( ) ; const value = this . getDisplayValue ( model ) ; input . innerText = value ; } name private activateEditMode ( ) { const classList = this . element . nativeElement . classList ; classList . add ( 'tag--editing' ) ; this . editing = true ; } name param input private storeNewValue ( input ) { const exists = ( tag ) => { return typeof tag === 'string' ? tag === input : tag [ this . displayBy ] === input ; } ; const hasId = ( ) => { return this . model [ this . identifyBy ] !== this . model [ this . displayBy ] ; } ; if ( exists ( this . model ) ) { return ; } const model = typeof this . model === 'string' ? input : { index : this . index , [ this . identifyBy ] : hasId ( ) ? this . model [ this . identifyBy ] : input , [ this . displayBy ] : input } ; if ( this . canAddTag ( model ) ) { this . onTagEdited . emit ( { tag : model , index : this . index } ) ; } else { this . setContentEditableText ( this . model ) ; } } name private getContentEditable ( ) { return this . element . nativeElement . querySelector ( '[contenteditable]' ) ; } }