import { Injectable , Optional } from '@angular/core' ; import { Group } from '../Group' ; import { DragulaOptions } from '../DragulaOptions' ; import { Subject , Observable } from 'rxjs' ; import { filter , map } from 'rxjs/operators' ; import { EventTypes , AllEvents } from '../EventTypes' ; import { DrakeFactory } from '../DrakeFactory' ; type FilterProjector < T extends { name : string ; } > = ( name : string , args : any [ ] ) => T ; type Dispatch = { event : EventTypes ; name : string ; args : any [ ] ; } ; const filterEvent = < T extends { name : string ; } > ( eventType , filterDragType , projector ) => ( input ) : Observable < T > => { return input . pipe ( filter ( ( { event , name } ) => { return event === eventType && ( filterDragType === undefined || name === filterDragType ) ; } ) , map ( ( { name , args } ) => projector ( name , args ) ) ) ; } ; const elContainerSourceProjector = ( name , [ el , container , source ] ) => ( { name , el , container , source } ) ; @ Injectable ( ) export class DragulaService { private dispatch$ = new Subject < Dispatch > ( ) ; public drag = ( groupName ? ) => this . dispatch$ . pipe ( filterEvent ( EventTypes . Drag , groupName , ( name , [ el , source ] ) => ( { name , el , source } ) ) ) ; public dragend = ( groupName ? ) => this . dispatch$ . pipe ( filterEvent ( EventTypes . DragEnd , groupName , ( name , [ el ] ) => ( { name , el } ) ) ) ; public drop = ( groupName ? ) => this . dispatch$ . pipe ( filterEvent ( EventTypes . Drop , groupName , ( name , [ el , target , source , sibling ] ) => { return { name , el , target , source , sibling } ; } ) ) ; private elContainerSource = ( eventType ) => ( groupName ? ) => this . dispatch$ . pipe ( filterEvent ( eventType , groupName , elContainerSourceProjector ) ) ; public cancel = this . elContainerSource ( EventTypes . Cancel ) ; public remove = this . elContainerSource ( EventTypes . Remove ) ; public shadow = this . elContainerSource ( EventTypes . Shadow ) ; public over = this . elContainerSource ( EventTypes . Over ) ; public out = this . elContainerSource ( EventTypes . Out ) ; public cloned = ( groupName ? ) => this . dispatch$ . pipe ( filterEvent ( EventTypes . Cloned , groupName , ( name , [ clone , original , cloneType ] ) => { return { name , clone , original , cloneType } ; } ) ) ; public dropModel = < T = any > ( groupName ? ) => this . dispatch$ . pipe ( filterEvent ( EventTypes . DropModel , groupName , ( name , [ el , target , source , sibling , item , sourceModel , targetModel , sourceIndex , targetIndex ] ) => { return { name , el , target , source , sibling , item , sourceModel , targetModel , sourceIndex , targetIndex } ; } ) ) ; public removeModel = < T = any > ( groupName ? ) => this . dispatch$ . pipe ( filterEvent ( EventTypes . RemoveModel , groupName , ( name , [ el , container , source , item , sourceModel , sourceIndex ] ) => { return { name , el , container , source , item , sourceModel , sourceIndex } ; } ) ) ; private groups : { [ k ] : Group ; } = { } ; constructor ( @ Optional ( ) private drakeFactory = null ) { if ( this . drakeFactory === null ) { this . drakeFactory = new DrakeFactory ( ) ; } } /** Public mainly for testing purposes. Prefer `createGroup()`. */ public add ( group ) { let existingGroup = this . find ( group . name ) ; if ( existingGroup ) { throw new Error ( 'Group named: "' + group . name + '" already exists.' ) ; } this . groups [ group . name ] = group ; this . handleModels ( group ) ; this . setupEvents ( group ) ; return group ; } public find ( name ) { return this . groups [ name ] ; } public destroy ( name ) { let group = this . find ( name ) ; if ( ! group ) { return ; } group . drake && group . drake . destroy ( ) ; delete this . groups [ name ] ; } /**
     * Creates a group with the specified name and options.
     *
     * Note: formerly known as `setOptions`
     */ public createGroup < T = any > ( name , options ) { return this . add ( new Group ( name , this . drakeFactory . build ( [ ] , options ) , options ) ) ; } private handleModels ( { name , drake , options } ) { let dragElm ; let dragIndex ; let dropIndex ; drake . on ( 'remove' , ( el , container , source ) => { if ( ! drake . models ) { return ; } let sourceModel = drake . models [ drake . containers . indexOf ( source ) ] ; sourceModel = sourceModel . slice ( 0 ) ; const item = sourceModel . splice ( dragIndex , 1 ) [ 0 ] ; this . dispatch$ . next ( { event : EventTypes . RemoveModel , name , args : [ el , container , source , item , sourceModel , dragIndex ] } ) ; } ) ; drake . on ( 'drag' , ( el , source ) => { if ( ! drake . models ) { return ; } dragElm = el ; dragIndex = this . domIndexOf ( el , source ) ; } ) ; drake . on ( 'drop' , ( dropElm , target , source , sibling ? ) => { if ( ! drake . models || ! target ) { return ; } dropIndex = this . domIndexOf ( dropElm , target ) ; let sourceModel = drake . models [ drake . containers . indexOf ( source ) ] ; let targetModel = drake . models [ drake . containers . indexOf ( target ) ] ; let item ; if ( target === source ) { sourceModel = sourceModel . slice ( 0 ) ; item = sourceModel . splice ( dragIndex , 1 ) [ 0 ] ; sourceModel . splice ( dropIndex , 0 , item ) ; targetModel = sourceModel ; } else { let isCopying = dragElm !== dropElm ; item = sourceModel [ dragIndex ] ; if ( isCopying ) { if ( ! options . copyItem ) { throw new Error ( "If you have enabled `copy` on a group, you must provide a `copyItem` function." ) ; } item = options . copyItem ( item ) ; } if ( ! isCopying ) { sourceModel = sourceModel . slice ( 0 ) ; sourceModel . splice ( dragIndex , 1 ) ; } targetModel = targetModel . slice ( 0 ) ; targetModel . splice ( dropIndex , 0 , item ) ; if ( isCopying ) { try { target . removeChild ( dropElm ) ; } catch ( e ) { } } } this . dispatch$ . next ( { event : EventTypes . DropModel , name , args : [ dropElm , target , source , sibling , item , sourceModel , targetModel , dragIndex , dropIndex ] } ) ; } ) ; } private setupEvents ( group ) { if ( group . initEvents ) { return ; } group . initEvents = true ; const name = group . name ; let that = this ; let emitter = ( event ) => { group . drake . on ( event , ( ... args ) => { this . dispatch$ . next ( { event , name , args } ) ; } ) ; } ; AllEvents . forEach ( emitter ) ; } private domIndexOf ( child , parent ) { return Array . prototype . indexOf . call ( parent . children , child ) ; } }