1212
1313import { disableTextSelection , restoreTextSelection } from './textSelection' ;
1414import { DOMAttributes , MoveEvents , PointerType } from '@react-types/shared' ;
15- import React , { useCallback , useMemo , useRef , useState } from 'react' ;
16- import { useEffectEvent , useGlobalListeners , useLayoutEffect } from '@react-aria/utils' ;
15+ import React , { useCallback , useMemo , useRef } from 'react' ;
16+ import { useEffectEvent , useGlobalListeners } from '@react-aria/utils' ;
1717
1818export interface MoveResult {
1919 /** Props to spread on the target element. */
@@ -87,108 +87,62 @@ export function useMove(props: MoveEvents): MoveResult {
8787 } , [ onMoveEnd , state ] ) ;
8888 let endEvent = useEffectEvent ( end ) ;
8989
90- let [ pointerDown , setPointerDown ] = useState < 'pointer' | 'mouse' | 'touch' | null > ( null ) ;
91- useLayoutEffect ( ( ) => {
92- if ( pointerDown === 'pointer' ) {
93- let onPointerMove = ( e : PointerEvent ) => {
94- if ( e . pointerId === state . current . id ) {
95- let pointerType = ( e . pointerType || 'mouse' ) as PointerType ;
90+ let moveProps = useMemo ( ( ) => {
91+ let moveProps : DOMAttributes = { } ;
9692
97- // Problems with PointerEvent#movementX/movementY:
98- // 1. it is always 0 on macOS Safari.
99- // 2. On Chrome Android, it's scaled by devicePixelRatio, but not on Chrome macOS
100- moveEvent ( e , pointerType , e . pageX - ( state . current . lastPosition ?. pageX ?? 0 ) , e . pageY - ( state . current . lastPosition ?. pageY ?? 0 ) ) ;
101- state . current . lastPosition = { pageX : e . pageX , pageY : e . pageY } ;
102- }
103- } ;
93+ let start = ( ) => {
94+ disableTextSelection ( ) ;
95+ state . current . didMove = false ;
96+ } ;
10497
105- let onPointerUp = ( e : PointerEvent ) => {
106- if ( e . pointerId === state . current . id ) {
107- let pointerType = ( e . pointerType || 'mouse' ) as PointerType ;
108- endEvent ( e , pointerType ) ;
109- state . current . id = null ;
110- removeGlobalListener ( window , 'pointermove' , onPointerMove , false ) ;
111- removeGlobalListener ( window , 'pointerup' , onPointerUp , false ) ;
112- removeGlobalListener ( window , 'pointercancel' , onPointerUp , false ) ;
113- setPointerDown ( null ) ;
114- }
115- } ;
116- addGlobalListener ( window , 'pointermove' , onPointerMove , false ) ;
117- addGlobalListener ( window , 'pointerup' , onPointerUp , false ) ;
118- addGlobalListener ( window , 'pointercancel' , onPointerUp , false ) ;
119- return ( ) => {
120- removeGlobalListener ( window , 'pointermove' , onPointerMove , false ) ;
121- removeGlobalListener ( window , 'pointerup' , onPointerUp , false ) ;
122- removeGlobalListener ( window , 'pointercancel' , onPointerUp , false ) ;
123- } ;
124- } else if ( pointerDown === 'mouse' && process . env . NODE_ENV === 'test' ) {
98+ if ( typeof PointerEvent === 'undefined' && process . env . NODE_ENV === 'test' ) {
12599 let onMouseMove = ( e : MouseEvent ) => {
126100 if ( e . button === 0 ) {
101+ // Should be safe to use the useEffectEvent because these are equivalent https://github.com/reactjs/react.dev/issues/8075#issuecomment-3400179389
102+ // However, the compiler is not smart enough to know that. As such, this whole file must be manually optimised as the compiler will bail.
103+ //
104+ // eslint-disable-next-line react-hooks/rules-of-hooks
127105 moveEvent ( e , 'mouse' , e . pageX - ( state . current . lastPosition ?. pageX ?? 0 ) , e . pageY - ( state . current . lastPosition ?. pageY ?? 0 ) ) ;
128106 state . current . lastPosition = { pageX : e . pageX , pageY : e . pageY } ;
129107 }
130108 } ;
131109 let onMouseUp = ( e : MouseEvent ) => {
132110 if ( e . button === 0 ) {
111+ // eslint-disable-next-line react-hooks/rules-of-hooks
133112 endEvent ( e , 'mouse' ) ;
134113 removeGlobalListener ( window , 'mousemove' , onMouseMove , false ) ;
135114 removeGlobalListener ( window , 'mouseup' , onMouseUp , false ) ;
136- setPointerDown ( null ) ;
137115 }
138116 } ;
139- addGlobalListener ( window , 'mousemove' , onMouseMove , false ) ;
140- addGlobalListener ( window , 'mouseup' , onMouseUp , false ) ;
141- return ( ) => {
142- removeGlobalListener ( window , 'mousemove' , onMouseMove , false ) ;
143- removeGlobalListener ( window , 'mouseup' , onMouseUp , false ) ;
117+ moveProps . onMouseDown = ( e : React . MouseEvent ) => {
118+ if ( e . button === 0 ) {
119+ start ( ) ;
120+ e . stopPropagation ( ) ;
121+ e . preventDefault ( ) ;
122+ state . current . lastPosition = { pageX : e . pageX , pageY : e . pageY } ;
123+ addGlobalListener ( window , 'mousemove' , onMouseMove , false ) ;
124+ addGlobalListener ( window , 'mouseup' , onMouseUp , false ) ;
125+ }
144126 } ;
145- } else if ( pointerDown === 'touch' && process . env . NODE_ENV === 'test' ) {
127+
146128 let onTouchMove = ( e : TouchEvent ) => {
147129 let touch = [ ...e . changedTouches ] . findIndex ( ( { identifier} ) => identifier === state . current . id ) ;
148130 if ( touch >= 0 ) {
149131 let { pageX, pageY} = e . changedTouches [ touch ] ;
132+ // eslint-disable-next-line react-hooks/rules-of-hooks
150133 moveEvent ( e , 'touch' , pageX - ( state . current . lastPosition ?. pageX ?? 0 ) , pageY - ( state . current . lastPosition ?. pageY ?? 0 ) ) ;
151134 state . current . lastPosition = { pageX, pageY} ;
152135 }
153136 } ;
154137 let onTouchEnd = ( e : TouchEvent ) => {
155138 let touch = [ ...e . changedTouches ] . findIndex ( ( { identifier} ) => identifier === state . current . id ) ;
156139 if ( touch >= 0 ) {
140+ // eslint-disable-next-line react-hooks/rules-of-hooks
157141 endEvent ( e , 'touch' ) ;
158142 state . current . id = null ;
159143 removeGlobalListener ( window , 'touchmove' , onTouchMove ) ;
160144 removeGlobalListener ( window , 'touchend' , onTouchEnd ) ;
161145 removeGlobalListener ( window , 'touchcancel' , onTouchEnd ) ;
162- setPointerDown ( null ) ;
163- }
164- } ;
165- addGlobalListener ( window , 'touchmove' , onTouchMove , false ) ;
166- addGlobalListener ( window , 'touchend' , onTouchEnd , false ) ;
167- addGlobalListener ( window , 'touchcancel' , onTouchEnd , false ) ;
168- return ( ) => {
169- removeGlobalListener ( window , 'touchmove' , onTouchMove , false ) ;
170- removeGlobalListener ( window , 'touchend' , onTouchEnd , false ) ;
171- removeGlobalListener ( window , 'touchcancel' , onTouchEnd , false ) ;
172- } ;
173- }
174- } , [ pointerDown , addGlobalListener , removeGlobalListener ] ) ;
175-
176- let moveProps = useMemo ( ( ) => {
177- let moveProps : DOMAttributes = { } ;
178-
179- let start = ( ) => {
180- disableTextSelection ( ) ;
181- state . current . didMove = false ;
182- } ;
183-
184- if ( typeof PointerEvent === 'undefined' && process . env . NODE_ENV === 'test' ) {
185- moveProps . onMouseDown = ( e : React . MouseEvent ) => {
186- if ( e . button === 0 ) {
187- start ( ) ;
188- e . stopPropagation ( ) ;
189- e . preventDefault ( ) ;
190- state . current . lastPosition = { pageX : e . pageX , pageY : e . pageY } ;
191- setPointerDown ( 'mouse' ) ;
192146 }
193147 } ;
194148 moveProps . onTouchStart = ( e : React . TouchEvent ) => {
@@ -202,25 +156,56 @@ export function useMove(props: MoveEvents): MoveResult {
202156 e . preventDefault ( ) ;
203157 state . current . lastPosition = { pageX, pageY} ;
204158 state . current . id = identifier ;
205- setPointerDown ( 'touch' ) ;
159+ addGlobalListener ( window , 'touchmove' , onTouchMove , false ) ;
160+ addGlobalListener ( window , 'touchend' , onTouchEnd , false ) ;
161+ addGlobalListener ( window , 'touchcancel' , onTouchEnd , false ) ;
206162 } ;
207163 } else {
164+ let onPointerMove = ( e : PointerEvent ) => {
165+ if ( e . pointerId === state . current . id ) {
166+ let pointerType = ( e . pointerType || 'mouse' ) as PointerType ;
167+
168+ // Problems with PointerEvent#movementX/movementY:
169+ // 1. it is always 0 on macOS Safari.
170+ // 2. On Chrome Android, it's scaled by devicePixelRatio, but not on Chrome macOS
171+ // eslint-disable-next-line react-hooks/rules-of-hooks
172+ moveEvent ( e , pointerType , e . pageX - ( state . current . lastPosition ?. pageX ?? 0 ) , e . pageY - ( state . current . lastPosition ?. pageY ?? 0 ) ) ;
173+ state . current . lastPosition = { pageX : e . pageX , pageY : e . pageY } ;
174+ }
175+ } ;
176+
177+ let onPointerUp = ( e : PointerEvent ) => {
178+ if ( e . pointerId === state . current . id ) {
179+ let pointerType = ( e . pointerType || 'mouse' ) as PointerType ;
180+ // eslint-disable-next-line react-hooks/rules-of-hooks
181+ endEvent ( e , pointerType ) ;
182+ state . current . id = null ;
183+ removeGlobalListener ( window , 'pointermove' , onPointerMove , false ) ;
184+ removeGlobalListener ( window , 'pointerup' , onPointerUp , false ) ;
185+ removeGlobalListener ( window , 'pointercancel' , onPointerUp , false ) ;
186+ }
187+ } ;
188+
208189 moveProps . onPointerDown = ( e : React . PointerEvent ) => {
209190 if ( e . button === 0 && state . current . id == null ) {
210191 start ( ) ;
211192 e . stopPropagation ( ) ;
212193 e . preventDefault ( ) ;
213194 state . current . lastPosition = { pageX : e . pageX , pageY : e . pageY } ;
214195 state . current . id = e . pointerId ;
215- setPointerDown ( 'pointer' ) ;
196+ addGlobalListener ( window , 'pointermove' , onPointerMove , false ) ;
197+ addGlobalListener ( window , 'pointerup' , onPointerUp , false ) ;
198+ addGlobalListener ( window , 'pointercancel' , onPointerUp , false ) ;
216199 }
217200 } ;
218201 }
219202
220203 let triggerKeyboardMove = ( e : EventBase , deltaX : number , deltaY : number ) => {
221204 start ( ) ;
222- move ( e , 'keyboard' , deltaX , deltaY ) ;
223- end ( e , 'keyboard' ) ;
205+ // eslint-disable-next-line react-hooks/rules-of-hooks
206+ moveEvent ( e , 'keyboard' , deltaX , deltaY ) ;
207+ // eslint-disable-next-line react-hooks/rules-of-hooks
208+ endEvent ( e , 'keyboard' ) ;
224209 } ;
225210
226211 moveProps . onKeyDown = ( e ) => {
@@ -253,7 +238,7 @@ export function useMove(props: MoveEvents): MoveResult {
253238 } ;
254239
255240 return moveProps ;
256- } , [ state , move , end ] ) ;
241+ } , [ addGlobalListener , removeGlobalListener , state ] ) ;
257242
258243 return { moveProps} ;
259244}
0 commit comments