@@ -23,14 +23,16 @@ type ScrollProps = {
2323 onScroll : ScrollEvent => void ,
2424 onScrollDragStart : ScrollEvent => void ,
2525 onScrollDragEnd : ScrollEvent => void ,
26- onScrollMomentumStart : ScrollEvent => void ,
27- onScrollMomentumEnd : ScrollEvent => void ,
2826} ;
2927
3028type ScrollState = {
29+ direction : ScrollDirection ,
3130 pointerType : PointerType ,
3231 scrollTarget : null | Element | Document ,
33- isPointerDown : boolean ,
32+ isDragging : boolean ,
33+ isTouching : boolean ,
34+ scrollLeft : number ,
35+ scrollTop : number ,
3436} ;
3537
3638type ScrollEventType =
@@ -58,15 +60,22 @@ type ScrollEvent = {|
5860 y : null | number ,
5961| } ;
6062
61- const targetEventTypes = [ 'scroll' , 'pointerdown' , 'keyup' ] ;
62- const rootEventTypes = [ 'pointermove' , 'pointerup' , 'pointercancel' ] ;
63+ const targetEventTypes = [
64+ 'scroll' ,
65+ 'pointerdown' ,
66+ 'touchstart' ,
67+ 'keyup' ,
68+ 'wheel' ,
69+ ] ;
70+ const rootEventTypes = [ 'touchcancel' , 'touchend' ] ;
6371
6472function createScrollEvent (
6573 event : ?ReactDOMResponderEvent ,
6674 context : ReactDOMResponderContext ,
6775 type : ScrollEventType ,
6876 target : Element | Document ,
6977 pointerType : PointerType ,
78+ direction : ScrollDirection ,
7079) : ScrollEvent {
7180 let clientX = null ;
7281 let clientY = null ;
@@ -84,7 +93,7 @@ function createScrollEvent(
8493 target,
8594 type,
8695 pointerType,
87- direction : '' , // TODO
96+ direction,
8897 timeStamp : context . getTimeStamp ( ) ,
8998 clientX,
9099 clientY,
@@ -107,12 +116,14 @@ function dispatchEvent(
107116) : void {
108117 const target = ( ( state . scrollTarget : any ) : Element | Document ) ;
109118 const pointerType = state . pointerType ;
119+ const direction = state . direction ;
110120 const syntheticEvent = createScrollEvent (
111121 event ,
112122 context ,
113123 name ,
114124 target ,
115125 pointerType ,
126+ direction ,
116127 ) ;
117128 context . dispatchEvent ( syntheticEvent , listener , eventPriority ) ;
118129}
@@ -122,9 +133,12 @@ const ScrollResponder: ReactDOMEventResponder = {
122133 targetEventTypes,
123134 createInitialState ( ) {
124135 return {
136+ direction : '' ,
137+ isTouching : false ,
125138 pointerType : '' ,
139+ prevScrollTop : 0 ,
140+ prevScrollLeft : 0 ,
126141 scrollTarget : null ,
127- isPointerDown : false ,
128142 } ;
129143 } ,
130144 allowMultipleHostChildren : true ,
@@ -138,17 +152,68 @@ const ScrollResponder: ReactDOMEventResponder = {
138152 const { pointerType, target, type} = event ;
139153
140154 if ( props . disabled ) {
141- if ( state . isPointerDown ) {
142- state . isPointerDown = false ;
155+ if ( state . isTouching ) {
156+ state . isTouching = false ;
143157 state . scrollTarget = null ;
144- context . addRootEventTypes ( rootEventTypes ) ;
158+ state . isDragging = false ;
159+ state . direction = '' ;
160+ context . removeRootEventTypes ( rootEventTypes ) ;
145161 }
146162 return ;
147163 }
148164
149165 switch ( type ) {
150166 case 'scroll' : {
167+ const prevScrollTarget = state . scrollTarget ;
168+ let scrollLeft = 0 ;
169+ let scrollTop = 0 ;
170+
171+ // Check if target is the document
172+ if ( target . nodeType === 9 ) {
173+ const bodyNode = ( ( target : any ) : Document ) . body ;
174+ if ( bodyNode !== null ) {
175+ scrollLeft = bodyNode . offsetLeft ;
176+ scrollTop = bodyNode . offsetTop ;
177+ }
178+ } else {
179+ scrollLeft = ( ( target : any ) : Element ) . scrollLeft ;
180+ scrollTop = ( ( target : any ) : Element ) . scrollTop ;
181+ }
182+
183+ if ( prevScrollTarget !== null ) {
184+ if ( scrollTop === state . scrollTop ) {
185+ if ( scrollLeft > state . scrollLeft ) {
186+ state . direction = 'right' ;
187+ } else {
188+ state . direction = 'left' ;
189+ }
190+ } else {
191+ if ( scrollTop > state . scrollTop ) {
192+ state . direction = 'down' ;
193+ } else {
194+ state . direction = 'up' ;
195+ }
196+ }
197+ } else {
198+ state . direction = '' ;
199+ }
151200 state . scrollTarget = ( ( target : any ) : Element | Document ) ;
201+ state . scrollLeft = scrollLeft ;
202+ state . scrollTop = scrollTop ;
203+
204+ if ( state . isTouching && ! state . isDragging ) {
205+ state . isDragging = true ;
206+ if ( props . onScrollDragStart ) {
207+ dispatchEvent (
208+ event ,
209+ context ,
210+ state ,
211+ 'scrolldragstart' ,
212+ props . onScrollDragStart ,
213+ UserBlockingEvent ,
214+ ) ;
215+ }
216+ }
152217 if ( props . onScroll ) {
153218 dispatchEvent (
154219 event ,
@@ -165,13 +230,19 @@ const ScrollResponder: ReactDOMEventResponder = {
165230 state . pointerType = pointerType ;
166231 break ;
167232 }
233+ case 'wheel' : {
234+ state . pointerType = 'mouse' ;
235+ break ;
236+ }
168237 case 'pointerdown' : {
169238 state . pointerType = pointerType ;
170- if ( ! state . isPointerDown ) {
171- state . isPointerDown = true ;
239+ break ;
240+ }
241+ case 'touchstart' : {
242+ if ( ! state . isTouching ) {
243+ state . isTouching = true ;
172244 context . addRootEventTypes ( rootEventTypes ) ;
173245 }
174- break ;
175246 }
176247 }
177248 } ,
@@ -181,20 +252,28 @@ const ScrollResponder: ReactDOMEventResponder = {
181252 props : ScrollProps ,
182253 state : ScrollState ,
183254 ) {
184- const { pointerType , type} = event ;
255+ const { type} = event ;
185256
186257 switch ( type ) {
187- case 'pointercancel' :
188- case 'pointerup' : {
189- state . pointerType = pointerType ;
190- if ( state . isPointerDown ) {
191- state . isPointerDown = false ;
258+ case 'touchcancel' :
259+ case 'touchend' : {
260+ if ( state . isTouching ) {
261+ if ( state . isDragging && props . onScrollDragEnd ) {
262+ dispatchEvent (
263+ event ,
264+ context ,
265+ state ,
266+ 'scrolldragend' ,
267+ props . onScrollDragEnd ,
268+ UserBlockingEvent ,
269+ ) ;
270+ }
271+ state . isTouching = false ;
272+ state . isDragging = false ;
273+ state . scrollTarget = null ;
274+ state . pointerType = '' ;
192275 context . removeRootEventTypes ( rootEventTypes ) ;
193276 }
194- break ;
195- }
196- case 'pointermove' : {
197- state . pointerType = pointerType ;
198277 }
199278 }
200279 } ,
0 commit comments