Skip to content

Commit ff9c96c

Browse files
committed
feat(modal): modal support resizable
1 parent 518dfa2 commit ff9c96c

File tree

12 files changed

+316
-37
lines changed

12 files changed

+316
-37
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"@types/lodash-es": "^4.17.12",
8484
"@types/react": "^18.0.0",
8585
"@types/react-syntax-highlighter": "~15.5.13",
86+
"@types/react-resizable": "^3.0.8",
8687
"@types/shortid": "^0.0.31",
8788
"@types/showdown": "^1.9.0",
8889
"@types/testing-library__jest-dom": "^5.14.5",
@@ -125,6 +126,7 @@
125126
"react-syntax-highlighter": "~15.5.0",
126127
"remark-gfm": "~3.0.1",
127128
"react-draggable": "~4.4.6",
129+
"react-resizable": "^3.0.5",
128130
"shortid": "^2.2.16",
129131
"showdown": "^1.9.0"
130132
},

pnpm-lock.yaml

Lines changed: 22 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/float/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import React, { useState } from 'react';
22
import Draggable, { DraggableEventHandler, type DraggableProps } from 'react-draggable';
33
import classNames from 'classnames';
44

5-
import useMergeOption from './useMergeOption';
5+
import useMergeOption, { MergeOption } from './useMergeOption';
66
import './index.scss';
77

88
export interface IFloatProps {
99
className?: string;
1010
style?: React.CSSProperties;
11-
draggable?: boolean | Partial<Omit<DraggableProps, 'position'>>;
11+
draggable?: MergeOption<Partial<Omit<DraggableProps, 'position'>>>;
1212
position?: DraggableProps['position'];
1313
onChange?: DraggableProps['onStop'];
1414
}

src/float/useMergeOption.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,19 @@ export type ReturnMergeOption<T extends Record<string, any>> = {
88
};
99

1010
export default function useMergeOption<T extends Record<string, any>>(
11-
opt: MergeOption<T>
11+
opt: MergeOption<T>,
12+
defaultOpt?: T
1213
): ReturnMergeOption<T> {
1314
return useMemo(() => {
14-
if (typeof opt === 'object') {
15+
if (typeof opt === 'object' && !!opt) {
1516
return {
1617
disabled: false,
17-
options: opt,
18+
options: { ...defaultOpt, ...opt },
1819
};
1920
}
2021
return {
2122
disabled: !opt,
22-
options: <T>{},
23+
options: <T>{ ...defaultOpt },
2324
};
2425
}, [opt]);
2526
}

src/modal/demos/draggable.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Modal, TinyTag } from 'dt-react-component';
44

55
export default function Basic() {
66
const [visible, setVisible] = useState(false);
7+
const [position, setPosition] = useState({ x: 120, y: 120 });
78

89
return (
910
<>
@@ -23,6 +24,8 @@ export default function Basic() {
2324
draggable={{
2425
bounds: 'body',
2526
}}
27+
position={position}
28+
onPositionChange={(_, { x, y }) => setPosition({ x, y })}
2629
visible={visible}
2730
onCancel={() => setVisible(false)}
2831
onOk={() => setVisible(false)}

src/modal/demos/resizable.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React, { useState } from 'react';
2+
import { Button } from 'antd';
3+
import { Modal, Resize, useModal } from 'dt-react-component';
4+
import { RectState } from 'dt-react-component/modal';
5+
6+
export default function Basic() {
7+
const modal = useModal<void>();
8+
const [rect, setRect] = useState<RectState>({ width: 520, height: 520 });
9+
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
10+
11+
return (
12+
<Resize onResize={() => setSize({ width: window.innerWidth, height: window.innerHeight })}>
13+
<Modal
14+
title="Resizable Modal"
15+
visible={modal.visible}
16+
resizable={{
17+
maxConstraints: [size.width - 40, size.height - 150],
18+
}}
19+
rect={rect}
20+
onRectChange={setRect}
21+
onCancel={() => modal.close()}
22+
onOk={() => modal.close()}
23+
>
24+
<ul>
25+
{Array.from({ length: 300 }).map((_, i) => (
26+
<li key={i} style={{ height: 30 }}>
27+
{i}
28+
</li>
29+
))}
30+
</ul>
31+
</Modal>
32+
<Button type="primary" onClick={() => modal.open()}>
33+
Resizable Modal
34+
</Button>
35+
</Resize>
36+
);
37+
}

src/modal/demos/window.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React, { useRef, useState } from 'react';
2+
import { ResizeHandle } from 'react-resizable';
3+
import { Button } from 'antd';
4+
import { Modal, Resize } from 'dt-react-component';
5+
import { RectState } from 'dt-react-component/modal';
6+
7+
export default function Basic() {
8+
const [visible, setVisible] = useState(false);
9+
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
10+
11+
const [position, setPosition] = useState({ x: 120, y: 120 });
12+
const [rect, setRect] = useState<RectState>({ width: 520, height: 520 });
13+
14+
const resizeDirection = useRef<ResizeHandle>('e');
15+
16+
// 限制 resize 超出当前屏幕
17+
const getMaxConstraints = (): [number, number] => {
18+
switch (resizeDirection.current) {
19+
case 'e':
20+
return [size.width - position.x, size.height];
21+
case 'n':
22+
return [size.width, position.y + rect.height];
23+
case 's':
24+
return [size.width, size.height - position.y];
25+
case 'w':
26+
return [position.x + rect.width, size.height];
27+
case 'ne':
28+
return [size.width - position.x, position.y + rect.height];
29+
case 'nw':
30+
return [position.x + rect.width, position.y + rect.height];
31+
case 'se':
32+
return [size.width - position.x, size.height - position.y];
33+
case 'sw':
34+
return [position.x + rect.width, size.height - position.y];
35+
default:
36+
return [0, 0];
37+
}
38+
};
39+
40+
return (
41+
<Resize onResize={() => setSize({ width: window.innerWidth, height: window.innerHeight })}>
42+
<Modal
43+
title="Window Modal"
44+
visible={visible}
45+
mask={false}
46+
resizable={{
47+
maxConstraints: getMaxConstraints(),
48+
onResize: (_, data) => {
49+
resizeDirection.current = data.handle;
50+
},
51+
}}
52+
rect={rect}
53+
draggable={{
54+
bounds: 'body',
55+
}}
56+
position={position}
57+
onPositionChange={setPosition}
58+
onRectChange={setRect}
59+
onCancel={() => setVisible(false)}
60+
onOk={() => setVisible(false)}
61+
>
62+
<>Just Dtstack It</>
63+
</Modal>
64+
<Button type="primary" onClick={() => setVisible(true)}>
65+
Window Modal
66+
</Button>
67+
</Resize>
68+
);
69+
}

src/modal/handle/index.scss

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.dt-modal-resize-handle {
2+
position: absolute;
3+
z-index: 1;
4+
pointer-events: initial;
5+
&.handle-w {
6+
cursor: col-resize;
7+
left: -5px;
8+
bottom: 0;
9+
width: 10px;
10+
height: 100%;
11+
}
12+
&.handle-e {
13+
cursor: col-resize;
14+
right: -5px;
15+
bottom: 0;
16+
width: 10px;
17+
height: 100%;
18+
}
19+
&.handle-n {
20+
cursor: row-resize;
21+
left: 0;
22+
top: -5px;
23+
width: 100%;
24+
height: 10px;
25+
}
26+
&.handle-s {
27+
cursor: row-resize;
28+
left: 0;
29+
bottom: -5px;
30+
width: 100%;
31+
height: 10px;
32+
}
33+
&.handle-se {
34+
cursor: se-resize;
35+
right: 0;
36+
bottom: -5px;
37+
width: 10px;
38+
height: 10px;
39+
z-index: 11;
40+
}
41+
&.handle-ne {
42+
cursor: ne-resize;
43+
right: 0;
44+
top: -5px;
45+
width: 10px;
46+
height: 10px;
47+
z-index: 11;
48+
}
49+
&.handle-nw {
50+
cursor: nw-resize;
51+
left: 0;
52+
top: -5px;
53+
width: 10px;
54+
height: 10px;
55+
z-index: 11;
56+
}
57+
&.handle-sw {
58+
cursor: sw-resize;
59+
left: 0;
60+
bottom: -5px;
61+
width: 10px;
62+
height: 10px;
63+
z-index: 11;
64+
}
65+
}

src/modal/handle/index.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
3+
import './index.scss';
4+
5+
const Handler = React.forwardRef<HTMLDivElement, any>((props, ref) => {
6+
const { handleAxis, ...restProps } = props;
7+
return (
8+
<div ref={ref} className={`dt-modal-resize-handle handle-${handleAxis}`} {...restProps} />
9+
);
10+
});
11+
12+
export default Handler;

src/modal/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ toc: content
1919
<code src="./demos/banner.tsx" title="支持 banner"></code>
2020
<code src="./demos/bannerProps.tsx" title="支持传 banner 的 Props 属性"></code>
2121
<code src="./demos/draggable.tsx" title="draggable"></code>
22+
<code src="./demos/resizable.tsx" title="resizable"></code>
23+
<code src="./demos/window.tsx" title="窗口模式即支持 draggable 同时也支持 resizable"></code>
2224

2325
## API
2426

0 commit comments

Comments
 (0)