import { AfterViewInit , Directive , ElementRef , EventEmitter , HostBinding , HostListener , Input , NgZone , OnDestroy , Output , Renderer2 } from "@angular/core" ; import { calculateDragImageOffset , DndDragImageOffsetFunction , DndEvent , setDragData , setDragImage } from "./dnd-utils" ; import { DndHandleDirective } from "./dnd-handle.directive" ; import { dndState , endDrag , startDrag } from "./dnd-state" ; import { EffectAllowed } from "./dnd-types" ; @ Directive ( { selector : "[dndDragImageRef]" } ) export class DndDragImageRefDirective { constructor ( parent , elementRef ) { parent . registerDragImage ( elementRef ) ; } } @ Directive ( { selector : "[dndDraggable]" } ) export class DndDraggableDirective implements AfterViewInit , OnDestroy { @ Input ( ) dndDraggable : any ; @ Input ( ) dndEffectAllowed : EffectAllowed = "copy" ; @ Input ( ) dndType ? : string ; @ Input ( ) dndDraggingClass = "dndDragging" ; @ Input ( ) dndDraggingSourceClass = "dndDraggingSource" ; @ Input ( ) dndDraggableDisabledClass = "dndDraggableDisabled" ; @ Input ( ) dndDragImageOffsetFunction : DndDragImageOffsetFunction = calculateDragImageOffset ; @ Output ( ) readonly dndStart : EventEmitter < DragEvent > = new EventEmitter < DragEvent > ( ) ; @ Output ( ) readonly dndDrag : EventEmitter < DragEvent > = new EventEmitter < DragEvent > ( ) ; @ Output ( ) readonly dndEnd : EventEmitter < DragEvent > = new EventEmitter < DragEvent > ( ) ; @ Output ( ) readonly dndMoved : EventEmitter < DragEvent > = new EventEmitter < DragEvent > ( ) ; @ Output ( ) readonly dndCopied : EventEmitter < DragEvent > = new EventEmitter < DragEvent > ( ) ; @ Output ( ) readonly dndLinked : EventEmitter < DragEvent > = new EventEmitter < DragEvent > ( ) ; @ Output ( ) readonly dndCanceled : EventEmitter < DragEvent > = new EventEmitter < DragEvent > ( ) ; @ HostBinding ( "attr.draggable" ) draggable = true ; private dndHandle ? : DndHandleDirective ; private dndDragImageElementRef ? : ElementRef ; private dragImage : Element | undefined ; private isDragStarted : boolean = false ; private readonly dragEventHandler : ( event : DragEvent ) => void = ( event ) => this . onDrag ( event ) ; @ Input ( ) set dndDisableIf ( value ) { this . draggable = ! value ; if ( this . draggable ) { this . renderer . removeClass ( this . elementRef . nativeElement , this . dndDraggableDisabledClass ) ; } else { this . renderer . addClass ( this . elementRef . nativeElement , this . dndDraggableDisabledClass ) ; } } @ Input ( ) set dndDisableDragIf ( value ) { this . dndDisableIf = value ; } constructor ( private elementRef , private renderer , private ngZone ) { } ngAfterViewInit ( ) { this . ngZone . runOutsideAngular ( ( ) => { this . elementRef . nativeElement . addEventListener ( "drag" , this . dragEventHandler ) ; } ) ; } ngOnDestroy ( ) { this . elementRef . nativeElement . removeEventListener ( "drag" , this . dragEventHandler ) ; if ( this . isDragStarted === true ) { endDrag ( ) ; } } @ HostListener ( "dragstart" , [ "$event" ] ) onDragStart ( event ) { if ( this . draggable === false ) { return false ; } if ( typeof this . dndHandle !== "undefined" && typeof event . _dndUsingHandle === "undefined" ) { return false ; } startDrag ( event , this . dndEffectAllowed , this . dndType ) ; this . isDragStarted = true ; setDragData ( event , { data : this . dndDraggable , type : this . dndType } , dndState . effectAllowed ! ) ; this . dragImage = this . determineDragImage ( ) ; this . renderer . addClass ( this . dragImage , this . dndDraggingClass ) ; if ( typeof this . dndDragImageElementRef !== "undefined" || typeof event . _dndUsingHandle !== "undefined" ) { setDragImage ( event , this . dragImage , this . dndDragImageOffsetFunction ) ; } const unregister = this . renderer . listen ( this . elementRef . nativeElement , "drag" , ( ) => { this . renderer . addClass ( this . elementRef . nativeElement , this . dndDraggingSourceClass ) ; unregister ( ) ; } ) ; this . dndStart . emit ( event ) ; event . stopPropagation ( ) ; return true ; } onDrag ( event ) { this . dndDrag . emit ( event ) ; } @ HostListener ( "dragend" , [ "$event" ] ) onDragEnd ( event ) { const dropEffect = dndState . dropEffect ; let dropEffectEmitter ; switch ( dropEffect ) { case "copy" : dropEffectEmitter = this . dndCopied ; break ; case "link" : dropEffectEmitter = this . dndLinked ; break ; case "move" : dropEffectEmitter = this . dndMoved ; break ; default : dropEffectEmitter = this . dndCanceled ; break ; } dropEffectEmitter . emit ( event ) ; this . dndEnd . emit ( event ) ; endDrag ( ) ; this . isDragStarted = false ; this . renderer . removeClass ( this . dragImage , this . dndDraggingClass ) ; window . setTimeout ( ( ) => { this . renderer . removeClass ( this . elementRef . nativeElement , this . dndDraggingSourceClass ) ; } , 0 ) ; event . stopPropagation ( ) ; } registerDragHandle ( handle ) { this . dndHandle = handle ; } registerDragImage ( elementRef ) { this . dndDragImageElementRef = elementRef ; } private determineDragImage ( ) { if ( typeof this . dndDragImageElementRef !== "undefined" ) { return this . dndDragImageElementRef . nativeElement as Element ; } else { return this . elementRef . nativeElement ; } } }