1212
1313import { Injectable } from '@angular/core' ;
1414import * as angular from 'angular' ;
15+ import * as JQuery from 'jquery' ;
16+
17+ interface CustomDragDropService {
18+ draggableScope : angular . IScope | null ;
19+ dragData : unknown | null ;
20+ dragIndex : number | null ;
21+ dragModelPath : null | string ;
22+ dragSettings : { placeholder ?: string } | null ;
23+ draggableElement : HTMLElement | null ;
24+
25+ callEventCallback ( scope : angular . IScope , callbackName : string , event : DragEvent , ui ?: UI ) : void ;
26+ updateModel ( scope : angular . IScope , modelPath : string , newValue : unknown , index ?: number ) : void ;
27+ removeFromModel ( scope : angular . IScope , modelPath : string , index ?: number ) : void ;
28+ }
29+
30+ interface UI {
31+ draggable : JQuery < HTMLElement > | null ;
32+ helper : null ;
33+ position : { top : number ; left : number } ;
34+ offset : { top : number ; left : number } ;
35+ }
1536
1637@Injectable ( {
1738 providedIn : 'root'
@@ -21,97 +42,97 @@ export class AngularDragDropService {
2142
2243 addDragDropDirectives ( module : angular . IModule ) : void {
2344 // Drag and drop service to maintain state across directives
24- // tslint:disable-next-line:no-any
25- module . service ( 'customDragDropService' , [
45+ module . factory ( 'customDragDropService' , [
2646 '$parse' ,
27- // tslint:disable-next-line:no-any
28- function ( $parse : any ) {
29- this . draggableScope = null ;
30- this . dragData = null ;
31- this . dragIndex = null ;
32- this . dragModelPath = null ;
33- this . dragSettings = null ;
34- this . draggableElement = null ;
35-
36- // tslint:disable-next-line:no-any
37- this . callEventCallback = function ( scope : any , callbackName : string , event : any , ui ?: any ) {
38- if ( ! callbackName ) {
39- return ;
40- }
41-
42- const objExtract = extract ( callbackName ) ;
43- const callback = objExtract . callback ;
44- const constructor = objExtract . constructor ;
45- const args = [ event , ui ] . concat ( objExtract . args ) ;
47+ function ( $parse : angular . IParseService ) : CustomDragDropService {
48+ return {
49+ draggableScope : null ,
50+ dragData : null ,
51+ dragIndex : null ,
52+ dragModelPath : null ,
53+ dragSettings : null ,
54+ draggableElement : null ,
55+
56+ callEventCallback : function ( scope , callbackStr , event , ui ) {
57+ if ( ! callbackStr ) {
58+ return ;
59+ }
4660
47- // call either $scope method or constructor's method
48- const targetScope = scope [ callback ] ? scope : scope [ constructor ] ;
49- const targetCallback = scope [ callback ] || scope [ constructor ] [ callback ] ;
61+ const { targetCallback, targetScope, args : extractedArgs } = extract ( callbackStr ) ;
62+ const fullArgs = [ event , ui ] . concat ( extractedArgs ) ;
5063
51- if ( typeof targetCallback === 'function' ) {
52- return targetCallback . apply ( targetScope , args ) ;
53- }
64+ if ( typeof targetCallback === 'function' ) {
65+ return targetCallback . apply ( targetScope , fullArgs ) ;
66+ }
5467
55- function extract ( _callbackName : string ) {
56- const atStartBracket =
57- _callbackName . indexOf ( '(' ) !== - 1 ? _callbackName . indexOf ( '(' ) : _callbackName . length ;
58- const atEndBracket =
59- _callbackName . lastIndexOf ( ')' ) !== - 1 ? _callbackName . lastIndexOf ( ')' ) : _callbackName . length ;
60- const argsString = _callbackName . substring ( atStartBracket + 1 , atEndBracket ) ;
61- let _constructor =
62- _callbackName . indexOf ( '.' ) !== - 1 ? _callbackName . substr ( 0 , _callbackName . indexOf ( '.' ) ) : null ;
63- _constructor =
64- scope [ _constructor ] && typeof scope [ _constructor ] . constructor === 'function' ? _constructor : null ;
65-
66- return {
67- callback : _callbackName . substring ( ( _constructor && _constructor . length + 1 ) || 0 , atStartBracket ) ,
68- // tslint:disable-next-line:no-any
69- args : argsString ? argsString . split ( ',' ) . map ( ( item : string ) => $parse ( item . trim ( ) ) ( scope ) ) : [ ] ,
70- constructor : _constructor
71- } ;
72- }
73- } ;
68+ function extract ( _callbackStr : string ) {
69+ const atStartBracket = _callbackStr . indexOf ( '(' ) !== - 1 ? _callbackStr . indexOf ( '(' ) : _callbackStr . length ;
70+ const atEndBracket =
71+ _callbackStr . lastIndexOf ( ')' ) !== - 1 ? _callbackStr . lastIndexOf ( ')' ) : _callbackStr . length ;
72+
73+ const argsString = _callbackStr . substring ( atStartBracket + 1 , atEndBracket ) ;
74+ const identifierTokens = argsString ? argsString . split ( ',' ) : [ ] ;
75+ const args = identifierTokens . map ( item => $parse ( item . trim ( ) ) ( scope ) ) ;
76+
77+ const constructorName =
78+ _callbackStr . indexOf ( '.' ) !== - 1 ? _callbackStr . substr ( 0 , _callbackStr . indexOf ( '.' ) ) : null ;
79+ // @ts -ignore
80+ const constructorCandid = constructorName && scope [ constructorName ] ;
81+ const constructor =
82+ constructorCandid && typeof constructorCandid . constructor === 'function' ? constructorCandid : null ;
83+
84+ const callbackName = _callbackStr . substring ( ( constructor && constructor . length + 1 ) || 0 , atStartBracket ) ;
85+ // @ts -ignore
86+ const callbackCandid = scope [ callbackName ] ;
87+ // If the expression is a method call, then the parsed constructor becomes its bound scope.
88+ const _scope = callbackCandid ? scope : constructor ;
89+ const callback = callbackCandid || constructor [ callbackName ] ;
90+
91+ return {
92+ args,
93+ targetScope : _scope ,
94+ targetCallback : callback
95+ } ;
96+ }
97+ } ,
7498
75- // tslint:disable-next-line:no-any
76- this . updateModel = function ( scope : any , modelPath : string , newValue : any , index ?: number ) {
77- const getter = $parse ( modelPath ) ;
78- const setter = getter . assign ;
79- const modelValue = getter ( scope ) ;
99+ updateModel : function ( scope , modelPath , newValue , index ) {
100+ const getter = $parse ( modelPath ) ;
101+ const setter = getter . assign ;
102+ const modelValue = getter ( scope ) ;
80103
81- if ( angular . isArray ( modelValue ) ) {
82- if ( typeof index === 'number' ) {
83- modelValue [ index ] = newValue ;
104+ if ( angular . isArray ( modelValue ) ) {
105+ if ( typeof index === 'number' ) {
106+ modelValue [ index ] = newValue ;
107+ } else {
108+ modelValue . push ( newValue ) ;
109+ }
84110 } else {
85- modelValue . push ( newValue ) ;
86- }
87- } else {
88- if ( setter ) {
89- setter ( scope , newValue ) ;
111+ if ( setter ) {
112+ setter ( scope , newValue ) ;
113+ }
90114 }
91- }
92115
93- scope . $apply ( ) ;
94- } ;
116+ scope . $apply ( ) ;
117+ } ,
95118
96- // tslint:disable-next-line:no-any
97- this . removeFromModel = function ( scope : any , modelPath : string , index : number ) {
98- const getter = $parse ( modelPath ) ;
99- const modelValue = getter ( scope ) ;
119+ removeFromModel : function ( scope , modelPath , index ) {
120+ const getter = $parse ( modelPath ) ;
121+ const modelValue = getter ( scope ) ;
100122
101- if ( angular . isArray ( modelValue ) && typeof index === 'number' ) {
102- modelValue . splice ( index , 1 ) ;
103- scope . $apply ( ) ;
123+ if ( angular . isArray ( modelValue ) && typeof index === 'number' ) {
124+ modelValue . splice ( index , 1 ) ;
125+ scope . $apply ( ) ;
126+ }
104127 }
105128 } ;
106129 }
107130 ] ) ;
108131
109132 // jqyoui-draggable directive
110- // tslint:disable-next-line:no-any
111133 module . directive ( 'jqyouiDraggable' , [
112134 'customDragDropService' ,
113- // tslint:disable-next-line:no-any
114- function ( dragDropService : any ) {
135+ function ( dragDropService : CustomDragDropService ) {
115136 return {
116137 restrict : 'A' ,
117138 link : function ( scope , element , attrs ) {
@@ -134,7 +155,7 @@ export class AngularDragDropService {
134155 return scope . $eval ( attrs . drag ) ;
135156 } , updateDraggable ) ;
136157
137- el . addEventListener ( 'dragstart' , function ( event : DragEvent ) {
158+ el . addEventListener ( 'dragstart' , function ( event ) {
138159 if ( ! isDragEnabled ( ) ) {
139160 event . preventDefault ( ) ;
140161 return ;
@@ -176,7 +197,7 @@ export class AngularDragDropService {
176197
177198 // Call onStart callback if provided
178199 if ( dragSettings . onStart ) {
179- const ui = {
200+ const ui : UI = {
180201 draggable : angular . element ( el ) ,
181202 helper : null ,
182203 position : { top : event . clientY , left : event . clientX } ,
@@ -186,7 +207,7 @@ export class AngularDragDropService {
186207 }
187208 } ) ;
188209
189- el . addEventListener ( 'dragend' , function ( event : DragEvent ) {
210+ el . addEventListener ( 'dragend' , function ( event ) {
190211 // Remove visual feedback
191212 el . style . opacity = '' ;
192213
@@ -209,23 +230,20 @@ export class AngularDragDropService {
209230 ] ) ;
210231
211232 // jqyoui-droppable directive
212- // tslint:disable-next-line:no-any
213233 module . directive ( 'jqyouiDroppable' , [
214234 'customDragDropService' ,
215- // tslint:disable-next-line:no-any
216- function ( dragDropService : any ) {
235+ function ( dragDropService : CustomDragDropService ) {
217236 return {
218237 restrict : 'A' ,
219- // tslint:disable-next-line:no-any
220- link : function ( scope : any , element : any , attrs : any ) {
238+ link : function ( scope , element , attrs ) {
221239 const el = element [ 0 ] ;
222240
223241 // Check if dropping is enabled
224242 const isDropEnabled = function ( ) {
225243 return attrs . drop === 'true' || scope . $eval ( attrs . drop ) === true ;
226244 } ;
227245
228- el . addEventListener ( 'dragover' , function ( event : DragEvent ) {
246+ el . addEventListener ( 'dragover' , function ( event ) {
229247 if ( ! isDropEnabled ( ) ) {
230248 return ;
231249 }
@@ -239,12 +257,12 @@ export class AngularDragDropService {
239257 el . style . backgroundColor = '#f0f0f0' ;
240258 } ) ;
241259
242- el . addEventListener ( 'dragleave' , function ( event : DragEvent ) {
260+ el . addEventListener ( 'dragleave' , function ( ) {
243261 // Remove visual feedback
244262 el . style . backgroundColor = '' ;
245263 } ) ;
246264
247- el . addEventListener ( 'drop' , function ( event : DragEvent ) {
265+ el . addEventListener ( 'drop' , function ( event ) {
248266 if ( ! isDropEnabled ( ) ) {
249267 return ;
250268 }
@@ -283,16 +301,16 @@ export class AngularDragDropService {
283301
284302 // Remove from source if it's a move operation (not copy)
285303 // Check placeholder setting to determine if we should remove from source
286- const dragSettings = dragDropService . dragSettings || { } ;
304+ const dragSettings : CustomDragDropService [ 'dragSettings' ] = dragDropService . dragSettings || { } ;
287305 const shouldRemoveFromSource =
288306 draggableScope &&
289307 typeof dragIndex === 'number' &&
290308 dragModelPath &&
291309 dragSettings . placeholder !== 'keep' &&
292310 ! dropSettings . deepCopy ;
293311
294- if ( shouldRemoveFromSource ) {
295- dragDropService . removeFromModel ( draggableScope , dragModelPath , dragIndex ) ;
312+ if ( shouldRemoveFromSource && draggableScope ) {
313+ dragDropService . removeFromModel ( draggableScope , dragModelPath ?? '' , dragIndex ?? undefined ) ;
296314 }
297315
298316 // Call onDrop callback if provided
0 commit comments