react-resizable-panels 0.0.50 → 0.0.52

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
@@ -169,6 +169,8 @@ function PanelGroupWithForwardedRef({
169
169
  callbacksRef.current.onLayout = onLayout;
170
170
  });
171
171
 
172
+ const panelIdToLastNotifiedSizeMapRef = useRef<Record<string, number>>({});
173
+
172
174
  // 0-1 values representing the relative size of each panel.
173
175
  const [sizes, setSizes] = useState<number[]>([]);
174
176
 
@@ -199,7 +201,14 @@ function PanelGroupWithForwardedRef({
199
201
 
200
202
  assert(total === 100, "Panel sizes must add up to 100%");
201
203
 
204
+ const { panels } = committedValuesRef.current;
205
+ const panelIdToLastNotifiedSizeMap =
206
+ panelIdToLastNotifiedSizeMapRef.current;
207
+ const panelsArray = panelsMapToSortedArray(panels);
208
+
202
209
  setSizes(sizes);
210
+
211
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
203
212
  },
204
213
  }),
205
214
  []
@@ -223,30 +232,24 @@ function PanelGroupWithForwardedRef({
223
232
  // Notify external code when sizes have changed.
224
233
  useEffect(() => {
225
234
  const { onLayout } = callbacksRef.current!;
226
- if (onLayout) {
227
- const { sizes } = committedValuesRef.current;
235
+ const { panels, sizes } = committedValuesRef.current;
228
236
 
229
- // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
230
- if (sizes.length > 0) {
237
+ // Don't commit layout until all panels have registered and re-rendered with their actual sizes.
238
+ if (sizes.length > 0) {
239
+ if (onLayout) {
231
240
  onLayout(sizes);
232
241
  }
233
- }
234
- }, [sizes]);
235
242
 
236
- // Notify Panel listeners about their initial sizes and collapsed state after mount.
237
- // Subsequent changes will be called by the resizeHandler.
238
- const didNotifyCallbacksAfterMountRef = useRef(false);
239
- useIsomorphicLayoutEffect(() => {
240
- if (didNotifyCallbacksAfterMountRef.current) {
241
- return;
242
- }
243
-
244
- const { panels, sizes } = committedValuesRef.current;
245
- if (sizes.length > 0) {
246
- didNotifyCallbacksAfterMountRef.current = true;
243
+ const panelIdToLastNotifiedSizeMap =
244
+ panelIdToLastNotifiedSizeMapRef.current;
247
245
 
246
+ // When possible, we notify before the next render so that rendering work can be batched together.
247
+ // Some cases are difficult to detect though,
248
+ // for example– panels that are conditionally rendered can affect the size of neighboring panels.
249
+ // In this case, the best we can do is notify on commit.
250
+ // The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
248
251
  const panelsArray = panelsMapToSortedArray(panels);
249
- callPanelCallbacks(panelsArray, [], sizes);
252
+ callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
250
253
  }
251
254
  }, [sizes]);
252
255
 
@@ -481,10 +484,18 @@ function PanelGroupWithForwardedRef({
481
484
  }
482
485
 
483
486
  if (sizesChanged) {
484
- // If resize change handlers have been declared, this is the time to call them.
485
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
487
+ const panelIdToLastNotifiedSizeMap =
488
+ panelIdToLastNotifiedSizeMapRef.current;
486
489
 
487
490
  setSizes(nextSizes);
491
+
492
+ // If resize change handlers have been declared, this is the time to call them.
493
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
494
+ callPanelCallbacks(
495
+ panelsArray,
496
+ nextSizes,
497
+ panelIdToLastNotifiedSizeMap
498
+ );
488
499
  }
489
500
 
490
501
  prevDeltaRef.current = delta;
@@ -512,7 +523,12 @@ function PanelGroupWithForwardedRef({
512
523
  const { panels, sizes: prevSizes } = committedValuesRef.current;
513
524
 
514
525
  const panel = panels.get(id);
515
- if (panel == null || !panel.current.collapsible) {
526
+ if (panel == null) {
527
+ return;
528
+ }
529
+
530
+ const { collapsedSize, collapsible } = panel.current;
531
+ if (!collapsible) {
516
532
  return;
517
533
  }
518
534
 
@@ -524,7 +540,7 @@ function PanelGroupWithForwardedRef({
524
540
  }
525
541
 
526
542
  const currentSize = prevSizes[index];
527
- if (currentSize === 0) {
543
+ if (currentSize === collapsedSize) {
528
544
  // Panel is already collapsed.
529
545
  return;
530
546
  }
@@ -537,7 +553,7 @@ function PanelGroupWithForwardedRef({
537
553
  }
538
554
 
539
555
  const isLastPanel = index === panelsArray.length - 1;
540
- const delta = isLastPanel ? currentSize : 0 - currentSize;
556
+ const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
541
557
 
542
558
  const nextSizes = adjustByDelta(
543
559
  null,
@@ -550,10 +566,14 @@ function PanelGroupWithForwardedRef({
550
566
  null
551
567
  );
552
568
  if (prevSizes !== nextSizes) {
553
- // If resize change handlers have been declared, this is the time to call them.
554
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
569
+ const panelIdToLastNotifiedSizeMap =
570
+ panelIdToLastNotifiedSizeMapRef.current;
555
571
 
556
572
  setSizes(nextSizes);
573
+
574
+ // If resize change handlers have been declared, this is the time to call them.
575
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
576
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
557
577
  }
558
578
  }, []);
559
579
 
@@ -565,8 +585,10 @@ function PanelGroupWithForwardedRef({
565
585
  return;
566
586
  }
567
587
 
588
+ const { collapsedSize, minSize } = panel.current;
589
+
568
590
  const sizeBeforeCollapse =
569
- panelSizeBeforeCollapse.current.get(id) || panel.current.minSize;
591
+ panelSizeBeforeCollapse.current.get(id) || minSize;
570
592
  if (!sizeBeforeCollapse) {
571
593
  return;
572
594
  }
@@ -579,7 +601,7 @@ function PanelGroupWithForwardedRef({
579
601
  }
580
602
 
581
603
  const currentSize = prevSizes[index];
582
- if (currentSize !== 0) {
604
+ if (currentSize !== collapsedSize) {
583
605
  // Panel is already expanded.
584
606
  return;
585
607
  }
@@ -590,7 +612,9 @@ function PanelGroupWithForwardedRef({
590
612
  }
591
613
 
592
614
  const isLastPanel = index === panelsArray.length - 1;
593
- const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
615
+ const delta = isLastPanel
616
+ ? collapsedSize - sizeBeforeCollapse
617
+ : sizeBeforeCollapse;
594
618
 
595
619
  const nextSizes = adjustByDelta(
596
620
  null,
@@ -603,10 +627,14 @@ function PanelGroupWithForwardedRef({
603
627
  null
604
628
  );
605
629
  if (prevSizes !== nextSizes) {
606
- // If resize change handlers have been declared, this is the time to call them.
607
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
630
+ const panelIdToLastNotifiedSizeMap =
631
+ panelIdToLastNotifiedSizeMapRef.current;
608
632
 
609
633
  setSizes(nextSizes);
634
+
635
+ // If resize change handlers have been declared, this is the time to call them.
636
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
637
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
610
638
  }
611
639
  }, []);
612
640
 
@@ -618,6 +646,8 @@ function PanelGroupWithForwardedRef({
618
646
  return;
619
647
  }
620
648
 
649
+ const { collapsedSize, collapsible, maxSize, minSize } = panel.current;
650
+
621
651
  const panelsArray = panelsMapToSortedArray(panels);
622
652
 
623
653
  const index = panelsArray.indexOf(panel);
@@ -630,13 +660,10 @@ function PanelGroupWithForwardedRef({
630
660
  return;
631
661
  }
632
662
 
633
- if (panel.current.collapsible && nextSize === 0) {
663
+ if (collapsible && nextSize === collapsedSize) {
634
664
  // This is a valid resize state.
635
665
  } else {
636
- nextSize = Math.min(
637
- panel.current.maxSize,
638
- Math.max(panel.current.minSize, nextSize)
639
- );
666
+ nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
640
667
  }
641
668
 
642
669
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
@@ -658,10 +685,14 @@ function PanelGroupWithForwardedRef({
658
685
  null
659
686
  );
660
687
  if (prevSizes !== nextSizes) {
661
- // If resize change handlers have been declared, this is the time to call them.
662
- callPanelCallbacks(panelsArray, prevSizes, nextSizes);
688
+ const panelIdToLastNotifiedSizeMap =
689
+ panelIdToLastNotifiedSizeMapRef.current;
663
690
 
664
691
  setSizes(nextSizes);
692
+
693
+ // If resize change handlers have been declared, this is the time to call them.
694
+ // Trigger user callbacks after updating state, so that user code can override the sizes.
695
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
665
696
  }
666
697
  }, []);
667
698
 
package/src/types.ts CHANGED
@@ -9,7 +9,7 @@ export type PanelGroupStorage = {
9
9
 
10
10
  export type PanelGroupOnLayout = (sizes: number[]) => void;
11
11
  export type PanelOnCollapse = (collapsed: boolean) => void;
12
- export type PanelOnResize = (size: number) => void;
12
+ export type PanelOnResize = (size: number, prevSize: number) => void;
13
13
  export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
14
14
 
15
15
  export type PanelCallbackRef = RefObject<{
@@ -20,6 +20,7 @@ export type PanelCallbackRef = RefObject<{
20
20
  export type PanelData = {
21
21
  current: {
22
22
  callbacksRef: PanelCallbackRef;
23
+ collapsedSize: number;
23
24
  collapsible: boolean;
24
25
  defaultSize: number | null;
25
26
  id: string;
@@ -119,25 +119,33 @@ export function adjustByDelta(
119
119
 
120
120
  export function callPanelCallbacks(
121
121
  panelsArray: PanelData[],
122
- prevSizes: number[],
123
- nextSizes: number[]
122
+ sizes: number[],
123
+ panelIdToLastNotifiedSizeMap: Record<string, number>
124
124
  ) {
125
- nextSizes.forEach((nextSize, index) => {
126
- const prevSize = prevSizes[index];
127
- if (prevSize !== nextSize) {
128
- const { callbacksRef, collapsible } = panelsArray[index].current;
125
+ sizes.forEach((size, index) => {
126
+ const { callbacksRef, collapsedSize, collapsible, id } =
127
+ panelsArray[index].current;
128
+
129
+ const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
130
+ if (lastNotifiedSize !== size) {
131
+ panelIdToLastNotifiedSizeMap[id] = size;
132
+
129
133
  const { onCollapse, onResize } = callbacksRef.current!;
130
134
 
131
135
  if (onResize) {
132
- onResize(nextSize);
136
+ onResize(size, lastNotifiedSize);
133
137
  }
134
138
 
135
139
  if (collapsible && onCollapse) {
136
- // Falsy check handles both previous size of 0
137
- // and initial size of undefined (when mounting)
138
- if (!prevSize && nextSize !== 0) {
140
+ if (
141
+ (lastNotifiedSize == null || lastNotifiedSize === collapsedSize) &&
142
+ size !== collapsedSize
143
+ ) {
139
144
  onCollapse(false);
140
- } else if (prevSize !== 0 && nextSize === 0) {
145
+ } else if (
146
+ lastNotifiedSize !== collapsedSize &&
147
+ size === collapsedSize
148
+ ) {
141
149
  onCollapse(true);
142
150
  }
143
151
  }
@@ -274,11 +282,13 @@ function safeResizePanel(
274
282
  ): number {
275
283
  const nextSizeUnsafe = prevSize + delta;
276
284
 
277
- if (panel.current.collapsible) {
278
- if (prevSize > 0) {
285
+ const { collapsedSize, collapsible, maxSize, minSize } = panel.current;
286
+
287
+ if (collapsible) {
288
+ if (prevSize > collapsedSize) {
279
289
  // Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
280
- if (nextSizeUnsafe <= panel.current.minSize / 2) {
281
- return 0;
290
+ if (nextSizeUnsafe <= minSize / 2 + collapsedSize) {
291
+ return collapsedSize;
282
292
  }
283
293
  } else {
284
294
  const isKeyboardEvent = event?.type?.startsWith("key");
@@ -286,17 +296,14 @@ function safeResizePanel(
286
296
  // Keyboard events should expand a collapsed panel to the min size,
287
297
  // but mouse events should wait until the panel has reached its min size
288
298
  // to avoid a visual flickering when dragging between collapsed and min size.
289
- if (nextSizeUnsafe < panel.current.minSize) {
290
- return 0;
299
+ if (nextSizeUnsafe < minSize) {
300
+ return collapsedSize;
291
301
  }
292
302
  }
293
303
  }
294
304
  }
295
305
 
296
- const nextSize = Math.min(
297
- panel.current.maxSize,
298
- Math.max(panel.current.minSize, nextSizeUnsafe)
299
- );
306
+ const nextSize = Math.min(maxSize, Math.max(minSize, nextSizeUnsafe));
300
307
 
301
308
  return nextSize;
302
309
  }