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,119 @@ 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
+ {
440
+ console.error(`Invalid Panel minSize provided, ${minSize}`);
441
+ }
442
+ panelData.current.minSize = 0;
443
+ }
444
+ if (maxSize != null) {
445
+ if (maxSize < 0 || units === "percentages" && maxSize > 100) {
446
+ {
447
+ console.error(`Invalid Panel maxSize provided, ${maxSize}`);
448
+ }
449
+ panelData.current.maxSize = null;
450
+ }
451
+ }
452
+ if (defaultSize !== null) {
453
+ if (defaultSize < 0 || units === "percentages" && defaultSize > 100) {
454
+ {
455
+ console.error(`Invalid Panel defaultSize provided, ${defaultSize}`);
456
+ }
457
+ panelData.current.defaultSize = null;
458
+ } else if (defaultSize < minSize && !collapsible) {
459
+ {
460
+ console.error(`Panel minSize (${minSize}) cannot be greater than defaultSize (${defaultSize})`);
461
+ }
462
+ panelData.current.defaultSize = minSize;
463
+ } else if (maxSize != null && defaultSize > maxSize) {
464
+ {
465
+ console.error(`Panel maxSize (${maxSize}) cannot be less than defaultSize (${defaultSize})`);
466
+ }
467
+ panelData.current.defaultSize = maxSize;
468
+ }
469
+ }
470
+ }
471
+ function validatePanelGroupLayout({
472
+ groupId,
473
+ panels,
474
+ nextSizes,
475
+ prevSizes,
476
+ units
477
+ }) {
478
+ // Clone because this method modifies
479
+ nextSizes = [...nextSizes];
480
+ const panelsArray = panelsMapToSortedArray(panels);
481
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
482
+ let remainingSize = 0;
483
+
484
+ // First, check all of the proposed sizes against the min/max constraints
485
+ for (let index = 0; index < panelsArray.length; index++) {
486
+ const panel = panelsArray[index];
487
+ const prevSize = prevSizes[index];
488
+ const nextSize = nextSizes[index];
489
+ const safeNextSize = safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize);
490
+ if (nextSize != safeNextSize) {
491
+ remainingSize += nextSize - safeNextSize;
492
+ nextSizes[index] = safeNextSize;
493
+ {
494
+ console.error(`Invalid size (${nextSize}) specified for Panel "${panel.current.id}" given the panel's min/max size constraints`);
495
+ }
496
+ }
497
+ }
498
+
499
+ // If there is additional, left over space, assign it to any panel(s) that permits it
500
+ // (It's not worth taking multiple additional passes to evenly distribute)
501
+ if (remainingSize.toFixed(3) !== "0.000") {
502
+ for (let index = 0; index < panelsArray.length; index++) {
503
+ const panel = panelsArray[index];
504
+ let {
505
+ maxSize,
506
+ minSize
507
+ } = panel.current;
508
+ if (units === "pixels") {
509
+ minSize = minSize / groupSizePixels * 100;
510
+ if (maxSize != null) {
511
+ maxSize = maxSize / groupSizePixels * 100;
512
+ }
513
+ }
514
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSizes[index] + remainingSize));
515
+ if (size !== nextSizes[index]) {
516
+ remainingSize -= size - nextSizes[index];
517
+ nextSizes[index] = size;
518
+
519
+ // Fuzzy comparison to account for imprecise floating point math
520
+ if (Math.abs(remainingSize).toFixed(3) === "0.000") {
521
+ break;
522
+ }
523
+ }
524
+ }
525
+ }
526
+
527
+ // If we still have remainder, the requested layout wasn't valid and we should warn about it
528
+ if (remainingSize.toFixed(3) !== "0.000") {
529
+ {
530
+ console.error(`"Invalid panel group configuration; default panel sizes should total 100% but was ${100 - remainingSize}%`);
531
+ }
532
+ }
533
+ return nextSizes;
405
534
  }
406
535
 
407
536
  function assert(expectedCondition, message = "Assertion failed!") {
@@ -427,6 +556,7 @@ function useWindowSplitterPanelGroupBehavior({
427
556
  panels
428
557
  } = committedValuesRef.current;
429
558
  const groupElement = getPanelGroup(groupId);
559
+ assert(groupElement != null, `No group found for id "${groupId}"`);
430
560
  const {
431
561
  height,
432
562
  width
@@ -439,23 +569,28 @@ function useWindowSplitterPanelGroupBehavior({
439
569
  if (idBefore == null || idAfter == null) {
440
570
  return () => {};
441
571
  }
442
- let minSize = 0;
443
- let maxSize = 100;
572
+ let currentMinSize = 0;
573
+ let currentMaxSize = 100;
444
574
  let totalMinSize = 0;
445
575
  let totalMaxSize = 0;
446
576
 
447
577
  // A panel's effective min/max sizes also need to account for other panel's sizes.
448
578
  panelsArray.forEach(panelData => {
449
- if (panelData.current.id === idBefore) {
450
- maxSize = panelData.current.maxSize;
451
- minSize = panelData.current.minSize;
579
+ const {
580
+ id,
581
+ maxSize,
582
+ minSize
583
+ } = panelData.current;
584
+ if (id === idBefore) {
585
+ currentMinSize = minSize;
586
+ currentMaxSize = maxSize != null ? maxSize : 100;
452
587
  } else {
453
- totalMinSize += panelData.current.minSize;
454
- totalMaxSize += panelData.current.maxSize;
588
+ totalMinSize += minSize;
589
+ totalMaxSize += maxSize != null ? maxSize : 100;
455
590
  }
456
591
  });
457
- const ariaValueMax = Math.min(maxSize, 100 - totalMinSize);
458
- const ariaValueMin = Math.max(minSize, (panelsArray.length - 1) * 100 - totalMaxSize);
592
+ const ariaValueMax = Math.min(currentMaxSize, 100 - totalMinSize);
593
+ const ariaValueMin = Math.max(currentMinSize, (panelsArray.length - 1) * 100 - totalMaxSize);
459
594
  const flexGrow = getFlexGrow(panels, idBefore, sizes);
460
595
  handle.setAttribute("aria-valuemax", "" + Math.round(ariaValueMax));
461
596
  handle.setAttribute("aria-valuemin", "" + Math.round(ariaValueMin));
@@ -479,7 +614,7 @@ function useWindowSplitterPanelGroupBehavior({
479
614
  } else {
480
615
  delta = -(direction === "horizontal" ? width : height);
481
616
  }
482
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
617
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
483
618
  if (sizes !== nextSizes) {
484
619
  setSizes(nextSizes);
485
620
  }
@@ -786,9 +921,6 @@ const defaultStorage = {
786
921
  // * dragHandleRect, sizes:
787
922
  // When resizing is done via mouse/touch event– some initial state is stored
788
923
  // 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
924
  function PanelGroupWithForwardedRef({
793
925
  autoSaveId,
794
926
  children = null,
@@ -800,7 +932,8 @@ function PanelGroupWithForwardedRef({
800
932
  onLayout,
801
933
  storage = defaultStorage,
802
934
  style: styleFromProps = {},
803
- tagName: Type = "div"
935
+ tagName: Type = "div",
936
+ units = "percentages"
804
937
  }) {
805
938
  const groupId = useUniqueId(idFromProps);
806
939
  const [activeHandleId, setActiveHandleId] = useState(null);
@@ -813,6 +946,7 @@ function PanelGroupWithForwardedRef({
813
946
  const devWarningsRef = useRef({
814
947
  didLogDefaultSizeWarning: false,
815
948
  didLogIdAndOrderWarning: false,
949
+ didLogInvalidLayoutWarning: false,
816
950
  prevPanelIds: []
817
951
  });
818
952
 
@@ -835,28 +969,52 @@ function PanelGroupWithForwardedRef({
835
969
  // Store committed values to avoid unnecessarily re-running memoization/effects functions.
836
970
  const committedValuesRef = useRef({
837
971
  direction,
972
+ id: groupId,
838
973
  panels,
839
- sizes
974
+ sizes,
975
+ units
840
976
  });
841
977
  useImperativeHandle(forwardedRef, () => ({
842
- getLayout: () => {
978
+ getId: () => groupId,
979
+ getLayout: unitsFromParams => {
843
980
  const {
844
- sizes
981
+ sizes,
982
+ units: unitsFromProps
845
983
  } = committedValuesRef.current;
846
- return sizes;
984
+ const units = unitsFromParams ?? unitsFromProps;
985
+ if (units === "pixels") {
986
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
987
+ return sizes.map(size => size / 100 * groupSizePixels);
988
+ } else {
989
+ return sizes;
990
+ }
847
991
  },
848
- setLayout: sizes => {
849
- const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
850
- assert(total === 100, "Panel sizes must add up to 100%");
992
+ setLayout: (sizes, unitsFromParams) => {
851
993
  const {
852
- panels
994
+ id: groupId,
995
+ panels,
996
+ sizes: prevSizes,
997
+ units
853
998
  } = committedValuesRef.current;
999
+ if ((unitsFromParams || units) === "pixels") {
1000
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1001
+ sizes = sizes.map(size => size / groupSizePixels * 100);
1002
+ }
854
1003
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
855
1004
  const panelsArray = panelsMapToSortedArray(panels);
856
- setSizes(sizes);
857
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
1005
+ const nextSizes = validatePanelGroupLayout({
1006
+ groupId,
1007
+ panels,
1008
+ nextSizes: sizes,
1009
+ prevSizes,
1010
+ units
1011
+ });
1012
+ if (!areEqual(prevSizes, nextSizes)) {
1013
+ setSizes(nextSizes);
1014
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1015
+ }
858
1016
  }
859
- }), []);
1017
+ }), [groupId]);
860
1018
  useWindowSplitterPanelGroupBehavior({
861
1019
  committedValuesRef,
862
1020
  groupId,
@@ -927,6 +1085,22 @@ function PanelGroupWithForwardedRef({
927
1085
  }
928
1086
  }
929
1087
  }, [autoSaveId, panels, sizes, storage]);
1088
+ const getPanelSize = useCallback((id, unitsFromParams) => {
1089
+ const {
1090
+ panels,
1091
+ units: unitsFromProps
1092
+ } = committedValuesRef.current;
1093
+ const panelsArray = panelsMapToSortedArray(panels);
1094
+ const index = panelsArray.findIndex(panel => panel.current.id === id);
1095
+ const size = sizes[index];
1096
+ const units = unitsFromParams ?? unitsFromProps;
1097
+ if (units === "pixels") {
1098
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1099
+ return size / 100 * groupSizePixels;
1100
+ } else {
1101
+ return size;
1102
+ }
1103
+ }, [groupId, sizes]);
930
1104
  const getPanelStyle = useCallback((id, defaultSize) => {
931
1105
  const {
932
1106
  panels
@@ -965,6 +1139,10 @@ function PanelGroupWithForwardedRef({
965
1139
  };
966
1140
  }, [activeHandleId, disablePointerEventsDuringResize, sizes]);
967
1141
  const registerPanel = useCallback((id, panelRef) => {
1142
+ const {
1143
+ units
1144
+ } = committedValuesRef.current;
1145
+ validatePanelProps(units, panelRef);
968
1146
  setPanels(prevPanels => {
969
1147
  if (prevPanels.has(id)) {
970
1148
  return prevPanels;
@@ -1001,7 +1179,10 @@ function PanelGroupWithForwardedRef({
1001
1179
  }
1002
1180
  const size = isHorizontal ? rect.width : rect.height;
1003
1181
  const delta = movement / size * 100;
1004
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1182
+
1183
+ // If a validateLayout method has been provided
1184
+ // it's important to use it before updating the mouse cursor
1185
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1005
1186
  const sizesChanged = !areEqual(prevSizes, nextSizes);
1006
1187
 
1007
1188
  // Don't update cursor for resizes triggered by keyboard interactions.
@@ -1028,6 +1209,8 @@ function PanelGroupWithForwardedRef({
1028
1209
  }
1029
1210
  if (sizesChanged) {
1030
1211
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1212
+
1213
+ // It's okay to bypass in this case because we already validated above
1031
1214
  setSizes(nextSizes);
1032
1215
 
1033
1216
  // If resize change handlers have been declared, this is the time to call them.
@@ -1081,7 +1264,7 @@ function PanelGroupWithForwardedRef({
1081
1264
  }
1082
1265
  const isLastPanel = index === panelsArray.length - 1;
1083
1266
  const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
1084
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1267
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1085
1268
  if (prevSizes !== nextSizes) {
1086
1269
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1087
1270
  setSizes(nextSizes);
@@ -1124,7 +1307,7 @@ function PanelGroupWithForwardedRef({
1124
1307
  }
1125
1308
  const isLastPanel = index === panelsArray.length - 1;
1126
1309
  const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
1127
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1310
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1128
1311
  if (prevSizes !== nextSizes) {
1129
1312
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1130
1313
  setSizes(nextSizes);
@@ -1134,21 +1317,34 @@ function PanelGroupWithForwardedRef({
1134
1317
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1135
1318
  }
1136
1319
  }, []);
1137
- const resizePanel = useCallback((id, nextSize) => {
1320
+ const resizePanel = useCallback((id, nextSize, unitsFromParams) => {
1138
1321
  const {
1322
+ id: groupId,
1139
1323
  panels,
1140
- sizes: prevSizes
1324
+ sizes: prevSizes,
1325
+ units
1141
1326
  } = committedValuesRef.current;
1327
+ if ((unitsFromParams || units) === "pixels") {
1328
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1329
+ nextSize = nextSize / groupSizePixels * 100;
1330
+ }
1142
1331
  const panel = panels.get(id);
1143
1332
  if (panel == null) {
1144
1333
  return;
1145
1334
  }
1146
- const {
1335
+ let {
1147
1336
  collapsedSize,
1148
1337
  collapsible,
1149
1338
  maxSize,
1150
1339
  minSize
1151
1340
  } = panel.current;
1341
+ if (units === "pixels") {
1342
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1343
+ minSize = minSize / groupSizePixels * 100;
1344
+ if (maxSize != null) {
1345
+ maxSize = maxSize / groupSizePixels * 100;
1346
+ }
1347
+ }
1152
1348
  const panelsArray = panelsMapToSortedArray(panels);
1153
1349
  const index = panelsArray.indexOf(panel);
1154
1350
  if (index < 0) {
@@ -1159,7 +1355,13 @@ function PanelGroupWithForwardedRef({
1159
1355
  return;
1160
1356
  }
1161
1357
  if (collapsible && nextSize === collapsedSize) ; else {
1162
- nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
1358
+ const unsafeNextSize = nextSize;
1359
+ nextSize = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
1360
+ {
1361
+ if (unsafeNextSize !== nextSize) {
1362
+ console.error(`Invalid size (${unsafeNextSize}) specified for Panel "${panel.current.id}" given the panel's min/max size constraints`);
1363
+ }
1364
+ }
1163
1365
  }
1164
1366
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
1165
1367
  if (idBefore == null || idAfter == null) {
@@ -1167,7 +1369,7 @@ function PanelGroupWithForwardedRef({
1167
1369
  }
1168
1370
  const isLastPanel = index === panelsArray.length - 1;
1169
1371
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1170
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1372
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1171
1373
  if (prevSizes !== nextSizes) {
1172
1374
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1173
1375
  setSizes(nextSizes);
@@ -1182,6 +1384,7 @@ function PanelGroupWithForwardedRef({
1182
1384
  collapsePanel,
1183
1385
  direction,
1184
1386
  expandPanel,
1387
+ getPanelSize,
1185
1388
  getPanelStyle,
1186
1389
  groupId,
1187
1390
  registerPanel,
@@ -1203,8 +1406,9 @@ function PanelGroupWithForwardedRef({
1203
1406
  setActiveHandleId(null);
1204
1407
  initialDragStateRef.current = null;
1205
1408
  },
1409
+ units,
1206
1410
  unregisterPanel
1207
- }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, unregisterPanel]);
1411
+ }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelSize, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, units, unregisterPanel]);
1208
1412
  const style = {
1209
1413
  display: "flex",
1210
1414
  flexDirection: direction === "horizontal" ? "row" : "column",
@@ -1219,6 +1423,7 @@ function PanelGroupWithForwardedRef({
1219
1423
  "data-panel-group": "",
1220
1424
  "data-panel-group-direction": direction,
1221
1425
  "data-panel-group-id": groupId,
1426
+ "data-panel-group-units": units,
1222
1427
  style: {
1223
1428
  ...style,
1224
1429
  ...styleFromProps
@@ -1371,3 +1576,4 @@ PanelResizeHandle.displayName = "PanelResizeHandle";
1371
1576
  exports.Panel = Panel;
1372
1577
  exports.PanelGroup = PanelGroup;
1373
1578
  exports.PanelResizeHandle = PanelResizeHandle;
1579
+ 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.development.node.cjs.js";