react-resizable-panels 0.0.26 → 0.0.28

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/src/PanelGroup.ts CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  ReactNode,
6
6
  useCallback,
7
7
  useEffect,
8
- useLayoutEffect,
9
8
  useMemo,
10
9
  useRef,
11
10
  useState,
@@ -22,8 +21,9 @@ import {
22
21
  getResizeHandlePanelIds,
23
22
  panelsMapToSortedArray,
24
23
  } from "./utils/group";
25
- import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterBehavior";
24
+ import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
26
25
  import useUniqueId from "./hooks/useUniqueId";
26
+ import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterBehavior";
27
27
  import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
28
28
 
29
29
  export type CommittedValues = {
@@ -74,7 +74,7 @@ export default function PanelGroup({
74
74
  sizes,
75
75
  });
76
76
 
77
- useLayoutEffect(() => {
77
+ useIsomorphicLayoutEffect(() => {
78
78
  committedValuesRef.current.direction = direction;
79
79
  committedValuesRef.current.panels = panels;
80
80
  committedValuesRef.current.sizes = sizes;
@@ -91,7 +91,7 @@ export default function PanelGroup({
91
91
  // Once all panels have registered themselves,
92
92
  // Compute the initial sizes based on default weights.
93
93
  // This assumes that panels register during initial mount (no conditional rendering)!
94
- useLayoutEffect(() => {
94
+ useIsomorphicLayoutEffect(() => {
95
95
  const sizes = committedValuesRef.current.sizes;
96
96
  if (sizes.length === panels.size) {
97
97
  // Only compute (or restore) default sizes once per panel configuration.
@@ -168,6 +168,20 @@ export default function PanelGroup({
168
168
  (id: string): CSSProperties => {
169
169
  const { panels } = committedValuesRef.current;
170
170
 
171
+ // Before mounting, Panels will not yet have registered themselves.
172
+ // This includes server rendering.
173
+ // At this point the best we can do is render everything with the same size.
174
+ if (panels.size === 0) {
175
+ return {
176
+ flexBasis: "auto",
177
+ flexGrow: 1,
178
+ flexShrink: 1,
179
+
180
+ // Without this, Panel sizes may be unintentionally overridden by their content.
181
+ overflow: "hidden",
182
+ };
183
+ }
184
+
171
185
  const flexGrow = getFlexGrow(panels, id, sizes);
172
186
 
173
187
  return {
@@ -221,7 +235,9 @@ export default function PanelGroup({
221
235
  event,
222
236
  groupId,
223
237
  handleId,
238
+ panelsArray,
224
239
  direction,
240
+ prevSizes,
225
241
  dragOffsetRef.current
226
242
  );
227
243
  if (movement === 0) {
@@ -262,10 +278,19 @@ export default function PanelGroup({
262
278
  nextSizes.forEach((nextSize, index) => {
263
279
  const prevSize = prevSizes[index];
264
280
  if (prevSize !== nextSize) {
265
- const onResize = panelsArray[index].onResizeRef.current;
281
+ const { onCollapse, onResize } =
282
+ panelsArray[index].callbacksRef.current;
266
283
  if (onResize) {
267
284
  onResize(nextSize);
268
285
  }
286
+
287
+ if (onCollapse) {
288
+ if (prevSize === 0 && nextSize !== 0) {
289
+ onCollapse(false);
290
+ } else if (prevSize !== 0 && nextSize === 0) {
291
+ onCollapse(true);
292
+ }
293
+ }
269
294
  }
270
295
  });
271
296
 
@@ -0,0 +1,6 @@
1
+ import { useEffect, useLayoutEffect } from "react";
2
+
3
+ const useIsomorphicLayoutEffect =
4
+ typeof window !== "undefined" ? useLayoutEffect : useEffect;
5
+
6
+ export default useIsomorphicLayoutEffect;
package/src/types.ts CHANGED
@@ -2,14 +2,19 @@ import { RefObject } from "react";
2
2
 
3
3
  export type Direction = "horizontal" | "vertical";
4
4
 
5
+ export type PanelOnCollapse = (collapsed: boolean) => void;
5
6
  export type PanelOnResize = (size: number) => void;
6
7
 
7
8
  export type PanelData = {
9
+ callbacksRef: RefObject<{
10
+ onCollapse: PanelOnCollapse | null;
11
+ onResize: PanelOnResize | null;
12
+ }>;
13
+ collapsible: boolean;
8
14
  defaultSize: number;
9
15
  id: string;
10
16
  maxSize: number;
11
17
  minSize: number;
12
- onResizeRef: RefObject<PanelOnResize | null>;
13
18
  order: number | null;
14
19
  };
15
20
 
@@ -1,5 +1,10 @@
1
- import { Direction, ResizeEvent } from "../types";
2
- import { getPanelGroup, getResizeHandle } from "./group";
1
+ import { PRECISION } from "../constants";
2
+ import { Direction, PanelData, ResizeEvent } from "../types";
3
+ import {
4
+ getPanelGroup,
5
+ getResizeHandle,
6
+ getResizeHandlePanelIds,
7
+ } from "./group";
3
8
 
4
9
  export type Coordinates = {
5
10
  movement: number;
@@ -41,33 +46,72 @@ export function getMovement(
41
46
  event: ResizeEvent,
42
47
  groupId: string,
43
48
  handleId: string,
49
+ panelsArray: PanelData[],
44
50
  direction: Direction,
51
+ sizes: number[],
45
52
  initialOffset: number
46
53
  ): number {
47
- const isHorizontal = direction === "horizontal";
54
+ if (isKeyDown(event)) {
55
+ const isHorizontal = direction === "horizontal";
48
56
 
49
- const groupElement = getPanelGroup(groupId);
50
- const rect = groupElement.getBoundingClientRect();
51
- const size = isHorizontal ? rect.width : rect.height;
57
+ const groupElement = getPanelGroup(groupId);
58
+ const rect = groupElement.getBoundingClientRect();
59
+ const groupSizeInPixels = isHorizontal ? rect.width : rect.height;
52
60
 
53
- if (isKeyDown(event)) {
54
61
  const denominator = event.shiftKey ? 10 : 100;
55
- const delta = size / denominator;
62
+ const delta = groupSizeInPixels / denominator;
56
63
 
64
+ let movement = 0;
57
65
  switch (event.key) {
58
66
  case "ArrowDown":
59
- return isHorizontal ? 0 : delta;
67
+ movement = isHorizontal ? 0 : delta;
68
+ break;
60
69
  case "ArrowLeft":
61
- return isHorizontal ? -delta : 0;
70
+ movement = isHorizontal ? -delta : 0;
71
+ break;
62
72
  case "ArrowRight":
63
- return isHorizontal ? delta : 0;
73
+ movement = isHorizontal ? delta : 0;
74
+ break;
64
75
  case "ArrowUp":
65
- return isHorizontal ? 0 : -delta;
76
+ movement = isHorizontal ? 0 : -delta;
77
+ break;
66
78
  case "End":
67
- return size;
79
+ movement = groupSizeInPixels;
80
+ break;
68
81
  case "Home":
69
- return -size;
82
+ movement = -groupSizeInPixels;
83
+ break;
70
84
  }
85
+
86
+ // If the Panel being resized is collapsible,
87
+ // we need to special case resizing around the minSize boundary.
88
+ // If contracting, Panels should shrink to their minSize and then snap to fully collapsed.
89
+ // If expanding from collapsed, they should snap back to their minSize.
90
+ const [idBefore, idAfter] = getResizeHandlePanelIds(
91
+ groupId,
92
+ handleId,
93
+ panelsArray
94
+ );
95
+ const targetPanelId = movement < 0 ? idBefore : idAfter;
96
+ const targetPanelIndex = panelsArray.findIndex(
97
+ (panel) => panel.id === targetPanelId
98
+ );
99
+ const targetPanel = panelsArray[targetPanelIndex];
100
+ if (targetPanel.collapsible) {
101
+ const prevSize = sizes[targetPanelIndex];
102
+ if (
103
+ prevSize === 0 ||
104
+ prevSize.toPrecision(PRECISION) ===
105
+ targetPanel.minSize.toPrecision(PRECISION)
106
+ ) {
107
+ movement =
108
+ movement < 0
109
+ ? -targetPanel.minSize * groupSizeInPixels
110
+ : targetPanel.minSize * groupSizeInPixels;
111
+ }
112
+ }
113
+
114
+ return movement;
71
115
  } else {
72
116
  return getDragOffset(event, handleId, direction, initialOffset);
73
117
  }
@@ -178,9 +178,17 @@ function safeResizePanel(
178
178
  prevSize: number
179
179
  ): number {
180
180
  const nextSizeUnsafe = prevSize + delta;
181
+
182
+ if (panel.collapsible) {
183
+ if (nextSizeUnsafe <= 0) {
184
+ return 0;
185
+ }
186
+ }
187
+
181
188
  const nextSize = Math.min(
182
189
  panel.maxSize,
183
190
  Math.max(panel.minSize, nextSizeUnsafe)
184
191
  );
192
+
185
193
  return nextSize;
186
194
  }