Skip to content

Commit 4c4bca6

Browse files
committed
feat: improve drag and resize
1 parent 33181f7 commit 4c4bca6

File tree

1 file changed

+170
-42
lines changed

1 file changed

+170
-42
lines changed

electron/renderer/components/grid/grid3.tsx

+170-42
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,28 @@ export interface GridMouseEvent {
3333
}
3434

3535
export const Grid3: React.FC<Grid3Props> = (props: Grid3Props): ReactNode => {
36-
const { dimensions } = props;
36+
const gridDimensions = props.dimensions;
3737

3838
const logger = useLogger('page:grid3');
3939

4040
const gridItemRef = useRef<HTMLDivElement>(null);
41+
4142
const [position, setPosition] = useState({ x: 0, y: 0 });
43+
const [dimension, setDimension] = useState({ width: 100, height: 100 });
44+
4245
const [isDragging, setIsDragging] = useState(false);
4346
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
4447

45-
const onMouseDown = useCallback(
48+
const [isResizing, setIsResizing] = useState(false);
49+
const [resizeOffset, setResizeOffset] = useState({ x: 0, y: 0 });
50+
const [dimensionBeforeResize, setDimensionBeforeResize] = useState(dimension);
51+
52+
const onDragStart = useCallback(
4653
(event: GridMouseEvent) => {
47-
logger.debug('onMouseDown');
54+
logger.debug('onDragStart');
55+
4856
setIsDragging(true);
57+
4958
setDragOffset({
5059
x: event.clientX - position.x,
5160
y: event.clientY - position.y,
@@ -54,13 +63,35 @@ export const Grid3: React.FC<Grid3Props> = (props: Grid3Props): ReactNode => {
5463
[logger, position]
5564
);
5665

66+
const onResizeStart = useCallback(
67+
(event: GridMouseEvent) => {
68+
logger.debug('onResizeStart');
69+
70+
setIsResizing(true);
71+
72+
setResizeOffset({
73+
x: event.clientX,
74+
y: event.clientY,
75+
});
76+
77+
setDimensionBeforeResize({
78+
width: dimension.width,
79+
height: dimension.height,
80+
});
81+
},
82+
[logger, dimension]
83+
);
84+
5785
const onMouseMove = useCallback(
5886
(event: GridMouseEvent) => {
59-
if (!gridItemRef.current) {
60-
return;
61-
}
87+
const handleDrag = () => {
88+
if (!gridItemRef.current) {
89+
return;
90+
}
91+
92+
const currentWidth = gridItemRef.current.clientWidth;
93+
const currentHeight = gridItemRef.current.clientHeight;
6294

63-
if (isDragging) {
6495
let newX = event.clientX - dragOffset.x;
6596
let newY = event.clientY - dragOffset.y;
6697

@@ -81,20 +112,18 @@ export const Grid3: React.FC<Grid3Props> = (props: Grid3Props): ReactNode => {
81112
// If the new position would reach farther right than the parent
82113
// then adjust the position of the right edge of the element
83114
// to be at the right edge of the parent.
84-
if (newX + gridItemRef.current.clientWidth > dimensions.width) {
85-
newX = dimensions.width - gridItemRef.current.clientWidth;
115+
if (newX + currentWidth > gridDimensions.width) {
116+
newX = gridDimensions.width - currentWidth;
86117
}
87118

88119
// If the new position would reach farther down than the parent
89120
// then adjust the position of the bottom edge of the element
90121
// to be at the bottom edge of the parent.
91-
if (newY + gridItemRef.current.clientHeight > dimensions.height) {
92-
newY = dimensions.height - gridItemRef.current.clientHeight;
122+
if (newY + currentHeight > gridDimensions.height) {
123+
newY = gridDimensions.height - currentHeight;
93124
}
94125

95126
logger.debug('onMouseMove - dragging', {
96-
calcX: event.clientX - dragOffset.x,
97-
calcY: event.clientY - dragOffset.y,
98127
newX,
99128
newY,
100129
});
@@ -103,67 +132,166 @@ export const Grid3: React.FC<Grid3Props> = (props: Grid3Props): ReactNode => {
103132
x: newX,
104133
y: newY,
105134
});
135+
};
136+
137+
const handleResize = () => {
138+
if (!gridItemRef.current) {
139+
return;
140+
}
141+
142+
const currentLeft = gridItemRef.current.offsetLeft;
143+
const currentTop = gridItemRef.current.offsetTop;
144+
145+
const currentWidth = dimensionBeforeResize.width;
146+
const currentHeight = dimensionBeforeResize.height;
147+
148+
const deltaWidth = event.clientX - resizeOffset.x;
149+
const deltaHeight = event.clientY - resizeOffset.y;
150+
151+
let newWidth = currentWidth + deltaWidth;
152+
let newHeight = currentHeight + deltaHeight;
153+
154+
// If the new size would shrink the element to be smaller than
155+
// the minimum size then set the size to the minimum.
156+
const minWidth = 50;
157+
const minHeight = 50;
158+
159+
if (newWidth < minWidth) {
160+
newWidth = minWidth;
161+
}
162+
163+
if (newHeight < minHeight) {
164+
newHeight = minHeight;
165+
}
166+
167+
// If the new size would reach farther right than the parent then
168+
// adjust the size of the element to be at the right edge of the parent.
169+
if (currentLeft + newWidth > gridDimensions.width) {
170+
newWidth = gridDimensions.width - currentLeft;
171+
}
172+
173+
// If the new size would reach farther down than the parent then
174+
// adjust the size of the element to be at the bottom edge of the parent.
175+
if (currentTop + newHeight > gridDimensions.height) {
176+
newHeight = gridDimensions.height - currentTop;
177+
}
178+
179+
logger.debug('onMouseMove - resizing', {
180+
deltaWidth,
181+
deltaHeight,
182+
oldWidth: currentWidth,
183+
oldHeight: currentHeight,
184+
newWidth,
185+
newHeight,
186+
currentTop,
187+
currentLeft,
188+
});
189+
190+
setDimension({
191+
width: newWidth,
192+
height: newHeight,
193+
});
194+
};
195+
196+
if (isDragging) {
197+
handleDrag();
106198
}
107-
},
108-
[logger, dimensions, isDragging, dragOffset]
109-
);
110199

111-
const onMouseUp = useCallback(
112-
(_event: GridMouseEvent) => {
113-
logger.debug('onMouseUp');
114-
setIsDragging(false);
200+
if (isResizing) {
201+
handleResize();
202+
}
115203
},
116-
[logger]
204+
[
205+
logger,
206+
gridDimensions,
207+
isDragging,
208+
dragOffset,
209+
isResizing,
210+
resizeOffset,
211+
dimensionBeforeResize,
212+
]
117213
);
118214

215+
const onMouseUp = useCallback(() => {
216+
logger.debug('onMouseUp');
217+
setIsDragging(false);
218+
setIsResizing(false);
219+
}, [logger]);
220+
119221
// When we attach the `mousemove` event listeners to the draggable element
120222
// then if the mouse moves too quickly then it can leave the element behind.
121223
// Once that happens, then the mouse events stop being emitted and so it
122224
// doesn't reposition to stay under the mouse cursor.
123225
// A workaround is to attach the `mousemove` event listeners to the `window`
124226
// so that the event always fires.
125227
useEffect(() => {
126-
const windowOnMouseMove = (event: WindowEventMap['mousemove']) => {
127-
onMouseMove(event);
128-
};
228+
// Events that signal the user has stopped dragging or resizing.
229+
const onMouseUpEventAliases: Array<keyof WindowEventMap> = [
230+
'mouseup',
231+
'mouseleave',
232+
'blur',
233+
];
129234

130-
const windowOnMouseUp = (event: WindowEventMap['mouseup']) => {
131-
onMouseUp(event);
132-
};
235+
onMouseUpEventAliases.forEach((eventName) => {
236+
window.addEventListener(eventName, onMouseUp);
237+
});
133238

134-
window.addEventListener('mousemove', windowOnMouseMove);
135-
window.addEventListener('mouseup', windowOnMouseUp);
239+
window.addEventListener('mousemove', onMouseMove);
136240

137241
return () => {
138-
window.removeEventListener('mousemove', windowOnMouseMove);
139-
window.removeEventListener('mouseup', windowOnMouseUp);
242+
onMouseUpEventAliases.forEach((eventName) => {
243+
window.removeEventListener(eventName, onMouseUp);
244+
});
245+
246+
window.removeEventListener('mousemove', onMouseMove);
140247
};
141248
}, [onMouseMove, onMouseUp]);
142249

143250
return (
144251
<div
145252
style={{
146-
height: dimensions.height,
147-
width: dimensions.width,
253+
position: 'relative',
254+
height: gridDimensions.height,
255+
width: gridDimensions.width,
148256
}}
149257
>
150258
<div
151259
ref={gridItemRef}
152260
style={{
153-
width: '100px',
154-
height: '100px',
155-
backgroundColor: 'lightblue',
156261
position: 'relative',
157262
top: `${position.y}px`,
158263
left: `${position.x}px`,
159-
cursor: 'move',
264+
width: `${dimension.width}px`,
265+
height: `${dimension.height}px`,
266+
backgroundColor: 'lightblue',
267+
overflow: 'auto',
160268
}}
161-
onMouseDown={onMouseDown}
162-
// onMouseMove={onMouseMove}
163-
// onMouseUp={onMouseUp}
164-
// onMouseLeave={onMouseLeave}
165269
>
166-
Drag me!
270+
<div
271+
style={{
272+
position: 'relative',
273+
width: '100%',
274+
height: '20px',
275+
backgroundColor: 'red',
276+
cursor: isDragging ? 'grabbing' : 'grab',
277+
textAlign: 'center',
278+
}}
279+
onMouseDown={onDragStart}
280+
>
281+
Drag Handle
282+
</div>
283+
284+
<div
285+
style={{
286+
position: 'absolute',
287+
bottom: 0,
288+
right: 0,
289+
borderLeft: '10px solid transparent',
290+
borderBottom: '10px solid green',
291+
cursor: 'se-resize',
292+
}}
293+
onMouseDown={onResizeStart}
294+
></div>
167295
</div>
168296
</div>
169297
);

0 commit comments

Comments
 (0)