react-resizable-panels 0.0.57 → 0.0.59

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 (30) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/declarations/src/Panel.d.ts +7 -3
  3. package/dist/declarations/src/PanelGroup.d.ts +3 -1
  4. package/dist/declarations/src/PanelResizeHandle.d.ts +3 -1
  5. package/dist/declarations/src/types.d.ts +3 -0
  6. package/dist/react-resizable-panels.browser.cjs.js +206 -77
  7. package/dist/react-resizable-panels.browser.development.cjs.js +206 -77
  8. package/dist/react-resizable-panels.browser.development.esm.js +206 -77
  9. package/dist/react-resizable-panels.browser.esm.js +206 -77
  10. package/dist/react-resizable-panels.cjs.js +206 -77
  11. package/dist/react-resizable-panels.cjs.js.map +1 -1
  12. package/dist/react-resizable-panels.development.cjs.js +206 -77
  13. package/dist/react-resizable-panels.development.esm.js +206 -77
  14. package/dist/react-resizable-panels.development.node.cjs.js +175 -75
  15. package/dist/react-resizable-panels.development.node.esm.js +175 -75
  16. package/dist/react-resizable-panels.esm.js +206 -77
  17. package/dist/react-resizable-panels.esm.js.map +1 -1
  18. package/dist/react-resizable-panels.node.cjs.js +175 -75
  19. package/dist/react-resizable-panels.node.esm.js +175 -75
  20. package/package.json +1 -1
  21. package/src/Panel.ts +8 -2
  22. package/src/PanelGroup.ts +89 -3
  23. package/src/PanelResizeHandle.ts +5 -0
  24. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +15 -15
  25. package/src/types.ts +4 -0
  26. package/src/utils/adjustLayoutByDelta.test.ts +238 -8
  27. package/src/utils/adjustLayoutByDelta.ts +122 -72
  28. package/src/utils/resizePanel.test.ts +61 -1
  29. package/src/utils/resizePanel.ts +7 -1
  30. package/src/utils/validatePanelGroupLayout.test.ts +36 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.59
4
+
5
+ - Support imperative panel API usage on-mount.
6
+ - Made PanelGroup bailout condition smarter (don't bailout for empty groups unless pixel constraints are used).
7
+ - Improved window splitter compatibility by better handling "Enter" key.
8
+
9
+ ## 0.0.58
10
+
11
+ - Change group layout to more thoroughly distribute resize delta to support more flexible group size configurations.
12
+ - Add data attribute support to `Panel`, `PanelGroup`, and `PanelResizeHandle`.
13
+ - Update API documentation to reflect changed imperative API method names.
14
+ - `PanelOnResize` TypeScript def updated to reflect that previous size param is `undefined` the first time it is called.
15
+
3
16
  ## 0.0.57
4
17
 
5
18
  - [#207](https://github.com/bvaughn/react-resizable-panels/pull/207): Fix DEV conditional error that broke data attributes (and selectors).
@@ -1,8 +1,8 @@
1
- import { MixedSizes } from "./types.js";
1
+ import { DataAttributes, MixedSizes } from "./types.js";
2
2
  import { ElementType, ForwardedRef, PropsWithChildren } from "./vendor/react.js";
3
3
  export type PanelOnCollapse = () => void;
4
4
  export type PanelOnExpand = () => void;
5
- export type PanelOnResize = (mixedSizes: MixedSizes, prevMixedSizes: MixedSizes) => void;
5
+ export type PanelOnResize = (mixedSizes: MixedSizes, prevMixedSizes: MixedSizes | undefined) => void;
6
6
  export type PanelCallbacks = {
7
7
  onCollapse?: PanelOnCollapse;
8
8
  onExpand?: PanelOnExpand;
@@ -31,6 +31,8 @@ export type ImperativePanelHandle = {
31
31
  expand: () => void;
32
32
  getId(): string;
33
33
  getSize(): MixedSizes;
34
+ isCollapsed: () => boolean;
35
+ isExpanded: () => boolean;
34
36
  resize: (size: Partial<MixedSizes>) => void;
35
37
  };
36
38
  export type PanelProps = PropsWithChildren<{
@@ -38,6 +40,7 @@ export type PanelProps = PropsWithChildren<{
38
40
  collapsedSizePercentage?: number | undefined;
39
41
  collapsedSizePixels?: number | undefined;
40
42
  collapsible?: boolean | undefined;
43
+ dataAttributes?: DataAttributes;
41
44
  defaultSizePercentage?: number | undefined;
42
45
  defaultSizePixels?: number | undefined;
43
46
  id?: string;
@@ -52,7 +55,7 @@ export type PanelProps = PropsWithChildren<{
52
55
  style?: object;
53
56
  tagName?: ElementType;
54
57
  }>;
55
- export declare function PanelWithForwardedRef({ children, className: classNameFromProps, collapsedSizePercentage, collapsedSizePixels, collapsible, defaultSizePercentage, defaultSizePixels, forwardedRef, id: idFromProps, maxSizePercentage, maxSizePixels, minSizePercentage, minSizePixels, onCollapse, onExpand, onResize, order, style: styleFromProps, tagName: Type, }: PanelProps & {
58
+ export declare function PanelWithForwardedRef({ children, className: classNameFromProps, collapsedSizePercentage, collapsedSizePixels, collapsible, dataAttributes, defaultSizePercentage, defaultSizePixels, forwardedRef, id: idFromProps, maxSizePercentage, maxSizePixels, minSizePercentage, minSizePixels, onCollapse, onExpand, onResize, order, style: styleFromProps, tagName: Type, }: PanelProps & {
56
59
  forwardedRef: ForwardedRef<ImperativePanelHandle>;
57
60
  }): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
58
61
  export declare namespace PanelWithForwardedRef {
@@ -63,6 +66,7 @@ export declare const Panel: import("react").ForwardRefExoticComponent<{
63
66
  collapsedSizePercentage?: number | undefined;
64
67
  collapsedSizePixels?: number | undefined;
65
68
  collapsible?: boolean | undefined;
69
+ dataAttributes?: DataAttributes | undefined;
66
70
  defaultSizePercentage?: number | undefined;
67
71
  defaultSizePixels?: number | undefined;
68
72
  id?: string | undefined;
@@ -1,4 +1,4 @@
1
- import { Direction, MixedSizes } from "./types.js";
1
+ import { DataAttributes, Direction, MixedSizes } from "./types.js";
2
2
  import { CSSProperties, ElementType, PropsWithChildren } from "./vendor/react.js";
3
3
  export type ImperativePanelGroupHandle = {
4
4
  getId: () => string;
@@ -13,6 +13,7 @@ export type PanelGroupOnLayout = (layout: MixedSizes[]) => void;
13
13
  export type PanelGroupProps = PropsWithChildren<{
14
14
  autoSaveId?: string;
15
15
  className?: string;
16
+ dataAttributes?: DataAttributes;
16
17
  direction: Direction;
17
18
  id?: string | null;
18
19
  keyboardResizeByPercentage?: number | null;
@@ -25,6 +26,7 @@ export type PanelGroupProps = PropsWithChildren<{
25
26
  export declare const PanelGroup: import("react").ForwardRefExoticComponent<{
26
27
  autoSaveId?: string | undefined;
27
28
  className?: string | undefined;
29
+ dataAttributes?: DataAttributes | undefined;
28
30
  direction: Direction;
29
31
  id?: string | null | undefined;
30
32
  keyboardResizeByPercentage?: number | null | undefined;
@@ -1,15 +1,17 @@
1
1
  import { CSSProperties, ElementType, ReactNode } from "./vendor/react.js";
2
+ import { DataAttributes } from "./types.js";
2
3
  export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
3
4
  export type PanelResizeHandleProps = {
4
5
  children?: ReactNode;
5
6
  className?: string;
7
+ dataAttributes?: DataAttributes;
6
8
  disabled?: boolean;
7
9
  id?: string | null;
8
10
  onDragging?: PanelResizeHandleOnDragging;
9
11
  style?: CSSProperties;
10
12
  tagName?: ElementType;
11
13
  };
12
- export declare function PanelResizeHandle({ children, className: classNameFromProps, disabled, id: idFromProps, onDragging, style: styleFromProps, tagName: Type, }: PanelResizeHandleProps): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
14
+ export declare function PanelResizeHandle({ children, className: classNameFromProps, dataAttributes, disabled, id: idFromProps, onDragging, style: styleFromProps, tagName: Type, }: PanelResizeHandleProps): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
13
15
  export declare namespace PanelResizeHandle {
14
16
  var displayName: string;
15
17
  }
@@ -5,3 +5,6 @@ export type MixedSizes = {
5
5
  };
6
6
  export type ResizeEvent = KeyboardEvent | MouseEvent | TouchEvent;
7
7
  export type ResizeHandler = (event: ResizeEvent) => void;
8
+ export type DataAttributes = {
9
+ [attribute: string]: string | number | boolean | undefined;
10
+ };
@@ -68,6 +68,7 @@ function PanelWithForwardedRef({
68
68
  collapsedSizePercentage,
69
69
  collapsedSizePixels,
70
70
  collapsible,
71
+ dataAttributes,
71
72
  defaultSizePercentage,
72
73
  defaultSizePixels,
73
74
  forwardedRef,
@@ -181,6 +182,7 @@ function PanelWithForwardedRef({
181
182
  ...style,
182
183
  ...styleFromProps
183
184
  },
185
+ ...dataAttributes,
184
186
  // CSS selectors
185
187
  "data-panel": "",
186
188
  "data-panel-id": panelId,
@@ -196,8 +198,6 @@ const Panel = forwardRef((props, ref) => createElement(PanelWithForwardedRef, {
196
198
  PanelWithForwardedRef.displayName = "Panel";
197
199
  Panel.displayName = "forwardRef(Panel)";
198
200
 
199
- const PRECISION = 10;
200
-
201
201
  function convertPixelsToPercentage(pixels, groupSizePixels) {
202
202
  return pixels / groupSizePixels * 100;
203
203
  }
@@ -275,6 +275,8 @@ function computePercentagePanelConstraints(panelConstraintsArray, panelIndex, gr
275
275
  };
276
276
  }
277
277
 
278
+ const PRECISION = 10;
279
+
278
280
  function fuzzyCompareNumbers(actual, expected, fractionDigits = PRECISION) {
279
281
  actual = parseFloat(actual.toFixed(fractionDigits));
280
282
  expected = parseFloat(expected.toFixed(fractionDigits));
@@ -318,7 +320,13 @@ function resizePanel({
318
320
  if (minSizePercentage != null) {
319
321
  if (fuzzyCompareNumbers(size, minSizePercentage) < 0) {
320
322
  if (collapsible) {
321
- size = collapsedSizePercentage;
323
+ // Collapsible panels should snap closed or open only once they cross the halfway point between collapsed and min size.
324
+ const halfwayPoint = (collapsedSizePercentage + minSizePercentage) / 2;
325
+ if (fuzzyCompareNumbers(size, halfwayPoint) < 0) {
326
+ size = collapsedSizePercentage;
327
+ } else {
328
+ size = minSizePercentage;
329
+ }
322
330
  } else {
323
331
  size = minSizePercentage;
324
332
  }
@@ -345,60 +353,123 @@ function adjustLayoutByDelta({
345
353
  const nextLayout = [...prevLayout];
346
354
  let deltaApplied = 0;
347
355
 
356
+ //const DEBUG = [];
357
+ //DEBUG.push(`adjustLayoutByDelta() ${prevLayout.join(", ")}`);
358
+ //DEBUG.push(` delta: ${delta}`);
359
+ //DEBUG.push(` pivotIndices: ${pivotIndices.join(", ")}`);
360
+ //DEBUG.push(` trigger: ${trigger}`);
361
+ //DEBUG.push("");
362
+
348
363
  // A resizing panel affects the panels before or after it.
349
364
  //
350
- // A negative delta means the panel immediately after the resizer should grow/expand by decreasing its offset.
365
+ // A negative delta means the panel(s) immediately after the resize handle should grow/expand by decreasing its offset.
351
366
  // Other panels may also need to shrink/contract (and shift) to make room, depending on the min weights.
352
367
  //
353
- // A positive delta means the panel immediately before the resizer should "expand".
354
- // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resizer.
368
+ // A positive delta means the panel(s) immediately before the resize handle should "expand".
369
+ // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resize handle.
355
370
 
356
- // First, check the panel we're pivoting around;
357
- // We should only expand or contract by as much as its constraints allow
358
371
  {
359
- const pivotIndex = delta < 0 ? pivotIndices[1] : pivotIndices[0];
360
- const initialSize = nextLayout[pivotIndex];
361
- const {
362
- collapsible
363
- } = panelConstraints[pivotIndex];
364
- const {
365
- collapsedSizePercentage,
366
- minSizePercentage
367
- } = computePercentagePanelConstraints(panelConstraints, pivotIndex, groupSizePixels);
368
- const isCollapsed = collapsible && fuzzyNumbersEqual(initialSize, collapsedSizePercentage);
369
- let unsafeSize = initialSize + Math.abs(delta);
370
- if (isCollapsed) {
371
- switch (trigger) {
372
- case "keyboard":
373
- if (minSizePercentage > unsafeSize) {
374
- unsafeSize = minSizePercentage;
372
+ // If this is a resize triggered by a keyboard event, our logic for expanding/collapsing is different.
373
+ // We no longer check the halfway threshold because this may prevent the panel from expanding at all.
374
+ if (trigger === "keyboard") {
375
+ {
376
+ // Check if we should expand a collapsed panel
377
+ const index = delta < 0 ? pivotIndices[1] : pivotIndices[0];
378
+ const constraints = panelConstraints[index];
379
+ //DEBUG.push(`edge case check 1: ${index}`);
380
+ //DEBUG.push(` -> collapsible? ${constraints.collapsible}`);
381
+ if (constraints.collapsible) {
382
+ const prevSize = prevLayout[index];
383
+ const {
384
+ collapsedSizePercentage,
385
+ minSizePercentage
386
+ } = computePercentagePanelConstraints(panelConstraints, index, groupSizePixels);
387
+ if (fuzzyNumbersEqual(prevSize, collapsedSizePercentage)) {
388
+ const localDelta = minSizePercentage - prevSize;
389
+ //DEBUG.push(` -> expand delta: ${localDelta}`);
390
+
391
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
392
+ delta = delta < 0 ? 0 - localDelta : localDelta;
393
+ //DEBUG.push(` -> delta: ${delta}`);
394
+ }
395
+ }
396
+ }
397
+ }
398
+
399
+ {
400
+ // Check if we should collapse a panel at its minimum size
401
+ const index = delta < 0 ? pivotIndices[0] : pivotIndices[1];
402
+ const constraints = panelConstraints[index];
403
+ //DEBUG.push(`edge case check 2: ${index}`);
404
+ //DEBUG.push(` -> collapsible? ${constraints.collapsible}`);
405
+ if (constraints.collapsible) {
406
+ const prevSize = prevLayout[index];
407
+ const {
408
+ collapsedSizePercentage,
409
+ minSizePercentage
410
+ } = computePercentagePanelConstraints(panelConstraints, index, groupSizePixels);
411
+ if (fuzzyNumbersEqual(prevSize, minSizePercentage)) {
412
+ const localDelta = prevSize - collapsedSizePercentage;
413
+ //DEBUG.push(` -> expand delta: ${localDelta}`);
414
+
415
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
416
+ delta = delta < 0 ? 0 - localDelta : localDelta;
417
+ //DEBUG.push(` -> delta: ${delta}`);
418
+ }
375
419
  }
420
+ }
376
421
  }
377
422
  }
378
- const safeSize = resizePanel({
379
- groupSizePixels,
380
- panelConstraints,
381
- panelIndex: pivotIndex,
382
- size: unsafeSize
383
- });
384
- if (fuzzyNumbersEqual(initialSize, safeSize)) {
385
- // If there's no room for the pivot panel to grow, we should ignore this change
386
- return nextLayout;
387
- } else {
388
- delta = delta < 0 ? initialSize - safeSize : safeSize - initialSize;
423
+ //DEBUG.push("");
424
+ }
425
+
426
+ {
427
+ // Pre-calculate max available delta in the opposite direction of our pivot.
428
+ // This will be the maximum amount we're allowed to expand/contract the panels in the primary direction.
429
+ // If this amount is less than the requested delta, adjust the requested delta.
430
+ // If this amount is greater than the requested delta, that's useful information too–
431
+ // as an expanding panel might change from collapsed to min size.
432
+
433
+ const increment = delta < 0 ? 1 : -1;
434
+ let index = delta < 0 ? pivotIndices[1] : pivotIndices[0];
435
+ let maxAvailableDelta = 0;
436
+
437
+ //DEBUG.push("pre calc...");
438
+ while (true) {
439
+ const prevSize = prevLayout[index];
440
+ const maxSafeSize = resizePanel({
441
+ groupSizePixels,
442
+ panelConstraints,
443
+ panelIndex: index,
444
+ size: 100
445
+ });
446
+ const delta = maxSafeSize - prevSize;
447
+ //DEBUG.push(` ${index}: ${prevSize} -> ${maxSafeSize}`);
448
+
449
+ maxAvailableDelta += delta;
450
+ index += increment;
451
+ if (index < 0 || index >= panelConstraints.length) {
452
+ break;
453
+ }
389
454
  }
455
+
456
+ //DEBUG.push(` -> max available delta: ${maxAvailableDelta}`);
457
+ const minAbsDelta = Math.min(Math.abs(delta), Math.abs(maxAvailableDelta));
458
+ delta = delta < 0 ? 0 - minAbsDelta : minAbsDelta;
459
+ //DEBUG.push(` -> adjusted delta: ${delta}`);
460
+ //DEBUG.push("");
390
461
  }
391
462
 
392
- // Delta added to a panel needs to be subtracted from other panels
393
- // within the constraints that those panels allow
394
463
  {
464
+ // Delta added to a panel needs to be subtracted from other panels (within the constraints that those panels allow).
465
+
395
466
  const pivotIndex = delta < 0 ? pivotIndices[0] : pivotIndices[1];
396
467
  let index = pivotIndex;
397
468
  while (index >= 0 && index < panelConstraints.length) {
398
469
  const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
399
470
  const prevSize = prevLayout[index];
400
471
  const unsafeSize = prevSize - deltaRemaining;
401
- let safeSize = resizePanel({
472
+ const safeSize = resizePanel({
402
473
  groupSizePixels,
403
474
  panelConstraints,
404
475
  panelIndex: index,
@@ -420,13 +491,18 @@ function adjustLayoutByDelta({
420
491
  }
421
492
  }
422
493
  }
494
+ //DEBUG.push(`after 1: ${nextLayout.join(", ")}`);
495
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
496
+ //DEBUG.push("");
423
497
 
424
498
  // If we were unable to resize any of the panels panels, return the previous state.
425
499
  // This will essentially bailout and ignore e.g. drags past a panel's boundaries
426
500
  if (fuzzyNumbersEqual(deltaApplied, 0)) {
501
+ //console.log(DEBUG.join("\n"));
427
502
  return prevLayout;
428
503
  }
429
504
  {
505
+ // Now distribute the applied delta to the panels in the other direction
430
506
  const pivotIndex = delta < 0 ? pivotIndices[1] : pivotIndices[0];
431
507
  const unsafeSize = prevLayout[pivotIndex] + deltaApplied;
432
508
  const safeSize = resizePanel({
@@ -466,29 +542,21 @@ function adjustLayoutByDelta({
466
542
  index++;
467
543
  }
468
544
  }
469
-
470
- // If we can't redistribute, this layout is invalid;
471
- // There may be an incremental layout that is valid though
472
- if (!fuzzyNumbersEqual(deltaRemaining, 0)) {
473
- try {
474
- return adjustLayoutByDelta({
475
- delta: delta < 0 ? delta + 1 : delta - 1,
476
- groupSizePixels,
477
- layout: prevLayout,
478
- panelConstraints,
479
- pivotIndices,
480
- trigger
481
- });
482
- } catch (error) {
483
- if (error instanceof RangeError) {
484
- console.error(`Could not apply delta ${delta} to layout`);
485
- return prevLayout;
486
- }
487
- } finally {
488
- }
489
- }
490
545
  }
491
546
  }
547
+ //DEBUG.push(`after 2: ${nextLayout.join(", ")}`);
548
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
549
+ //DEBUG.push("");
550
+
551
+ const totalSize = nextLayout.reduce((total, size) => size + total, 0);
552
+ deltaApplied = 100 - totalSize;
553
+ //DEBUG.push(`total size: ${totalSize}`);
554
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
555
+ //console.log(DEBUG.join("\n"));
556
+
557
+ if (!fuzzyNumbersEqual(totalSize, 100)) {
558
+ return prevLayout;
559
+ }
492
560
  return nextLayout;
493
561
  }
494
562
 
@@ -682,15 +750,10 @@ function useWindowSplitterPanelGroupBehavior({
682
750
  }, [groupId, layout, panelDataArray]);
683
751
  useEffect(() => {
684
752
  const {
685
- direction,
686
753
  panelDataArray
687
754
  } = committedValuesRef.current;
688
755
  const groupElement = getPanelGroupElement(groupId);
689
756
  assert(groupElement != null, `No group found for id "${groupId}"`);
690
- const {
691
- height,
692
- width
693
- } = groupElement.getBoundingClientRect();
694
757
  const handles = getResizeHandleElementsForGroup(groupId);
695
758
  const cleanupFunctions = handles.map(handle => {
696
759
  const handleId = handle.getAttribute("data-panel-resize-handle-id");
@@ -710,21 +773,19 @@ function useWindowSplitterPanelGroupBehavior({
710
773
  if (index >= 0) {
711
774
  const panelData = panelDataArray[index];
712
775
  const size = layout[index];
713
- if (size != null) {
714
- var _getPercentageSizeFro;
776
+ if (size != null && panelData.constraints.collapsible) {
777
+ var _getPercentageSizeFro, _getPercentageSizeFro2;
715
778
  const groupSizePixels = getAvailableGroupSizePixels(groupId);
716
- const minSize = (_getPercentageSizeFro = getPercentageSizeFromMixedSizes({
779
+ const collapsedSize = (_getPercentageSizeFro = getPercentageSizeFromMixedSizes({
780
+ sizePercentage: panelData.constraints.collapsedSizePercentage,
781
+ sizePixels: panelData.constraints.collapsedSizePixels
782
+ }, groupSizePixels)) !== null && _getPercentageSizeFro !== void 0 ? _getPercentageSizeFro : 0;
783
+ const minSize = (_getPercentageSizeFro2 = getPercentageSizeFromMixedSizes({
717
784
  sizePercentage: panelData.constraints.minSizePercentage,
718
785
  sizePixels: panelData.constraints.minSizePixels
719
- }, groupSizePixels)) !== null && _getPercentageSizeFro !== void 0 ? _getPercentageSizeFro : 0;
720
- let delta = 0;
721
- if (size.toPrecision(PRECISION) <= minSize.toPrecision(PRECISION)) {
722
- delta = direction === "horizontal" ? width : height;
723
- } else {
724
- delta = -(direction === "horizontal" ? width : height);
725
- }
786
+ }, groupSizePixels)) !== null && _getPercentageSizeFro2 !== void 0 ? _getPercentageSizeFro2 : 0;
726
787
  const nextLayout = adjustLayoutByDelta({
727
- delta,
788
+ delta: fuzzyNumbersEqual(size, collapsedSize) ? minSize - collapsedSize : collapsedSize - size,
728
789
  groupSizePixels,
729
790
  layout,
730
791
  panelConstraints: panelDataArray.map(panelData => panelData.constraints),
@@ -1185,6 +1246,7 @@ function PanelGroupWithForwardedRef({
1185
1246
  autoSaveId,
1186
1247
  children,
1187
1248
  className: classNameFromProps = "",
1249
+ dataAttributes,
1188
1250
  direction,
1189
1251
  forwardedRef,
1190
1252
  id: idFromProps,
@@ -1202,6 +1264,7 @@ function PanelGroupWithForwardedRef({
1202
1264
  const panelIdToLastNotifiedMixedSizesMapRef = useRef({});
1203
1265
  const panelSizeBeforeCollapseRef = useRef(new Map());
1204
1266
  const prevDeltaRef = useRef(0);
1267
+ const [imperativeApiQueue, setImperativeApiQueue] = useState([]);
1205
1268
  const committedValuesRef = useRef({
1206
1269
  direction,
1207
1270
  dragState,
@@ -1310,8 +1373,12 @@ function PanelGroupWithForwardedRef({
1310
1373
  }
1311
1374
  const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
1312
1375
  if (groupSizePixels <= 0) {
1313
- // Wait until the group has rendered a non-zero size before computing layout.
1314
- return;
1376
+ if (shouldMonitorPixelBasedConstraints(panelDataArray.map(({
1377
+ constraints
1378
+ }) => constraints))) {
1379
+ // Wait until the group has rendered a non-zero size before computing layout.
1380
+ return;
1381
+ }
1315
1382
  }
1316
1383
  if (unsafeLayout == null) {
1317
1384
  unsafeLayout = calculateUnsafeDefaultLayout({
@@ -1389,6 +1456,17 @@ function PanelGroupWithForwardedRef({
1389
1456
  onLayout,
1390
1457
  panelDataArray
1391
1458
  } = committedValuesRef.current;
1459
+
1460
+ // See issues/211
1461
+ if (panelDataArray.find(({
1462
+ id
1463
+ }) => id === panelData.id) == null) {
1464
+ setImperativeApiQueue(prev => [...prev, {
1465
+ panelData,
1466
+ type: "collapse"
1467
+ }]);
1468
+ return;
1469
+ }
1392
1470
  if (panelData.constraints.collapsible) {
1393
1471
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1394
1472
  const {
@@ -1432,6 +1510,17 @@ function PanelGroupWithForwardedRef({
1432
1510
  onLayout,
1433
1511
  panelDataArray
1434
1512
  } = committedValuesRef.current;
1513
+
1514
+ // See issues/211
1515
+ if (panelDataArray.find(({
1516
+ id
1517
+ }) => id === panelData.id) == null) {
1518
+ setImperativeApiQueue(prev => [...prev, {
1519
+ panelData,
1520
+ type: "expand"
1521
+ }]);
1522
+ return;
1523
+ }
1435
1524
  if (panelData.constraints.collapsible) {
1436
1525
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1437
1526
  const {
@@ -1627,6 +1716,18 @@ function PanelGroupWithForwardedRef({
1627
1716
  onLayout,
1628
1717
  panelDataArray
1629
1718
  } = committedValuesRef.current;
1719
+
1720
+ // See issues/211
1721
+ if (panelDataArray.find(({
1722
+ id
1723
+ }) => id === panelData.id) == null) {
1724
+ setImperativeApiQueue(prev => [...prev, {
1725
+ panelData,
1726
+ mixedSizes,
1727
+ type: "resize"
1728
+ }]);
1729
+ return;
1730
+ }
1630
1731
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1631
1732
  const {
1632
1733
  groupSizePixels,
@@ -1684,6 +1785,31 @@ function PanelGroupWithForwardedRef({
1684
1785
  return panelDataArray;
1685
1786
  });
1686
1787
  }, []);
1788
+
1789
+ // Handle imperative API calls that were made before panels were registered
1790
+ useIsomorphicLayoutEffect(() => {
1791
+ const queue = imperativeApiQueue;
1792
+ while (queue.length > 0) {
1793
+ const current = queue.shift();
1794
+ switch (current.type) {
1795
+ case "collapse":
1796
+ {
1797
+ collapsePanel(current.panelData);
1798
+ break;
1799
+ }
1800
+ case "expand":
1801
+ {
1802
+ expandPanel(current.panelData);
1803
+ break;
1804
+ }
1805
+ case "resize":
1806
+ {
1807
+ resizePanel(current.panelData, current.mixedSizes);
1808
+ break;
1809
+ }
1810
+ }
1811
+ }
1812
+ }, [collapsePanel, expandPanel, imperativeApiQueue, layout, panelDataArray, resizePanel]);
1687
1813
  const context = useMemo(() => ({
1688
1814
  collapsePanel,
1689
1815
  direction,
@@ -1717,6 +1843,7 @@ function PanelGroupWithForwardedRef({
1717
1843
  ...style,
1718
1844
  ...styleFromProps
1719
1845
  },
1846
+ ...dataAttributes,
1720
1847
  // CSS selectors
1721
1848
  "data-panel-group": "",
1722
1849
  "data-panel-group-direction": direction,
@@ -1804,6 +1931,7 @@ function useWindowSplitterResizeHandlerBehavior({
1804
1931
  function PanelResizeHandle({
1805
1932
  children = null,
1806
1933
  className: classNameFromProps = "",
1934
+ dataAttributes,
1807
1935
  disabled = false,
1808
1936
  id: idFromProps = null,
1809
1937
  onDragging,
@@ -1926,6 +2054,7 @@ function PanelResizeHandle({
1926
2054
  ...styleFromProps
1927
2055
  },
1928
2056
  tabIndex: 0,
2057
+ ...dataAttributes,
1929
2058
  // CSS selectors
1930
2059
  "data-panel-group-direction": direction,
1931
2060
  "data-panel-group-id": groupId,