1
- import { DIRECTION_RIGHT } from '../../gestures/hammer' ;
2
1
import { DragGesture } from '../../gestures/drag-gesture' ;
2
+ import { ItemSliding } from './item-sliding' ;
3
3
import { List } from '../list/list' ;
4
4
5
- import { CSS , nativeRaf , closest } from '../../util/dom' ;
5
+ import { closest } from '../../util/dom' ;
6
6
7
+ const DRAG_THRESHOLD = 20 ;
8
+ const MAX_ATTACK_ANGLE = 20 ;
7
9
8
10
export class ItemSlidingGesture extends DragGesture {
9
- canDrag : boolean = true ;
10
- data = { } ;
11
- openItems : number = 0 ;
12
11
onTap : any ;
13
- onMouseOut : any ;
14
- preventDrag : boolean = false ;
15
- dragEnded : boolean = true ;
12
+ selectedContainer : ItemSliding = null ;
13
+ openContainer : ItemSliding = null ;
16
14
17
15
constructor ( public list : List , public listEle : HTMLElement ) {
18
16
super ( listEle , {
19
17
direction : 'x' ,
20
18
threshold : DRAG_THRESHOLD
21
19
} ) ;
22
-
23
20
this . listen ( ) ;
21
+ }
24
22
25
- this . onTap = ( ev : UIEvent ) => {
26
- if ( ! isFromOptionButtons ( ev . target ) ) {
27
- let didClose = this . closeOpened ( ) ;
28
- if ( didClose ) {
29
- console . debug ( 'tap close sliding item' ) ;
30
- preventDefault ( ev ) ;
31
- }
32
- }
33
- } ;
34
-
35
- this . onMouseOut = ( ev : any ) => {
36
- if ( ev . target . tagName === 'ION-ITEM-SLIDING' ) {
37
- console . debug ( 'tap close sliding item' ) ;
38
- this . onDragEnd ( ev ) ;
39
- }
40
- } ;
23
+ onTapCallback ( ev : any ) {
24
+ if ( isFromOptionButtons ( ev . target ) ) {
25
+ return ;
26
+ }
27
+ let didClose = this . closeOpened ( ) ;
28
+ if ( didClose ) {
29
+ console . debug ( 'tap close sliding item, preventDefault' ) ;
30
+ ev . preventDefault ( ) ;
31
+ }
41
32
}
42
33
43
34
onDragStart ( ev : any ) : boolean {
44
- let itemContainerEle = getItemContainer ( ev . target ) ;
45
- if ( ! itemContainerEle ) {
46
- console . debug ( 'onDragStart, no itemContainerEle' ) ;
35
+ let angle = Math . abs ( ev . angle ) ;
36
+ if ( angle > MAX_ATTACK_ANGLE && Math . abs ( angle - 180 ) > MAX_ATTACK_ANGLE ) {
37
+ this . closeOpened ( ) ;
47
38
return false ;
48
39
}
49
40
50
- this . closeOpened ( itemContainerEle ) ;
41
+ if ( this . selectedContainer ) {
42
+ console . debug ( 'onDragStart, another container is already selected' ) ;
43
+ return false ;
44
+ }
51
45
52
- let openAmout = this . getOpenAmount ( itemContainerEle ) ;
53
- let itemData = this . get ( itemContainerEle ) ;
54
- this . preventDrag = ( openAmout > 0 ) ;
46
+ let container = getContainer ( ev ) ;
47
+ if ( ! container ) {
48
+ console . debug ( 'onDragStart, no itemContainerEle' ) ;
49
+ return false ;
50
+ }
55
51
56
- if ( this . preventDrag ) {
52
+ // Close open container if it is not the selected one.
53
+ if ( container !== this . openContainer ) {
57
54
this . closeOpened ( ) ;
58
- console . debug ( 'onDragStart, preventDefault' ) ;
59
- preventDefault ( ev ) ;
60
- return ;
61
55
}
62
56
63
- itemContainerEle . classList . add ( 'active-slide' ) ;
64
-
65
- this . set ( itemContainerEle , 'offsetX' , openAmout ) ;
66
- this . set ( itemContainerEle , 'startX' , ev . center [ this . direction ] ) ;
67
-
68
- this . dragEnded = false ;
57
+ // Close all item sliding containers but the selected one
58
+ this . selectedContainer = container ;
59
+ this . openContainer = container ;
60
+ container . startSliding ( ev . center . x ) ;
69
61
70
62
return true ;
71
63
}
72
64
73
65
onDrag ( ev : any ) : boolean {
74
- if ( this . dragEnded || this . preventDrag || Math . abs ( ev . deltaY ) > 30 ) {
75
- console . debug ( 'onDrag preventDrag, dragEnded:' , this . dragEnded , 'preventDrag:' , this . preventDrag , 'ev.deltaY:' , Math . abs ( ev . deltaY ) ) ;
76
- this . preventDrag = true ;
77
- return ;
78
- }
79
-
80
- let itemContainerEle = getItemContainer ( ev . target ) ;
81
- if ( ! itemContainerEle || ! isActive ( itemContainerEle ) ) {
82
- console . debug ( 'onDrag, no itemContainerEle' ) ;
83
- return ;
84
- }
85
-
86
- let itemData = this . get ( itemContainerEle ) ;
87
-
88
- if ( ! itemData . optsWidth ) {
89
- itemData . optsWidth = getOptionsWidth ( itemContainerEle ) ;
90
- if ( ! itemData . optsWidth ) {
91
- console . debug ( 'onDrag, no optsWidth' ) ;
92
- return ;
93
- }
66
+ if ( this . selectedContainer ) {
67
+ this . selectedContainer . moveSliding ( ev . center . x ) ;
68
+ ev . preventDefault ( ) ;
94
69
}
95
-
96
- let x = ev . center [ this . direction ] ;
97
- let delta = x - itemData . startX ;
98
-
99
- let newX = Math . max ( 0 , itemData . offsetX - delta ) ;
100
-
101
- if ( newX > itemData . optsWidth ) {
102
- // Calculate the new X position, capped at the top of the buttons
103
- newX = - Math . min ( - itemData . optsWidth , - itemData . optsWidth + ( ( ( delta + itemData . optsWidth ) * 0.4 ) ) ) ;
104
- }
105
-
106
- if ( newX > 5 && ev . srcEvent . type . indexOf ( 'mouse' ) > - 1 && ! itemData . hasMouseOut ) {
107
- itemContainerEle . addEventListener ( 'mouseout' , this . onMouseOut ) ;
108
- itemData . hasMouseOut = true ;
109
- }
110
-
111
- nativeRaf ( ( ) => {
112
- if ( ! this . dragEnded && ! this . preventDrag ) {
113
- isItemActive ( itemContainerEle , true ) ;
114
- this . open ( itemContainerEle , newX , false ) ;
115
- }
116
- } ) ;
70
+ return ;
117
71
}
118
72
119
73
onDragEnd ( ev : any ) {
120
- this . preventDrag = false ;
121
- this . dragEnded = true ;
122
-
123
- let itemContainerEle = getItemContainer ( ev . target ) ;
124
- if ( ! itemContainerEle || ! isActive ( itemContainerEle ) ) {
125
- console . debug ( 'onDragEnd, no itemContainerEle' ) ;
126
- return ;
127
- }
128
-
129
- // If we are currently dragging, we want to snap back into place
130
- // The final resting point X will be the width of the exposed buttons
131
- let itemData = this . get ( itemContainerEle ) ;
132
-
133
- var restingPoint = itemData . optsWidth ;
134
-
135
- // Check if the drag didn't clear the buttons mid-point
136
- // and we aren't moving fast enough to swipe open
137
-
138
- if ( this . getOpenAmount ( itemContainerEle ) < ( restingPoint / 2 ) ) {
139
-
140
- // If we are going left but too slow, or going right, go back to resting
141
- if ( ev . direction & DIRECTION_RIGHT || Math . abs ( ev . velocityX ) < 0.3 ) {
142
- restingPoint = 0 ;
143
- }
144
- }
145
-
146
- itemContainerEle . removeEventListener ( 'mouseout' , this . onMouseOut ) ;
147
- itemData . hasMouseOut = false ;
148
-
149
- nativeRaf ( ( ) => {
150
- this . open ( itemContainerEle , restingPoint , true ) ;
151
- } ) ;
152
- }
153
-
154
- closeOpened ( doNotCloseEle ?: HTMLElement ) {
155
- let didClose = false ;
156
- if ( this . openItems ) {
157
- let openItemElements = this . listEle . querySelectorAll ( '.active-slide' ) ;
158
- for ( let i = 0 ; i < openItemElements . length ; i ++ ) {
159
- if ( openItemElements [ i ] !== doNotCloseEle ) {
160
- this . open ( openItemElements [ i ] , 0 , true ) ;
161
- didClose = true ;
162
- }
163
- }
164
- }
165
- return didClose ;
166
- }
167
-
168
- open ( itemContainerEle : any , openAmount : number , isFinal : boolean ) {
169
- let slidingEle = itemContainerEle . querySelector ( 'ion-item,[ion-item]' ) ;
170
- if ( ! slidingEle ) {
171
- console . debug ( 'open, no slidingEle, openAmount:' , openAmount ) ;
172
- return ;
173
- }
174
-
175
- this . set ( itemContainerEle , 'openAmount' , openAmount ) ;
176
-
177
- clearTimeout ( this . get ( itemContainerEle ) . timerId ) ;
178
-
179
- if ( openAmount ) {
180
- this . openItems ++ ;
181
-
182
- } else {
183
- let timerId = setTimeout ( ( ) => {
184
- if ( slidingEle . style [ CSS . transform ] === '' ) {
185
- isItemActive ( itemContainerEle , false ) ;
186
- this . openItems -- ;
187
- }
188
- } , 400 ) ;
189
- this . set ( itemContainerEle , 'timerId' , timerId ) ;
190
- }
191
-
192
- slidingEle . style [ CSS . transition ] = ( isFinal ? '' : 'none' ) ;
193
- slidingEle . style [ CSS . transform ] = ( openAmount ? 'translate3d(' + - openAmount + 'px,0,0)' : '' ) ;
194
-
195
- if ( isFinal ) {
196
- if ( openAmount ) {
197
- isItemActive ( itemContainerEle , true ) ;
198
- this . on ( 'tap' , this . onTap ) ;
199
-
200
- } else {
74
+ if ( this . selectedContainer ) {
75
+ let openAmount = this . selectedContainer . endSliding ( ev . velocityX ) ;
76
+ this . selectedContainer = null ;
77
+
78
+ // TODO: I am not sure listening for a tap event is the best idea
79
+ // we should try mousedown/touchstart
80
+ if ( openAmount === 0 ) {
81
+ this . openContainer = null ;
201
82
this . off ( 'tap' , this . onTap ) ;
83
+ this . onTap = null ;
84
+ } else if ( ! this . onTap ) {
85
+ this . onTap = ( event : any ) => this . onTapCallback ( event ) ;
86
+ this . on ( 'tap' , this . onTap ) ;
202
87
}
203
88
}
204
89
}
205
90
206
- getOpenAmount ( itemContainerEle : any ) {
207
- return this . get ( itemContainerEle ) . openAmount || 0 ;
208
- }
209
-
210
- get ( itemContainerEle : any ) {
211
- return this . data [ itemContainerEle && itemContainerEle . $ionSlide ] || { } ;
212
- }
213
-
214
- set ( itemContainerEle : any , key : any , value : any ) {
215
- if ( ! this . data [ itemContainerEle . $ionSlide ] ) {
216
- this . data [ itemContainerEle . $ionSlide ] = { } ;
91
+ closeOpened ( ) : boolean {
92
+ if ( this . openContainer ) {
93
+ this . openContainer . close ( ) ;
94
+ this . openContainer = null ;
95
+ this . selectedContainer = null ;
96
+ this . off ( 'tap' , this . onTap ) ;
97
+ this . onTap = null ;
98
+ return true ;
217
99
}
218
- this . data [ itemContainerEle . $ionSlide ] [ key ] = value ;
100
+ return false ;
219
101
}
220
102
221
103
unlisten ( ) {
@@ -224,34 +106,14 @@ export class ItemSlidingGesture extends DragGesture {
224
106
}
225
107
}
226
108
227
- function isItemActive ( ele : any , isActive : boolean ) {
228
- ele . classList [ isActive ? 'add' : 'remove' ] ( 'active-slide' ) ;
229
- ele . classList [ isActive ? 'add' : 'remove' ] ( 'active-options' ) ;
230
- }
231
-
232
- function preventDefault ( ev : any ) {
233
- console . debug ( 'sliding item preventDefault' , ev . type ) ;
234
- ev . preventDefault ( ) ;
235
- }
236
-
237
- function getItemContainer ( ele : any ) {
238
- return closest ( ele , 'ion-item-sliding' , true ) ;
239
- }
240
-
241
- function isFromOptionButtons ( ele : any ) {
242
- return ! ! closest ( ele , 'ion-item-options' , true ) ;
243
- }
244
-
245
- function getOptionsWidth ( itemContainerEle : any ) {
246
- let optsEle = itemContainerEle . querySelector ( 'ion-item-options' ) ;
247
- if ( optsEle ) {
248
- return optsEle . offsetWidth ;
109
+ function getContainer ( ev : any ) : ItemSliding {
110
+ let ele = closest ( ev . target , 'ion-item-sliding' , true ) ;
111
+ if ( ele ) {
112
+ return ele [ '$ionComponent' ] ;
249
113
}
114
+ return null ;
250
115
}
251
116
252
- function isActive ( itemContainerEle : any ) {
253
- return itemContainerEle . classList . contains ( 'active-slide' ) ;
117
+ function isFromOptionButtons ( ele : HTMLElement ) : boolean {
118
+ return ! ! closest ( ele , 'ion-item-options' , true ) ;
254
119
}
255
-
256
-
257
- const DRAG_THRESHOLD = 20 ;
0 commit comments