react-resizable-panels 0.0.54 → 0.0.55

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/declarations/src/Panel.d.ts +5 -4
  3. package/dist/declarations/src/PanelGroup.d.ts +7 -3
  4. package/dist/declarations/src/index.d.ts +3 -2
  5. package/dist/declarations/src/types.d.ts +2 -1
  6. package/dist/declarations/src/utils/group.d.ts +29 -0
  7. package/dist/react-resizable-panels.browser.cjs.js +385 -108
  8. package/dist/react-resizable-panels.browser.cjs.mjs +2 -1
  9. package/dist/react-resizable-panels.browser.development.cjs.js +417 -108
  10. package/dist/react-resizable-panels.browser.development.cjs.mjs +2 -1
  11. package/dist/react-resizable-panels.browser.development.esm.js +417 -109
  12. package/dist/react-resizable-panels.browser.esm.js +385 -109
  13. package/dist/react-resizable-panels.cjs.js +385 -108
  14. package/dist/react-resizable-panels.cjs.js.map +1 -0
  15. package/dist/react-resizable-panels.cjs.mjs +2 -1
  16. package/dist/react-resizable-panels.development.cjs.js +417 -108
  17. package/dist/react-resizable-panels.development.cjs.mjs +2 -1
  18. package/dist/react-resizable-panels.development.esm.js +417 -109
  19. package/dist/react-resizable-panels.development.node.cjs.js +282 -76
  20. package/dist/react-resizable-panels.development.node.cjs.mjs +2 -1
  21. package/dist/react-resizable-panels.development.node.esm.js +282 -77
  22. package/dist/react-resizable-panels.esm.js +385 -109
  23. package/dist/react-resizable-panels.esm.js.map +1 -0
  24. package/dist/react-resizable-panels.node.cjs.js +254 -76
  25. package/dist/react-resizable-panels.node.cjs.mjs +2 -1
  26. package/dist/react-resizable-panels.node.esm.js +254 -77
  27. package/package.json +1 -1
  28. package/src/Panel.ts +32 -32
  29. package/src/PanelContexts.ts +4 -2
  30. package/src/PanelGroup.ts +221 -111
  31. package/src/hooks/useWindowSplitterBehavior.ts +14 -11
  32. package/src/index.ts +11 -3
  33. package/src/types.ts +2 -1
  34. package/src/utils/group.ts +327 -28
@@ -43,8 +43,8 @@ function PanelWithForwardedRef({
43
43
  defaultSize = null,
44
44
  forwardedRef,
45
45
  id: idFromProps = null,
46
- maxSize = 100,
47
- minSize = 10,
46
+ maxSize = null,
47
+ minSize,
48
48
  onCollapse = null,
49
49
  onResize = null,
50
50
  order = null,
@@ -59,11 +59,22 @@ function PanelWithForwardedRef({
59
59
  const {
60
60
  collapsePanel,
61
61
  expandPanel,
62
+ getPanelSize,
62
63
  getPanelStyle,
63
64
  registerPanel,
64
65
  resizePanel,
66
+ units,
65
67
  unregisterPanel
66
68
  } = context;
69
+ if (minSize == null) {
70
+ if (units === "percentages") {
71
+ // Mimics legacy default value for percentage based panel groups
72
+ minSize = 10;
73
+ } else {
74
+ // There is no meaningful minimum pixel default we can provide
75
+ minSize = 0;
76
+ }
77
+ }
67
78
 
68
79
  // Use a ref to guard against users passing inline props
69
80
  const callbacksRef = useRef({
@@ -74,22 +85,6 @@ function PanelWithForwardedRef({
74
85
  callbacksRef.current.onCollapse = onCollapse;
75
86
  callbacksRef.current.onResize = onResize;
76
87
  });
77
-
78
- // Basic props validation
79
- if (minSize < 0 || minSize > 100) {
80
- throw Error(`Panel minSize must be between 0 and 100, but was ${minSize}`);
81
- } else if (maxSize < 0 || maxSize > 100) {
82
- throw Error(`Panel maxSize must be between 0 and 100, but was ${maxSize}`);
83
- } else {
84
- if (defaultSize !== null) {
85
- if (defaultSize < 0 || defaultSize > 100) {
86
- throw Error(`Panel defaultSize must be between 0 and 100, but was ${defaultSize}`);
87
- } else if (minSize > defaultSize && !collapsible) {
88
- console.error(`Panel minSize ${minSize} cannot be greater than defaultSize ${defaultSize}`);
89
- defaultSize = minSize;
90
- }
91
- }
92
- }
93
88
  const style = getPanelStyle(panelId, defaultSize);
94
89
  const committedValuesRef = useRef({
95
90
  size: parseSizeFromStyle(style)
@@ -111,11 +106,14 @@ function PanelWithForwardedRef({
111
106
  getCollapsed() {
112
107
  return committedValuesRef.current.size === 0;
113
108
  },
114
- getSize() {
115
- return committedValuesRef.current.size;
109
+ getId() {
110
+ return panelId;
111
+ },
112
+ getSize(units) {
113
+ return getPanelSize(panelId, units);
116
114
  },
117
- resize: percentage => resizePanel(panelId, percentage)
118
- }), [collapsePanel, expandPanel, panelId, resizePanel]);
115
+ resize: (percentage, units) => resizePanel(panelId, percentage, units)
116
+ }), [collapsePanel, expandPanel, getPanelSize, panelId, resizePanel]);
119
117
  return createElement(Type, {
120
118
  children,
121
119
  className: classNameFromProps,
@@ -151,7 +149,13 @@ function parseSizeFromStyle(style) {
151
149
 
152
150
  const PRECISION = 10;
153
151
 
154
- function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse, initialDragState) {
152
+ function adjustByDelta(event, committedValues, idBefore, idAfter, deltaPixels, prevSizes, panelSizeBeforeCollapse, initialDragState) {
153
+ const {
154
+ id: groupId,
155
+ panels,
156
+ units
157
+ } = committedValues;
158
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
155
159
  const {
156
160
  sizes: initialSizes
157
161
  } = initialDragState || {};
@@ -159,9 +163,6 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
159
163
  // If we're resizing by mouse or touch, use the initial sizes as a base.
160
164
  // This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
161
165
  const baseSizes = initialSizes || prevSizes;
162
- if (delta === 0) {
163
- return baseSizes;
164
- }
165
166
  const panelsArray = panelsMapToSortedArray(panels);
166
167
  const nextSizes = baseSizes.concat();
167
168
  let deltaApplied = 0;
@@ -176,11 +177,11 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
176
177
 
177
178
  // Max-bounds check the panel being expanded first.
178
179
  {
179
- const pivotId = delta < 0 ? idAfter : idBefore;
180
+ const pivotId = deltaPixels < 0 ? idAfter : idBefore;
180
181
  const index = panelsArray.findIndex(panel => panel.current.id === pivotId);
181
182
  const panel = panelsArray[index];
182
183
  const baseSize = baseSizes[index];
183
- const nextSize = safeResizePanel(panel, Math.abs(delta), baseSize, event);
184
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize + Math.abs(deltaPixels), event);
184
185
  if (baseSize === nextSize) {
185
186
  // If there's no room for the pivot panel to grow, we can ignore this drag update.
186
187
  return baseSizes;
@@ -188,29 +189,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
188
189
  if (nextSize === 0 && baseSize > 0) {
189
190
  panelSizeBeforeCollapse.set(pivotId, baseSize);
190
191
  }
191
- delta = delta < 0 ? baseSize - nextSize : nextSize - baseSize;
192
+ deltaPixels = deltaPixels < 0 ? baseSize - nextSize : nextSize - baseSize;
192
193
  }
193
194
  }
194
- let pivotId = delta < 0 ? idBefore : idAfter;
195
+ let pivotId = deltaPixels < 0 ? idBefore : idAfter;
195
196
  let index = panelsArray.findIndex(panel => panel.current.id === pivotId);
196
197
  while (true) {
197
198
  const panel = panelsArray[index];
198
199
  const baseSize = baseSizes[index];
199
- const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
200
- const nextSize = safeResizePanel(panel, 0 - deltaRemaining, baseSize, event);
200
+ const deltaRemaining = Math.abs(deltaPixels) - Math.abs(deltaApplied);
201
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize - deltaRemaining, event);
201
202
  if (baseSize !== nextSize) {
202
203
  if (nextSize === 0 && baseSize > 0) {
203
204
  panelSizeBeforeCollapse.set(panel.current.id, baseSize);
204
205
  }
205
206
  deltaApplied += baseSize - nextSize;
206
207
  nextSizes[index] = nextSize;
207
- if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(delta).toPrecision(PRECISION), undefined, {
208
+ if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(deltaPixels).toPrecision(PRECISION), undefined, {
208
209
  numeric: true
209
210
  }) >= 0) {
210
211
  break;
211
212
  }
212
213
  }
213
- if (delta < 0) {
214
+ if (deltaPixels < 0) {
214
215
  if (--index < 0) {
215
216
  break;
216
217
  }
@@ -228,7 +229,7 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
228
229
  }
229
230
 
230
231
  // Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
231
- pivotId = delta < 0 ? idAfter : idBefore;
232
+ pivotId = deltaPixels < 0 ? idAfter : idBefore;
232
233
  index = panelsArray.findIndex(panel => panel.current.id === pivotId);
233
234
  nextSizes[index] = baseSizes[index] + deltaApplied;
234
235
  return nextSizes;
@@ -280,6 +281,23 @@ function getBeforeAndAfterIds(id, panelsArray) {
280
281
  const idAfter = isLastPanel ? id : panelsArray[index + 1].current.id;
281
282
  return [idBefore, idAfter];
282
283
  }
284
+ function getAvailableGroupSizePixels(groupId) {
285
+ const panelGroupElement = getPanelGroup(groupId);
286
+ if (panelGroupElement == null) {
287
+ return NaN;
288
+ }
289
+ const direction = panelGroupElement.getAttribute("data-panel-group-direction");
290
+ const resizeHandles = getResizeHandlesForGroup(groupId);
291
+ if (direction === "horizontal") {
292
+ return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
293
+ return accumulated + handle.offsetWidth;
294
+ }, 0);
295
+ } else {
296
+ return panelGroupElement.offsetHeight - resizeHandles.reduce((accumulated, handle) => {
297
+ return accumulated + handle.offsetHeight;
298
+ }, 0);
299
+ }
300
+ }
283
301
 
284
302
  // This method returns a number between 1 and 100 representing
285
303
  // the % of the group's overall space this panel should occupy.
@@ -350,18 +368,24 @@ function panelsMapToSortedArray(panels) {
350
368
  }
351
369
  });
352
370
  }
353
- function safeResizePanel(panel, delta, prevSize, event) {
354
- const nextSizeUnsafe = prevSize + delta;
355
- const {
371
+ function safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize, event = null) {
372
+ let {
356
373
  collapsedSize,
357
374
  collapsible,
358
375
  maxSize,
359
376
  minSize
360
377
  } = panel.current;
378
+ if (units === "pixels") {
379
+ collapsedSize = collapsedSize / groupSizePixels * 100;
380
+ if (maxSize != null) {
381
+ maxSize = maxSize / groupSizePixels * 100;
382
+ }
383
+ minSize = minSize / groupSizePixels * 100;
384
+ }
361
385
  if (collapsible) {
362
386
  if (prevSize > collapsedSize) {
363
387
  // Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
364
- if (nextSizeUnsafe <= minSize / 2 + collapsedSize) {
388
+ if (nextSize <= minSize / 2 + collapsedSize) {
365
389
  return collapsedSize;
366
390
  }
367
391
  } else {
@@ -370,14 +394,97 @@ function safeResizePanel(panel, delta, prevSize, event) {
370
394
  // Keyboard events should expand a collapsed panel to the min size,
371
395
  // but mouse events should wait until the panel has reached its min size
372
396
  // to avoid a visual flickering when dragging between collapsed and min size.
373
- if (nextSizeUnsafe < minSize) {
397
+ if (nextSize < minSize) {
374
398
  return collapsedSize;
375
399
  }
376
400
  }
377
401
  }
378
402
  }
379
- const nextSize = Math.min(maxSize, Math.max(minSize, nextSizeUnsafe));
380
- return nextSize;
403
+ return Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
404
+ }
405
+ function validatePanelProps(units, panelData) {
406
+ const {
407
+ collapsible,
408
+ defaultSize,
409
+ maxSize,
410
+ minSize
411
+ } = panelData.current;
412
+
413
+ // Basic props validation
414
+ if (minSize < 0 || units === "percentages" && minSize > 100) {
415
+ panelData.current.minSize = 0;
416
+ }
417
+ if (maxSize != null) {
418
+ if (maxSize < 0 || units === "percentages" && maxSize > 100) {
419
+ panelData.current.maxSize = null;
420
+ }
421
+ }
422
+ if (defaultSize !== null) {
423
+ if (defaultSize < 0 || units === "percentages" && defaultSize > 100) {
424
+ panelData.current.defaultSize = null;
425
+ } else if (defaultSize < minSize && !collapsible) {
426
+ panelData.current.defaultSize = minSize;
427
+ } else if (maxSize != null && defaultSize > maxSize) {
428
+ panelData.current.defaultSize = maxSize;
429
+ }
430
+ }
431
+ }
432
+ function validatePanelGroupLayout({
433
+ groupId,
434
+ panels,
435
+ nextSizes,
436
+ prevSizes,
437
+ units
438
+ }) {
439
+ // Clone because this method modifies
440
+ nextSizes = [...nextSizes];
441
+ const panelsArray = panelsMapToSortedArray(panels);
442
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
443
+ let remainingSize = 0;
444
+
445
+ // First, check all of the proposed sizes against the min/max constraints
446
+ for (let index = 0; index < panelsArray.length; index++) {
447
+ const panel = panelsArray[index];
448
+ const prevSize = prevSizes[index];
449
+ const nextSize = nextSizes[index];
450
+ const safeNextSize = safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize);
451
+ if (nextSize != safeNextSize) {
452
+ remainingSize += nextSize - safeNextSize;
453
+ nextSizes[index] = safeNextSize;
454
+ }
455
+ }
456
+
457
+ // If there is additional, left over space, assign it to any panel(s) that permits it
458
+ // (It's not worth taking multiple additional passes to evenly distribute)
459
+ if (remainingSize.toFixed(3) !== "0.000") {
460
+ for (let index = 0; index < panelsArray.length; index++) {
461
+ const panel = panelsArray[index];
462
+ let {
463
+ maxSize,
464
+ minSize
465
+ } = panel.current;
466
+ if (units === "pixels") {
467
+ minSize = minSize / groupSizePixels * 100;
468
+ if (maxSize != null) {
469
+ maxSize = maxSize / groupSizePixels * 100;
470
+ }
471
+ }
472
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSizes[index] + remainingSize));
473
+ if (size !== nextSizes[index]) {
474
+ remainingSize -= size - nextSizes[index];
475
+ nextSizes[index] = size;
476
+
477
+ // Fuzzy comparison to account for imprecise floating point math
478
+ if (Math.abs(remainingSize).toFixed(3) === "0.000") {
479
+ break;
480
+ }
481
+ }
482
+ }
483
+ }
484
+
485
+ // If we still have remainder, the requested layout wasn't valid and we should warn about it
486
+ if (remainingSize.toFixed(3) !== "0.000") ;
487
+ return nextSizes;
381
488
  }
382
489
 
383
490
  function assert(expectedCondition, message = "Assertion failed!") {
@@ -403,6 +510,7 @@ function useWindowSplitterPanelGroupBehavior({
403
510
  panels
404
511
  } = committedValuesRef.current;
405
512
  const groupElement = getPanelGroup(groupId);
513
+ assert(groupElement != null, `No group found for id "${groupId}"`);
406
514
  const {
407
515
  height,
408
516
  width
@@ -415,23 +523,28 @@ function useWindowSplitterPanelGroupBehavior({
415
523
  if (idBefore == null || idAfter == null) {
416
524
  return () => {};
417
525
  }
418
- let minSize = 0;
419
- let maxSize = 100;
526
+ let currentMinSize = 0;
527
+ let currentMaxSize = 100;
420
528
  let totalMinSize = 0;
421
529
  let totalMaxSize = 0;
422
530
 
423
531
  // A panel's effective min/max sizes also need to account for other panel's sizes.
424
532
  panelsArray.forEach(panelData => {
425
- if (panelData.current.id === idBefore) {
426
- maxSize = panelData.current.maxSize;
427
- minSize = panelData.current.minSize;
533
+ const {
534
+ id,
535
+ maxSize,
536
+ minSize
537
+ } = panelData.current;
538
+ if (id === idBefore) {
539
+ currentMinSize = minSize;
540
+ currentMaxSize = maxSize != null ? maxSize : 100;
428
541
  } else {
429
- totalMinSize += panelData.current.minSize;
430
- totalMaxSize += panelData.current.maxSize;
542
+ totalMinSize += minSize;
543
+ totalMaxSize += maxSize != null ? maxSize : 100;
431
544
  }
432
545
  });
433
- const ariaValueMax = Math.min(maxSize, 100 - totalMinSize);
434
- const ariaValueMin = Math.max(minSize, (panelsArray.length - 1) * 100 - totalMaxSize);
546
+ const ariaValueMax = Math.min(currentMaxSize, 100 - totalMinSize);
547
+ const ariaValueMin = Math.max(currentMinSize, (panelsArray.length - 1) * 100 - totalMaxSize);
435
548
  const flexGrow = getFlexGrow(panels, idBefore, sizes);
436
549
  handle.setAttribute("aria-valuemax", "" + Math.round(ariaValueMax));
437
550
  handle.setAttribute("aria-valuemin", "" + Math.round(ariaValueMin));
@@ -455,7 +568,7 @@ function useWindowSplitterPanelGroupBehavior({
455
568
  } else {
456
569
  delta = -(direction === "horizontal" ? width : height);
457
570
  }
458
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
571
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
459
572
  if (sizes !== nextSizes) {
460
573
  setSizes(nextSizes);
461
574
  }
@@ -762,9 +875,6 @@ const defaultStorage = {
762
875
  // * dragHandleRect, sizes:
763
876
  // When resizing is done via mouse/touch event– some initial state is stored
764
877
  // so that any panels that contract will also expand if drag direction is reversed.
765
- // TODO
766
- // Within an active drag, remember original positions to refine more easily on expand.
767
- // Look at what the Chrome devtools Sources does.
768
878
  function PanelGroupWithForwardedRef({
769
879
  autoSaveId,
770
880
  children = null,
@@ -776,7 +886,8 @@ function PanelGroupWithForwardedRef({
776
886
  onLayout,
777
887
  storage = defaultStorage,
778
888
  style: styleFromProps = {},
779
- tagName: Type = "div"
889
+ tagName: Type = "div",
890
+ units = "percentages"
780
891
  }) {
781
892
  const groupId = useUniqueId(idFromProps);
782
893
  const [activeHandleId, setActiveHandleId] = useState(null);
@@ -789,6 +900,7 @@ function PanelGroupWithForwardedRef({
789
900
  useRef({
790
901
  didLogDefaultSizeWarning: false,
791
902
  didLogIdAndOrderWarning: false,
903
+ didLogInvalidLayoutWarning: false,
792
904
  prevPanelIds: []
793
905
  });
794
906
 
@@ -811,28 +923,52 @@ function PanelGroupWithForwardedRef({
811
923
  // Store committed values to avoid unnecessarily re-running memoization/effects functions.
812
924
  const committedValuesRef = useRef({
813
925
  direction,
926
+ id: groupId,
814
927
  panels,
815
- sizes
928
+ sizes,
929
+ units
816
930
  });
817
931
  useImperativeHandle(forwardedRef, () => ({
818
- getLayout: () => {
932
+ getId: () => groupId,
933
+ getLayout: unitsFromParams => {
819
934
  const {
820
- sizes
935
+ sizes,
936
+ units: unitsFromProps
821
937
  } = committedValuesRef.current;
822
- return sizes;
938
+ const units = unitsFromParams ?? unitsFromProps;
939
+ if (units === "pixels") {
940
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
941
+ return sizes.map(size => size / 100 * groupSizePixels);
942
+ } else {
943
+ return sizes;
944
+ }
823
945
  },
824
- setLayout: sizes => {
825
- const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
826
- assert(total === 100, "Panel sizes must add up to 100%");
946
+ setLayout: (sizes, unitsFromParams) => {
827
947
  const {
828
- panels
948
+ id: groupId,
949
+ panels,
950
+ sizes: prevSizes,
951
+ units
829
952
  } = committedValuesRef.current;
953
+ if ((unitsFromParams || units) === "pixels") {
954
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
955
+ sizes = sizes.map(size => size / groupSizePixels * 100);
956
+ }
830
957
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
831
958
  const panelsArray = panelsMapToSortedArray(panels);
832
- setSizes(sizes);
833
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
959
+ const nextSizes = validatePanelGroupLayout({
960
+ groupId,
961
+ panels,
962
+ nextSizes: sizes,
963
+ prevSizes,
964
+ units
965
+ });
966
+ if (!areEqual(prevSizes, nextSizes)) {
967
+ setSizes(nextSizes);
968
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
969
+ }
834
970
  }
835
- }), []);
971
+ }), [groupId]);
836
972
  useWindowSplitterPanelGroupBehavior({
837
973
  committedValuesRef,
838
974
  groupId,
@@ -883,6 +1019,22 @@ function PanelGroupWithForwardedRef({
883
1019
  debounceMap[autoSaveId](autoSaveId, panelsArray, sizes, storage);
884
1020
  }
885
1021
  }, [autoSaveId, panels, sizes, storage]);
1022
+ const getPanelSize = useCallback((id, unitsFromParams) => {
1023
+ const {
1024
+ panels,
1025
+ units: unitsFromProps
1026
+ } = committedValuesRef.current;
1027
+ const panelsArray = panelsMapToSortedArray(panels);
1028
+ const index = panelsArray.findIndex(panel => panel.current.id === id);
1029
+ const size = sizes[index];
1030
+ const units = unitsFromParams ?? unitsFromProps;
1031
+ if (units === "pixels") {
1032
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1033
+ return size / 100 * groupSizePixels;
1034
+ } else {
1035
+ return size;
1036
+ }
1037
+ }, [groupId, sizes]);
886
1038
  const getPanelStyle = useCallback((id, defaultSize) => {
887
1039
  const {
888
1040
  panels
@@ -913,6 +1065,10 @@ function PanelGroupWithForwardedRef({
913
1065
  };
914
1066
  }, [activeHandleId, disablePointerEventsDuringResize, sizes]);
915
1067
  const registerPanel = useCallback((id, panelRef) => {
1068
+ const {
1069
+ units
1070
+ } = committedValuesRef.current;
1071
+ validatePanelProps(units, panelRef);
916
1072
  setPanels(prevPanels => {
917
1073
  if (prevPanels.has(id)) {
918
1074
  return prevPanels;
@@ -949,7 +1105,10 @@ function PanelGroupWithForwardedRef({
949
1105
  }
950
1106
  const size = isHorizontal ? rect.width : rect.height;
951
1107
  const delta = movement / size * 100;
952
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1108
+
1109
+ // If a validateLayout method has been provided
1110
+ // it's important to use it before updating the mouse cursor
1111
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
953
1112
  const sizesChanged = !areEqual(prevSizes, nextSizes);
954
1113
 
955
1114
  // Don't update cursor for resizes triggered by keyboard interactions.
@@ -976,6 +1135,8 @@ function PanelGroupWithForwardedRef({
976
1135
  }
977
1136
  if (sizesChanged) {
978
1137
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1138
+
1139
+ // It's okay to bypass in this case because we already validated above
979
1140
  setSizes(nextSizes);
980
1141
 
981
1142
  // If resize change handlers have been declared, this is the time to call them.
@@ -1029,7 +1190,7 @@ function PanelGroupWithForwardedRef({
1029
1190
  }
1030
1191
  const isLastPanel = index === panelsArray.length - 1;
1031
1192
  const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
1032
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1193
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1033
1194
  if (prevSizes !== nextSizes) {
1034
1195
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1035
1196
  setSizes(nextSizes);
@@ -1072,7 +1233,7 @@ function PanelGroupWithForwardedRef({
1072
1233
  }
1073
1234
  const isLastPanel = index === panelsArray.length - 1;
1074
1235
  const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
1075
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1236
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1076
1237
  if (prevSizes !== nextSizes) {
1077
1238
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1078
1239
  setSizes(nextSizes);
@@ -1082,21 +1243,34 @@ function PanelGroupWithForwardedRef({
1082
1243
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1083
1244
  }
1084
1245
  }, []);
1085
- const resizePanel = useCallback((id, nextSize) => {
1246
+ const resizePanel = useCallback((id, nextSize, unitsFromParams) => {
1086
1247
  const {
1248
+ id: groupId,
1087
1249
  panels,
1088
- sizes: prevSizes
1250
+ sizes: prevSizes,
1251
+ units
1089
1252
  } = committedValuesRef.current;
1253
+ if ((unitsFromParams || units) === "pixels") {
1254
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1255
+ nextSize = nextSize / groupSizePixels * 100;
1256
+ }
1090
1257
  const panel = panels.get(id);
1091
1258
  if (panel == null) {
1092
1259
  return;
1093
1260
  }
1094
- const {
1261
+ let {
1095
1262
  collapsedSize,
1096
1263
  collapsible,
1097
1264
  maxSize,
1098
1265
  minSize
1099
1266
  } = panel.current;
1267
+ if (units === "pixels") {
1268
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1269
+ minSize = minSize / groupSizePixels * 100;
1270
+ if (maxSize != null) {
1271
+ maxSize = maxSize / groupSizePixels * 100;
1272
+ }
1273
+ }
1100
1274
  const panelsArray = panelsMapToSortedArray(panels);
1101
1275
  const index = panelsArray.indexOf(panel);
1102
1276
  if (index < 0) {
@@ -1107,7 +1281,7 @@ function PanelGroupWithForwardedRef({
1107
1281
  return;
1108
1282
  }
1109
1283
  if (collapsible && nextSize === collapsedSize) ; else {
1110
- nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
1284
+ nextSize = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
1111
1285
  }
1112
1286
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
1113
1287
  if (idBefore == null || idAfter == null) {
@@ -1115,7 +1289,7 @@ function PanelGroupWithForwardedRef({
1115
1289
  }
1116
1290
  const isLastPanel = index === panelsArray.length - 1;
1117
1291
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1118
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1292
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1119
1293
  if (prevSizes !== nextSizes) {
1120
1294
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1121
1295
  setSizes(nextSizes);
@@ -1130,6 +1304,7 @@ function PanelGroupWithForwardedRef({
1130
1304
  collapsePanel,
1131
1305
  direction,
1132
1306
  expandPanel,
1307
+ getPanelSize,
1133
1308
  getPanelStyle,
1134
1309
  groupId,
1135
1310
  registerPanel,
@@ -1151,8 +1326,9 @@ function PanelGroupWithForwardedRef({
1151
1326
  setActiveHandleId(null);
1152
1327
  initialDragStateRef.current = null;
1153
1328
  },
1329
+ units,
1154
1330
  unregisterPanel
1155
- }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, unregisterPanel]);
1331
+ }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelSize, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, units, unregisterPanel]);
1156
1332
  const style = {
1157
1333
  display: "flex",
1158
1334
  flexDirection: direction === "horizontal" ? "row" : "column",
@@ -1167,6 +1343,7 @@ function PanelGroupWithForwardedRef({
1167
1343
  "data-panel-group": "",
1168
1344
  "data-panel-group-direction": direction,
1169
1345
  "data-panel-group-id": groupId,
1346
+ "data-panel-group-units": units,
1170
1347
  style: {
1171
1348
  ...style,
1172
1349
  ...styleFromProps
@@ -1316,4 +1493,4 @@ function PanelResizeHandle({
1316
1493
  }
1317
1494
  PanelResizeHandle.displayName = "PanelResizeHandle";
1318
1495
 
1319
- export { Panel, PanelGroup, PanelResizeHandle };
1496
+ export { Panel, PanelGroup, PanelResizeHandle, getAvailableGroupSizePixels };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "0.0.54",
3
+ "version": "0.0.55",
4
4
  "description": "React components for resizable panel groups/layouts",
5
5
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
6
6
  "license": "MIT",