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
@@ -67,8 +67,8 @@ function PanelWithForwardedRef({
67
67
  defaultSize = null,
68
68
  forwardedRef,
69
69
  id: idFromProps = null,
70
- maxSize = 100,
71
- minSize = 10,
70
+ maxSize = null,
71
+ minSize,
72
72
  onCollapse = null,
73
73
  onResize = null,
74
74
  order = null,
@@ -83,11 +83,22 @@ function PanelWithForwardedRef({
83
83
  const {
84
84
  collapsePanel,
85
85
  expandPanel,
86
+ getPanelSize,
86
87
  getPanelStyle,
87
88
  registerPanel,
88
89
  resizePanel,
90
+ units,
89
91
  unregisterPanel
90
92
  } = context;
93
+ if (minSize == null) {
94
+ if (units === "percentages") {
95
+ // Mimics legacy default value for percentage based panel groups
96
+ minSize = 10;
97
+ } else {
98
+ // There is no meaningful minimum pixel default we can provide
99
+ minSize = 0;
100
+ }
101
+ }
91
102
 
92
103
  // Use a ref to guard against users passing inline props
93
104
  const callbacksRef = useRef({
@@ -98,22 +109,6 @@ function PanelWithForwardedRef({
98
109
  callbacksRef.current.onCollapse = onCollapse;
99
110
  callbacksRef.current.onResize = onResize;
100
111
  });
101
-
102
- // Basic props validation
103
- if (minSize < 0 || minSize > 100) {
104
- throw Error(`Panel minSize must be between 0 and 100, but was ${minSize}`);
105
- } else if (maxSize < 0 || maxSize > 100) {
106
- throw Error(`Panel maxSize must be between 0 and 100, but was ${maxSize}`);
107
- } else {
108
- if (defaultSize !== null) {
109
- if (defaultSize < 0 || defaultSize > 100) {
110
- throw Error(`Panel defaultSize must be between 0 and 100, but was ${defaultSize}`);
111
- } else if (minSize > defaultSize && !collapsible) {
112
- console.error(`Panel minSize ${minSize} cannot be greater than defaultSize ${defaultSize}`);
113
- defaultSize = minSize;
114
- }
115
- }
116
- }
117
112
  const style = getPanelStyle(panelId, defaultSize);
118
113
  const committedValuesRef = useRef({
119
114
  size: parseSizeFromStyle(style)
@@ -135,11 +130,14 @@ function PanelWithForwardedRef({
135
130
  getCollapsed() {
136
131
  return committedValuesRef.current.size === 0;
137
132
  },
138
- getSize() {
139
- return committedValuesRef.current.size;
133
+ getId() {
134
+ return panelId;
135
+ },
136
+ getSize(units) {
137
+ return getPanelSize(panelId, units);
140
138
  },
141
- resize: percentage => resizePanel(panelId, percentage)
142
- }), [collapsePanel, expandPanel, panelId, resizePanel]);
139
+ resize: (percentage, units) => resizePanel(panelId, percentage, units)
140
+ }), [collapsePanel, expandPanel, getPanelSize, panelId, resizePanel]);
143
141
  return createElement(Type, {
144
142
  children,
145
143
  className: classNameFromProps,
@@ -175,7 +173,13 @@ function parseSizeFromStyle(style) {
175
173
 
176
174
  const PRECISION = 10;
177
175
 
178
- function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse, initialDragState) {
176
+ function adjustByDelta(event, committedValues, idBefore, idAfter, deltaPixels, prevSizes, panelSizeBeforeCollapse, initialDragState) {
177
+ const {
178
+ id: groupId,
179
+ panels,
180
+ units
181
+ } = committedValues;
182
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
179
183
  const {
180
184
  sizes: initialSizes
181
185
  } = initialDragState || {};
@@ -183,9 +187,6 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
183
187
  // If we're resizing by mouse or touch, use the initial sizes as a base.
184
188
  // This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
185
189
  const baseSizes = initialSizes || prevSizes;
186
- if (delta === 0) {
187
- return baseSizes;
188
- }
189
190
  const panelsArray = panelsMapToSortedArray(panels);
190
191
  const nextSizes = baseSizes.concat();
191
192
  let deltaApplied = 0;
@@ -200,11 +201,11 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
200
201
 
201
202
  // Max-bounds check the panel being expanded first.
202
203
  {
203
- const pivotId = delta < 0 ? idAfter : idBefore;
204
+ const pivotId = deltaPixels < 0 ? idAfter : idBefore;
204
205
  const index = panelsArray.findIndex(panel => panel.current.id === pivotId);
205
206
  const panel = panelsArray[index];
206
207
  const baseSize = baseSizes[index];
207
- const nextSize = safeResizePanel(panel, Math.abs(delta), baseSize, event);
208
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize + Math.abs(deltaPixels), event);
208
209
  if (baseSize === nextSize) {
209
210
  // If there's no room for the pivot panel to grow, we can ignore this drag update.
210
211
  return baseSizes;
@@ -212,29 +213,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
212
213
  if (nextSize === 0 && baseSize > 0) {
213
214
  panelSizeBeforeCollapse.set(pivotId, baseSize);
214
215
  }
215
- delta = delta < 0 ? baseSize - nextSize : nextSize - baseSize;
216
+ deltaPixels = deltaPixels < 0 ? baseSize - nextSize : nextSize - baseSize;
216
217
  }
217
218
  }
218
- let pivotId = delta < 0 ? idBefore : idAfter;
219
+ let pivotId = deltaPixels < 0 ? idBefore : idAfter;
219
220
  let index = panelsArray.findIndex(panel => panel.current.id === pivotId);
220
221
  while (true) {
221
222
  const panel = panelsArray[index];
222
223
  const baseSize = baseSizes[index];
223
- const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
224
- const nextSize = safeResizePanel(panel, 0 - deltaRemaining, baseSize, event);
224
+ const deltaRemaining = Math.abs(deltaPixels) - Math.abs(deltaApplied);
225
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize - deltaRemaining, event);
225
226
  if (baseSize !== nextSize) {
226
227
  if (nextSize === 0 && baseSize > 0) {
227
228
  panelSizeBeforeCollapse.set(panel.current.id, baseSize);
228
229
  }
229
230
  deltaApplied += baseSize - nextSize;
230
231
  nextSizes[index] = nextSize;
231
- if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(delta).toPrecision(PRECISION), undefined, {
232
+ if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(deltaPixels).toPrecision(PRECISION), undefined, {
232
233
  numeric: true
233
234
  }) >= 0) {
234
235
  break;
235
236
  }
236
237
  }
237
- if (delta < 0) {
238
+ if (deltaPixels < 0) {
238
239
  if (--index < 0) {
239
240
  break;
240
241
  }
@@ -252,7 +253,7 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
252
253
  }
253
254
 
254
255
  // Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
255
- pivotId = delta < 0 ? idAfter : idBefore;
256
+ pivotId = deltaPixels < 0 ? idAfter : idBefore;
256
257
  index = panelsArray.findIndex(panel => panel.current.id === pivotId);
257
258
  nextSizes[index] = baseSizes[index] + deltaApplied;
258
259
  return nextSizes;
@@ -304,6 +305,23 @@ function getBeforeAndAfterIds(id, panelsArray) {
304
305
  const idAfter = isLastPanel ? id : panelsArray[index + 1].current.id;
305
306
  return [idBefore, idAfter];
306
307
  }
308
+ function getAvailableGroupSizePixels(groupId) {
309
+ const panelGroupElement = getPanelGroup(groupId);
310
+ if (panelGroupElement == null) {
311
+ return NaN;
312
+ }
313
+ const direction = panelGroupElement.getAttribute("data-panel-group-direction");
314
+ const resizeHandles = getResizeHandlesForGroup(groupId);
315
+ if (direction === "horizontal") {
316
+ return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
317
+ return accumulated + handle.offsetWidth;
318
+ }, 0);
319
+ } else {
320
+ return panelGroupElement.offsetHeight - resizeHandles.reduce((accumulated, handle) => {
321
+ return accumulated + handle.offsetHeight;
322
+ }, 0);
323
+ }
324
+ }
307
325
 
308
326
  // This method returns a number between 1 and 100 representing
309
327
  // the % of the group's overall space this panel should occupy.
@@ -374,18 +392,24 @@ function panelsMapToSortedArray(panels) {
374
392
  }
375
393
  });
376
394
  }
377
- function safeResizePanel(panel, delta, prevSize, event) {
378
- const nextSizeUnsafe = prevSize + delta;
379
- const {
395
+ function safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize, event = null) {
396
+ let {
380
397
  collapsedSize,
381
398
  collapsible,
382
399
  maxSize,
383
400
  minSize
384
401
  } = panel.current;
402
+ if (units === "pixels") {
403
+ collapsedSize = collapsedSize / groupSizePixels * 100;
404
+ if (maxSize != null) {
405
+ maxSize = maxSize / groupSizePixels * 100;
406
+ }
407
+ minSize = minSize / groupSizePixels * 100;
408
+ }
385
409
  if (collapsible) {
386
410
  if (prevSize > collapsedSize) {
387
411
  // Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
388
- if (nextSizeUnsafe <= minSize / 2 + collapsedSize) {
412
+ if (nextSize <= minSize / 2 + collapsedSize) {
389
413
  return collapsedSize;
390
414
  }
391
415
  } else {
@@ -394,14 +418,97 @@ function safeResizePanel(panel, delta, prevSize, event) {
394
418
  // Keyboard events should expand a collapsed panel to the min size,
395
419
  // but mouse events should wait until the panel has reached its min size
396
420
  // to avoid a visual flickering when dragging between collapsed and min size.
397
- if (nextSizeUnsafe < minSize) {
421
+ if (nextSize < minSize) {
398
422
  return collapsedSize;
399
423
  }
400
424
  }
401
425
  }
402
426
  }
403
- const nextSize = Math.min(maxSize, Math.max(minSize, nextSizeUnsafe));
404
- return nextSize;
427
+ return Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
428
+ }
429
+ function validatePanelProps(units, panelData) {
430
+ const {
431
+ collapsible,
432
+ defaultSize,
433
+ maxSize,
434
+ minSize
435
+ } = panelData.current;
436
+
437
+ // Basic props validation
438
+ if (minSize < 0 || units === "percentages" && minSize > 100) {
439
+ panelData.current.minSize = 0;
440
+ }
441
+ if (maxSize != null) {
442
+ if (maxSize < 0 || units === "percentages" && maxSize > 100) {
443
+ panelData.current.maxSize = null;
444
+ }
445
+ }
446
+ if (defaultSize !== null) {
447
+ if (defaultSize < 0 || units === "percentages" && defaultSize > 100) {
448
+ panelData.current.defaultSize = null;
449
+ } else if (defaultSize < minSize && !collapsible) {
450
+ panelData.current.defaultSize = minSize;
451
+ } else if (maxSize != null && defaultSize > maxSize) {
452
+ panelData.current.defaultSize = maxSize;
453
+ }
454
+ }
455
+ }
456
+ function validatePanelGroupLayout({
457
+ groupId,
458
+ panels,
459
+ nextSizes,
460
+ prevSizes,
461
+ units
462
+ }) {
463
+ // Clone because this method modifies
464
+ nextSizes = [...nextSizes];
465
+ const panelsArray = panelsMapToSortedArray(panels);
466
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
467
+ let remainingSize = 0;
468
+
469
+ // First, check all of the proposed sizes against the min/max constraints
470
+ for (let index = 0; index < panelsArray.length; index++) {
471
+ const panel = panelsArray[index];
472
+ const prevSize = prevSizes[index];
473
+ const nextSize = nextSizes[index];
474
+ const safeNextSize = safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize);
475
+ if (nextSize != safeNextSize) {
476
+ remainingSize += nextSize - safeNextSize;
477
+ nextSizes[index] = safeNextSize;
478
+ }
479
+ }
480
+
481
+ // If there is additional, left over space, assign it to any panel(s) that permits it
482
+ // (It's not worth taking multiple additional passes to evenly distribute)
483
+ if (remainingSize.toFixed(3) !== "0.000") {
484
+ for (let index = 0; index < panelsArray.length; index++) {
485
+ const panel = panelsArray[index];
486
+ let {
487
+ maxSize,
488
+ minSize
489
+ } = panel.current;
490
+ if (units === "pixels") {
491
+ minSize = minSize / groupSizePixels * 100;
492
+ if (maxSize != null) {
493
+ maxSize = maxSize / groupSizePixels * 100;
494
+ }
495
+ }
496
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSizes[index] + remainingSize));
497
+ if (size !== nextSizes[index]) {
498
+ remainingSize -= size - nextSizes[index];
499
+ nextSizes[index] = size;
500
+
501
+ // Fuzzy comparison to account for imprecise floating point math
502
+ if (Math.abs(remainingSize).toFixed(3) === "0.000") {
503
+ break;
504
+ }
505
+ }
506
+ }
507
+ }
508
+
509
+ // If we still have remainder, the requested layout wasn't valid and we should warn about it
510
+ if (remainingSize.toFixed(3) !== "0.000") ;
511
+ return nextSizes;
405
512
  }
406
513
 
407
514
  function assert(expectedCondition, message = "Assertion failed!") {
@@ -427,6 +534,7 @@ function useWindowSplitterPanelGroupBehavior({
427
534
  panels
428
535
  } = committedValuesRef.current;
429
536
  const groupElement = getPanelGroup(groupId);
537
+ assert(groupElement != null, `No group found for id "${groupId}"`);
430
538
  const {
431
539
  height,
432
540
  width
@@ -439,23 +547,28 @@ function useWindowSplitterPanelGroupBehavior({
439
547
  if (idBefore == null || idAfter == null) {
440
548
  return () => {};
441
549
  }
442
- let minSize = 0;
443
- let maxSize = 100;
550
+ let currentMinSize = 0;
551
+ let currentMaxSize = 100;
444
552
  let totalMinSize = 0;
445
553
  let totalMaxSize = 0;
446
554
 
447
555
  // A panel's effective min/max sizes also need to account for other panel's sizes.
448
556
  panelsArray.forEach(panelData => {
449
- if (panelData.current.id === idBefore) {
450
- maxSize = panelData.current.maxSize;
451
- minSize = panelData.current.minSize;
557
+ const {
558
+ id,
559
+ maxSize,
560
+ minSize
561
+ } = panelData.current;
562
+ if (id === idBefore) {
563
+ currentMinSize = minSize;
564
+ currentMaxSize = maxSize != null ? maxSize : 100;
452
565
  } else {
453
- totalMinSize += panelData.current.minSize;
454
- totalMaxSize += panelData.current.maxSize;
566
+ totalMinSize += minSize;
567
+ totalMaxSize += maxSize != null ? maxSize : 100;
455
568
  }
456
569
  });
457
- const ariaValueMax = Math.min(maxSize, 100 - totalMinSize);
458
- const ariaValueMin = Math.max(minSize, (panelsArray.length - 1) * 100 - totalMaxSize);
570
+ const ariaValueMax = Math.min(currentMaxSize, 100 - totalMinSize);
571
+ const ariaValueMin = Math.max(currentMinSize, (panelsArray.length - 1) * 100 - totalMaxSize);
459
572
  const flexGrow = getFlexGrow(panels, idBefore, sizes);
460
573
  handle.setAttribute("aria-valuemax", "" + Math.round(ariaValueMax));
461
574
  handle.setAttribute("aria-valuemin", "" + Math.round(ariaValueMin));
@@ -479,7 +592,7 @@ function useWindowSplitterPanelGroupBehavior({
479
592
  } else {
480
593
  delta = -(direction === "horizontal" ? width : height);
481
594
  }
482
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
595
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
483
596
  if (sizes !== nextSizes) {
484
597
  setSizes(nextSizes);
485
598
  }
@@ -786,9 +899,6 @@ const defaultStorage = {
786
899
  // * dragHandleRect, sizes:
787
900
  // When resizing is done via mouse/touch event– some initial state is stored
788
901
  // so that any panels that contract will also expand if drag direction is reversed.
789
- // TODO
790
- // Within an active drag, remember original positions to refine more easily on expand.
791
- // Look at what the Chrome devtools Sources does.
792
902
  function PanelGroupWithForwardedRef({
793
903
  autoSaveId,
794
904
  children = null,
@@ -800,7 +910,8 @@ function PanelGroupWithForwardedRef({
800
910
  onLayout,
801
911
  storage = defaultStorage,
802
912
  style: styleFromProps = {},
803
- tagName: Type = "div"
913
+ tagName: Type = "div",
914
+ units = "percentages"
804
915
  }) {
805
916
  const groupId = useUniqueId(idFromProps);
806
917
  const [activeHandleId, setActiveHandleId] = useState(null);
@@ -813,6 +924,7 @@ function PanelGroupWithForwardedRef({
813
924
  useRef({
814
925
  didLogDefaultSizeWarning: false,
815
926
  didLogIdAndOrderWarning: false,
927
+ didLogInvalidLayoutWarning: false,
816
928
  prevPanelIds: []
817
929
  });
818
930
 
@@ -835,28 +947,52 @@ function PanelGroupWithForwardedRef({
835
947
  // Store committed values to avoid unnecessarily re-running memoization/effects functions.
836
948
  const committedValuesRef = useRef({
837
949
  direction,
950
+ id: groupId,
838
951
  panels,
839
- sizes
952
+ sizes,
953
+ units
840
954
  });
841
955
  useImperativeHandle(forwardedRef, () => ({
842
- getLayout: () => {
956
+ getId: () => groupId,
957
+ getLayout: unitsFromParams => {
843
958
  const {
844
- sizes
959
+ sizes,
960
+ units: unitsFromProps
845
961
  } = committedValuesRef.current;
846
- return sizes;
962
+ const units = unitsFromParams ?? unitsFromProps;
963
+ if (units === "pixels") {
964
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
965
+ return sizes.map(size => size / 100 * groupSizePixels);
966
+ } else {
967
+ return sizes;
968
+ }
847
969
  },
848
- setLayout: sizes => {
849
- const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
850
- assert(total === 100, "Panel sizes must add up to 100%");
970
+ setLayout: (sizes, unitsFromParams) => {
851
971
  const {
852
- panels
972
+ id: groupId,
973
+ panels,
974
+ sizes: prevSizes,
975
+ units
853
976
  } = committedValuesRef.current;
977
+ if ((unitsFromParams || units) === "pixels") {
978
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
979
+ sizes = sizes.map(size => size / groupSizePixels * 100);
980
+ }
854
981
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
855
982
  const panelsArray = panelsMapToSortedArray(panels);
856
- setSizes(sizes);
857
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
983
+ const nextSizes = validatePanelGroupLayout({
984
+ groupId,
985
+ panels,
986
+ nextSizes: sizes,
987
+ prevSizes,
988
+ units
989
+ });
990
+ if (!areEqual(prevSizes, nextSizes)) {
991
+ setSizes(nextSizes);
992
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
993
+ }
858
994
  }
859
- }), []);
995
+ }), [groupId]);
860
996
  useWindowSplitterPanelGroupBehavior({
861
997
  committedValuesRef,
862
998
  groupId,
@@ -907,6 +1043,22 @@ function PanelGroupWithForwardedRef({
907
1043
  debounceMap[autoSaveId](autoSaveId, panelsArray, sizes, storage);
908
1044
  }
909
1045
  }, [autoSaveId, panels, sizes, storage]);
1046
+ const getPanelSize = useCallback((id, unitsFromParams) => {
1047
+ const {
1048
+ panels,
1049
+ units: unitsFromProps
1050
+ } = committedValuesRef.current;
1051
+ const panelsArray = panelsMapToSortedArray(panels);
1052
+ const index = panelsArray.findIndex(panel => panel.current.id === id);
1053
+ const size = sizes[index];
1054
+ const units = unitsFromParams ?? unitsFromProps;
1055
+ if (units === "pixels") {
1056
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1057
+ return size / 100 * groupSizePixels;
1058
+ } else {
1059
+ return size;
1060
+ }
1061
+ }, [groupId, sizes]);
910
1062
  const getPanelStyle = useCallback((id, defaultSize) => {
911
1063
  const {
912
1064
  panels
@@ -937,6 +1089,10 @@ function PanelGroupWithForwardedRef({
937
1089
  };
938
1090
  }, [activeHandleId, disablePointerEventsDuringResize, sizes]);
939
1091
  const registerPanel = useCallback((id, panelRef) => {
1092
+ const {
1093
+ units
1094
+ } = committedValuesRef.current;
1095
+ validatePanelProps(units, panelRef);
940
1096
  setPanels(prevPanels => {
941
1097
  if (prevPanels.has(id)) {
942
1098
  return prevPanels;
@@ -973,7 +1129,10 @@ function PanelGroupWithForwardedRef({
973
1129
  }
974
1130
  const size = isHorizontal ? rect.width : rect.height;
975
1131
  const delta = movement / size * 100;
976
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1132
+
1133
+ // If a validateLayout method has been provided
1134
+ // it's important to use it before updating the mouse cursor
1135
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
977
1136
  const sizesChanged = !areEqual(prevSizes, nextSizes);
978
1137
 
979
1138
  // Don't update cursor for resizes triggered by keyboard interactions.
@@ -1000,6 +1159,8 @@ function PanelGroupWithForwardedRef({
1000
1159
  }
1001
1160
  if (sizesChanged) {
1002
1161
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1162
+
1163
+ // It's okay to bypass in this case because we already validated above
1003
1164
  setSizes(nextSizes);
1004
1165
 
1005
1166
  // If resize change handlers have been declared, this is the time to call them.
@@ -1053,7 +1214,7 @@ function PanelGroupWithForwardedRef({
1053
1214
  }
1054
1215
  const isLastPanel = index === panelsArray.length - 1;
1055
1216
  const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
1056
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1217
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1057
1218
  if (prevSizes !== nextSizes) {
1058
1219
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1059
1220
  setSizes(nextSizes);
@@ -1096,7 +1257,7 @@ function PanelGroupWithForwardedRef({
1096
1257
  }
1097
1258
  const isLastPanel = index === panelsArray.length - 1;
1098
1259
  const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
1099
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1260
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1100
1261
  if (prevSizes !== nextSizes) {
1101
1262
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1102
1263
  setSizes(nextSizes);
@@ -1106,21 +1267,34 @@ function PanelGroupWithForwardedRef({
1106
1267
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1107
1268
  }
1108
1269
  }, []);
1109
- const resizePanel = useCallback((id, nextSize) => {
1270
+ const resizePanel = useCallback((id, nextSize, unitsFromParams) => {
1110
1271
  const {
1272
+ id: groupId,
1111
1273
  panels,
1112
- sizes: prevSizes
1274
+ sizes: prevSizes,
1275
+ units
1113
1276
  } = committedValuesRef.current;
1277
+ if ((unitsFromParams || units) === "pixels") {
1278
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1279
+ nextSize = nextSize / groupSizePixels * 100;
1280
+ }
1114
1281
  const panel = panels.get(id);
1115
1282
  if (panel == null) {
1116
1283
  return;
1117
1284
  }
1118
- const {
1285
+ let {
1119
1286
  collapsedSize,
1120
1287
  collapsible,
1121
1288
  maxSize,
1122
1289
  minSize
1123
1290
  } = panel.current;
1291
+ if (units === "pixels") {
1292
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1293
+ minSize = minSize / groupSizePixels * 100;
1294
+ if (maxSize != null) {
1295
+ maxSize = maxSize / groupSizePixels * 100;
1296
+ }
1297
+ }
1124
1298
  const panelsArray = panelsMapToSortedArray(panels);
1125
1299
  const index = panelsArray.indexOf(panel);
1126
1300
  if (index < 0) {
@@ -1131,7 +1305,7 @@ function PanelGroupWithForwardedRef({
1131
1305
  return;
1132
1306
  }
1133
1307
  if (collapsible && nextSize === collapsedSize) ; else {
1134
- nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
1308
+ nextSize = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
1135
1309
  }
1136
1310
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
1137
1311
  if (idBefore == null || idAfter == null) {
@@ -1139,7 +1313,7 @@ function PanelGroupWithForwardedRef({
1139
1313
  }
1140
1314
  const isLastPanel = index === panelsArray.length - 1;
1141
1315
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1142
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1316
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1143
1317
  if (prevSizes !== nextSizes) {
1144
1318
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1145
1319
  setSizes(nextSizes);
@@ -1154,6 +1328,7 @@ function PanelGroupWithForwardedRef({
1154
1328
  collapsePanel,
1155
1329
  direction,
1156
1330
  expandPanel,
1331
+ getPanelSize,
1157
1332
  getPanelStyle,
1158
1333
  groupId,
1159
1334
  registerPanel,
@@ -1175,8 +1350,9 @@ function PanelGroupWithForwardedRef({
1175
1350
  setActiveHandleId(null);
1176
1351
  initialDragStateRef.current = null;
1177
1352
  },
1353
+ units,
1178
1354
  unregisterPanel
1179
- }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, unregisterPanel]);
1355
+ }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelSize, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, units, unregisterPanel]);
1180
1356
  const style = {
1181
1357
  display: "flex",
1182
1358
  flexDirection: direction === "horizontal" ? "row" : "column",
@@ -1191,6 +1367,7 @@ function PanelGroupWithForwardedRef({
1191
1367
  "data-panel-group": "",
1192
1368
  "data-panel-group-direction": direction,
1193
1369
  "data-panel-group-id": groupId,
1370
+ "data-panel-group-units": units,
1194
1371
  style: {
1195
1372
  ...style,
1196
1373
  ...styleFromProps
@@ -1343,3 +1520,4 @@ PanelResizeHandle.displayName = "PanelResizeHandle";
1343
1520
  exports.Panel = Panel;
1344
1521
  exports.PanelGroup = PanelGroup;
1345
1522
  exports.PanelResizeHandle = PanelResizeHandle;
1523
+ exports.getAvailableGroupSizePixels = getAvailableGroupSizePixels;
@@ -1,5 +1,6 @@
1
1
  export {
2
2
  Panel,
3
3
  PanelGroup,
4
- PanelResizeHandle
4
+ PanelResizeHandle,
5
+ getAvailableGroupSizePixels
5
6
  } from "./react-resizable-panels.node.cjs.js";