11import { NodeViewWrapper } from '@tiptap/react' ;
22import { clamp , isNumber , throttle } from 'lodash-es' ;
3- import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
3+ import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
44
55import { IMAGE_MAX_SIZE , IMAGE_MIN_SIZE , IMAGE_THROTTLE_WAIT_TIME } from '@/constants' ;
66
@@ -17,6 +17,8 @@ const ResizeDirection = {
1717} ;
1818
1919function ImageView ( props : any ) {
20+ const { updateAttributes } = props ;
21+
2022 const [ maxSize , setMaxSize ] = useState < Size > ( {
2123 width : IMAGE_MAX_SIZE ,
2224 height : IMAGE_MAX_SIZE ,
@@ -36,13 +38,12 @@ function ImageView(props: any) {
3638
3739 const [ resizing , setResizing ] = useState < boolean > ( false ) ;
3840
39- const [ resizerState , setResizerState ] = useState ( {
40- x : 0 ,
41- y : 0 ,
42- w : 0 ,
43- h : 0 ,
44- dir : '' ,
45- } ) ;
41+ const resizeSession = useRef < {
42+ pointerId : number ;
43+ x : number ;
44+ width : number ;
45+ direction : string ;
46+ } | null > ( null ) ;
4647
4748 const { align, inline } = props ?. node ?. attrs ;
4849 const isBlockNode = props ?. node ?. type ?. name === 'imageBlock' ;
@@ -109,10 +110,13 @@ function ImageView(props: any) {
109110 [ props ?. editor ]
110111 ) ;
111112
112- function onMouseDown ( e : MouseEvent , dir : string ) {
113+ function onPointerDown ( e : React . PointerEvent < HTMLSpanElement > , dir : string ) {
114+ if ( ! e . isPrimary || ( e . pointerType === 'mouse' && e . button !== 0 ) ) {
115+ return ;
116+ }
117+
113118 e . preventDefault ( ) ;
114119 e . stopPropagation ( ) ;
115-
116120 const originalWidth = originalSize . width ;
117121 const originalHeight = originalSize . height ;
118122 const aspectRatio = originalWidth / originalHeight ;
@@ -134,85 +138,74 @@ function ImageView(props: any) {
134138 width = width > maxWidth ? maxWidth : width ;
135139 }
136140
137- setResizing ( true ) ;
138-
139- setResizerState ( {
141+ resizeSession . current = {
142+ pointerId : e . pointerId ,
140143 x : e . clientX ,
141- y : e . clientY ,
142- w : width ,
143- h : height ,
144- dir ,
145- } ) ;
144+ width ,
145+ direction : dir ,
146+ } ;
147+ e . currentTarget . setPointerCapture ( e . pointerId ) ;
148+ setResizing ( true ) ;
146149 }
147150
148- const onMouseMove = useCallback (
149- throttle ( ( e : MouseEvent ) => {
150- e . preventDefault ( ) ;
151- e . stopPropagation ( ) ;
152-
153- if ( ! resizing ) {
154- return ;
155- }
156-
157- const { x, w, dir } = resizerState ;
158-
159- const dx = ( e . clientX - x ) * ( / l / . test ( dir ) ? - 1 : 1 ) ;
160- // const dy = (e.clientY - y) * (/t/.test(dir) ? -1 : 1)
161-
162- const width = clamp ( w + dx , IMAGE_MIN_SIZE , maxSize . width ) ;
163- const height = null ;
164-
165- props . updateAttributes ( {
166- width,
167- height,
168- } ) ;
169- } , IMAGE_THROTTLE_WAIT_TIME ) ,
170- [ resizing , resizerState , maxSize , props . updateAttributes ]
151+ const resizeToPointer = useMemo (
152+ ( ) =>
153+ throttle ( ( clientX : number , pointerId : number ) => {
154+ const session = resizeSession . current ;
155+ if ( ! session || session . pointerId !== pointerId ) {
156+ return ;
157+ }
158+
159+ const dx = ( clientX - session . x ) * ( / l / . test ( session . direction ) ? - 1 : 1 ) ;
160+ const width = clamp ( session . width + dx , IMAGE_MIN_SIZE , maxSize . width ) ;
161+
162+ updateAttributes ( {
163+ width,
164+ height : null ,
165+ } ) ;
166+ } , IMAGE_THROTTLE_WAIT_TIME ) ,
167+ [ maxSize . width , updateAttributes ]
171168 ) ;
172169
173- const onMouseUp = useCallback (
174- ( e : MouseEvent ) => {
175- e . preventDefault ( ) ;
176- e . stopPropagation ( ) ;
177- if ( ! resizing ) {
178- return ;
179- }
180-
181- setResizerState ( {
182- x : 0 ,
183- y : 0 ,
184- w : 0 ,
185- h : 0 ,
186- dir : '' ,
187- } ) ;
188- setResizing ( false ) ;
170+ function onPointerMove ( e : React . PointerEvent < HTMLSpanElement > ) {
171+ if ( resizeSession . current ?. pointerId !== e . pointerId ) {
172+ return ;
173+ }
189174
190- selectImage ( ) ;
191- } ,
192- [ resizing , selectImage ]
193- ) ;
175+ e . preventDefault ( ) ;
176+ e . stopPropagation ( ) ;
177+ resizeToPointer ( e . clientX , e . pointerId ) ;
178+ }
194179
195- const onEvents = useCallback ( ( ) => {
196- document ?. addEventListener ( 'mousemove' , onMouseMove , true ) ;
197- document ?. addEventListener ( 'mouseup' , onMouseUp , true ) ;
198- } , [ onMouseMove , onMouseUp ] ) ;
180+ function finishResize ( e : React . PointerEvent < HTMLSpanElement > , updateFinalPosition : boolean ) {
181+ if ( resizeSession . current ?. pointerId !== e . pointerId ) {
182+ return ;
183+ }
199184
200- const offEvents = useCallback ( ( ) => {
201- document ?. removeEventListener ( 'mousemove' , onMouseMove , true ) ;
202- document ?. removeEventListener ( 'mouseup' , onMouseUp , true ) ;
203- } , [ onMouseMove , onMouseUp ] ) ;
185+ e . preventDefault ( ) ;
186+ e . stopPropagation ( ) ;
204187
205- useEffect ( ( ) => {
206- if ( resizing ) {
207- onEvents ( ) ;
188+ if ( updateFinalPosition ) {
189+ resizeToPointer ( e . clientX , e . pointerId ) ;
190+ resizeToPointer . flush ( ) ;
208191 } else {
209- offEvents ( ) ;
192+ resizeToPointer . cancel ( ) ;
193+ }
194+
195+ if ( e . currentTarget . hasPointerCapture ( e . pointerId ) ) {
196+ e . currentTarget . releasePointerCapture ( e . pointerId ) ;
210197 }
211198
199+ resizeSession . current = null ;
200+ setResizing ( false ) ;
201+ selectImage ( ) ;
202+ }
203+
204+ useEffect ( ( ) => {
212205 return ( ) => {
213- offEvents ( ) ;
206+ resizeToPointer . cancel ( ) ;
214207 } ;
215- } , [ resizing , onEvents , offEvents ] ) ;
208+ } , [ resizeToPointer ] ) ;
216209
217210 const resizeOb : ResizeObserver = useMemo ( ( ) => {
218211 return new ResizeObserver ( ( ) => getMaxSize ( ) ) ;
@@ -264,7 +257,10 @@ function ImageView(props: any) {
264257 < span
265258 className = { `image-resizer__handler image-resizer__handler--${ direction } ` }
266259 key = { `image-dir-${ direction } ` }
267- onMouseDown = { ( e : any ) => onMouseDown ( e , direction ) }
260+ onPointerCancel = { ( e ) => finishResize ( e , false ) }
261+ onPointerDown = { ( e ) => onPointerDown ( e , direction ) }
262+ onPointerMove = { onPointerMove }
263+ onPointerUp = { ( e ) => finishResize ( e , true ) }
268264 > </ span >
269265 ) ;
270266 } ) }
0 commit comments