1
- // eslint-disable max-lines
1
+ /* eslint-disable max-lines */
2
2
import type { ComponentType , VNode , h as hType } from 'preact' ;
3
3
// biome-ignore lint: needed for preact
4
4
import { h } from 'preact' ; // eslint-disable-line @typescript-eslint/no-unused-vars
@@ -8,6 +8,10 @@ import type { Dialog } from '../../types';
8
8
import { createScreenshotInputStyles } from './ScreenshotInput.css' ;
9
9
import { useTakeScreenshot } from './useTakeScreenshot' ;
10
10
11
+ const CROP_BUTTON_SIZE = 30 ;
12
+ const CROP_BUTTON_BORDER = 3 ;
13
+ const CROP_BUTTON_OFFSET = CROP_BUTTON_SIZE + CROP_BUTTON_BORDER ;
14
+
11
15
interface FactoryParams {
12
16
h : typeof hType ;
13
17
imageBuffer : HTMLCanvasElement ;
@@ -19,10 +23,10 @@ interface Props {
19
23
}
20
24
21
25
interface Box {
22
- startx : number ;
23
- starty : number ;
24
- endx : number ;
25
- endy : number ;
26
+ startX : number ;
27
+ startY : number ;
28
+ endX : number ;
29
+ endY : number ;
26
30
}
27
31
28
32
interface Rect {
@@ -34,10 +38,10 @@ interface Rect {
34
38
35
39
const constructRect = ( box : Box ) : Rect => {
36
40
return {
37
- x : Math . min ( box . startx , box . endx ) ,
38
- y : Math . min ( box . starty , box . endy ) ,
39
- width : Math . abs ( box . startx - box . endx ) ,
40
- height : Math . abs ( box . starty - box . endy ) ,
41
+ x : Math . min ( box . startX , box . endX ) ,
42
+ y : Math . min ( box . startY , box . endY ) ,
43
+ width : Math . abs ( box . startX - box . endX ) ,
44
+ height : Math . abs ( box . startY - box . endY ) ,
41
45
} ;
42
46
} ;
43
47
@@ -51,7 +55,7 @@ const getContainedSize = (img: HTMLCanvasElement): Box => {
51
55
}
52
56
const x = ( img . clientWidth - width ) / 2 ;
53
57
const y = ( img . clientHeight - height ) / 2 ;
54
- return { startx : x , starty : y , endx : width + x , endy : height + y } ;
58
+ return { startX : x , startY : y , endX : width + x , endY : height + y } ;
55
59
} ;
56
60
57
61
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -62,7 +66,7 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
62
66
const canvasContainerRef = useRef < HTMLDivElement > ( null ) ;
63
67
const cropContainerRef = useRef < HTMLDivElement > ( null ) ;
64
68
const croppingRef = useRef < HTMLCanvasElement > ( null ) ;
65
- const [ croppingRect , setCroppingRect ] = useState < Box > ( { startx : 0 , starty : 0 , endx : 0 , endy : 0 } ) ;
69
+ const [ croppingRect , setCroppingRect ] = useState < Box > ( { startX : 0 , startY : 0 , endX : 0 , endY : 0 } ) ;
66
70
const [ confirmCrop , setConfirmCrop ] = useState ( false ) ;
67
71
68
72
useEffect ( ( ) => {
@@ -85,7 +89,7 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
85
89
cropButton . style . top = `${ imageDimensions . y } px` ;
86
90
}
87
91
88
- setCroppingRect ( { startx : 0 , starty : 0 , endx : imageDimensions . width , endy : imageDimensions . height } ) ;
92
+ setCroppingRect ( { startX : 0 , startY : 0 , endX : imageDimensions . width , endY : imageDimensions . height } ) ;
89
93
}
90
94
91
95
useEffect ( ( ) => {
@@ -118,44 +122,51 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
118
122
setConfirmCrop ( false ) ;
119
123
const handleMouseMove = makeHandleMouseMove ( corner ) ;
120
124
const handleMouseUp = ( ) : void => {
121
- croppingRef . current && croppingRef . current . removeEventListener ( 'mousemove' , handleMouseMove ) ;
125
+ DOCUMENT . removeEventListener ( 'mousemove' , handleMouseMove ) ;
122
126
DOCUMENT . removeEventListener ( 'mouseup' , handleMouseUp ) ;
123
127
setConfirmCrop ( true ) ;
124
128
} ;
125
129
126
130
DOCUMENT . addEventListener ( 'mouseup' , handleMouseUp ) ;
127
- croppingRef . current && croppingRef . current . addEventListener ( 'mousemove' , handleMouseMove ) ;
131
+ DOCUMENT . addEventListener ( 'mousemove' , handleMouseMove ) ;
128
132
}
129
133
130
134
const makeHandleMouseMove = useCallback ( ( corner : string ) => {
131
135
return function ( e : MouseEvent ) {
136
+ if ( ! croppingRef . current ) {
137
+ return ;
138
+ }
139
+ const cropCanvas = croppingRef . current ;
140
+ const cropBoundingRect = cropCanvas . getBoundingClientRect ( ) ;
141
+ const mouseX = e . clientX - cropBoundingRect . x ;
142
+ const mouseY = e . clientY - cropBoundingRect . y ;
132
143
switch ( corner ) {
133
144
case 'topleft' :
134
145
setCroppingRect ( prev => ( {
135
146
...prev ,
136
- startx : Math . min ( e . offsetX , prev . endx - 30 ) ,
137
- starty : Math . min ( e . offsetY , prev . endy - 30 ) ,
147
+ startX : Math . min ( Math . max ( 0 , mouseX ) , prev . endX - CROP_BUTTON_OFFSET ) ,
148
+ startY : Math . min ( Math . max ( 0 , mouseY ) , prev . endY - CROP_BUTTON_OFFSET ) ,
138
149
} ) ) ;
139
150
break ;
140
151
case 'topright' :
141
152
setCroppingRect ( prev => ( {
142
153
...prev ,
143
- endx : Math . max ( e . offsetX , prev . startx + 30 ) ,
144
- starty : Math . min ( e . offsetY , prev . endy - 30 ) ,
154
+ endX : Math . max ( Math . min ( mouseX , cropCanvas . width ) , prev . startX + CROP_BUTTON_OFFSET ) ,
155
+ startY : Math . min ( Math . max ( 0 , mouseY ) , prev . endY - CROP_BUTTON_OFFSET ) ,
145
156
} ) ) ;
146
157
break ;
147
158
case 'bottomleft' :
148
159
setCroppingRect ( prev => ( {
149
160
...prev ,
150
- startx : Math . min ( e . offsetX , prev . endx - 30 ) ,
151
- endy : Math . max ( e . offsetY , prev . starty + 30 ) ,
161
+ startX : Math . min ( Math . max ( 0 , mouseX ) , prev . endX - CROP_BUTTON_OFFSET ) ,
162
+ endY : Math . max ( Math . min ( mouseY , cropCanvas . height ) , prev . startY + CROP_BUTTON_OFFSET ) ,
152
163
} ) ) ;
153
164
break ;
154
165
case 'bottomright' :
155
166
setCroppingRect ( prev => ( {
156
167
...prev ,
157
- endx : Math . max ( e . offsetX , prev . startx + 30 ) ,
158
- endy : Math . max ( e . offsetY , prev . starty + 30 ) ,
168
+ endX : Math . max ( Math . min ( mouseX , cropCanvas . width ) , prev . startX + CROP_BUTTON_OFFSET ) ,
169
+ endY : Math . max ( Math . min ( mouseY , cropCanvas . height ) , prev . startY + CROP_BUTTON_OFFSET ) ,
159
170
} ) ) ;
160
171
break ;
161
172
}
@@ -229,33 +240,33 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
229
240
< div class = "cropButtonContainer" style = { { position : 'absolute' } } ref = { cropContainerRef } >
230
241
< canvas style = { { position : 'absolute' } } ref = { croppingRef } > </ canvas >
231
242
< CropCorner
232
- left = { croppingRect . startx }
233
- top = { croppingRect . starty }
243
+ left = { croppingRect . startX }
244
+ top = { croppingRect . startY }
234
245
onGrabButton = { onGrabButton }
235
246
corner = "topleft"
236
247
> </ CropCorner >
237
248
< CropCorner
238
- left = { croppingRect . endx - 30 }
239
- top = { croppingRect . starty }
249
+ left = { croppingRect . endX - CROP_BUTTON_SIZE }
250
+ top = { croppingRect . startY }
240
251
onGrabButton = { onGrabButton }
241
252
corner = "topright"
242
253
> </ CropCorner >
243
254
< CropCorner
244
- left = { croppingRect . startx }
245
- top = { croppingRect . endy - 30 }
255
+ left = { croppingRect . startX }
256
+ top = { croppingRect . endY - CROP_BUTTON_SIZE }
246
257
onGrabButton = { onGrabButton }
247
258
corner = "bottomleft"
248
259
> </ CropCorner >
249
260
< CropCorner
250
- left = { croppingRect . endx - 30 }
251
- top = { croppingRect . endy - 30 }
261
+ left = { croppingRect . endX - CROP_BUTTON_SIZE }
262
+ top = { croppingRect . endY - CROP_BUTTON_SIZE }
252
263
onGrabButton = { onGrabButton }
253
264
corner = "bottomright"
254
265
> </ CropCorner >
255
266
< div
256
267
style = { {
257
- left : Math . max ( 0 , croppingRect . endx - 191 ) ,
258
- top : Math . max ( 0 , croppingRect . endy + 8 ) ,
268
+ left : Math . max ( 0 , croppingRect . endX - 191 ) ,
269
+ top : Math . max ( 0 , croppingRect . endY + 8 ) ,
259
270
display : confirmCrop ? 'flex' : 'none' ,
260
271
} }
261
272
class = "crop-btn-group"
@@ -265,10 +276,10 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor
265
276
e . preventDefault ( ) ;
266
277
if ( croppingRef . current ) {
267
278
setCroppingRect ( {
268
- startx : 0 ,
269
- starty : 0 ,
270
- endx : croppingRef . current . width ,
271
- endy : croppingRef . current . height ,
279
+ startX : 0 ,
280
+ startY : 0 ,
281
+ endX : croppingRef . current . width ,
282
+ endY : croppingRef . current . height ,
272
283
} ) ;
273
284
}
274
285
setConfirmCrop ( false ) ;
@@ -316,7 +327,8 @@ function CropCorner({
316
327
borderLeft : corner === 'topleft' || corner === 'bottomleft' ? 'solid purple' : 'none' ,
317
328
borderRight : corner === 'topright' || corner === 'bottomright' ? 'solid purple' : 'none' ,
318
329
borderBottom : corner === 'bottomleft' || corner === 'bottomright' ? 'solid purple' : 'none' ,
319
- borderWidth : '3px' ,
330
+ borderWidth : `${ CROP_BUTTON_BORDER } px` ,
331
+ cursor : corner === 'topleft' || corner === 'bottomright' ? 'nwse-resize' : 'nesw-resize' ,
320
332
} }
321
333
onMouseDown = { e => {
322
334
e . preventDefault ( ) ;
0 commit comments