Skip to content

Commit e16a142

Browse files
committed
feat: refactor resizable drawer to use drag hook
1 parent 9d462a0 commit e16a142

File tree

6 files changed

+108
-104
lines changed

6 files changed

+108
-104
lines changed

assets/index.less

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@
5959
pointer-events: auto;
6060
}
6161

62-
// Resizable styles
63-
&-resizable-line {
62+
// Dragger styles
63+
&-resizable-dragger {
6464
position: absolute;
6565
z-index: 2;
6666
pointer-events: auto;

docs/examples/resizable.tsx

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,32 @@ import type { Placement } from '@/index';
99

1010
export default () => {
1111
const [open, setOpen] = React.useState(false);
12-
const [placement, setPlacement] = React.useState('right');
13-
const [resizable, setResizable] = React.useState(true);
12+
const [placement, setPlacement] = React.useState<Placement>('right');
13+
14+
const buttons = [
15+
{ placement: 'left' as Placement, label: 'Left Drawer' },
16+
{ placement: 'right' as Placement, label: 'Right Drawer' },
17+
{ placement: 'top' as Placement, label: 'Top Drawer' },
18+
{ placement: 'bottom' as Placement, label: 'Bottom Drawer' },
19+
];
20+
21+
const openDrawer = (direction: Placement) => {
22+
setPlacement(direction);
23+
setOpen(true);
24+
};
1425

1526
return (
1627
<div>
17-
<div style={{ marginBottom: 16 }}>
18-
<button onClick={() => setOpen(true)} style={{ marginRight: 8 }}>
19-
Open Drawer
20-
</button>
21-
<label style={{ marginRight: 8 }}>
22-
<input
23-
type="checkbox"
24-
checked={resizable}
25-
onChange={e => setResizable(e.target.checked)}
26-
/>
27-
Resizable
28-
</label>
29-
<span>Placement: </span>
30-
<select value={placement} onChange={e => setPlacement(e.target.value)}>
31-
<option value="left">Left</option>
32-
<option value="right">Right</option>
33-
<option value="top">Top</option>
34-
<option value="bottom">Bottom</option>
35-
</select>
28+
<div style={{ marginBottom: 16, display: 'flex', gap: 8 }}>
29+
{buttons.map(({ placement, label }) => (
30+
<button
31+
key={placement}
32+
onClick={() => openDrawer(placement)}
33+
style={{ padding: '8px 16px' }}
34+
>
35+
{label}
36+
</button>
37+
))}
3638
</div>
3739
<Drawer
3840
width={placement === 'left' || placement === 'right' ? 320 : undefined}
@@ -41,14 +43,11 @@ export default () => {
4143
open={open}
4244
key={placement}
4345
onClose={() => setOpen(false)}
44-
resizable={resizable}
46+
resizable
4547
{...motionProps}
4648
>
4749
<div style={{ marginTop: 24, color: '#888' }}>
48-
<p>
49-
You can drag the drawer edge to resize (only works when
50-
&quot;Resizable&quot; is checked).
51-
</p>
50+
<p>You can drag the drawer edge to resize.</p>
5251
</div>
5352
</Drawer>
5453
</div>

src/DrawerPopup.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
DrawerPanelEvents,
1212
} from './DrawerPanel';
1313
import DrawerPanel from './DrawerPanel';
14-
import ResizableLine from './ResizableLine';
14+
import useDrag from './hooks/useDrag';
1515
import { parseWidthHeight } from './util';
1616
import type { DrawerClassNames, DrawerStyles } from './inter';
1717

@@ -296,7 +296,6 @@ const DrawerPopup: React.ForwardRefRenderFunction<
296296

297297
// ============================ Resizable ============================
298298
const [currentSize, setCurrentSize] = React.useState<number>();
299-
const [isDragging, setIsDragging] = React.useState(false);
300299
const [maxSize, setMaxSize] = React.useState(0);
301300
const wrapperRef = React.useRef<HTMLDivElement>(null);
302301

@@ -330,9 +329,20 @@ const DrawerPopup: React.ForwardRefRenderFunction<
330329
onResizeEnd?.();
331330
}, [onResizeEnd]);
332331

333-
const handleDraggingChange = React.useCallback((dragging: boolean) => {
334-
setIsDragging(dragging);
335-
}, []);
332+
// Use drag hook for resizable functionality
333+
const { dragElementProps, isDragging } = useDrag({
334+
prefixCls: `${prefixCls}-resizable`,
335+
direction: placement,
336+
className: drawerClassNames?.dragger,
337+
style: styles?.dragger,
338+
minSize: 0,
339+
maxSize,
340+
disabled: !resizable,
341+
container: wrapperRef.current,
342+
onResize: handleResize,
343+
onResizeStart: handleResizeStart,
344+
onResizeEnd: handleResizeEnd,
345+
});
336346

337347
const dynamicWrapperStyle = React.useMemo(() => {
338348
const style: React.CSSProperties = { ...wrapperStyle };
@@ -403,21 +413,7 @@ const DrawerPopup: React.ForwardRefRenderFunction<
403413
}}
404414
{...pickAttrs(props, { data: true })}
405415
>
406-
{resizable && (
407-
<ResizableLine
408-
prefixCls={`${prefixCls}-resizable`}
409-
direction={placement}
410-
className={drawerClassNames?.resizableLine}
411-
style={styles?.resizableLine}
412-
minSize={0}
413-
maxSize={maxSize}
414-
isDragging={isDragging}
415-
onResize={handleResize}
416-
onResizeStart={handleResizeStart}
417-
onResizeEnd={handleResizeEnd}
418-
onDraggingChange={handleDraggingChange}
419-
/>
420-
)}
416+
{resizable && <div {...dragElementProps} />}
421417
{drawerRender ? drawerRender(content) : content}
422418
</div>
423419
);
Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,80 @@
1-
import classNames from 'classnames';
21
import * as React from 'react';
3-
import type { Placement } from './Drawer';
2+
import classNames from 'classnames';
3+
import type { Placement } from '../Drawer';
44

5-
export interface ResizableLineProps {
5+
export interface UseDragOptions {
66
prefixCls?: string;
77
direction: Placement;
88
className?: string;
99
style?: React.CSSProperties;
1010
minSize?: number;
1111
maxSize?: number;
12-
isDragging?: boolean;
12+
disabled?: boolean;
13+
container?: HTMLElement | null;
1314
onResize?: (size: number) => void;
1415
onResizeEnd?: (size: number) => void;
1516
onResizeStart?: (size: number) => void;
16-
onDraggingChange?: (dragging: boolean) => void;
1717
}
1818

19-
const ResizableLine: React.FC<ResizableLineProps> = ({
20-
prefixCls = 'resizable',
21-
direction,
22-
className,
23-
style,
24-
minSize = 100,
25-
maxSize,
26-
isDragging = false,
27-
onResize,
28-
onResizeEnd,
29-
onResizeStart,
30-
onDraggingChange,
31-
}) => {
32-
const lineRef = React.useRef<HTMLDivElement>(null);
19+
export interface UseDragReturn {
20+
dragElementProps: {
21+
className: string;
22+
style: React.CSSProperties;
23+
onMouseDown: (e: React.MouseEvent) => void;
24+
};
25+
isDragging: boolean;
26+
}
27+
28+
export default function useDrag(options: UseDragOptions): UseDragReturn {
29+
const {
30+
prefixCls = 'resizable',
31+
direction,
32+
className,
33+
style,
34+
minSize = 100,
35+
maxSize,
36+
disabled = false,
37+
container,
38+
onResize,
39+
onResizeEnd,
40+
onResizeStart,
41+
} = options;
42+
43+
const [isDragging, setIsDragging] = React.useState(false);
3344
const [startPos, setStartPos] = React.useState(0);
3445
const [startSize, setStartSize] = React.useState(0);
3546

3647
const isHorizontal = direction === 'left' || direction === 'right';
3748

3849
const handleMouseDown = React.useCallback(
3950
(e: React.MouseEvent) => {
51+
if (disabled) return;
52+
4053
e.preventDefault();
4154
e.stopPropagation();
4255

43-
onDraggingChange?.(true);
56+
setIsDragging(true);
4457

4558
if (isHorizontal) {
4659
setStartPos(e.clientX);
4760
} else {
4861
setStartPos(e.clientY);
4962
}
5063

51-
// Get the current size of the parent container
52-
const parentElement = lineRef.current?.parentElement;
53-
if (parentElement) {
54-
const rect = parentElement.getBoundingClientRect();
64+
// Get the current size of the container
65+
if (container) {
66+
const rect = container.getBoundingClientRect();
5567
const currentSize = isHorizontal ? rect.width : rect.height;
5668
setStartSize(currentSize);
5769
onResizeStart?.(currentSize);
5870
}
5971
},
60-
[isHorizontal, onResizeStart, onDraggingChange],
72+
[disabled, isHorizontal, container, onResizeStart],
6173
);
6274

6375
const handleMouseMove = React.useCallback(
6476
(e: MouseEvent) => {
65-
if (!isDragging) return;
77+
if (!isDragging || disabled) return;
6678

6779
const currentPos = isHorizontal ? e.clientX : e.clientY;
6880
let delta = currentPos - startPos;
@@ -87,6 +99,7 @@ const ResizableLine: React.FC<ResizableLineProps> = ({
8799
},
88100
[
89101
isDragging,
102+
disabled,
90103
startPos,
91104
startSize,
92105
direction,
@@ -98,18 +111,17 @@ const ResizableLine: React.FC<ResizableLineProps> = ({
98111
);
99112

100113
const handleMouseUp = React.useCallback(() => {
101-
if (isDragging) {
102-
onDraggingChange?.(false);
114+
if (isDragging && !disabled) {
115+
setIsDragging(false);
103116

104117
// Get the final size after resize
105-
const parentElement = lineRef.current?.parentElement;
106-
if (parentElement) {
107-
const rect = parentElement.getBoundingClientRect();
118+
if (container) {
119+
const rect = container.getBoundingClientRect();
108120
const finalSize = isHorizontal ? rect.width : rect.height;
109121
onResizeEnd?.(finalSize);
110122
}
111123
}
112-
}, [isDragging, onResizeEnd, isHorizontal, onDraggingChange]);
124+
}, [isDragging, disabled, container, onResizeEnd, isHorizontal]);
113125

114126
React.useEffect(() => {
115127
if (isDragging) {
@@ -123,30 +135,27 @@ const ResizableLine: React.FC<ResizableLineProps> = ({
123135
}
124136
}, [isDragging, handleMouseMove, handleMouseUp]);
125137

126-
const resizeLineClassName = classNames(
127-
`${prefixCls}-line`,
128-
`${prefixCls}-line-${direction}`,
138+
const dragElementClassName = classNames(
139+
`${prefixCls}-dragger`,
140+
`${prefixCls}-dragger-${direction}`,
129141
{
130-
[`${prefixCls}-line-dragging`]: isDragging,
142+
[`${prefixCls}-dragger-dragging`]: isDragging,
143+
[`${prefixCls}-dragger-horizontal`]: isHorizontal,
144+
[`${prefixCls}-dragger-vertical`]: !isHorizontal,
131145
},
132146
className,
133147
);
134148

135-
const resizeLineStyle: React.CSSProperties = {
136-
position: 'absolute',
137-
zIndex: 2,
138-
cursor: isHorizontal ? 'col-resize' : 'row-resize',
149+
const dragElementStyle: React.CSSProperties = {
139150
...style,
140151
};
141152

142-
return (
143-
<div
144-
ref={lineRef}
145-
className={resizeLineClassName}
146-
style={resizeLineStyle}
147-
onMouseDown={handleMouseDown}
148-
/>
149-
);
150-
};
151-
152-
export default ResizableLine;
153+
return {
154+
dragElementProps: {
155+
className: dragElementClassName,
156+
style: dragElementStyle,
157+
onMouseDown: handleMouseDown,
158+
},
159+
isDragging,
160+
};
161+
}

src/inter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ export interface DrawerClassNames {
22
mask?: string;
33
wrapper?: string;
44
section?: string;
5-
resizableLine?: string;
5+
dragger?: string;
66
}
77

88
export interface DrawerStyles {
99
mask?: React.CSSProperties;
1010
wrapper?: React.CSSProperties;
1111
section?: React.CSSProperties;
12-
resizableLine?: React.CSSProperties;
12+
dragger?: React.CSSProperties;
1313
}

tests/index.spec.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,11 +501,11 @@ describe('rc-drawer-menu', () => {
501501
);
502502
contentWrapper.getBoundingClientRect = mockGetBoundingClientRect;
503503

504-
const resizableLine = document.querySelector('.rc-drawer-resizable-line');
505-
expect(resizableLine).toBeTruthy();
504+
const dragger = document.querySelector('.rc-drawer-resizable-dragger');
505+
expect(dragger).toBeTruthy();
506506

507507
// Simulate drag from 200px to 100px (should reduce width by 100px)
508-
fireEvent.mouseDown(resizableLine, { clientX: 200 });
508+
fireEvent.mouseDown(dragger, { clientX: 200 });
509509
fireEvent.mouseMove(document, { clientX: 100, clientY: 0 });
510510
fireEvent.mouseUp(document, { clientX: 100, clientY: 0 });
511511

0 commit comments

Comments
 (0)