react-resizable-panels 0.0.53 → 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 (41) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/declarations/src/Panel.d.ts +6 -5
  3. package/dist/declarations/src/PanelGroup.d.ts +8 -4
  4. package/dist/declarations/src/PanelResizeHandle.d.ts +5 -2
  5. package/dist/declarations/src/index.d.ts +9 -8
  6. package/dist/declarations/src/types.d.ts +4 -2
  7. package/dist/declarations/src/utils/group.d.ts +29 -0
  8. package/dist/react-resizable-panels.browser.cjs.js +1709 -0
  9. package/dist/react-resizable-panels.browser.cjs.mjs +6 -0
  10. package/dist/react-resizable-panels.browser.development.cjs.js +1764 -0
  11. package/dist/react-resizable-panels.browser.development.cjs.mjs +6 -0
  12. package/dist/react-resizable-panels.browser.development.esm.js +1737 -0
  13. package/dist/react-resizable-panels.browser.esm.js +1682 -0
  14. package/dist/react-resizable-panels.cjs.js +395 -126
  15. package/dist/react-resizable-panels.cjs.js.map +1 -0
  16. package/dist/react-resizable-panels.cjs.mjs +2 -1
  17. package/dist/react-resizable-panels.development.cjs.js +452 -135
  18. package/dist/react-resizable-panels.development.cjs.mjs +6 -0
  19. package/dist/react-resizable-panels.development.esm.js +452 -136
  20. package/dist/react-resizable-panels.development.node.cjs.js +1579 -0
  21. package/dist/react-resizable-panels.development.node.cjs.mjs +6 -0
  22. package/dist/react-resizable-panels.development.node.esm.js +1552 -0
  23. package/dist/react-resizable-panels.esm.js +395 -127
  24. package/dist/react-resizable-panels.esm.js.map +1 -0
  25. package/dist/react-resizable-panels.node.cjs.js +1523 -0
  26. package/dist/react-resizable-panels.node.cjs.mjs +6 -0
  27. package/dist/react-resizable-panels.node.esm.js +1496 -0
  28. package/package.json +26 -1
  29. package/src/Panel.ts +37 -37
  30. package/src/PanelContexts.ts +5 -6
  31. package/src/PanelGroup.ts +269 -121
  32. package/src/PanelResizeHandle.ts +1 -4
  33. package/src/env-conditions/browser.ts +1 -0
  34. package/src/env-conditions/node.ts +1 -0
  35. package/src/env-conditions/unknown.ts +1 -0
  36. package/src/hooks/useIsomorphicEffect.ts +2 -9
  37. package/src/hooks/useWindowSplitterBehavior.ts +14 -11
  38. package/src/index.ts +11 -3
  39. package/src/types.ts +3 -1
  40. package/src/utils/group.ts +327 -28
  41. package/src/utils/ssr.ts +0 -7
@@ -24,6 +24,8 @@ function _interopNamespace(e) {
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
26
26
 
27
+ const isBrowser = typeof window !== "undefined";
28
+
27
29
  // This module exists to work around Webpack issue https://github.com/webpack/webpack/issues/14814
28
30
 
29
31
  // eslint-disable-next-line no-restricted-imports
@@ -45,8 +47,7 @@ const {
45
47
  // `toString()` prevents bundlers from trying to `import { useId } from 'react'`
46
48
  const useId = React__namespace["useId".toString()];
47
49
 
48
- const canUseEffectHooks = !!(typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined");
49
- const useIsomorphicLayoutEffect = canUseEffectHooks ? useLayoutEffect : () => {};
50
+ const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : () => {};
50
51
 
51
52
  const wrappedUseId = typeof useId === "function" ? useId : () => null;
52
53
  let counter = 0;
@@ -60,10 +61,6 @@ function useUniqueId(idFromParams = null) {
60
61
  }
61
62
 
62
63
  const PanelGroupContext = createContext(null);
63
-
64
- // Workaround for Parcel scope hoisting (which renames objects/functions).
65
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
66
- // See github.com/parcel-bundler/parcel/issues/8724
67
64
  PanelGroupContext.displayName = "PanelGroupContext";
68
65
 
69
66
  function PanelWithForwardedRef({
@@ -74,8 +71,8 @@ function PanelWithForwardedRef({
74
71
  defaultSize = null,
75
72
  forwardedRef,
76
73
  id: idFromProps = null,
77
- maxSize = 100,
78
- minSize = 10,
74
+ maxSize = null,
75
+ minSize,
79
76
  onCollapse = null,
80
77
  onResize = null,
81
78
  order = null,
@@ -90,11 +87,22 @@ function PanelWithForwardedRef({
90
87
  const {
91
88
  collapsePanel,
92
89
  expandPanel,
90
+ getPanelSize,
93
91
  getPanelStyle,
94
92
  registerPanel,
95
93
  resizePanel,
94
+ units,
96
95
  unregisterPanel
97
96
  } = context;
97
+ if (minSize == null) {
98
+ if (units === "percentages") {
99
+ // Mimics legacy default value for percentage based panel groups
100
+ minSize = 10;
101
+ } else {
102
+ // There is no meaningful minimum pixel default we can provide
103
+ minSize = 0;
104
+ }
105
+ }
98
106
 
99
107
  // Use a ref to guard against users passing inline props
100
108
  const callbacksRef = useRef({
@@ -105,22 +113,6 @@ function PanelWithForwardedRef({
105
113
  callbacksRef.current.onCollapse = onCollapse;
106
114
  callbacksRef.current.onResize = onResize;
107
115
  });
108
-
109
- // Basic props validation
110
- if (minSize < 0 || minSize > 100) {
111
- throw Error(`Panel minSize must be between 0 and 100, but was ${minSize}`);
112
- } else if (maxSize < 0 || maxSize > 100) {
113
- throw Error(`Panel maxSize must be between 0 and 100, but was ${maxSize}`);
114
- } else {
115
- if (defaultSize !== null) {
116
- if (defaultSize < 0 || defaultSize > 100) {
117
- throw Error(`Panel defaultSize must be between 0 and 100, but was ${defaultSize}`);
118
- } else if (minSize > defaultSize && !collapsible) {
119
- console.error(`Panel minSize ${minSize} cannot be greater than defaultSize ${defaultSize}`);
120
- defaultSize = minSize;
121
- }
122
- }
123
- }
124
116
  const style = getPanelStyle(panelId, defaultSize);
125
117
  const committedValuesRef = useRef({
126
118
  size: parseSizeFromStyle(style)
@@ -131,6 +123,7 @@ function PanelWithForwardedRef({
131
123
  collapsible,
132
124
  defaultSize,
133
125
  id: panelId,
126
+ idWasAutoGenerated: idFromProps == null,
134
127
  maxSize,
135
128
  minSize,
136
129
  order
@@ -142,6 +135,7 @@ function PanelWithForwardedRef({
142
135
  panelDataRef.current.collapsible = collapsible;
143
136
  panelDataRef.current.defaultSize = defaultSize;
144
137
  panelDataRef.current.id = panelId;
138
+ panelDataRef.current.idWasAutoGenerated = idFromProps == null;
145
139
  panelDataRef.current.maxSize = maxSize;
146
140
  panelDataRef.current.minSize = minSize;
147
141
  panelDataRef.current.order = order;
@@ -158,11 +152,14 @@ function PanelWithForwardedRef({
158
152
  getCollapsed() {
159
153
  return committedValuesRef.current.size === 0;
160
154
  },
161
- getSize() {
162
- return committedValuesRef.current.size;
155
+ getId() {
156
+ return panelId;
163
157
  },
164
- resize: percentage => resizePanel(panelId, percentage)
165
- }), [collapsePanel, expandPanel, panelId, resizePanel]);
158
+ getSize(units) {
159
+ return getPanelSize(panelId, units);
160
+ },
161
+ resize: (percentage, units) => resizePanel(panelId, percentage, units)
162
+ }), [collapsePanel, expandPanel, getPanelSize, panelId, resizePanel]);
166
163
  return createElement(Type, {
167
164
  children,
168
165
  className: classNameFromProps,
@@ -181,10 +178,6 @@ const Panel = forwardRef((props, ref) => createElement(PanelWithForwardedRef, {
181
178
  ...props,
182
179
  forwardedRef: ref
183
180
  }));
184
-
185
- // Workaround for Parcel scope hoisting (which renames objects/functions).
186
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
187
- // See github.com/parcel-bundler/parcel/issues/8724
188
181
  PanelWithForwardedRef.displayName = "Panel";
189
182
  Panel.displayName = "forwardRef(Panel)";
190
183
 
@@ -202,7 +195,13 @@ function parseSizeFromStyle(style) {
202
195
 
203
196
  const PRECISION = 10;
204
197
 
205
- function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse, initialDragState) {
198
+ function adjustByDelta(event, committedValues, idBefore, idAfter, deltaPixels, prevSizes, panelSizeBeforeCollapse, initialDragState) {
199
+ const {
200
+ id: groupId,
201
+ panels,
202
+ units
203
+ } = committedValues;
204
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
206
205
  const {
207
206
  sizes: initialSizes
208
207
  } = initialDragState || {};
@@ -210,9 +209,6 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
210
209
  // If we're resizing by mouse or touch, use the initial sizes as a base.
211
210
  // This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
212
211
  const baseSizes = initialSizes || prevSizes;
213
- if (delta === 0) {
214
- return baseSizes;
215
- }
216
212
  const panelsArray = panelsMapToSortedArray(panels);
217
213
  const nextSizes = baseSizes.concat();
218
214
  let deltaApplied = 0;
@@ -227,11 +223,11 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
227
223
 
228
224
  // Max-bounds check the panel being expanded first.
229
225
  {
230
- const pivotId = delta < 0 ? idAfter : idBefore;
226
+ const pivotId = deltaPixels < 0 ? idAfter : idBefore;
231
227
  const index = panelsArray.findIndex(panel => panel.current.id === pivotId);
232
228
  const panel = panelsArray[index];
233
229
  const baseSize = baseSizes[index];
234
- const nextSize = safeResizePanel(panel, Math.abs(delta), baseSize, event);
230
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize + Math.abs(deltaPixels), event);
235
231
  if (baseSize === nextSize) {
236
232
  // If there's no room for the pivot panel to grow, we can ignore this drag update.
237
233
  return baseSizes;
@@ -239,29 +235,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
239
235
  if (nextSize === 0 && baseSize > 0) {
240
236
  panelSizeBeforeCollapse.set(pivotId, baseSize);
241
237
  }
242
- delta = delta < 0 ? baseSize - nextSize : nextSize - baseSize;
238
+ deltaPixels = deltaPixels < 0 ? baseSize - nextSize : nextSize - baseSize;
243
239
  }
244
240
  }
245
- let pivotId = delta < 0 ? idBefore : idAfter;
241
+ let pivotId = deltaPixels < 0 ? idBefore : idAfter;
246
242
  let index = panelsArray.findIndex(panel => panel.current.id === pivotId);
247
243
  while (true) {
248
244
  const panel = panelsArray[index];
249
245
  const baseSize = baseSizes[index];
250
- const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
251
- const nextSize = safeResizePanel(panel, 0 - deltaRemaining, baseSize, event);
246
+ const deltaRemaining = Math.abs(deltaPixels) - Math.abs(deltaApplied);
247
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize - deltaRemaining, event);
252
248
  if (baseSize !== nextSize) {
253
249
  if (nextSize === 0 && baseSize > 0) {
254
250
  panelSizeBeforeCollapse.set(panel.current.id, baseSize);
255
251
  }
256
252
  deltaApplied += baseSize - nextSize;
257
253
  nextSizes[index] = nextSize;
258
- if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(delta).toPrecision(PRECISION), undefined, {
254
+ if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(deltaPixels).toPrecision(PRECISION), undefined, {
259
255
  numeric: true
260
256
  }) >= 0) {
261
257
  break;
262
258
  }
263
259
  }
264
- if (delta < 0) {
260
+ if (deltaPixels < 0) {
265
261
  if (--index < 0) {
266
262
  break;
267
263
  }
@@ -279,7 +275,7 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
279
275
  }
280
276
 
281
277
  // Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
282
- pivotId = delta < 0 ? idAfter : idBefore;
278
+ pivotId = deltaPixels < 0 ? idAfter : idBefore;
283
279
  index = panelsArray.findIndex(panel => panel.current.id === pivotId);
284
280
  nextSizes[index] = baseSizes[index] + deltaApplied;
285
281
  return nextSizes;
@@ -318,6 +314,93 @@ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
318
314
  }
319
315
  });
320
316
  }
317
+ function calculateDefaultLayout({
318
+ groupId,
319
+ panels,
320
+ units
321
+ }) {
322
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
323
+ const panelsArray = panelsMapToSortedArray(panels);
324
+ const sizes = Array(panelsArray.length);
325
+ let numPanelsWithSizes = 0;
326
+ let remainingSize = 100;
327
+
328
+ // Assigning default sizes requires a couple of passes:
329
+ // First, all panels with defaultSize should be set as-is
330
+ for (let index = 0; index < panelsArray.length; index++) {
331
+ const panel = panelsArray[index];
332
+ const {
333
+ defaultSize
334
+ } = panel.current;
335
+ if (defaultSize != null) {
336
+ numPanelsWithSizes++;
337
+ sizes[index] = units === "pixels" ? defaultSize / groupSizePixels * 100 : defaultSize;
338
+ remainingSize -= sizes[index];
339
+ }
340
+ }
341
+
342
+ // Remaining total size should be distributed evenly between panels
343
+ // This may require two passes, depending on min/max constraints
344
+ for (let index = 0; index < panelsArray.length; index++) {
345
+ const panel = panelsArray[index];
346
+ let {
347
+ defaultSize,
348
+ id,
349
+ maxSize,
350
+ minSize
351
+ } = panel.current;
352
+ if (defaultSize != null) {
353
+ continue;
354
+ }
355
+ if (units === "pixels") {
356
+ minSize = minSize / groupSizePixels * 100;
357
+ if (maxSize != null) {
358
+ maxSize = maxSize / groupSizePixels * 100;
359
+ }
360
+ }
361
+ const remainingPanels = panelsArray.length - numPanelsWithSizes;
362
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, remainingSize / remainingPanels));
363
+ sizes[index] = size;
364
+ numPanelsWithSizes++;
365
+ remainingSize -= size;
366
+ }
367
+
368
+ // If there is additional, left over space, assign it to any panel(s) that permits it
369
+ // (It's not worth taking multiple additional passes to evenly distribute)
370
+ if (remainingSize !== 0) {
371
+ for (let index = 0; index < panelsArray.length; index++) {
372
+ const panel = panelsArray[index];
373
+ let {
374
+ maxSize,
375
+ minSize
376
+ } = panel.current;
377
+ if (units === "pixels") {
378
+ minSize = minSize / groupSizePixels * 100;
379
+ if (maxSize != null) {
380
+ maxSize = maxSize / groupSizePixels * 100;
381
+ }
382
+ }
383
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, sizes[index] + remainingSize));
384
+ if (size !== sizes[index]) {
385
+ remainingSize -= size - sizes[index];
386
+ sizes[index] = size;
387
+
388
+ // Fuzzy comparison to account for imprecise floating point math
389
+ if (Math.abs(remainingSize).toFixed(3) === "0.000") {
390
+ break;
391
+ }
392
+ }
393
+ }
394
+ }
395
+
396
+ // Finally, if there is still left-over size, log an error
397
+ if (Math.abs(remainingSize).toFixed(3) !== "0.000") {
398
+ {
399
+ console.error(`Invalid panel group configuration; default panel sizes should total 100% but was ${(100 - remainingSize).toFixed(1)}%. This can cause the cursor to become misaligned while dragging.`);
400
+ }
401
+ }
402
+ return sizes;
403
+ }
321
404
  function getBeforeAndAfterIds(id, panelsArray) {
322
405
  if (panelsArray.length < 2) {
323
406
  return [null, null];
@@ -331,6 +414,23 @@ function getBeforeAndAfterIds(id, panelsArray) {
331
414
  const idAfter = isLastPanel ? id : panelsArray[index + 1].current.id;
332
415
  return [idBefore, idAfter];
333
416
  }
417
+ function getAvailableGroupSizePixels(groupId) {
418
+ const panelGroupElement = getPanelGroup(groupId);
419
+ if (panelGroupElement == null) {
420
+ return NaN;
421
+ }
422
+ const direction = panelGroupElement.getAttribute("data-panel-group-direction");
423
+ const resizeHandles = getResizeHandlesForGroup(groupId);
424
+ if (direction === "horizontal") {
425
+ return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
426
+ return accumulated + handle.offsetWidth;
427
+ }, 0);
428
+ } else {
429
+ return panelGroupElement.offsetHeight - resizeHandles.reduce((accumulated, handle) => {
430
+ return accumulated + handle.offsetHeight;
431
+ }, 0);
432
+ }
433
+ }
334
434
 
335
435
  // This method returns a number between 1 and 100 representing
336
436
  // the % of the group's overall space this panel should occupy.
@@ -401,18 +501,24 @@ function panelsMapToSortedArray(panels) {
401
501
  }
402
502
  });
403
503
  }
404
- function safeResizePanel(panel, delta, prevSize, event) {
405
- const nextSizeUnsafe = prevSize + delta;
406
- const {
504
+ function safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize, event = null) {
505
+ let {
407
506
  collapsedSize,
408
507
  collapsible,
409
508
  maxSize,
410
509
  minSize
411
510
  } = panel.current;
511
+ if (units === "pixels") {
512
+ collapsedSize = collapsedSize / groupSizePixels * 100;
513
+ if (maxSize != null) {
514
+ maxSize = maxSize / groupSizePixels * 100;
515
+ }
516
+ minSize = minSize / groupSizePixels * 100;
517
+ }
412
518
  if (collapsible) {
413
519
  if (prevSize > collapsedSize) {
414
520
  // Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
415
- if (nextSizeUnsafe <= minSize / 2 + collapsedSize) {
521
+ if (nextSize <= minSize / 2 + collapsedSize) {
416
522
  return collapsedSize;
417
523
  }
418
524
  } else {
@@ -421,14 +527,119 @@ function safeResizePanel(panel, delta, prevSize, event) {
421
527
  // Keyboard events should expand a collapsed panel to the min size,
422
528
  // but mouse events should wait until the panel has reached its min size
423
529
  // to avoid a visual flickering when dragging between collapsed and min size.
424
- if (nextSizeUnsafe < minSize) {
530
+ if (nextSize < minSize) {
425
531
  return collapsedSize;
426
532
  }
427
533
  }
428
534
  }
429
535
  }
430
- const nextSize = Math.min(maxSize, Math.max(minSize, nextSizeUnsafe));
431
- return nextSize;
536
+ return Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
537
+ }
538
+ function validatePanelProps(units, panelData) {
539
+ const {
540
+ collapsible,
541
+ defaultSize,
542
+ maxSize,
543
+ minSize
544
+ } = panelData.current;
545
+
546
+ // Basic props validation
547
+ if (minSize < 0 || units === "percentages" && minSize > 100) {
548
+ {
549
+ console.error(`Invalid Panel minSize provided, ${minSize}`);
550
+ }
551
+ panelData.current.minSize = 0;
552
+ }
553
+ if (maxSize != null) {
554
+ if (maxSize < 0 || units === "percentages" && maxSize > 100) {
555
+ {
556
+ console.error(`Invalid Panel maxSize provided, ${maxSize}`);
557
+ }
558
+ panelData.current.maxSize = null;
559
+ }
560
+ }
561
+ if (defaultSize !== null) {
562
+ if (defaultSize < 0 || units === "percentages" && defaultSize > 100) {
563
+ {
564
+ console.error(`Invalid Panel defaultSize provided, ${defaultSize}`);
565
+ }
566
+ panelData.current.defaultSize = null;
567
+ } else if (defaultSize < minSize && !collapsible) {
568
+ {
569
+ console.error(`Panel minSize (${minSize}) cannot be greater than defaultSize (${defaultSize})`);
570
+ }
571
+ panelData.current.defaultSize = minSize;
572
+ } else if (maxSize != null && defaultSize > maxSize) {
573
+ {
574
+ console.error(`Panel maxSize (${maxSize}) cannot be less than defaultSize (${defaultSize})`);
575
+ }
576
+ panelData.current.defaultSize = maxSize;
577
+ }
578
+ }
579
+ }
580
+ function validatePanelGroupLayout({
581
+ groupId,
582
+ panels,
583
+ nextSizes,
584
+ prevSizes,
585
+ units
586
+ }) {
587
+ // Clone because this method modifies
588
+ nextSizes = [...nextSizes];
589
+ const panelsArray = panelsMapToSortedArray(panels);
590
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
591
+ let remainingSize = 0;
592
+
593
+ // First, check all of the proposed sizes against the min/max constraints
594
+ for (let index = 0; index < panelsArray.length; index++) {
595
+ const panel = panelsArray[index];
596
+ const prevSize = prevSizes[index];
597
+ const nextSize = nextSizes[index];
598
+ const safeNextSize = safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize);
599
+ if (nextSize != safeNextSize) {
600
+ remainingSize += nextSize - safeNextSize;
601
+ nextSizes[index] = safeNextSize;
602
+ {
603
+ console.error(`Invalid size (${nextSize}) specified for Panel "${panel.current.id}" given the panel's min/max size constraints`);
604
+ }
605
+ }
606
+ }
607
+
608
+ // If there is additional, left over space, assign it to any panel(s) that permits it
609
+ // (It's not worth taking multiple additional passes to evenly distribute)
610
+ if (remainingSize.toFixed(3) !== "0.000") {
611
+ for (let index = 0; index < panelsArray.length; index++) {
612
+ const panel = panelsArray[index];
613
+ let {
614
+ maxSize,
615
+ minSize
616
+ } = panel.current;
617
+ if (units === "pixels") {
618
+ minSize = minSize / groupSizePixels * 100;
619
+ if (maxSize != null) {
620
+ maxSize = maxSize / groupSizePixels * 100;
621
+ }
622
+ }
623
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSizes[index] + remainingSize));
624
+ if (size !== nextSizes[index]) {
625
+ remainingSize -= size - nextSizes[index];
626
+ nextSizes[index] = size;
627
+
628
+ // Fuzzy comparison to account for imprecise floating point math
629
+ if (Math.abs(remainingSize).toFixed(3) === "0.000") {
630
+ break;
631
+ }
632
+ }
633
+ }
634
+ }
635
+
636
+ // If we still have remainder, the requested layout wasn't valid and we should warn about it
637
+ if (remainingSize.toFixed(3) !== "0.000") {
638
+ {
639
+ console.error(`"Invalid panel group configuration; default panel sizes should total 100% but was ${100 - remainingSize}%`);
640
+ }
641
+ }
642
+ return nextSizes;
432
643
  }
433
644
 
434
645
  function assert(expectedCondition, message = "Assertion failed!") {
@@ -454,6 +665,7 @@ function useWindowSplitterPanelGroupBehavior({
454
665
  panels
455
666
  } = committedValuesRef.current;
456
667
  const groupElement = getPanelGroup(groupId);
668
+ assert(groupElement != null, `No group found for id "${groupId}"`);
457
669
  const {
458
670
  height,
459
671
  width
@@ -466,23 +678,28 @@ function useWindowSplitterPanelGroupBehavior({
466
678
  if (idBefore == null || idAfter == null) {
467
679
  return () => {};
468
680
  }
469
- let minSize = 0;
470
- let maxSize = 100;
681
+ let currentMinSize = 0;
682
+ let currentMaxSize = 100;
471
683
  let totalMinSize = 0;
472
684
  let totalMaxSize = 0;
473
685
 
474
686
  // A panel's effective min/max sizes also need to account for other panel's sizes.
475
687
  panelsArray.forEach(panelData => {
476
- if (panelData.current.id === idBefore) {
477
- maxSize = panelData.current.maxSize;
478
- minSize = panelData.current.minSize;
688
+ const {
689
+ id,
690
+ maxSize,
691
+ minSize
692
+ } = panelData.current;
693
+ if (id === idBefore) {
694
+ currentMinSize = minSize;
695
+ currentMaxSize = maxSize != null ? maxSize : 100;
479
696
  } else {
480
- totalMinSize += panelData.current.minSize;
481
- totalMaxSize += panelData.current.maxSize;
697
+ totalMinSize += minSize;
698
+ totalMaxSize += maxSize != null ? maxSize : 100;
482
699
  }
483
700
  });
484
- const ariaValueMax = Math.min(maxSize, 100 - totalMinSize);
485
- const ariaValueMin = Math.max(minSize, (panelsArray.length - 1) * 100 - totalMaxSize);
701
+ const ariaValueMax = Math.min(currentMaxSize, 100 - totalMinSize);
702
+ const ariaValueMin = Math.max(currentMinSize, (panelsArray.length - 1) * 100 - totalMaxSize);
486
703
  const flexGrow = getFlexGrow(panels, idBefore, sizes);
487
704
  handle.setAttribute("aria-valuemax", "" + Math.round(ariaValueMax));
488
705
  handle.setAttribute("aria-valuemin", "" + Math.round(ariaValueMin));
@@ -506,7 +723,7 @@ function useWindowSplitterPanelGroupBehavior({
506
723
  } else {
507
724
  delta = -(direction === "horizontal" ? width : height);
508
725
  }
509
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
726
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
510
727
  if (sizes !== nextSizes) {
511
728
  setSizes(nextSizes);
512
729
  }
@@ -778,13 +995,6 @@ function savePanelGroupLayout(autoSaveId, panels, sizes, storage) {
778
995
  }
779
996
  }
780
997
 
781
- function isServerRendering() {
782
- try {
783
- return typeof window === undefined;
784
- } catch (error) {}
785
- return true;
786
- }
787
-
788
998
  const debounceMap = {};
789
999
 
790
1000
  // PanelGroup might be rendering in a server-side environment where localStorage is not available
@@ -828,9 +1038,6 @@ const defaultStorage = {
828
1038
  // * dragHandleRect, sizes:
829
1039
  // When resizing is done via mouse/touch event– some initial state is stored
830
1040
  // so that any panels that contract will also expand if drag direction is reversed.
831
- // TODO
832
- // Within an active drag, remember original positions to refine more easily on expand.
833
- // Look at what the Chrome devtools Sources does.
834
1041
  function PanelGroupWithForwardedRef({
835
1042
  autoSaveId,
836
1043
  children = null,
@@ -842,7 +1049,8 @@ function PanelGroupWithForwardedRef({
842
1049
  onLayout,
843
1050
  storage = defaultStorage,
844
1051
  style: styleFromProps = {},
845
- tagName: Type = "div"
1052
+ tagName: Type = "div",
1053
+ units = "percentages"
846
1054
  }) {
847
1055
  const groupId = useUniqueId(idFromProps);
848
1056
  const [activeHandleId, setActiveHandleId] = useState(null);
@@ -852,6 +1060,12 @@ function PanelGroupWithForwardedRef({
852
1060
  // We store the initial Panel sizes in this ref, and apply move deltas to them instead of to the current sizes.
853
1061
  // This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
854
1062
  const initialDragStateRef = useRef(null);
1063
+ const devWarningsRef = useRef({
1064
+ didLogDefaultSizeWarning: false,
1065
+ didLogIdAndOrderWarning: false,
1066
+ didLogInvalidLayoutWarning: false,
1067
+ prevPanelIds: []
1068
+ });
855
1069
 
856
1070
  // Use a ref to guard against users passing inline props
857
1071
  const callbacksRef = useRef({
@@ -872,32 +1086,58 @@ function PanelGroupWithForwardedRef({
872
1086
  // Store committed values to avoid unnecessarily re-running memoization/effects functions.
873
1087
  const committedValuesRef = useRef({
874
1088
  direction,
1089
+ id: groupId,
875
1090
  panels,
876
- sizes
1091
+ sizes,
1092
+ units
877
1093
  });
878
1094
  useImperativeHandle(forwardedRef, () => ({
879
- getLayout: () => {
1095
+ getId: () => groupId,
1096
+ getLayout: unitsFromParams => {
880
1097
  const {
881
- sizes
1098
+ sizes,
1099
+ units: unitsFromProps
882
1100
  } = committedValuesRef.current;
883
- return sizes;
1101
+ const units = unitsFromParams ?? unitsFromProps;
1102
+ if (units === "pixels") {
1103
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1104
+ return sizes.map(size => size / 100 * groupSizePixels);
1105
+ } else {
1106
+ return sizes;
1107
+ }
884
1108
  },
885
- setLayout: sizes => {
886
- const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
887
- assert(total === 100, "Panel sizes must add up to 100%");
1109
+ setLayout: (sizes, unitsFromParams) => {
888
1110
  const {
889
- panels
1111
+ id: groupId,
1112
+ panels,
1113
+ sizes: prevSizes,
1114
+ units
890
1115
  } = committedValuesRef.current;
1116
+ if ((unitsFromParams || units) === "pixels") {
1117
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1118
+ sizes = sizes.map(size => size / groupSizePixels * 100);
1119
+ }
891
1120
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
892
1121
  const panelsArray = panelsMapToSortedArray(panels);
893
- setSizes(sizes);
894
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
1122
+ const nextSizes = validatePanelGroupLayout({
1123
+ groupId,
1124
+ panels,
1125
+ nextSizes: sizes,
1126
+ prevSizes,
1127
+ units
1128
+ });
1129
+ if (!areEqual(prevSizes, nextSizes)) {
1130
+ setSizes(nextSizes);
1131
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1132
+ }
895
1133
  }
896
- }), []);
1134
+ }), [groupId]);
897
1135
  useIsomorphicLayoutEffect(() => {
898
1136
  committedValuesRef.current.direction = direction;
1137
+ committedValuesRef.current.id = groupId;
899
1138
  committedValuesRef.current.panels = panels;
900
1139
  committedValuesRef.current.sizes = sizes;
1140
+ committedValuesRef.current.units = units;
901
1141
  });
902
1142
  useWindowSplitterPanelGroupBehavior({
903
1143
  committedValuesRef,
@@ -939,7 +1179,11 @@ function PanelGroupWithForwardedRef({
939
1179
  // Compute the initial sizes based on default weights.
940
1180
  // This assumes that panels register during initial mount (no conditional rendering)!
941
1181
  useIsomorphicLayoutEffect(() => {
942
- const sizes = committedValuesRef.current.sizes;
1182
+ const {
1183
+ id: groupId,
1184
+ sizes,
1185
+ units
1186
+ } = committedValuesRef.current;
943
1187
  if (sizes.length === panels.size) {
944
1188
  // Only compute (or restore) default sizes once per panel configuration.
945
1189
  return;
@@ -953,39 +1197,23 @@ function PanelGroupWithForwardedRef({
953
1197
  defaultSizes = loadPanelLayout(autoSaveId, panelsArray, storage);
954
1198
  }
955
1199
  if (defaultSizes != null) {
956
- setSizes(defaultSizes);
1200
+ // Validate saved sizes in case something has changed since last render
1201
+ // e.g. for pixel groups, this could be the size of the window
1202
+ const validatedSizes = validatePanelGroupLayout({
1203
+ groupId,
1204
+ panels,
1205
+ nextSizes: defaultSizes,
1206
+ prevSizes: defaultSizes,
1207
+ units
1208
+ });
1209
+ setSizes(validatedSizes);
957
1210
  } else {
958
- const panelsArray = panelsMapToSortedArray(panels);
959
- let panelsWithNullDefaultSize = 0;
960
- let totalDefaultSize = 0;
961
- let totalMinSize = 0;
962
-
963
- // TODO
964
- // Implicit default size calculations below do not account for inferred min/max size values.
965
- // e.g. if Panel A has a maxSize of 40 then Panels A and B can't both have an implicit default size of 50.
966
- // For now, these logic edge cases are left to the user to handle via props.
967
-
968
- panelsArray.forEach(panel => {
969
- totalMinSize += panel.current.minSize;
970
- if (panel.current.defaultSize === null) {
971
- panelsWithNullDefaultSize++;
972
- } else {
973
- totalDefaultSize += panel.current.defaultSize;
974
- }
1211
+ const sizes = calculateDefaultLayout({
1212
+ groupId,
1213
+ panels,
1214
+ units
975
1215
  });
976
- if (totalDefaultSize > 100) {
977
- throw new Error(`Default panel sizes cannot exceed 100%`);
978
- } else if (panelsArray.length > 1 && panelsWithNullDefaultSize === 0 && totalDefaultSize !== 100) {
979
- throw new Error(`Invalid default sizes specified for panels`);
980
- } else if (totalMinSize > 100) {
981
- throw new Error(`Minimum panel sizes cannot exceed 100%`);
982
- }
983
- setSizes(panelsArray.map(panel => {
984
- if (panel.current.defaultSize === null) {
985
- return (100 - totalDefaultSize) / panelsWithNullDefaultSize;
986
- }
987
- return panel.current.defaultSize;
988
- }));
1216
+ setSizes(sizes);
989
1217
  }
990
1218
  }, [autoSaveId, panels, storage]);
991
1219
  useEffect(() => {
@@ -1002,7 +1230,69 @@ function PanelGroupWithForwardedRef({
1002
1230
  }
1003
1231
  debounceMap[autoSaveId](autoSaveId, panelsArray, sizes, storage);
1004
1232
  }
1233
+ {
1234
+ const {
1235
+ didLogIdAndOrderWarning,
1236
+ prevPanelIds
1237
+ } = devWarningsRef.current;
1238
+ if (!didLogIdAndOrderWarning) {
1239
+ const {
1240
+ panels
1241
+ } = committedValuesRef.current;
1242
+ const panelIds = Array.from(panels.keys());
1243
+ devWarningsRef.current.prevPanelIds = panelIds;
1244
+ const panelsHaveChanged = prevPanelIds.length > 0 && !areEqual(prevPanelIds, panelIds);
1245
+ if (panelsHaveChanged) {
1246
+ if (Array.from(panels.values()).find(panel => panel.current.idWasAutoGenerated || panel.current.order == null)) {
1247
+ devWarningsRef.current.didLogIdAndOrderWarning = true;
1248
+ console.warn(`WARNING: Panel id and order props recommended when panels are dynamically rendered`);
1249
+ }
1250
+ }
1251
+ }
1252
+ }
1005
1253
  }, [autoSaveId, panels, sizes, storage]);
1254
+ useIsomorphicLayoutEffect(() => {
1255
+ // Pixel panel constraints need to be reassessed after a group resize
1256
+ // We can avoid the ResizeObserver overhead for relative layouts
1257
+ if (units === "pixels") {
1258
+ const resizeObserver = new ResizeObserver(() => {
1259
+ const {
1260
+ panels,
1261
+ sizes: prevSizes
1262
+ } = committedValuesRef.current;
1263
+ const nextSizes = validatePanelGroupLayout({
1264
+ groupId,
1265
+ panels,
1266
+ nextSizes: prevSizes,
1267
+ prevSizes,
1268
+ units
1269
+ });
1270
+ if (!areEqual(prevSizes, nextSizes)) {
1271
+ setSizes(nextSizes);
1272
+ }
1273
+ });
1274
+ resizeObserver.observe(getPanelGroup(groupId));
1275
+ return () => {
1276
+ resizeObserver.disconnect();
1277
+ };
1278
+ }
1279
+ }, [groupId, units]);
1280
+ const getPanelSize = useCallback((id, unitsFromParams) => {
1281
+ const {
1282
+ panels,
1283
+ units: unitsFromProps
1284
+ } = committedValuesRef.current;
1285
+ const panelsArray = panelsMapToSortedArray(panels);
1286
+ const index = panelsArray.findIndex(panel => panel.current.id === id);
1287
+ const size = sizes[index];
1288
+ const units = unitsFromParams ?? unitsFromProps;
1289
+ if (units === "pixels") {
1290
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1291
+ return size / 100 * groupSizePixels;
1292
+ } else {
1293
+ return size;
1294
+ }
1295
+ }, [groupId, sizes]);
1006
1296
  const getPanelStyle = useCallback((id, defaultSize) => {
1007
1297
  const {
1008
1298
  panels
@@ -1013,8 +1303,11 @@ function PanelGroupWithForwardedRef({
1013
1303
  // At this point the best we can do is render everything with the same size.
1014
1304
  if (panels.size === 0) {
1015
1305
  {
1016
- if (isServerRendering() && defaultSize == null) {
1017
- console.warn(`WARNING: Panel defaultSize prop recommended to avoid layout shift after server rendering`);
1306
+ if (!devWarningsRef.current.didLogDefaultSizeWarning) {
1307
+ if (!isBrowser && defaultSize == null) {
1308
+ devWarningsRef.current.didLogDefaultSizeWarning = true;
1309
+ console.warn(`WARNING: Panel defaultSize prop recommended to avoid layout shift after server rendering`);
1310
+ }
1018
1311
  }
1019
1312
  }
1020
1313
  return {
@@ -1038,6 +1331,10 @@ function PanelGroupWithForwardedRef({
1038
1331
  };
1039
1332
  }, [activeHandleId, disablePointerEventsDuringResize, sizes]);
1040
1333
  const registerPanel = useCallback((id, panelRef) => {
1334
+ const {
1335
+ units
1336
+ } = committedValuesRef.current;
1337
+ validatePanelProps(units, panelRef);
1041
1338
  setPanels(prevPanels => {
1042
1339
  if (prevPanels.has(id)) {
1043
1340
  return prevPanels;
@@ -1074,7 +1371,10 @@ function PanelGroupWithForwardedRef({
1074
1371
  }
1075
1372
  const size = isHorizontal ? rect.width : rect.height;
1076
1373
  const delta = movement / size * 100;
1077
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1374
+
1375
+ // If a validateLayout method has been provided
1376
+ // it's important to use it before updating the mouse cursor
1377
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1078
1378
  const sizesChanged = !areEqual(prevSizes, nextSizes);
1079
1379
 
1080
1380
  // Don't update cursor for resizes triggered by keyboard interactions.
@@ -1101,6 +1401,8 @@ function PanelGroupWithForwardedRef({
1101
1401
  }
1102
1402
  if (sizesChanged) {
1103
1403
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1404
+
1405
+ // It's okay to bypass in this case because we already validated above
1104
1406
  setSizes(nextSizes);
1105
1407
 
1106
1408
  // If resize change handlers have been declared, this is the time to call them.
@@ -1154,7 +1456,7 @@ function PanelGroupWithForwardedRef({
1154
1456
  }
1155
1457
  const isLastPanel = index === panelsArray.length - 1;
1156
1458
  const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
1157
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1459
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1158
1460
  if (prevSizes !== nextSizes) {
1159
1461
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1160
1462
  setSizes(nextSizes);
@@ -1197,7 +1499,7 @@ function PanelGroupWithForwardedRef({
1197
1499
  }
1198
1500
  const isLastPanel = index === panelsArray.length - 1;
1199
1501
  const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
1200
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1502
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1201
1503
  if (prevSizes !== nextSizes) {
1202
1504
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1203
1505
  setSizes(nextSizes);
@@ -1207,21 +1509,34 @@ function PanelGroupWithForwardedRef({
1207
1509
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1208
1510
  }
1209
1511
  }, []);
1210
- const resizePanel = useCallback((id, nextSize) => {
1512
+ const resizePanel = useCallback((id, nextSize, unitsFromParams) => {
1211
1513
  const {
1514
+ id: groupId,
1212
1515
  panels,
1213
- sizes: prevSizes
1516
+ sizes: prevSizes,
1517
+ units
1214
1518
  } = committedValuesRef.current;
1519
+ if ((unitsFromParams || units) === "pixels") {
1520
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1521
+ nextSize = nextSize / groupSizePixels * 100;
1522
+ }
1215
1523
  const panel = panels.get(id);
1216
1524
  if (panel == null) {
1217
1525
  return;
1218
1526
  }
1219
- const {
1527
+ let {
1220
1528
  collapsedSize,
1221
1529
  collapsible,
1222
1530
  maxSize,
1223
1531
  minSize
1224
1532
  } = panel.current;
1533
+ if (units === "pixels") {
1534
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1535
+ minSize = minSize / groupSizePixels * 100;
1536
+ if (maxSize != null) {
1537
+ maxSize = maxSize / groupSizePixels * 100;
1538
+ }
1539
+ }
1225
1540
  const panelsArray = panelsMapToSortedArray(panels);
1226
1541
  const index = panelsArray.indexOf(panel);
1227
1542
  if (index < 0) {
@@ -1232,7 +1547,13 @@ function PanelGroupWithForwardedRef({
1232
1547
  return;
1233
1548
  }
1234
1549
  if (collapsible && nextSize === collapsedSize) ; else {
1235
- nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
1550
+ const unsafeNextSize = nextSize;
1551
+ nextSize = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
1552
+ {
1553
+ if (unsafeNextSize !== nextSize) {
1554
+ console.error(`Invalid size (${unsafeNextSize}) specified for Panel "${panel.current.id}" given the panel's min/max size constraints`);
1555
+ }
1556
+ }
1236
1557
  }
1237
1558
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
1238
1559
  if (idBefore == null || idAfter == null) {
@@ -1240,7 +1561,7 @@ function PanelGroupWithForwardedRef({
1240
1561
  }
1241
1562
  const isLastPanel = index === panelsArray.length - 1;
1242
1563
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1243
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1564
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1244
1565
  if (prevSizes !== nextSizes) {
1245
1566
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1246
1567
  setSizes(nextSizes);
@@ -1255,6 +1576,7 @@ function PanelGroupWithForwardedRef({
1255
1576
  collapsePanel,
1256
1577
  direction,
1257
1578
  expandPanel,
1579
+ getPanelSize,
1258
1580
  getPanelStyle,
1259
1581
  groupId,
1260
1582
  registerPanel,
@@ -1276,8 +1598,9 @@ function PanelGroupWithForwardedRef({
1276
1598
  setActiveHandleId(null);
1277
1599
  initialDragStateRef.current = null;
1278
1600
  },
1601
+ units,
1279
1602
  unregisterPanel
1280
- }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, unregisterPanel]);
1603
+ }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelSize, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, units, unregisterPanel]);
1281
1604
  const style = {
1282
1605
  display: "flex",
1283
1606
  flexDirection: direction === "horizontal" ? "row" : "column",
@@ -1292,6 +1615,7 @@ function PanelGroupWithForwardedRef({
1292
1615
  "data-panel-group": "",
1293
1616
  "data-panel-group-direction": direction,
1294
1617
  "data-panel-group-id": groupId,
1618
+ "data-panel-group-units": units,
1295
1619
  style: {
1296
1620
  ...style,
1297
1621
  ...styleFromProps
@@ -1304,10 +1628,6 @@ const PanelGroup = forwardRef((props, ref) => createElement(PanelGroupWithForwar
1304
1628
  ...props,
1305
1629
  forwardedRef: ref
1306
1630
  }));
1307
-
1308
- // Workaround for Parcel scope hoisting (which renames objects/functions).
1309
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
1310
- // See github.com/parcel-bundler/parcel/issues/8724
1311
1631
  PanelGroupWithForwardedRef.displayName = "PanelGroup";
1312
1632
  PanelGroup.displayName = "forwardRef(PanelGroup)";
1313
1633
 
@@ -1443,12 +1763,9 @@ function PanelResizeHandle({
1443
1763
  tabIndex: 0
1444
1764
  });
1445
1765
  }
1446
-
1447
- // Workaround for Parcel scope hoisting (which renames objects/functions).
1448
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
1449
- // See github.com/parcel-bundler/parcel/issues/8724
1450
1766
  PanelResizeHandle.displayName = "PanelResizeHandle";
1451
1767
 
1452
1768
  exports.Panel = Panel;
1453
1769
  exports.PanelGroup = PanelGroup;
1454
1770
  exports.PanelResizeHandle = PanelResizeHandle;
1771
+ exports.getAvailableGroupSizePixels = getAvailableGroupSizePixels;