react-resizable-panels 0.0.6 → 0.0.8
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 +5 -0
- package/README.md +7 -1
- package/dist/react-resizable-panels.d.ts +5 -1
- package/dist/react-resizable-panels.d.ts.map +1 -1
- package/dist/react-resizable-panels.js +132 -49
- package/dist/react-resizable-panels.js.map +1 -1
- package/dist/react-resizable-panels.module.js +132 -50
- package/dist/react-resizable-panels.module.js.map +1 -1
- package/package.json +1 -1
- package/src/PanelContexts.ts +6 -0
- package/src/PanelGroup.tsx +52 -31
- package/src/PanelResizeHandle.tsx +34 -26
- package/src/hooks/useUniqueId.ts +4 -4
- package/src/index.ts +2 -1
- package/src/types.ts +2 -1
- package/src/utils/serialization.ts +49 -0
- package/src/utils/touch.ts +44 -0
|
@@ -1,34 +1,44 @@
|
|
|
1
1
|
import { ReactNode, useContext, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import useUniqueId from "./hooks/useUniqueId";
|
|
4
|
-
import { PanelGroupContext } from "./PanelContexts";
|
|
5
|
-
import { ResizeHandler } from "./types";
|
|
4
|
+
import { PanelContext, PanelGroupContext } from "./PanelContexts";
|
|
5
|
+
import type { ResizeHandler, ResizeEvent } from "./types";
|
|
6
6
|
|
|
7
7
|
export default function PanelResizeHandle({
|
|
8
8
|
children = null,
|
|
9
9
|
className = "",
|
|
10
10
|
disabled = false,
|
|
11
|
+
id: idProp = null,
|
|
11
12
|
}: {
|
|
12
13
|
children?: ReactNode;
|
|
13
14
|
className?: string;
|
|
14
15
|
disabled?: boolean;
|
|
16
|
+
id?: string | null;
|
|
15
17
|
}) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
+
const panelContext = useContext(PanelContext);
|
|
19
|
+
const panelGroupContext = useContext(PanelGroupContext);
|
|
20
|
+
if (panelContext === null || panelGroupContext === null) {
|
|
18
21
|
throw Error(
|
|
19
22
|
`PanelResizeHandle components must be rendered within a PanelGroup container`
|
|
20
23
|
);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
const id = useUniqueId();
|
|
26
|
+
const id = useUniqueId(idProp);
|
|
24
27
|
|
|
25
|
-
const {
|
|
28
|
+
const { activeHandleId } = panelContext;
|
|
29
|
+
const {
|
|
30
|
+
direction,
|
|
31
|
+
groupId,
|
|
32
|
+
registerResizeHandle,
|
|
33
|
+
startDragging,
|
|
34
|
+
stopDragging,
|
|
35
|
+
} = panelGroupContext;
|
|
36
|
+
|
|
37
|
+
const isDragging = activeHandleId === id;
|
|
26
38
|
|
|
27
|
-
const setGroupId = useState<string | null>(null);
|
|
28
39
|
const [resizeHandler, setResizeHandler] = useState<ResizeHandler | null>(
|
|
29
40
|
null
|
|
30
41
|
);
|
|
31
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
32
42
|
|
|
33
43
|
useEffect(() => {
|
|
34
44
|
if (disabled) {
|
|
@@ -47,40 +57,38 @@ export default function PanelResizeHandle({
|
|
|
47
57
|
document.body.style.cursor =
|
|
48
58
|
direction === "horizontal" ? "ew-resize" : "ns-resize";
|
|
49
59
|
|
|
50
|
-
const
|
|
51
|
-
setIsDragging(false);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const onMouseMove = (event: MouseEvent) => {
|
|
60
|
+
const onMove = (event: ResizeEvent) => {
|
|
55
61
|
resizeHandler(event);
|
|
56
62
|
};
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
document.body.addEventListener("mouseleave", onMouseLeave);
|
|
63
|
-
document.body.addEventListener("mousemove", onMouseMove);
|
|
64
|
-
document.body.addEventListener("mouseup", onMouseUp);
|
|
64
|
+
document.body.addEventListener("mouseleave", stopDragging);
|
|
65
|
+
document.body.addEventListener("mousemove", onMove);
|
|
66
|
+
document.body.addEventListener("touchmove", onMove);
|
|
67
|
+
document.body.addEventListener("mouseup", stopDragging);
|
|
65
68
|
|
|
66
69
|
return () => {
|
|
67
70
|
document.body.style.cursor = "";
|
|
68
71
|
|
|
69
|
-
document.body.removeEventListener("mouseleave",
|
|
70
|
-
document.body.removeEventListener("mousemove",
|
|
71
|
-
document.body.removeEventListener("
|
|
72
|
+
document.body.removeEventListener("mouseleave", stopDragging);
|
|
73
|
+
document.body.removeEventListener("mousemove", onMove);
|
|
74
|
+
document.body.removeEventListener("touchmove", onMove);
|
|
75
|
+
document.body.removeEventListener("mouseup", stopDragging);
|
|
72
76
|
};
|
|
73
|
-
}, [direction, disabled, isDragging, resizeHandler]);
|
|
77
|
+
}, [direction, disabled, isDragging, resizeHandler, stopDragging]);
|
|
74
78
|
|
|
75
79
|
return (
|
|
76
80
|
<div
|
|
77
81
|
className={className}
|
|
78
82
|
data-panel-group-id={groupId}
|
|
79
83
|
data-panel-resize-handle-id={id}
|
|
80
|
-
onMouseDown={() =>
|
|
81
|
-
onMouseUp={
|
|
84
|
+
onMouseDown={() => startDragging(id)}
|
|
85
|
+
onMouseUp={stopDragging}
|
|
86
|
+
onTouchCancel={stopDragging}
|
|
87
|
+
onTouchEnd={stopDragging}
|
|
88
|
+
onTouchStart={() => startDragging(id)}
|
|
82
89
|
style={{
|
|
83
90
|
cursor: direction === "horizontal" ? "ew-resize" : "ns-resize",
|
|
91
|
+
touchAction: "none",
|
|
84
92
|
}}
|
|
85
93
|
>
|
|
86
94
|
{children}
|
package/src/hooks/useUniqueId.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { useRef } from "react";
|
|
|
2
2
|
|
|
3
3
|
let counter = 0;
|
|
4
4
|
|
|
5
|
-
export default function useUniqueId(): string {
|
|
6
|
-
const idRef = useRef<
|
|
5
|
+
export default function useUniqueId(id: string | null = null): string {
|
|
6
|
+
const idRef = useRef<string | null>(id);
|
|
7
7
|
if (idRef.current === null) {
|
|
8
|
-
idRef.current = counter++;
|
|
8
|
+
idRef.current = "" + counter++;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
return
|
|
11
|
+
return idRef.current;
|
|
12
12
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Panel from "./Panel";
|
|
2
|
+
import { PanelContext } from "./PanelContexts";
|
|
2
3
|
import PanelGroup from "./PanelGroup";
|
|
3
4
|
import PanelResizeHandle from "./PanelResizeHandle";
|
|
4
5
|
|
|
5
|
-
export { Panel, PanelGroup, PanelResizeHandle };
|
|
6
|
+
export { Panel, PanelContext, PanelGroup, PanelResizeHandle };
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { PanelData } from "../types";
|
|
2
|
+
|
|
3
|
+
type SerializedPanelGroupState = { [panelIds: string]: number[] };
|
|
4
|
+
|
|
5
|
+
function loadSerializedPanelGroupState(
|
|
6
|
+
autoSaveId: string
|
|
7
|
+
): SerializedPanelGroupState | null {
|
|
8
|
+
try {
|
|
9
|
+
const serialized = localStorage.getItem(`PanelGroup:sizes:${autoSaveId}`);
|
|
10
|
+
if (serialized) {
|
|
11
|
+
const parsed = JSON.parse(serialized);
|
|
12
|
+
if (typeof parsed === "object" && parsed != null) {
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
} catch (error) {}
|
|
17
|
+
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadPanelLayout(
|
|
22
|
+
autoSaveId: string,
|
|
23
|
+
panelIds: string[]
|
|
24
|
+
): number[] | null {
|
|
25
|
+
const state = loadSerializedPanelGroupState(autoSaveId);
|
|
26
|
+
if (state) {
|
|
27
|
+
return state[panelIds.join(",")] ?? null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function savePanelGroupLayout(
|
|
34
|
+
autoSaveId: string,
|
|
35
|
+
panelIds: string[],
|
|
36
|
+
sizes: number[]
|
|
37
|
+
): void {
|
|
38
|
+
const state = loadSerializedPanelGroupState(autoSaveId) || {};
|
|
39
|
+
state[panelIds.join(",")] = sizes;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
localStorage.setItem(
|
|
43
|
+
`PanelGroup:sizes:${autoSaveId}`,
|
|
44
|
+
JSON.stringify(state)
|
|
45
|
+
);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error(error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ResizeEvent } from "../types";
|
|
2
|
+
|
|
3
|
+
export type Coordinates = {
|
|
4
|
+
screenX: number;
|
|
5
|
+
screenY: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type Movement = {
|
|
9
|
+
movementX: number;
|
|
10
|
+
movementY: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX
|
|
14
|
+
export function getUpdatedCoordinates(
|
|
15
|
+
event: ResizeEvent,
|
|
16
|
+
prevCoordinates: Coordinates
|
|
17
|
+
): Coordinates & Movement {
|
|
18
|
+
const { screenX: prevScreenX, screenY: prevScreenY } = prevCoordinates;
|
|
19
|
+
|
|
20
|
+
const getMovementBetween = (current: number, prev: number) =>
|
|
21
|
+
prev === 0 ? 0 : current - prev;
|
|
22
|
+
|
|
23
|
+
if (isTouchMoveEvent(event)) {
|
|
24
|
+
const firstTouch = event.touches[0];
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
movementX: getMovementBetween(firstTouch.screenX, prevScreenX),
|
|
28
|
+
movementY: getMovementBetween(firstTouch.screenY, prevScreenY),
|
|
29
|
+
screenX: firstTouch.screenX,
|
|
30
|
+
screenY: firstTouch.screenY,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
movementX: getMovementBetween(event.screenX, prevScreenX),
|
|
36
|
+
movementY: getMovementBetween(event.screenY, prevScreenY),
|
|
37
|
+
screenX: event.screenX,
|
|
38
|
+
screenY: event.screenY,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function isTouchMoveEvent(event: ResizeEvent): event is TouchEvent {
|
|
43
|
+
return event.type === "touchmove";
|
|
44
|
+
}
|