react-resizable-panels 0.0.57 → 0.0.58

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 (29) hide show
  1. package/CHANGELOG.md +7 -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 +129 -57
  7. package/dist/react-resizable-panels.browser.development.cjs.js +129 -57
  8. package/dist/react-resizable-panels.browser.development.esm.js +129 -57
  9. package/dist/react-resizable-panels.browser.esm.js +129 -57
  10. package/dist/react-resizable-panels.cjs.js +129 -57
  11. package/dist/react-resizable-panels.cjs.js.map +1 -1
  12. package/dist/react-resizable-panels.development.cjs.js +129 -57
  13. package/dist/react-resizable-panels.development.esm.js +129 -57
  14. package/dist/react-resizable-panels.development.node.cjs.js +129 -57
  15. package/dist/react-resizable-panels.development.node.esm.js +129 -57
  16. package/dist/react-resizable-panels.esm.js +129 -57
  17. package/dist/react-resizable-panels.esm.js.map +1 -1
  18. package/dist/react-resizable-panels.node.cjs.js +129 -57
  19. package/dist/react-resizable-panels.node.esm.js +129 -57
  20. package/package.json +1 -1
  21. package/src/Panel.ts +8 -2
  22. package/src/PanelGroup.ts +5 -1
  23. package/src/PanelResizeHandle.ts +5 -0
  24. package/src/types.ts +4 -0
  25. package/src/utils/adjustLayoutByDelta.test.ts +238 -8
  26. package/src/utils/adjustLayoutByDelta.ts +122 -72
  27. package/src/utils/resizePanel.test.ts +61 -1
  28. package/src/utils/resizePanel.ts +7 -1
  29. package/src/utils/validatePanelGroupLayout.test.ts +36 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.58
4
+
5
+ - Change group layout to more thoroughly distribute resize delta to support more flexible group size configurations.
6
+ - Add data attribute support to `Panel`, `PanelGroup`, and `PanelResizeHandle`.
7
+ - Update API documentation to reflect changed imperative API method names.
8
+ - `PanelOnResize` TypeScript def updated to reflect that previous size param is `undefined` the first time it is called.
9
+
3
10
  ## 0.0.57
4
11
 
5
12
  - [#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,
@@ -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
 
@@ -1185,6 +1253,7 @@ function PanelGroupWithForwardedRef({
1185
1253
  autoSaveId,
1186
1254
  children,
1187
1255
  className: classNameFromProps = "",
1256
+ dataAttributes,
1188
1257
  direction,
1189
1258
  forwardedRef,
1190
1259
  id: idFromProps,
@@ -1717,6 +1786,7 @@ function PanelGroupWithForwardedRef({
1717
1786
  ...style,
1718
1787
  ...styleFromProps
1719
1788
  },
1789
+ ...dataAttributes,
1720
1790
  // CSS selectors
1721
1791
  "data-panel-group": "",
1722
1792
  "data-panel-group-direction": direction,
@@ -1804,6 +1874,7 @@ function useWindowSplitterResizeHandlerBehavior({
1804
1874
  function PanelResizeHandle({
1805
1875
  children = null,
1806
1876
  className: classNameFromProps = "",
1877
+ dataAttributes,
1807
1878
  disabled = false,
1808
1879
  id: idFromProps = null,
1809
1880
  onDragging,
@@ -1926,6 +1997,7 @@ function PanelResizeHandle({
1926
1997
  ...styleFromProps
1927
1998
  },
1928
1999
  tabIndex: 0,
2000
+ ...dataAttributes,
1929
2001
  // CSS selectors
1930
2002
  "data-panel-group-direction": direction,
1931
2003
  "data-panel-group-id": groupId,
@@ -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,
@@ -187,6 +188,7 @@ function PanelWithForwardedRef({
187
188
  ...style,
188
189
  ...styleFromProps
189
190
  },
191
+ ...dataAttributes,
190
192
  // CSS selectors
191
193
  "data-panel": "",
192
194
  "data-panel-id": panelId,
@@ -324,7 +326,13 @@ function resizePanel({
324
326
  if (minSizePercentage != null) {
325
327
  if (fuzzyCompareNumbers(size, minSizePercentage) < 0) {
326
328
  if (collapsible) {
327
- size = collapsedSizePercentage;
329
+ // Collapsible panels should snap closed or open only once they cross the halfway point between collapsed and min size.
330
+ const halfwayPoint = (collapsedSizePercentage + minSizePercentage) / 2;
331
+ if (fuzzyCompareNumbers(size, halfwayPoint) < 0) {
332
+ size = collapsedSizePercentage;
333
+ } else {
334
+ size = minSizePercentage;
335
+ }
328
336
  } else {
329
337
  size = minSizePercentage;
330
338
  }
@@ -351,60 +359,123 @@ function adjustLayoutByDelta({
351
359
  const nextLayout = [...prevLayout];
352
360
  let deltaApplied = 0;
353
361
 
362
+ //const DEBUG = [];
363
+ //DEBUG.push(`adjustLayoutByDelta() ${prevLayout.join(", ")}`);
364
+ //DEBUG.push(` delta: ${delta}`);
365
+ //DEBUG.push(` pivotIndices: ${pivotIndices.join(", ")}`);
366
+ //DEBUG.push(` trigger: ${trigger}`);
367
+ //DEBUG.push("");
368
+
354
369
  // A resizing panel affects the panels before or after it.
355
370
  //
356
- // A negative delta means the panel immediately after the resizer should grow/expand by decreasing its offset.
371
+ // A negative delta means the panel(s) immediately after the resize handle should grow/expand by decreasing its offset.
357
372
  // Other panels may also need to shrink/contract (and shift) to make room, depending on the min weights.
358
373
  //
359
- // A positive delta means the panel immediately before the resizer should "expand".
360
- // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resizer.
374
+ // A positive delta means the panel(s) immediately before the resize handle should "expand".
375
+ // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resize handle.
361
376
 
362
- // First, check the panel we're pivoting around;
363
- // We should only expand or contract by as much as its constraints allow
364
377
  {
365
- const pivotIndex = delta < 0 ? pivotIndices[1] : pivotIndices[0];
366
- const initialSize = nextLayout[pivotIndex];
367
- const {
368
- collapsible
369
- } = panelConstraints[pivotIndex];
370
- const {
371
- collapsedSizePercentage,
372
- minSizePercentage
373
- } = computePercentagePanelConstraints(panelConstraints, pivotIndex, groupSizePixels);
374
- const isCollapsed = collapsible && fuzzyNumbersEqual(initialSize, collapsedSizePercentage);
375
- let unsafeSize = initialSize + Math.abs(delta);
376
- if (isCollapsed) {
377
- switch (trigger) {
378
- case "keyboard":
379
- if (minSizePercentage > unsafeSize) {
380
- unsafeSize = minSizePercentage;
378
+ // If this is a resize triggered by a keyboard event, our logic for expanding/collapsing is different.
379
+ // We no longer check the halfway threshold because this may prevent the panel from expanding at all.
380
+ if (trigger === "keyboard") {
381
+ {
382
+ // Check if we should expand a collapsed panel
383
+ const index = delta < 0 ? pivotIndices[1] : pivotIndices[0];
384
+ const constraints = panelConstraints[index];
385
+ //DEBUG.push(`edge case check 1: ${index}`);
386
+ //DEBUG.push(` -> collapsible? ${constraints.collapsible}`);
387
+ if (constraints.collapsible) {
388
+ const prevSize = prevLayout[index];
389
+ const {
390
+ collapsedSizePercentage,
391
+ minSizePercentage
392
+ } = computePercentagePanelConstraints(panelConstraints, index, groupSizePixels);
393
+ if (fuzzyNumbersEqual(prevSize, collapsedSizePercentage)) {
394
+ const localDelta = minSizePercentage - prevSize;
395
+ //DEBUG.push(` -> expand delta: ${localDelta}`);
396
+
397
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
398
+ delta = delta < 0 ? 0 - localDelta : localDelta;
399
+ //DEBUG.push(` -> delta: ${delta}`);
400
+ }
381
401
  }
402
+ }
403
+ }
404
+
405
+ {
406
+ // Check if we should collapse a panel at its minimum size
407
+ const index = delta < 0 ? pivotIndices[0] : pivotIndices[1];
408
+ const constraints = panelConstraints[index];
409
+ //DEBUG.push(`edge case check 2: ${index}`);
410
+ //DEBUG.push(` -> collapsible? ${constraints.collapsible}`);
411
+ if (constraints.collapsible) {
412
+ const prevSize = prevLayout[index];
413
+ const {
414
+ collapsedSizePercentage,
415
+ minSizePercentage
416
+ } = computePercentagePanelConstraints(panelConstraints, index, groupSizePixels);
417
+ if (fuzzyNumbersEqual(prevSize, minSizePercentage)) {
418
+ const localDelta = prevSize - collapsedSizePercentage;
419
+ //DEBUG.push(` -> expand delta: ${localDelta}`);
420
+
421
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
422
+ delta = delta < 0 ? 0 - localDelta : localDelta;
423
+ //DEBUG.push(` -> delta: ${delta}`);
424
+ }
425
+ }
426
+ }
382
427
  }
383
428
  }
384
- const safeSize = resizePanel({
385
- groupSizePixels,
386
- panelConstraints,
387
- panelIndex: pivotIndex,
388
- size: unsafeSize
389
- });
390
- if (fuzzyNumbersEqual(initialSize, safeSize)) {
391
- // If there's no room for the pivot panel to grow, we should ignore this change
392
- return nextLayout;
393
- } else {
394
- delta = delta < 0 ? initialSize - safeSize : safeSize - initialSize;
429
+ //DEBUG.push("");
430
+ }
431
+
432
+ {
433
+ // Pre-calculate max available delta in the opposite direction of our pivot.
434
+ // This will be the maximum amount we're allowed to expand/contract the panels in the primary direction.
435
+ // If this amount is less than the requested delta, adjust the requested delta.
436
+ // If this amount is greater than the requested delta, that's useful information too–
437
+ // as an expanding panel might change from collapsed to min size.
438
+
439
+ const increment = delta < 0 ? 1 : -1;
440
+ let index = delta < 0 ? pivotIndices[1] : pivotIndices[0];
441
+ let maxAvailableDelta = 0;
442
+
443
+ //DEBUG.push("pre calc...");
444
+ while (true) {
445
+ const prevSize = prevLayout[index];
446
+ const maxSafeSize = resizePanel({
447
+ groupSizePixels,
448
+ panelConstraints,
449
+ panelIndex: index,
450
+ size: 100
451
+ });
452
+ const delta = maxSafeSize - prevSize;
453
+ //DEBUG.push(` ${index}: ${prevSize} -> ${maxSafeSize}`);
454
+
455
+ maxAvailableDelta += delta;
456
+ index += increment;
457
+ if (index < 0 || index >= panelConstraints.length) {
458
+ break;
459
+ }
395
460
  }
461
+
462
+ //DEBUG.push(` -> max available delta: ${maxAvailableDelta}`);
463
+ const minAbsDelta = Math.min(Math.abs(delta), Math.abs(maxAvailableDelta));
464
+ delta = delta < 0 ? 0 - minAbsDelta : minAbsDelta;
465
+ //DEBUG.push(` -> adjusted delta: ${delta}`);
466
+ //DEBUG.push("");
396
467
  }
397
468
 
398
- // Delta added to a panel needs to be subtracted from other panels
399
- // within the constraints that those panels allow
400
469
  {
470
+ // Delta added to a panel needs to be subtracted from other panels (within the constraints that those panels allow).
471
+
401
472
  const pivotIndex = delta < 0 ? pivotIndices[0] : pivotIndices[1];
402
473
  let index = pivotIndex;
403
474
  while (index >= 0 && index < panelConstraints.length) {
404
475
  const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
405
476
  const prevSize = prevLayout[index];
406
477
  const unsafeSize = prevSize - deltaRemaining;
407
- let safeSize = resizePanel({
478
+ const safeSize = resizePanel({
408
479
  groupSizePixels,
409
480
  panelConstraints,
410
481
  panelIndex: index,
@@ -426,13 +497,18 @@ function adjustLayoutByDelta({
426
497
  }
427
498
  }
428
499
  }
500
+ //DEBUG.push(`after 1: ${nextLayout.join(", ")}`);
501
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
502
+ //DEBUG.push("");
429
503
 
430
504
  // If we were unable to resize any of the panels panels, return the previous state.
431
505
  // This will essentially bailout and ignore e.g. drags past a panel's boundaries
432
506
  if (fuzzyNumbersEqual(deltaApplied, 0)) {
507
+ //console.log(DEBUG.join("\n"));
433
508
  return prevLayout;
434
509
  }
435
510
  {
511
+ // Now distribute the applied delta to the panels in the other direction
436
512
  const pivotIndex = delta < 0 ? pivotIndices[1] : pivotIndices[0];
437
513
  const unsafeSize = prevLayout[pivotIndex] + deltaApplied;
438
514
  const safeSize = resizePanel({
@@ -472,29 +548,21 @@ function adjustLayoutByDelta({
472
548
  index++;
473
549
  }
474
550
  }
475
-
476
- // If we can't redistribute, this layout is invalid;
477
- // There may be an incremental layout that is valid though
478
- if (!fuzzyNumbersEqual(deltaRemaining, 0)) {
479
- try {
480
- return adjustLayoutByDelta({
481
- delta: delta < 0 ? delta + 1 : delta - 1,
482
- groupSizePixels,
483
- layout: prevLayout,
484
- panelConstraints,
485
- pivotIndices,
486
- trigger
487
- });
488
- } catch (error) {
489
- if (error instanceof RangeError) {
490
- console.error(`Could not apply delta ${delta} to layout`);
491
- return prevLayout;
492
- }
493
- } finally {
494
- }
495
- }
496
551
  }
497
552
  }
553
+ //DEBUG.push(`after 2: ${nextLayout.join(", ")}`);
554
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
555
+ //DEBUG.push("");
556
+
557
+ const totalSize = nextLayout.reduce((total, size) => size + total, 0);
558
+ deltaApplied = 100 - totalSize;
559
+ //DEBUG.push(`total size: ${totalSize}`);
560
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
561
+ //console.log(DEBUG.join("\n"));
562
+
563
+ if (!fuzzyNumbersEqual(totalSize, 100)) {
564
+ return prevLayout;
565
+ }
498
566
  return nextLayout;
499
567
  }
500
568
 
@@ -1278,6 +1346,7 @@ function PanelGroupWithForwardedRef({
1278
1346
  autoSaveId,
1279
1347
  children,
1280
1348
  className: classNameFromProps = "",
1349
+ dataAttributes,
1281
1350
  direction,
1282
1351
  forwardedRef,
1283
1352
  id: idFromProps,
@@ -1852,6 +1921,7 @@ function PanelGroupWithForwardedRef({
1852
1921
  ...style,
1853
1922
  ...styleFromProps
1854
1923
  },
1924
+ ...dataAttributes,
1855
1925
  // CSS selectors
1856
1926
  "data-panel-group": "",
1857
1927
  "data-panel-group-direction": direction,
@@ -1939,6 +2009,7 @@ function useWindowSplitterResizeHandlerBehavior({
1939
2009
  function PanelResizeHandle({
1940
2010
  children = null,
1941
2011
  className: classNameFromProps = "",
2012
+ dataAttributes,
1942
2013
  disabled = false,
1943
2014
  id: idFromProps = null,
1944
2015
  onDragging,
@@ -2061,6 +2132,7 @@ function PanelResizeHandle({
2061
2132
  ...styleFromProps
2062
2133
  },
2063
2134
  tabIndex: 0,
2135
+ ...dataAttributes,
2064
2136
  // CSS selectors
2065
2137
  "data-panel-group-direction": direction,
2066
2138
  "data-panel-group-id": groupId,