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.
@@ -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) => setActiveHandleId(id),
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 className={className}>{children}</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: idProp = null,
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 isDragging = activeHandleId === id;
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(id);
65
+ const resizeHandler = registerResizeHandle(resizeHandleId);
67
66
  setResizeHandler(() => resizeHandler);
68
67
  }
69
- }, [disabled, id, registerResizeHandle]);
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: id,
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={id}
110
- onMouseDown={() => startDragging(id)}
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(id)}
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
  >
@@ -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(id: string | null = null): string {
6
- const idRef = useRef<string | null>(id);
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
  }
@@ -11,15 +11,38 @@ export type Size = {
11
11
  width: number;
12
12
  };
13
13
 
14
- const element = document.createElement("div");
15
- element.getBoundingClientRect();
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
- const handleElement = getResizeHandle(handleId)!;
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 isMouseMoveEvent(event: ResizeEvent): event is MouseEvent {
75
- return event.type === "mousemove";
85
+ export function isMouseEvent(event: ResizeEvent): event is MouseEvent {
86
+ return event.type.startsWith("mouse");
76
87
  }
77
88
 
78
- export function isTouchMoveEvent(event: ResizeEvent): event is TouchEvent {
79
- return event.type === "touchmove";
89
+ export function isTouchEvent(event: ResizeEvent): event is TouchEvent {
90
+ return event.type.startsWith("touch");
80
91
  }