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
@@ -1,5 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
+ const isBrowser = typeof window !== "undefined";
4
+
3
5
  // This module exists to work around Webpack issue https://github.com/webpack/webpack/issues/14814
4
6
 
5
7
  // eslint-disable-next-line no-restricted-imports
@@ -21,8 +23,7 @@ const {
21
23
  // `toString()` prevents bundlers from trying to `import { useId } from 'react'`
22
24
  const useId = React["useId".toString()];
23
25
 
24
- const canUseEffectHooks = !!(typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined");
25
- const useIsomorphicLayoutEffect = canUseEffectHooks ? useLayoutEffect : () => {};
26
+ const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : () => {};
26
27
 
27
28
  const wrappedUseId = typeof useId === "function" ? useId : () => null;
28
29
  let counter = 0;
@@ -36,10 +37,6 @@ function useUniqueId(idFromParams = null) {
36
37
  }
37
38
 
38
39
  const PanelGroupContext = createContext(null);
39
-
40
- // Workaround for Parcel scope hoisting (which renames objects/functions).
41
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
42
- // See github.com/parcel-bundler/parcel/issues/8724
43
40
  PanelGroupContext.displayName = "PanelGroupContext";
44
41
 
45
42
  function PanelWithForwardedRef({
@@ -50,8 +47,8 @@ function PanelWithForwardedRef({
50
47
  defaultSize = null,
51
48
  forwardedRef,
52
49
  id: idFromProps = null,
53
- maxSize = 100,
54
- minSize = 10,
50
+ maxSize = null,
51
+ minSize,
55
52
  onCollapse = null,
56
53
  onResize = null,
57
54
  order = null,
@@ -66,11 +63,22 @@ function PanelWithForwardedRef({
66
63
  const {
67
64
  collapsePanel,
68
65
  expandPanel,
66
+ getPanelSize,
69
67
  getPanelStyle,
70
68
  registerPanel,
71
69
  resizePanel,
70
+ units,
72
71
  unregisterPanel
73
72
  } = context;
73
+ if (minSize == null) {
74
+ if (units === "percentages") {
75
+ // Mimics legacy default value for percentage based panel groups
76
+ minSize = 10;
77
+ } else {
78
+ // There is no meaningful minimum pixel default we can provide
79
+ minSize = 0;
80
+ }
81
+ }
74
82
 
75
83
  // Use a ref to guard against users passing inline props
76
84
  const callbacksRef = useRef({
@@ -81,22 +89,6 @@ function PanelWithForwardedRef({
81
89
  callbacksRef.current.onCollapse = onCollapse;
82
90
  callbacksRef.current.onResize = onResize;
83
91
  });
84
-
85
- // Basic props validation
86
- if (minSize < 0 || minSize > 100) {
87
- throw Error(`Panel minSize must be between 0 and 100, but was ${minSize}`);
88
- } else if (maxSize < 0 || maxSize > 100) {
89
- throw Error(`Panel maxSize must be between 0 and 100, but was ${maxSize}`);
90
- } else {
91
- if (defaultSize !== null) {
92
- if (defaultSize < 0 || defaultSize > 100) {
93
- throw Error(`Panel defaultSize must be between 0 and 100, but was ${defaultSize}`);
94
- } else if (minSize > defaultSize && !collapsible) {
95
- console.error(`Panel minSize ${minSize} cannot be greater than defaultSize ${defaultSize}`);
96
- defaultSize = minSize;
97
- }
98
- }
99
- }
100
92
  const style = getPanelStyle(panelId, defaultSize);
101
93
  const committedValuesRef = useRef({
102
94
  size: parseSizeFromStyle(style)
@@ -107,6 +99,7 @@ function PanelWithForwardedRef({
107
99
  collapsible,
108
100
  defaultSize,
109
101
  id: panelId,
102
+ idWasAutoGenerated: idFromProps == null,
110
103
  maxSize,
111
104
  minSize,
112
105
  order
@@ -118,6 +111,7 @@ function PanelWithForwardedRef({
118
111
  panelDataRef.current.collapsible = collapsible;
119
112
  panelDataRef.current.defaultSize = defaultSize;
120
113
  panelDataRef.current.id = panelId;
114
+ panelDataRef.current.idWasAutoGenerated = idFromProps == null;
121
115
  panelDataRef.current.maxSize = maxSize;
122
116
  panelDataRef.current.minSize = minSize;
123
117
  panelDataRef.current.order = order;
@@ -134,11 +128,14 @@ function PanelWithForwardedRef({
134
128
  getCollapsed() {
135
129
  return committedValuesRef.current.size === 0;
136
130
  },
137
- getSize() {
138
- return committedValuesRef.current.size;
131
+ getId() {
132
+ return panelId;
139
133
  },
140
- resize: percentage => resizePanel(panelId, percentage)
141
- }), [collapsePanel, expandPanel, panelId, resizePanel]);
134
+ getSize(units) {
135
+ return getPanelSize(panelId, units);
136
+ },
137
+ resize: (percentage, units) => resizePanel(panelId, percentage, units)
138
+ }), [collapsePanel, expandPanel, getPanelSize, panelId, resizePanel]);
142
139
  return createElement(Type, {
143
140
  children,
144
141
  className: classNameFromProps,
@@ -157,10 +154,6 @@ const Panel = forwardRef((props, ref) => createElement(PanelWithForwardedRef, {
157
154
  ...props,
158
155
  forwardedRef: ref
159
156
  }));
160
-
161
- // Workaround for Parcel scope hoisting (which renames objects/functions).
162
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
163
- // See github.com/parcel-bundler/parcel/issues/8724
164
157
  PanelWithForwardedRef.displayName = "Panel";
165
158
  Panel.displayName = "forwardRef(Panel)";
166
159
 
@@ -178,7 +171,13 @@ function parseSizeFromStyle(style) {
178
171
 
179
172
  const PRECISION = 10;
180
173
 
181
- function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse, initialDragState) {
174
+ function adjustByDelta(event, committedValues, idBefore, idAfter, deltaPixels, prevSizes, panelSizeBeforeCollapse, initialDragState) {
175
+ const {
176
+ id: groupId,
177
+ panels,
178
+ units
179
+ } = committedValues;
180
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
182
181
  const {
183
182
  sizes: initialSizes
184
183
  } = initialDragState || {};
@@ -186,9 +185,6 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
186
185
  // If we're resizing by mouse or touch, use the initial sizes as a base.
187
186
  // This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
188
187
  const baseSizes = initialSizes || prevSizes;
189
- if (delta === 0) {
190
- return baseSizes;
191
- }
192
188
  const panelsArray = panelsMapToSortedArray(panels);
193
189
  const nextSizes = baseSizes.concat();
194
190
  let deltaApplied = 0;
@@ -203,11 +199,11 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
203
199
 
204
200
  // Max-bounds check the panel being expanded first.
205
201
  {
206
- const pivotId = delta < 0 ? idAfter : idBefore;
202
+ const pivotId = deltaPixels < 0 ? idAfter : idBefore;
207
203
  const index = panelsArray.findIndex(panel => panel.current.id === pivotId);
208
204
  const panel = panelsArray[index];
209
205
  const baseSize = baseSizes[index];
210
- const nextSize = safeResizePanel(panel, Math.abs(delta), baseSize, event);
206
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize + Math.abs(deltaPixels), event);
211
207
  if (baseSize === nextSize) {
212
208
  // If there's no room for the pivot panel to grow, we can ignore this drag update.
213
209
  return baseSizes;
@@ -215,29 +211,29 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
215
211
  if (nextSize === 0 && baseSize > 0) {
216
212
  panelSizeBeforeCollapse.set(pivotId, baseSize);
217
213
  }
218
- delta = delta < 0 ? baseSize - nextSize : nextSize - baseSize;
214
+ deltaPixels = deltaPixels < 0 ? baseSize - nextSize : nextSize - baseSize;
219
215
  }
220
216
  }
221
- let pivotId = delta < 0 ? idBefore : idAfter;
217
+ let pivotId = deltaPixels < 0 ? idBefore : idAfter;
222
218
  let index = panelsArray.findIndex(panel => panel.current.id === pivotId);
223
219
  while (true) {
224
220
  const panel = panelsArray[index];
225
221
  const baseSize = baseSizes[index];
226
- const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
227
- const nextSize = safeResizePanel(panel, 0 - deltaRemaining, baseSize, event);
222
+ const deltaRemaining = Math.abs(deltaPixels) - Math.abs(deltaApplied);
223
+ const nextSize = safeResizePanel(units, groupSizePixels, panel, baseSize, baseSize - deltaRemaining, event);
228
224
  if (baseSize !== nextSize) {
229
225
  if (nextSize === 0 && baseSize > 0) {
230
226
  panelSizeBeforeCollapse.set(panel.current.id, baseSize);
231
227
  }
232
228
  deltaApplied += baseSize - nextSize;
233
229
  nextSizes[index] = nextSize;
234
- if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(delta).toPrecision(PRECISION), undefined, {
230
+ if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(deltaPixels).toPrecision(PRECISION), undefined, {
235
231
  numeric: true
236
232
  }) >= 0) {
237
233
  break;
238
234
  }
239
235
  }
240
- if (delta < 0) {
236
+ if (deltaPixels < 0) {
241
237
  if (--index < 0) {
242
238
  break;
243
239
  }
@@ -255,7 +251,7 @@ function adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panel
255
251
  }
256
252
 
257
253
  // Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
258
- pivotId = delta < 0 ? idAfter : idBefore;
254
+ pivotId = deltaPixels < 0 ? idAfter : idBefore;
259
255
  index = panelsArray.findIndex(panel => panel.current.id === pivotId);
260
256
  nextSizes[index] = baseSizes[index] + deltaApplied;
261
257
  return nextSizes;
@@ -294,6 +290,93 @@ function callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap) {
294
290
  }
295
291
  });
296
292
  }
293
+ function calculateDefaultLayout({
294
+ groupId,
295
+ panels,
296
+ units
297
+ }) {
298
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
299
+ const panelsArray = panelsMapToSortedArray(panels);
300
+ const sizes = Array(panelsArray.length);
301
+ let numPanelsWithSizes = 0;
302
+ let remainingSize = 100;
303
+
304
+ // Assigning default sizes requires a couple of passes:
305
+ // First, all panels with defaultSize should be set as-is
306
+ for (let index = 0; index < panelsArray.length; index++) {
307
+ const panel = panelsArray[index];
308
+ const {
309
+ defaultSize
310
+ } = panel.current;
311
+ if (defaultSize != null) {
312
+ numPanelsWithSizes++;
313
+ sizes[index] = units === "pixels" ? defaultSize / groupSizePixels * 100 : defaultSize;
314
+ remainingSize -= sizes[index];
315
+ }
316
+ }
317
+
318
+ // Remaining total size should be distributed evenly between panels
319
+ // This may require two passes, depending on min/max constraints
320
+ for (let index = 0; index < panelsArray.length; index++) {
321
+ const panel = panelsArray[index];
322
+ let {
323
+ defaultSize,
324
+ id,
325
+ maxSize,
326
+ minSize
327
+ } = panel.current;
328
+ if (defaultSize != null) {
329
+ continue;
330
+ }
331
+ if (units === "pixels") {
332
+ minSize = minSize / groupSizePixels * 100;
333
+ if (maxSize != null) {
334
+ maxSize = maxSize / groupSizePixels * 100;
335
+ }
336
+ }
337
+ const remainingPanels = panelsArray.length - numPanelsWithSizes;
338
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, remainingSize / remainingPanels));
339
+ sizes[index] = size;
340
+ numPanelsWithSizes++;
341
+ remainingSize -= size;
342
+ }
343
+
344
+ // If there is additional, left over space, assign it to any panel(s) that permits it
345
+ // (It's not worth taking multiple additional passes to evenly distribute)
346
+ if (remainingSize !== 0) {
347
+ for (let index = 0; index < panelsArray.length; index++) {
348
+ const panel = panelsArray[index];
349
+ let {
350
+ maxSize,
351
+ minSize
352
+ } = panel.current;
353
+ if (units === "pixels") {
354
+ minSize = minSize / groupSizePixels * 100;
355
+ if (maxSize != null) {
356
+ maxSize = maxSize / groupSizePixels * 100;
357
+ }
358
+ }
359
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, sizes[index] + remainingSize));
360
+ if (size !== sizes[index]) {
361
+ remainingSize -= size - sizes[index];
362
+ sizes[index] = size;
363
+
364
+ // Fuzzy comparison to account for imprecise floating point math
365
+ if (Math.abs(remainingSize).toFixed(3) === "0.000") {
366
+ break;
367
+ }
368
+ }
369
+ }
370
+ }
371
+
372
+ // Finally, if there is still left-over size, log an error
373
+ if (Math.abs(remainingSize).toFixed(3) !== "0.000") {
374
+ {
375
+ 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.`);
376
+ }
377
+ }
378
+ return sizes;
379
+ }
297
380
  function getBeforeAndAfterIds(id, panelsArray) {
298
381
  if (panelsArray.length < 2) {
299
382
  return [null, null];
@@ -307,6 +390,23 @@ function getBeforeAndAfterIds(id, panelsArray) {
307
390
  const idAfter = isLastPanel ? id : panelsArray[index + 1].current.id;
308
391
  return [idBefore, idAfter];
309
392
  }
393
+ function getAvailableGroupSizePixels(groupId) {
394
+ const panelGroupElement = getPanelGroup(groupId);
395
+ if (panelGroupElement == null) {
396
+ return NaN;
397
+ }
398
+ const direction = panelGroupElement.getAttribute("data-panel-group-direction");
399
+ const resizeHandles = getResizeHandlesForGroup(groupId);
400
+ if (direction === "horizontal") {
401
+ return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
402
+ return accumulated + handle.offsetWidth;
403
+ }, 0);
404
+ } else {
405
+ return panelGroupElement.offsetHeight - resizeHandles.reduce((accumulated, handle) => {
406
+ return accumulated + handle.offsetHeight;
407
+ }, 0);
408
+ }
409
+ }
310
410
 
311
411
  // This method returns a number between 1 and 100 representing
312
412
  // the % of the group's overall space this panel should occupy.
@@ -377,18 +477,24 @@ function panelsMapToSortedArray(panels) {
377
477
  }
378
478
  });
379
479
  }
380
- function safeResizePanel(panel, delta, prevSize, event) {
381
- const nextSizeUnsafe = prevSize + delta;
382
- const {
480
+ function safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize, event = null) {
481
+ let {
383
482
  collapsedSize,
384
483
  collapsible,
385
484
  maxSize,
386
485
  minSize
387
486
  } = panel.current;
487
+ if (units === "pixels") {
488
+ collapsedSize = collapsedSize / groupSizePixels * 100;
489
+ if (maxSize != null) {
490
+ maxSize = maxSize / groupSizePixels * 100;
491
+ }
492
+ minSize = minSize / groupSizePixels * 100;
493
+ }
388
494
  if (collapsible) {
389
495
  if (prevSize > collapsedSize) {
390
496
  // Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
391
- if (nextSizeUnsafe <= minSize / 2 + collapsedSize) {
497
+ if (nextSize <= minSize / 2 + collapsedSize) {
392
498
  return collapsedSize;
393
499
  }
394
500
  } else {
@@ -397,14 +503,119 @@ function safeResizePanel(panel, delta, prevSize, event) {
397
503
  // Keyboard events should expand a collapsed panel to the min size,
398
504
  // but mouse events should wait until the panel has reached its min size
399
505
  // to avoid a visual flickering when dragging between collapsed and min size.
400
- if (nextSizeUnsafe < minSize) {
506
+ if (nextSize < minSize) {
401
507
  return collapsedSize;
402
508
  }
403
509
  }
404
510
  }
405
511
  }
406
- const nextSize = Math.min(maxSize, Math.max(minSize, nextSizeUnsafe));
407
- return nextSize;
512
+ return Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
513
+ }
514
+ function validatePanelProps(units, panelData) {
515
+ const {
516
+ collapsible,
517
+ defaultSize,
518
+ maxSize,
519
+ minSize
520
+ } = panelData.current;
521
+
522
+ // Basic props validation
523
+ if (minSize < 0 || units === "percentages" && minSize > 100) {
524
+ {
525
+ console.error(`Invalid Panel minSize provided, ${minSize}`);
526
+ }
527
+ panelData.current.minSize = 0;
528
+ }
529
+ if (maxSize != null) {
530
+ if (maxSize < 0 || units === "percentages" && maxSize > 100) {
531
+ {
532
+ console.error(`Invalid Panel maxSize provided, ${maxSize}`);
533
+ }
534
+ panelData.current.maxSize = null;
535
+ }
536
+ }
537
+ if (defaultSize !== null) {
538
+ if (defaultSize < 0 || units === "percentages" && defaultSize > 100) {
539
+ {
540
+ console.error(`Invalid Panel defaultSize provided, ${defaultSize}`);
541
+ }
542
+ panelData.current.defaultSize = null;
543
+ } else if (defaultSize < minSize && !collapsible) {
544
+ {
545
+ console.error(`Panel minSize (${minSize}) cannot be greater than defaultSize (${defaultSize})`);
546
+ }
547
+ panelData.current.defaultSize = minSize;
548
+ } else if (maxSize != null && defaultSize > maxSize) {
549
+ {
550
+ console.error(`Panel maxSize (${maxSize}) cannot be less than defaultSize (${defaultSize})`);
551
+ }
552
+ panelData.current.defaultSize = maxSize;
553
+ }
554
+ }
555
+ }
556
+ function validatePanelGroupLayout({
557
+ groupId,
558
+ panels,
559
+ nextSizes,
560
+ prevSizes,
561
+ units
562
+ }) {
563
+ // Clone because this method modifies
564
+ nextSizes = [...nextSizes];
565
+ const panelsArray = panelsMapToSortedArray(panels);
566
+ const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
567
+ let remainingSize = 0;
568
+
569
+ // First, check all of the proposed sizes against the min/max constraints
570
+ for (let index = 0; index < panelsArray.length; index++) {
571
+ const panel = panelsArray[index];
572
+ const prevSize = prevSizes[index];
573
+ const nextSize = nextSizes[index];
574
+ const safeNextSize = safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize);
575
+ if (nextSize != safeNextSize) {
576
+ remainingSize += nextSize - safeNextSize;
577
+ nextSizes[index] = safeNextSize;
578
+ {
579
+ console.error(`Invalid size (${nextSize}) specified for Panel "${panel.current.id}" given the panel's min/max size constraints`);
580
+ }
581
+ }
582
+ }
583
+
584
+ // If there is additional, left over space, assign it to any panel(s) that permits it
585
+ // (It's not worth taking multiple additional passes to evenly distribute)
586
+ if (remainingSize.toFixed(3) !== "0.000") {
587
+ for (let index = 0; index < panelsArray.length; index++) {
588
+ const panel = panelsArray[index];
589
+ let {
590
+ maxSize,
591
+ minSize
592
+ } = panel.current;
593
+ if (units === "pixels") {
594
+ minSize = minSize / groupSizePixels * 100;
595
+ if (maxSize != null) {
596
+ maxSize = maxSize / groupSizePixels * 100;
597
+ }
598
+ }
599
+ const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSizes[index] + remainingSize));
600
+ if (size !== nextSizes[index]) {
601
+ remainingSize -= size - nextSizes[index];
602
+ nextSizes[index] = size;
603
+
604
+ // Fuzzy comparison to account for imprecise floating point math
605
+ if (Math.abs(remainingSize).toFixed(3) === "0.000") {
606
+ break;
607
+ }
608
+ }
609
+ }
610
+ }
611
+
612
+ // If we still have remainder, the requested layout wasn't valid and we should warn about it
613
+ if (remainingSize.toFixed(3) !== "0.000") {
614
+ {
615
+ console.error(`"Invalid panel group configuration; default panel sizes should total 100% but was ${100 - remainingSize}%`);
616
+ }
617
+ }
618
+ return nextSizes;
408
619
  }
409
620
 
410
621
  function assert(expectedCondition, message = "Assertion failed!") {
@@ -430,6 +641,7 @@ function useWindowSplitterPanelGroupBehavior({
430
641
  panels
431
642
  } = committedValuesRef.current;
432
643
  const groupElement = getPanelGroup(groupId);
644
+ assert(groupElement != null, `No group found for id "${groupId}"`);
433
645
  const {
434
646
  height,
435
647
  width
@@ -442,23 +654,28 @@ function useWindowSplitterPanelGroupBehavior({
442
654
  if (idBefore == null || idAfter == null) {
443
655
  return () => {};
444
656
  }
445
- let minSize = 0;
446
- let maxSize = 100;
657
+ let currentMinSize = 0;
658
+ let currentMaxSize = 100;
447
659
  let totalMinSize = 0;
448
660
  let totalMaxSize = 0;
449
661
 
450
662
  // A panel's effective min/max sizes also need to account for other panel's sizes.
451
663
  panelsArray.forEach(panelData => {
452
- if (panelData.current.id === idBefore) {
453
- maxSize = panelData.current.maxSize;
454
- minSize = panelData.current.minSize;
664
+ const {
665
+ id,
666
+ maxSize,
667
+ minSize
668
+ } = panelData.current;
669
+ if (id === idBefore) {
670
+ currentMinSize = minSize;
671
+ currentMaxSize = maxSize != null ? maxSize : 100;
455
672
  } else {
456
- totalMinSize += panelData.current.minSize;
457
- totalMaxSize += panelData.current.maxSize;
673
+ totalMinSize += minSize;
674
+ totalMaxSize += maxSize != null ? maxSize : 100;
458
675
  }
459
676
  });
460
- const ariaValueMax = Math.min(maxSize, 100 - totalMinSize);
461
- const ariaValueMin = Math.max(minSize, (panelsArray.length - 1) * 100 - totalMaxSize);
677
+ const ariaValueMax = Math.min(currentMaxSize, 100 - totalMinSize);
678
+ const ariaValueMin = Math.max(currentMinSize, (panelsArray.length - 1) * 100 - totalMaxSize);
462
679
  const flexGrow = getFlexGrow(panels, idBefore, sizes);
463
680
  handle.setAttribute("aria-valuemax", "" + Math.round(ariaValueMax));
464
681
  handle.setAttribute("aria-valuemin", "" + Math.round(ariaValueMin));
@@ -482,7 +699,7 @@ function useWindowSplitterPanelGroupBehavior({
482
699
  } else {
483
700
  delta = -(direction === "horizontal" ? width : height);
484
701
  }
485
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
702
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, sizes, panelSizeBeforeCollapse.current, null);
486
703
  if (sizes !== nextSizes) {
487
704
  setSizes(nextSizes);
488
705
  }
@@ -754,13 +971,6 @@ function savePanelGroupLayout(autoSaveId, panels, sizes, storage) {
754
971
  }
755
972
  }
756
973
 
757
- function isServerRendering() {
758
- try {
759
- return typeof window === undefined;
760
- } catch (error) {}
761
- return true;
762
- }
763
-
764
974
  const debounceMap = {};
765
975
 
766
976
  // PanelGroup might be rendering in a server-side environment where localStorage is not available
@@ -804,9 +1014,6 @@ const defaultStorage = {
804
1014
  // * dragHandleRect, sizes:
805
1015
  // When resizing is done via mouse/touch event– some initial state is stored
806
1016
  // so that any panels that contract will also expand if drag direction is reversed.
807
- // TODO
808
- // Within an active drag, remember original positions to refine more easily on expand.
809
- // Look at what the Chrome devtools Sources does.
810
1017
  function PanelGroupWithForwardedRef({
811
1018
  autoSaveId,
812
1019
  children = null,
@@ -818,7 +1025,8 @@ function PanelGroupWithForwardedRef({
818
1025
  onLayout,
819
1026
  storage = defaultStorage,
820
1027
  style: styleFromProps = {},
821
- tagName: Type = "div"
1028
+ tagName: Type = "div",
1029
+ units = "percentages"
822
1030
  }) {
823
1031
  const groupId = useUniqueId(idFromProps);
824
1032
  const [activeHandleId, setActiveHandleId] = useState(null);
@@ -828,6 +1036,12 @@ function PanelGroupWithForwardedRef({
828
1036
  // We store the initial Panel sizes in this ref, and apply move deltas to them instead of to the current sizes.
829
1037
  // This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
830
1038
  const initialDragStateRef = useRef(null);
1039
+ const devWarningsRef = useRef({
1040
+ didLogDefaultSizeWarning: false,
1041
+ didLogIdAndOrderWarning: false,
1042
+ didLogInvalidLayoutWarning: false,
1043
+ prevPanelIds: []
1044
+ });
831
1045
 
832
1046
  // Use a ref to guard against users passing inline props
833
1047
  const callbacksRef = useRef({
@@ -848,32 +1062,58 @@ function PanelGroupWithForwardedRef({
848
1062
  // Store committed values to avoid unnecessarily re-running memoization/effects functions.
849
1063
  const committedValuesRef = useRef({
850
1064
  direction,
1065
+ id: groupId,
851
1066
  panels,
852
- sizes
1067
+ sizes,
1068
+ units
853
1069
  });
854
1070
  useImperativeHandle(forwardedRef, () => ({
855
- getLayout: () => {
1071
+ getId: () => groupId,
1072
+ getLayout: unitsFromParams => {
856
1073
  const {
857
- sizes
1074
+ sizes,
1075
+ units: unitsFromProps
858
1076
  } = committedValuesRef.current;
859
- return sizes;
1077
+ const units = unitsFromParams ?? unitsFromProps;
1078
+ if (units === "pixels") {
1079
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1080
+ return sizes.map(size => size / 100 * groupSizePixels);
1081
+ } else {
1082
+ return sizes;
1083
+ }
860
1084
  },
861
- setLayout: sizes => {
862
- const total = sizes.reduce((accumulated, current) => accumulated + current, 0);
863
- assert(total === 100, "Panel sizes must add up to 100%");
1085
+ setLayout: (sizes, unitsFromParams) => {
864
1086
  const {
865
- panels
1087
+ id: groupId,
1088
+ panels,
1089
+ sizes: prevSizes,
1090
+ units
866
1091
  } = committedValuesRef.current;
1092
+ if ((unitsFromParams || units) === "pixels") {
1093
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1094
+ sizes = sizes.map(size => size / groupSizePixels * 100);
1095
+ }
867
1096
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
868
1097
  const panelsArray = panelsMapToSortedArray(panels);
869
- setSizes(sizes);
870
- callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
1098
+ const nextSizes = validatePanelGroupLayout({
1099
+ groupId,
1100
+ panels,
1101
+ nextSizes: sizes,
1102
+ prevSizes,
1103
+ units
1104
+ });
1105
+ if (!areEqual(prevSizes, nextSizes)) {
1106
+ setSizes(nextSizes);
1107
+ callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1108
+ }
871
1109
  }
872
- }), []);
1110
+ }), [groupId]);
873
1111
  useIsomorphicLayoutEffect(() => {
874
1112
  committedValuesRef.current.direction = direction;
1113
+ committedValuesRef.current.id = groupId;
875
1114
  committedValuesRef.current.panels = panels;
876
1115
  committedValuesRef.current.sizes = sizes;
1116
+ committedValuesRef.current.units = units;
877
1117
  });
878
1118
  useWindowSplitterPanelGroupBehavior({
879
1119
  committedValuesRef,
@@ -915,7 +1155,11 @@ function PanelGroupWithForwardedRef({
915
1155
  // Compute the initial sizes based on default weights.
916
1156
  // This assumes that panels register during initial mount (no conditional rendering)!
917
1157
  useIsomorphicLayoutEffect(() => {
918
- const sizes = committedValuesRef.current.sizes;
1158
+ const {
1159
+ id: groupId,
1160
+ sizes,
1161
+ units
1162
+ } = committedValuesRef.current;
919
1163
  if (sizes.length === panels.size) {
920
1164
  // Only compute (or restore) default sizes once per panel configuration.
921
1165
  return;
@@ -929,39 +1173,23 @@ function PanelGroupWithForwardedRef({
929
1173
  defaultSizes = loadPanelLayout(autoSaveId, panelsArray, storage);
930
1174
  }
931
1175
  if (defaultSizes != null) {
932
- setSizes(defaultSizes);
1176
+ // Validate saved sizes in case something has changed since last render
1177
+ // e.g. for pixel groups, this could be the size of the window
1178
+ const validatedSizes = validatePanelGroupLayout({
1179
+ groupId,
1180
+ panels,
1181
+ nextSizes: defaultSizes,
1182
+ prevSizes: defaultSizes,
1183
+ units
1184
+ });
1185
+ setSizes(validatedSizes);
933
1186
  } else {
934
- const panelsArray = panelsMapToSortedArray(panels);
935
- let panelsWithNullDefaultSize = 0;
936
- let totalDefaultSize = 0;
937
- let totalMinSize = 0;
938
-
939
- // TODO
940
- // Implicit default size calculations below do not account for inferred min/max size values.
941
- // 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.
942
- // For now, these logic edge cases are left to the user to handle via props.
943
-
944
- panelsArray.forEach(panel => {
945
- totalMinSize += panel.current.minSize;
946
- if (panel.current.defaultSize === null) {
947
- panelsWithNullDefaultSize++;
948
- } else {
949
- totalDefaultSize += panel.current.defaultSize;
950
- }
1187
+ const sizes = calculateDefaultLayout({
1188
+ groupId,
1189
+ panels,
1190
+ units
951
1191
  });
952
- if (totalDefaultSize > 100) {
953
- throw new Error(`Default panel sizes cannot exceed 100%`);
954
- } else if (panelsArray.length > 1 && panelsWithNullDefaultSize === 0 && totalDefaultSize !== 100) {
955
- throw new Error(`Invalid default sizes specified for panels`);
956
- } else if (totalMinSize > 100) {
957
- throw new Error(`Minimum panel sizes cannot exceed 100%`);
958
- }
959
- setSizes(panelsArray.map(panel => {
960
- if (panel.current.defaultSize === null) {
961
- return (100 - totalDefaultSize) / panelsWithNullDefaultSize;
962
- }
963
- return panel.current.defaultSize;
964
- }));
1192
+ setSizes(sizes);
965
1193
  }
966
1194
  }, [autoSaveId, panels, storage]);
967
1195
  useEffect(() => {
@@ -978,7 +1206,69 @@ function PanelGroupWithForwardedRef({
978
1206
  }
979
1207
  debounceMap[autoSaveId](autoSaveId, panelsArray, sizes, storage);
980
1208
  }
1209
+ {
1210
+ const {
1211
+ didLogIdAndOrderWarning,
1212
+ prevPanelIds
1213
+ } = devWarningsRef.current;
1214
+ if (!didLogIdAndOrderWarning) {
1215
+ const {
1216
+ panels
1217
+ } = committedValuesRef.current;
1218
+ const panelIds = Array.from(panels.keys());
1219
+ devWarningsRef.current.prevPanelIds = panelIds;
1220
+ const panelsHaveChanged = prevPanelIds.length > 0 && !areEqual(prevPanelIds, panelIds);
1221
+ if (panelsHaveChanged) {
1222
+ if (Array.from(panels.values()).find(panel => panel.current.idWasAutoGenerated || panel.current.order == null)) {
1223
+ devWarningsRef.current.didLogIdAndOrderWarning = true;
1224
+ console.warn(`WARNING: Panel id and order props recommended when panels are dynamically rendered`);
1225
+ }
1226
+ }
1227
+ }
1228
+ }
981
1229
  }, [autoSaveId, panels, sizes, storage]);
1230
+ useIsomorphicLayoutEffect(() => {
1231
+ // Pixel panel constraints need to be reassessed after a group resize
1232
+ // We can avoid the ResizeObserver overhead for relative layouts
1233
+ if (units === "pixels") {
1234
+ const resizeObserver = new ResizeObserver(() => {
1235
+ const {
1236
+ panels,
1237
+ sizes: prevSizes
1238
+ } = committedValuesRef.current;
1239
+ const nextSizes = validatePanelGroupLayout({
1240
+ groupId,
1241
+ panels,
1242
+ nextSizes: prevSizes,
1243
+ prevSizes,
1244
+ units
1245
+ });
1246
+ if (!areEqual(prevSizes, nextSizes)) {
1247
+ setSizes(nextSizes);
1248
+ }
1249
+ });
1250
+ resizeObserver.observe(getPanelGroup(groupId));
1251
+ return () => {
1252
+ resizeObserver.disconnect();
1253
+ };
1254
+ }
1255
+ }, [groupId, units]);
1256
+ const getPanelSize = useCallback((id, unitsFromParams) => {
1257
+ const {
1258
+ panels,
1259
+ units: unitsFromProps
1260
+ } = committedValuesRef.current;
1261
+ const panelsArray = panelsMapToSortedArray(panels);
1262
+ const index = panelsArray.findIndex(panel => panel.current.id === id);
1263
+ const size = sizes[index];
1264
+ const units = unitsFromParams ?? unitsFromProps;
1265
+ if (units === "pixels") {
1266
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1267
+ return size / 100 * groupSizePixels;
1268
+ } else {
1269
+ return size;
1270
+ }
1271
+ }, [groupId, sizes]);
982
1272
  const getPanelStyle = useCallback((id, defaultSize) => {
983
1273
  const {
984
1274
  panels
@@ -989,8 +1279,11 @@ function PanelGroupWithForwardedRef({
989
1279
  // At this point the best we can do is render everything with the same size.
990
1280
  if (panels.size === 0) {
991
1281
  {
992
- if (isServerRendering() && defaultSize == null) {
993
- console.warn(`WARNING: Panel defaultSize prop recommended to avoid layout shift after server rendering`);
1282
+ if (!devWarningsRef.current.didLogDefaultSizeWarning) {
1283
+ if (!isBrowser && defaultSize == null) {
1284
+ devWarningsRef.current.didLogDefaultSizeWarning = true;
1285
+ console.warn(`WARNING: Panel defaultSize prop recommended to avoid layout shift after server rendering`);
1286
+ }
994
1287
  }
995
1288
  }
996
1289
  return {
@@ -1014,6 +1307,10 @@ function PanelGroupWithForwardedRef({
1014
1307
  };
1015
1308
  }, [activeHandleId, disablePointerEventsDuringResize, sizes]);
1016
1309
  const registerPanel = useCallback((id, panelRef) => {
1310
+ const {
1311
+ units
1312
+ } = committedValuesRef.current;
1313
+ validatePanelProps(units, panelRef);
1017
1314
  setPanels(prevPanels => {
1018
1315
  if (prevPanels.has(id)) {
1019
1316
  return prevPanels;
@@ -1050,7 +1347,10 @@ function PanelGroupWithForwardedRef({
1050
1347
  }
1051
1348
  const size = isHorizontal ? rect.width : rect.height;
1052
1349
  const delta = movement / size * 100;
1053
- const nextSizes = adjustByDelta(event, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1350
+
1351
+ // If a validateLayout method has been provided
1352
+ // it's important to use it before updating the mouse cursor
1353
+ const nextSizes = adjustByDelta(event, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, initialDragStateRef.current);
1054
1354
  const sizesChanged = !areEqual(prevSizes, nextSizes);
1055
1355
 
1056
1356
  // Don't update cursor for resizes triggered by keyboard interactions.
@@ -1077,6 +1377,8 @@ function PanelGroupWithForwardedRef({
1077
1377
  }
1078
1378
  if (sizesChanged) {
1079
1379
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1380
+
1381
+ // It's okay to bypass in this case because we already validated above
1080
1382
  setSizes(nextSizes);
1081
1383
 
1082
1384
  // If resize change handlers have been declared, this is the time to call them.
@@ -1130,7 +1432,7 @@ function PanelGroupWithForwardedRef({
1130
1432
  }
1131
1433
  const isLastPanel = index === panelsArray.length - 1;
1132
1434
  const delta = isLastPanel ? currentSize : collapsedSize - currentSize;
1133
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1435
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1134
1436
  if (prevSizes !== nextSizes) {
1135
1437
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1136
1438
  setSizes(nextSizes);
@@ -1173,7 +1475,7 @@ function PanelGroupWithForwardedRef({
1173
1475
  }
1174
1476
  const isLastPanel = index === panelsArray.length - 1;
1175
1477
  const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
1176
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1478
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1177
1479
  if (prevSizes !== nextSizes) {
1178
1480
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1179
1481
  setSizes(nextSizes);
@@ -1183,21 +1485,34 @@ function PanelGroupWithForwardedRef({
1183
1485
  callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
1184
1486
  }
1185
1487
  }, []);
1186
- const resizePanel = useCallback((id, nextSize) => {
1488
+ const resizePanel = useCallback((id, nextSize, unitsFromParams) => {
1187
1489
  const {
1490
+ id: groupId,
1188
1491
  panels,
1189
- sizes: prevSizes
1492
+ sizes: prevSizes,
1493
+ units
1190
1494
  } = committedValuesRef.current;
1495
+ if ((unitsFromParams || units) === "pixels") {
1496
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1497
+ nextSize = nextSize / groupSizePixels * 100;
1498
+ }
1191
1499
  const panel = panels.get(id);
1192
1500
  if (panel == null) {
1193
1501
  return;
1194
1502
  }
1195
- const {
1503
+ let {
1196
1504
  collapsedSize,
1197
1505
  collapsible,
1198
1506
  maxSize,
1199
1507
  minSize
1200
1508
  } = panel.current;
1509
+ if (units === "pixels") {
1510
+ const groupSizePixels = getAvailableGroupSizePixels(groupId);
1511
+ minSize = minSize / groupSizePixels * 100;
1512
+ if (maxSize != null) {
1513
+ maxSize = maxSize / groupSizePixels * 100;
1514
+ }
1515
+ }
1201
1516
  const panelsArray = panelsMapToSortedArray(panels);
1202
1517
  const index = panelsArray.indexOf(panel);
1203
1518
  if (index < 0) {
@@ -1208,7 +1523,13 @@ function PanelGroupWithForwardedRef({
1208
1523
  return;
1209
1524
  }
1210
1525
  if (collapsible && nextSize === collapsedSize) ; else {
1211
- nextSize = Math.min(maxSize, Math.max(minSize, nextSize));
1526
+ const unsafeNextSize = nextSize;
1527
+ nextSize = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
1528
+ {
1529
+ if (unsafeNextSize !== nextSize) {
1530
+ console.error(`Invalid size (${unsafeNextSize}) specified for Panel "${panel.current.id}" given the panel's min/max size constraints`);
1531
+ }
1532
+ }
1212
1533
  }
1213
1534
  const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
1214
1535
  if (idBefore == null || idAfter == null) {
@@ -1216,7 +1537,7 @@ function PanelGroupWithForwardedRef({
1216
1537
  }
1217
1538
  const isLastPanel = index === panelsArray.length - 1;
1218
1539
  const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
1219
- const nextSizes = adjustByDelta(null, panels, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1540
+ const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
1220
1541
  if (prevSizes !== nextSizes) {
1221
1542
  const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1222
1543
  setSizes(nextSizes);
@@ -1231,6 +1552,7 @@ function PanelGroupWithForwardedRef({
1231
1552
  collapsePanel,
1232
1553
  direction,
1233
1554
  expandPanel,
1555
+ getPanelSize,
1234
1556
  getPanelStyle,
1235
1557
  groupId,
1236
1558
  registerPanel,
@@ -1252,8 +1574,9 @@ function PanelGroupWithForwardedRef({
1252
1574
  setActiveHandleId(null);
1253
1575
  initialDragStateRef.current = null;
1254
1576
  },
1577
+ units,
1255
1578
  unregisterPanel
1256
- }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, unregisterPanel]);
1579
+ }), [activeHandleId, collapsePanel, direction, expandPanel, getPanelSize, getPanelStyle, groupId, registerPanel, registerResizeHandle, resizePanel, units, unregisterPanel]);
1257
1580
  const style = {
1258
1581
  display: "flex",
1259
1582
  flexDirection: direction === "horizontal" ? "row" : "column",
@@ -1268,6 +1591,7 @@ function PanelGroupWithForwardedRef({
1268
1591
  "data-panel-group": "",
1269
1592
  "data-panel-group-direction": direction,
1270
1593
  "data-panel-group-id": groupId,
1594
+ "data-panel-group-units": units,
1271
1595
  style: {
1272
1596
  ...style,
1273
1597
  ...styleFromProps
@@ -1280,10 +1604,6 @@ const PanelGroup = forwardRef((props, ref) => createElement(PanelGroupWithForwar
1280
1604
  ...props,
1281
1605
  forwardedRef: ref
1282
1606
  }));
1283
-
1284
- // Workaround for Parcel scope hoisting (which renames objects/functions).
1285
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
1286
- // See github.com/parcel-bundler/parcel/issues/8724
1287
1607
  PanelGroupWithForwardedRef.displayName = "PanelGroup";
1288
1608
  PanelGroup.displayName = "forwardRef(PanelGroup)";
1289
1609
 
@@ -1419,10 +1739,6 @@ function PanelResizeHandle({
1419
1739
  tabIndex: 0
1420
1740
  });
1421
1741
  }
1422
-
1423
- // Workaround for Parcel scope hoisting (which renames objects/functions).
1424
- // Casting to :any is required to avoid corrupting the generated TypeScript types.
1425
- // See github.com/parcel-bundler/parcel/issues/8724
1426
1742
  PanelResizeHandle.displayName = "PanelResizeHandle";
1427
1743
 
1428
- export { Panel, PanelGroup, PanelResizeHandle };
1744
+ export { Panel, PanelGroup, PanelResizeHandle, getAvailableGroupSizePixels };