react-resizable-panels 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +6 -5
- package/dist/react-resizable-panels.d.ts +5 -4
- package/dist/react-resizable-panels.d.ts.map +1 -1
- package/dist/react-resizable-panels.js +66 -50
- package/dist/react-resizable-panels.js.map +1 -1
- package/dist/react-resizable-panels.module.js +67 -51
- package/dist/react-resizable-panels.module.js.map +1 -1
- package/package.json +1 -1
- package/src/Panel.tsx +12 -9
- package/src/PanelContexts.ts +2 -2
- package/src/PanelGroup.tsx +21 -6
- package/src/PanelResizeHandle.tsx +13 -11
- package/src/hooks/useUniqueId.ts +7 -3
- package/src/utils/coordinates.ts +31 -20
package/src/PanelGroup.tsx
CHANGED
|
@@ -8,12 +8,11 @@ import {
|
|
|
8
8
|
useRef,
|
|
9
9
|
useState,
|
|
10
10
|
} from "react";
|
|
11
|
-
import useUniqueId from "./hooks/useUniqueId";
|
|
12
11
|
|
|
13
12
|
import { PanelContext, PanelGroupContext } from "./PanelContexts";
|
|
14
13
|
import { Direction, PanelData, ResizeEvent } from "./types";
|
|
15
14
|
import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
|
|
16
|
-
import { getMovement } from "./utils/coordinates";
|
|
15
|
+
import { getDragOffset, getMovement } from "./utils/coordinates";
|
|
17
16
|
import {
|
|
18
17
|
adjustByDelta,
|
|
19
18
|
getOffset,
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
panelsMapToSortedArray,
|
|
23
22
|
} from "./utils/group";
|
|
24
23
|
import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterBehavior";
|
|
24
|
+
import useUniqueId from "./hooks/useUniqueId";
|
|
25
25
|
|
|
26
26
|
export type CommittedValues = {
|
|
27
27
|
direction: Direction;
|
|
@@ -39,6 +39,7 @@ type Props = {
|
|
|
39
39
|
className?: string;
|
|
40
40
|
direction: Direction;
|
|
41
41
|
height: number;
|
|
42
|
+
id?: string | null;
|
|
42
43
|
width: number;
|
|
43
44
|
};
|
|
44
45
|
|
|
@@ -52,9 +53,10 @@ export default function PanelGroup({
|
|
|
52
53
|
className = "",
|
|
53
54
|
direction,
|
|
54
55
|
height,
|
|
56
|
+
id: idFromProps = null,
|
|
55
57
|
width,
|
|
56
58
|
}: Props) {
|
|
57
|
-
const groupId = useUniqueId();
|
|
59
|
+
const groupId = useUniqueId(idFromProps);
|
|
58
60
|
|
|
59
61
|
const [activeHandleId, setActiveHandleId] = useState<string | null>(null);
|
|
60
62
|
const [panels, setPanels] = useState<PanelDataMap>(new Map());
|
|
@@ -62,6 +64,8 @@ export default function PanelGroup({
|
|
|
62
64
|
// 0-1 values representing the relative size of each panel.
|
|
63
65
|
const [sizes, setSizes] = useState<number[]>([]);
|
|
64
66
|
|
|
67
|
+
const dragOffsetRef = useRef<number>(0);
|
|
68
|
+
|
|
65
69
|
// Store committed values to avoid unnecessarily re-running memoization/effects functions.
|
|
66
70
|
const committedValuesRef = useRef<CommittedValues>({
|
|
67
71
|
direction,
|
|
@@ -200,7 +204,8 @@ export default function PanelGroup({
|
|
|
200
204
|
event,
|
|
201
205
|
handleId,
|
|
202
206
|
{ height, width },
|
|
203
|
-
direction
|
|
207
|
+
direction,
|
|
208
|
+
dragOffsetRef.current
|
|
204
209
|
);
|
|
205
210
|
|
|
206
211
|
const isHorizontal = direction === "horizontal";
|
|
@@ -243,7 +248,11 @@ export default function PanelGroup({
|
|
|
243
248
|
groupId,
|
|
244
249
|
registerPanel,
|
|
245
250
|
registerResizeHandle,
|
|
246
|
-
startDragging: (id: string) =>
|
|
251
|
+
startDragging: (id: string, event: ResizeEvent) => {
|
|
252
|
+
setActiveHandleId(id);
|
|
253
|
+
|
|
254
|
+
dragOffsetRef.current = getDragOffset(event, id, direction);
|
|
255
|
+
},
|
|
247
256
|
stopDragging: () => {
|
|
248
257
|
setActiveHandleId(null);
|
|
249
258
|
},
|
|
@@ -269,7 +278,13 @@ export default function PanelGroup({
|
|
|
269
278
|
return (
|
|
270
279
|
<PanelContext.Provider value={panelContext}>
|
|
271
280
|
<PanelGroupContext.Provider value={panelGroupContext}>
|
|
272
|
-
<div
|
|
281
|
+
<div
|
|
282
|
+
className={className}
|
|
283
|
+
data-panel-group-id={groupId}
|
|
284
|
+
style={{ height, position: "relative", width }}
|
|
285
|
+
>
|
|
286
|
+
{children}
|
|
287
|
+
</div>
|
|
273
288
|
</PanelGroupContext.Provider>
|
|
274
289
|
</PanelContext.Provider>
|
|
275
290
|
);
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
useRef,
|
|
7
7
|
useState,
|
|
8
8
|
} from "react";
|
|
9
|
-
|
|
10
9
|
import useUniqueId from "./hooks/useUniqueId";
|
|
10
|
+
|
|
11
11
|
import { useWindowSplitterResizeHandlerBehavior } from "./hooks/useWindowSplitterBehavior";
|
|
12
12
|
import { PanelContext, PanelGroupContext } from "./PanelContexts";
|
|
13
13
|
import type { ResizeHandler, ResizeEvent } from "./types";
|
|
@@ -16,7 +16,7 @@ export default function PanelResizeHandle({
|
|
|
16
16
|
children = null,
|
|
17
17
|
className = "",
|
|
18
18
|
disabled = false,
|
|
19
|
-
id:
|
|
19
|
+
id: idFromProps = null,
|
|
20
20
|
}: {
|
|
21
21
|
children?: ReactNode;
|
|
22
22
|
className?: string;
|
|
@@ -33,8 +33,6 @@ export default function PanelResizeHandle({
|
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const id = useUniqueId(idProp);
|
|
37
|
-
|
|
38
36
|
const { activeHandleId } = panelContext;
|
|
39
37
|
const {
|
|
40
38
|
direction,
|
|
@@ -44,7 +42,8 @@ export default function PanelResizeHandle({
|
|
|
44
42
|
stopDragging,
|
|
45
43
|
} = panelGroupContext;
|
|
46
44
|
|
|
47
|
-
const
|
|
45
|
+
const resizeHandleId = useUniqueId(idFromProps);
|
|
46
|
+
const isDragging = activeHandleId === resizeHandleId;
|
|
48
47
|
|
|
49
48
|
const [resizeHandler, setResizeHandler] = useState<ResizeHandler | null>(
|
|
50
49
|
null
|
|
@@ -63,10 +62,10 @@ export default function PanelResizeHandle({
|
|
|
63
62
|
if (disabled) {
|
|
64
63
|
setResizeHandler(null);
|
|
65
64
|
} else {
|
|
66
|
-
const resizeHandler = registerResizeHandle(
|
|
65
|
+
const resizeHandler = registerResizeHandle(resizeHandleId);
|
|
67
66
|
setResizeHandler(() => resizeHandler);
|
|
68
67
|
}
|
|
69
|
-
}, [disabled,
|
|
68
|
+
}, [disabled, resizeHandleId, registerResizeHandle]);
|
|
70
69
|
|
|
71
70
|
useEffect(() => {
|
|
72
71
|
if (disabled || resizeHandler == null || !isDragging) {
|
|
@@ -80,6 +79,7 @@ export default function PanelResizeHandle({
|
|
|
80
79
|
resizeHandler(event);
|
|
81
80
|
};
|
|
82
81
|
|
|
82
|
+
document.body.addEventListener("contextmenu", stopDraggingAndBlur);
|
|
83
83
|
document.body.addEventListener("mouseleave", stopDraggingAndBlur);
|
|
84
84
|
document.body.addEventListener("mousemove", onMove);
|
|
85
85
|
document.body.addEventListener("touchmove", onMove);
|
|
@@ -88,6 +88,7 @@ export default function PanelResizeHandle({
|
|
|
88
88
|
return () => {
|
|
89
89
|
document.body.style.cursor = "";
|
|
90
90
|
|
|
91
|
+
document.body.removeEventListener("contextmenu", stopDraggingAndBlur);
|
|
91
92
|
document.body.removeEventListener("mouseleave", stopDraggingAndBlur);
|
|
92
93
|
document.body.removeEventListener("mousemove", onMove);
|
|
93
94
|
document.body.removeEventListener("touchmove", onMove);
|
|
@@ -97,7 +98,7 @@ export default function PanelResizeHandle({
|
|
|
97
98
|
|
|
98
99
|
useWindowSplitterResizeHandlerBehavior({
|
|
99
100
|
disabled,
|
|
100
|
-
handleId:
|
|
101
|
+
handleId: resizeHandleId,
|
|
101
102
|
resizeHandler,
|
|
102
103
|
});
|
|
103
104
|
|
|
@@ -106,17 +107,18 @@ export default function PanelResizeHandle({
|
|
|
106
107
|
className={className}
|
|
107
108
|
data-panel-group-id={groupId}
|
|
108
109
|
data-panel-resize-handle-enabled={!disabled}
|
|
109
|
-
data-panel-resize-handle-id={
|
|
110
|
-
onMouseDown={() => startDragging(
|
|
110
|
+
data-panel-resize-handle-id={resizeHandleId}
|
|
111
|
+
onMouseDown={(event) => startDragging(resizeHandleId, event.nativeEvent)}
|
|
111
112
|
onMouseUp={stopDraggingAndBlur}
|
|
112
113
|
onTouchCancel={stopDraggingAndBlur}
|
|
113
114
|
onTouchEnd={stopDraggingAndBlur}
|
|
114
|
-
onTouchStart={() => startDragging(
|
|
115
|
+
onTouchStart={(event) => startDragging(resizeHandleId, event.nativeEvent)}
|
|
115
116
|
ref={divElementRef}
|
|
116
117
|
role="separator"
|
|
117
118
|
style={{
|
|
118
119
|
cursor: direction === "horizontal" ? "ew-resize" : "ns-resize",
|
|
119
120
|
touchAction: "none",
|
|
121
|
+
userSelect: "none",
|
|
120
122
|
}}
|
|
121
123
|
tabIndex={0}
|
|
122
124
|
>
|
package/src/hooks/useUniqueId.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { useRef } from "react";
|
|
1
|
+
import { useId, useRef } from "react";
|
|
2
2
|
|
|
3
3
|
let counter = 0;
|
|
4
4
|
|
|
5
|
-
export default function useUniqueId(
|
|
6
|
-
|
|
5
|
+
export default function useUniqueId(
|
|
6
|
+
idFromParams: string | null = null
|
|
7
|
+
): string {
|
|
8
|
+
const idFromUseId = typeof useId === "function" ? useId() : null;
|
|
9
|
+
|
|
10
|
+
const idRef = useRef<string | null>(idFromParams || idFromUseId || null);
|
|
7
11
|
if (idRef.current === null) {
|
|
8
12
|
idRef.current = "" + counter++;
|
|
9
13
|
}
|
package/src/utils/coordinates.ts
CHANGED
|
@@ -11,15 +11,38 @@ export type Size = {
|
|
|
11
11
|
width: number;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export function getDragOffset(
|
|
15
|
+
event: ResizeEvent,
|
|
16
|
+
handleId: string,
|
|
17
|
+
direction: Direction,
|
|
18
|
+
initialOffset: number = 0
|
|
19
|
+
): number {
|
|
20
|
+
const isHorizontal = direction === "horizontal";
|
|
21
|
+
|
|
22
|
+
let pointerOffset = 0;
|
|
23
|
+
if (isMouseEvent(event)) {
|
|
24
|
+
pointerOffset = isHorizontal ? event.clientX : event.clientY;
|
|
25
|
+
} else if (isTouchEvent(event)) {
|
|
26
|
+
const firstTouch = event.touches[0];
|
|
27
|
+
pointerOffset = isHorizontal ? firstTouch.screenX : firstTouch.screenY;
|
|
28
|
+
} else {
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const handleElement = getResizeHandle(handleId);
|
|
33
|
+
const rect = handleElement.getBoundingClientRect();
|
|
34
|
+
const elementOffset = isHorizontal ? rect.left : rect.top;
|
|
35
|
+
|
|
36
|
+
return pointerOffset - elementOffset - initialOffset;
|
|
37
|
+
}
|
|
16
38
|
|
|
17
39
|
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX
|
|
18
40
|
export function getMovement(
|
|
19
41
|
event: ResizeEvent,
|
|
20
42
|
handleId: string,
|
|
21
43
|
{ height, width }: Size,
|
|
22
|
-
direction: Direction
|
|
44
|
+
direction: Direction,
|
|
45
|
+
initialOffset: number
|
|
23
46
|
): number {
|
|
24
47
|
const isHorizontal = direction === "horizontal";
|
|
25
48
|
const size = isHorizontal ? width : height;
|
|
@@ -51,19 +74,7 @@ export function getMovement(
|
|
|
51
74
|
}
|
|
52
75
|
}
|
|
53
76
|
} else {
|
|
54
|
-
|
|
55
|
-
const rect = handleElement.getBoundingClientRect();
|
|
56
|
-
const elementOffset = isHorizontal ? rect.left : rect.top;
|
|
57
|
-
|
|
58
|
-
let pointerOffset = 0;
|
|
59
|
-
if (isMouseMoveEvent(event)) {
|
|
60
|
-
pointerOffset = isHorizontal ? event.clientX : event.clientY;
|
|
61
|
-
} else {
|
|
62
|
-
const firstTouch = event.touches[0];
|
|
63
|
-
pointerOffset = isHorizontal ? firstTouch.screenX : firstTouch.screenY;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return pointerOffset - elementOffset;
|
|
77
|
+
return getDragOffset(event, handleId, direction, initialOffset);
|
|
67
78
|
}
|
|
68
79
|
}
|
|
69
80
|
|
|
@@ -71,10 +82,10 @@ export function isKeyDown(event: ResizeEvent): event is KeyboardEvent {
|
|
|
71
82
|
return event.type === "keydown";
|
|
72
83
|
}
|
|
73
84
|
|
|
74
|
-
export function
|
|
75
|
-
return event.type
|
|
85
|
+
export function isMouseEvent(event: ResizeEvent): event is MouseEvent {
|
|
86
|
+
return event.type.startsWith("mouse");
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
export function
|
|
79
|
-
return event.type
|
|
89
|
+
export function isTouchEvent(event: ResizeEvent): event is TouchEvent {
|
|
90
|
+
return event.type.startsWith("touch");
|
|
80
91
|
}
|