import { Directive , Input , Output , OnDestroy , OnInit , ElementRef , HostListener , EventEmitter } from '@angular/core' ; import { DndState } from '../services' ; import { DndDraggableConfig , DndStateConfig , ALL_EFFECTS , MIME_TYPE , EDGE_MIME_TYPE , MSIE_MIME_TYPE , } from '../index' ; import { dropAccepted } from './dnd-list' ; import { Subscription } from 'rxjs' ; @ Directive ( { selector : '[dndDraggable]' , } ) export class DndDraggable implements OnInit , OnDestroy { @ Input ( 'dndDraggable' ) public option : DndDraggableConfig = < DndDraggableConfig > { draggable : true } ; @ Input ( 'dndType' ) public dndType : string ; @ Input ( 'dndObject' ) public dndObject : HTMLElement ; @ Input ( 'dndDragDisabled' ) public set disableDrag ( disable ) { if ( disable !== undefined ) { this . element . nativeElement . setAttribute ( this . draggableString , ( ! disable ) . toString ( ) ) ; } } @ Output ( 'dndDragStart' ) public dndDragStart : EventEmitter < any > = new EventEmitter ( ) ; @ Output ( 'dndDragEnd' ) public dndDragEnd : EventEmitter < any > = new EventEmitter ( ) ; @ Output ( 'dndCopied' ) public dndCopied : EventEmitter < any > = new EventEmitter ( ) ; @ Output ( 'dndLinked' ) public dndLinked : EventEmitter < any > = new EventEmitter ( ) ; @ Output ( 'dndMoved' ) public dndMoved : EventEmitter < any > = new EventEmitter ( ) ; @ Output ( 'dndCanceled' ) public dndCanceled : EventEmitter < any > = new EventEmitter ( ) ; @ Output ( 'dndSelected' ) public dndSelected : EventEmitter < any > = new EventEmitter ( ) ; private readonly dragState : DndStateConfig ; private dropSubscription : Subscription ; private draggableString : string = 'draggable' ; constructor ( private readonly element , private readonly dndState ) { this . dragState = dndState . dragState ; this . element . nativeElement . setAttribute ( this . draggableString , 'true' ) ; /**
         * Workaround to make element draggable in IE9
         */ this . element . nativeElement . onselectstart = function ( ) : void { if ( this . dragDrop ) this . dragDrop ( ) ; } ; } public ngOnInit ( ) { this . dropSubscription = dropAccepted . subscribe ( ( { item , list } ) => { if ( JSON . stringify ( this . dndObject ) === JSON . stringify ( item ) ) { let cb = { copy : 'dndCopied' , link : 'dndLinked' , move : 'dndMoved' , none : 'dndCanceled' } ; if ( this . dragState ) { ( this [ cb [ this . dragState . effectAllowed ] ] as EventEmitter < any > ) . emit ( ) ; } this . dndDragEnd . emit ( ) ; } } ) ; } public ngOnDestroy ( ) { this . dropSubscription . unsubscribe ( ) ; } @ HostListener ( 'dragstart' , [ '$event' ] ) public handleDragStart ( event ) { if ( this . element . nativeElement . getAttribute ( this . draggableString ) === 'false' ) return ; this . dragState . isDragging = true ; this . dragState . itemType = this . dndType ; this . dragState . dropEffect = 'none' ; if ( ! this . option ) { this . option = < DndDraggableConfig > { draggable : true } ; } this . dragState . effectAllowed = this . option . effectAllowed || ALL_EFFECTS [ 0 ] ; event . dataTransfer . effectAllowed = this . dragState . effectAllowed ; let mimeType = MIME_TYPE + ( this . dragState . itemType ? ( '-' + this . dragState . itemType ) : '' ) ; try { event . dataTransfer . setData ( mimeType , JSON . stringify ( this . dndObject ) ) ; } catch ( e ) { let data = JSON . stringify ( { item : this . dndObject , type : this . dragState . itemType } ) ; try { event . dataTransfer . setData ( EDGE_MIME_TYPE , data ) ; } catch ( e ) { let effectsAllowed = this . dndState . filterEffects ( ALL_EFFECTS , this . dragState . effectAllowed ) ; event . dataTransfer . effectAllowed = effectsAllowed [ 0 ] ; event . dataTransfer . setData ( MSIE_MIME_TYPE , data ) ; } } this . element . nativeElement . classList . add ( 'dndDragging' ) ; setTimeout ( ( ) => { if ( this . dragState . effectAllowed === 'move' ) { this . element . nativeElement . style . display = 'none' ; } } ) ; if ( ( event as any ) . _dndHandle && event . dataTransfer . setDragImage ) { event . dataTransfer . setDragImage ( this . element . nativeElement , 0 , 0 ) ; } this . dndDragStart . emit ( ) ; event . stopPropagation ( ) ; } @ HostListener ( 'dragend' , [ '$event' ] ) public handleDragEnd ( event ) { this . dragState . isDragging = false ; this . element . nativeElement . classList . remove ( 'dndDragging' ) ; this . element . nativeElement . style . removeProperty ( 'display' ) ; event . stopPropagation ( ) ; setTimeout ( ( ( ) => this . element . nativeElement . classList . remove ( 'dndDraggingSource' ) ) , 0 ) ; } @ HostListener ( 'click' , [ '$event' ] ) public handleClick ( event ) { if ( this . element . nativeElement . hasAttribute ( 'dndSelected' ) ) return ; event = event [ 'originalEvent' ] || event ; this . dndSelected . emit ( ) ; event . stopPropagation ( ) ; } private findElementWithAttribute ( element , attr ) { if ( element . hasAttribute ( attr ) ) return element ; if ( element . parentElement === null ) return ; return this . findElementWithAttribute ( element . parentElement , attr ) ; } }