import { Component , Input , Output , ElementRef , EventEmitter , ContentChild , TemplateRef , ChangeDetectionStrategy , OnDestroy , Directive } from "@angular/core" ; import { SkyhookDndService } from "@angular-skyhook/core" ; interface Card { id : number ; text : string ; } interface DraggingCard { id : number ; index : number ; } @ Directive ( { selector : "[cardInner]" } ) export class CardInnerDirective { } @ Component ( { selector : "app-card" , template : `
    <div class="card"
        [dropTarget]="cardTarget"
        [dragSource]="cardSource"
        [style.opacity]="opacity$|async">
        <div class="border">
            <ng-container *ngTemplateOutlet="cardInnerTemplate;
                                             context: {$implicit: card}">
            </ng-container>
        </div>
    </div>
    ` , styles : [ `
            .card {
                padding-bottom: 0.25rem;
                background-color: white;
                cursor: move;
            }
            .border {
                padding: 0.5rem 1rem;
                border: 1px dashed gray;
            }
     ` ] , changeDetection : ChangeDetectionStrategy . OnPush } ) export class CardComponent implements OnDestroy { @ Output ( ) beginDrag : EventEmitter < void > = new EventEmitter < void > ( ) ; @ Output ( ) endDrag : EventEmitter < boolean > = new EventEmitter ( ) ; @ Output ( ) onMove : EventEmitter < [ number , number ] > = new EventEmitter ( ) ; @ ContentChild ( CardInnerDirective , { static : false , read : TemplateRef } ) cardInnerTemplate ; @ Input ( ) card : Card ; @ Input ( ) index : number ; @ Input ( ) id : number ; @ Input ( ) text : string ; cardSource = this . dnd . dragSource < DraggingCard > ( "CARD" , { beginDrag : ( ) => { this . beginDrag . emit ( ) ; return { id : this . id , index : this . index } ; } , endDrag : monitor => { const didDrop = monitor . didDrop ( ) ; this . endDrag . emit ( didDrop ) ; } } ) ; cardTarget = this . dnd . dropTarget < DraggingCard > ( "CARD" , { hover : monitor => { const dragIndex = monitor . getItem ( ) . index ; const hoverIndex = this . index ; if ( dragIndex === hoverIndex ) { return ; } const hoverBoundingRect = this . elRef . nativeElement . getBoundingClientRect ( ) ; const hoverMiddleY = ( hoverBoundingRect . bottom - hoverBoundingRect . top ) / 2 ; const clientOffset = monitor . getClientOffset ( ) ; const hoverClientY = clientOffset . y - hoverBoundingRect . top ; if ( dragIndex < hoverIndex && hoverClientY < hoverMiddleY ) { return ; } if ( dragIndex > hoverIndex && hoverClientY > hoverMiddleY ) { return ; } this . onMove . emit ( [ dragIndex , hoverIndex ] ) ; monitor . getItem ( ) . index = hoverIndex ; } } ) ; isDragging$ = this . cardSource . listen ( m => m . isDragging ( ) ) ; opacity$ = this . cardSource . listen ( monitor => ( monitor . isDragging ( ) ? 0.2 : 1 ) ) ; constructor ( private elRef , private dnd ) { } moveCard ( a , b ) { this . onMove . emit ( [ a , b ] ) ; } ngOnDestroy ( ) { this . cardSource . unsubscribe ( ) ; this . cardTarget . unsubscribe ( ) ; } }