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
@@ -1,6 +1,5 @@
1
1
  import { isDevelopment } from "#is-development";
2
2
  import { PanelData } from "../Panel";
3
- import { PRECISION } from "../constants";
4
3
  import { Direction } from "../types";
5
4
  import { adjustLayoutByDelta } from "../utils/adjustLayoutByDelta";
6
5
  import { assert } from "../utils/assert";
@@ -12,6 +11,7 @@ import { getPanelGroupElement } from "../utils/dom/getPanelGroupElement";
12
11
  import { getResizeHandleElementsForGroup } from "../utils/dom/getResizeHandleElementsForGroup";
13
12
  import { getResizeHandlePanelIds } from "../utils/dom/getResizeHandlePanelIds";
14
13
  import { getPercentageSizeFromMixedSizes } from "../utils/getPercentageSizeFromMixedSizes";
14
+ import { fuzzyNumbersEqual } from "../utils/numbers/fuzzyNumbersEqual";
15
15
  import { RefObject, useEffect, useRef } from "../vendor/react";
16
16
  import useIsomorphicLayoutEffect from "./useIsomorphicEffect";
17
17
 
@@ -95,13 +95,11 @@ export function useWindowSplitterPanelGroupBehavior({
95
95
  }, [groupId, layout, panelDataArray]);
96
96
 
97
97
  useEffect(() => {
98
- const { direction, panelDataArray } = committedValuesRef.current!;
98
+ const { panelDataArray } = committedValuesRef.current!;
99
99
 
100
100
  const groupElement = getPanelGroupElement(groupId);
101
101
  assert(groupElement != null, `No group found for id "${groupId}"`);
102
102
 
103
- const { height, width } = groupElement.getBoundingClientRect();
104
-
105
103
  const handles = getResizeHandleElementsForGroup(groupId);
106
104
  const cleanupFunctions = handles.map((handle) => {
107
105
  const handleId = handle.getAttribute("data-panel-resize-handle-id")!;
@@ -130,9 +128,18 @@ export function useWindowSplitterPanelGroupBehavior({
130
128
  if (index >= 0) {
131
129
  const panelData = panelDataArray[index];
132
130
  const size = layout[index];
133
- if (size != null) {
131
+ if (size != null && panelData.constraints.collapsible) {
134
132
  const groupSizePixels = getAvailableGroupSizePixels(groupId);
135
133
 
134
+ const collapsedSize =
135
+ getPercentageSizeFromMixedSizes(
136
+ {
137
+ sizePercentage:
138
+ panelData.constraints.collapsedSizePercentage,
139
+ sizePixels: panelData.constraints.collapsedSizePixels,
140
+ },
141
+ groupSizePixels
142
+ ) ?? 0;
136
143
  const minSize =
137
144
  getPercentageSizeFromMixedSizes(
138
145
  {
@@ -142,17 +149,10 @@ export function useWindowSplitterPanelGroupBehavior({
142
149
  groupSizePixels
143
150
  ) ?? 0;
144
151
 
145
- let delta = 0;
146
- if (
147
- size.toPrecision(PRECISION) <= minSize.toPrecision(PRECISION)
148
- ) {
149
- delta = direction === "horizontal" ? width : height;
150
- } else {
151
- delta = -(direction === "horizontal" ? width : height);
152
- }
153
-
154
152
  const nextLayout = adjustLayoutByDelta({
155
- delta,
153
+ delta: fuzzyNumbersEqual(size, collapsedSize)
154
+ ? minSize - collapsedSize
155
+ : collapsedSize - size,
156
156
  groupSizePixels,
157
157
  layout,
158
158
  panelConstraints: panelDataArray.map(
package/src/types.ts CHANGED
@@ -7,3 +7,7 @@ export type MixedSizes = {
7
7
 
8
8
  export type ResizeEvent = KeyboardEvent | MouseEvent | TouchEvent;
9
9
  export type ResizeHandler = (event: ResizeEvent) => void;
10
+
11
+ export type DataAttributes = {
12
+ [attribute: string]: string | number | boolean | undefined;
13
+ };
@@ -82,7 +82,7 @@ describe("adjustLayoutByDelta", () => {
82
82
  it("[1++,2]", () => {
83
83
  expect(
84
84
  adjustLayoutByDelta({
85
- delta: 30,
85
+ delta: 40,
86
86
  groupSizePixels: 100,
87
87
  layout: [50, 50],
88
88
  panelConstraints: [
@@ -122,7 +122,7 @@ describe("adjustLayoutByDelta", () => {
122
122
  });
123
123
 
124
124
  // Edge case
125
- // Expanding from a collapsed state to less than the min size via keyboard should snap to min size
125
+ // Keyboard interactions should always expand a collapsed panel
126
126
  it("[1++,2]", () => {
127
127
  expect(
128
128
  adjustLayoutByDelta({
@@ -143,6 +143,27 @@ describe("adjustLayoutByDelta", () => {
143
143
  ).toEqual([25, 75]);
144
144
  });
145
145
 
146
+ // Edge case
147
+ // Keyboard interactions should always collapse a collapsible panel once it's at the minimum size
148
+ it("[1++,2]", () => {
149
+ expect(
150
+ adjustLayoutByDelta({
151
+ delta: 5,
152
+ groupSizePixels: NaN,
153
+ layout: [75, 25],
154
+ panelConstraints: [
155
+ {},
156
+ {
157
+ collapsible: true,
158
+ minSizePercentage: 25,
159
+ },
160
+ ],
161
+ pivotIndices: [0, 1],
162
+ trigger: "keyboard",
163
+ })
164
+ ).toEqual([100, 0]);
165
+ });
166
+
146
167
  // Edge case
147
168
  // Expanding from a collapsed state to less than the min size via imperative API should do nothing
148
169
  it("[1++,2]", () => {
@@ -174,6 +195,7 @@ describe("adjustLayoutByDelta", () => {
174
195
  // Expanding from a collapsed state to less than the min size via keyboard should snap to min size
175
196
  it("[1++,2]", () => {
176
197
  // collapsed 4%, min size 6%, max size 15%
198
+
177
199
  expect(
178
200
  adjustLayoutByDelta({
179
201
  delta: 1,
@@ -395,6 +417,24 @@ describe("adjustLayoutByDelta", () => {
395
417
  pivotIndices: [0, 1],
396
418
  trigger: "imperative-api",
397
419
  })
420
+ ).toEqual([25, 75]);
421
+
422
+ expect(
423
+ adjustLayoutByDelta({
424
+ delta: -36,
425
+ groupSizePixels: 100,
426
+ layout: [50, 50],
427
+ panelConstraints: [
428
+ {
429
+ collapsedSizePercentage: 5,
430
+ collapsible: true,
431
+ minSizePercentage: 25,
432
+ },
433
+ {},
434
+ ],
435
+ pivotIndices: [0, 1],
436
+ trigger: "imperative-api",
437
+ })
398
438
  ).toEqual([5, 95]);
399
439
  });
400
440
 
@@ -420,6 +460,50 @@ describe("adjustLayoutByDelta", () => {
420
460
  ).toEqual([25, 75]);
421
461
  });
422
462
 
463
+ // Edge case
464
+ // Keyboard interactions should always expand a collapsed panel
465
+ it("[1--,2]", () => {
466
+ expect(
467
+ adjustLayoutByDelta({
468
+ delta: -5,
469
+ groupSizePixels: NaN,
470
+ layout: [90, 10],
471
+ panelConstraints: [
472
+ {},
473
+ {
474
+ collapsedSizePercentage: 10,
475
+ collapsible: true,
476
+ minSizePercentage: 25,
477
+ },
478
+ ],
479
+ pivotIndices: [0, 1],
480
+ trigger: "keyboard",
481
+ })
482
+ ).toEqual([75, 25]);
483
+ });
484
+
485
+ // Edge case
486
+ // Keyboard interactions should always collapse a collapsible panel once it's at the minimum size
487
+ it("[1++,2]", () => {
488
+ expect(
489
+ adjustLayoutByDelta({
490
+ delta: -5,
491
+ groupSizePixels: NaN,
492
+ layout: [25, 75],
493
+ panelConstraints: [
494
+ {
495
+ collapsedSizePercentage: 10,
496
+ collapsible: true,
497
+ minSizePercentage: 25,
498
+ },
499
+ {},
500
+ ],
501
+ pivotIndices: [0, 1],
502
+ trigger: "keyboard",
503
+ })
504
+ ).toEqual([10, 90]);
505
+ });
506
+
423
507
  it("[1++,2,3]", () => {
424
508
  expect(
425
509
  adjustLayoutByDelta({
@@ -722,7 +806,7 @@ describe("adjustLayoutByDelta", () => {
722
806
  pivotIndices: [0, 1],
723
807
  trigger: "imperative-api",
724
808
  })
725
- ).toEqual([45, 50, 5]);
809
+ ).toEqual([25, 50, 25]);
726
810
  });
727
811
 
728
812
  it("[1,2++,3]", () => {
@@ -834,6 +918,21 @@ describe("adjustLayoutByDelta", () => {
834
918
  pivotIndices: [1, 2],
835
919
  trigger: "imperative-api",
836
920
  })
921
+ ).toEqual([25, 55, 20]);
922
+
923
+ expect(
924
+ adjustLayoutByDelta({
925
+ delta: 16,
926
+ groupSizePixels: 100,
927
+ layout: [25, 50, 25],
928
+ panelConstraints: [
929
+ {},
930
+ {},
931
+ { collapsible: true, minSizePercentage: 20 },
932
+ ],
933
+ pivotIndices: [1, 2],
934
+ trigger: "imperative-api",
935
+ })
837
936
  ).toEqual([25, 75, 0]);
838
937
  });
839
938
 
@@ -907,15 +1006,53 @@ describe("adjustLayoutByDelta", () => {
907
1006
  pivotIndices: [1, 2],
908
1007
  trigger: "imperative-api",
909
1008
  })
1009
+ ).toEqual([20, 20, 60]);
1010
+
1011
+ expect(
1012
+ adjustLayoutByDelta({
1013
+ delta: -40,
1014
+ groupSizePixels: 100,
1015
+ layout: [25, 50, 25],
1016
+ panelConstraints: [
1017
+ {},
1018
+ {
1019
+ collapsedSizePercentage: 5,
1020
+ collapsible: true,
1021
+ minSizePercentage: 20,
1022
+ },
1023
+ {},
1024
+ ],
1025
+ pivotIndices: [1, 2],
1026
+ trigger: "imperative-api",
1027
+ })
910
1028
  ).toEqual([25, 5, 70]);
911
1029
  });
912
1030
 
913
1031
  it("[1,2--,3]", () => {
914
1032
  expect(
915
1033
  adjustLayoutByDelta({
916
- delta: -60,
1034
+ delta: -10,
917
1035
  groupSizePixels: 100,
918
- layout: [25, 50, 25],
1036
+ layout: [25, 0, 75],
1037
+ panelConstraints: [
1038
+ {
1039
+ collapsedSizePercentage: 5,
1040
+ collapsible: true,
1041
+ minSizePercentage: 20,
1042
+ },
1043
+ {},
1044
+ {},
1045
+ ],
1046
+ pivotIndices: [1, 2],
1047
+ trigger: "imperative-api",
1048
+ })
1049
+ ).toEqual([20, 0, 80]);
1050
+
1051
+ expect(
1052
+ adjustLayoutByDelta({
1053
+ delta: -20,
1054
+ groupSizePixels: 100,
1055
+ layout: [25, 0, 75],
919
1056
  panelConstraints: [
920
1057
  {
921
1058
  collapsedSizePercentage: 5,
@@ -1041,6 +1178,34 @@ describe("adjustLayoutByDelta", () => {
1041
1178
  pivotIndices: [0, 1],
1042
1179
  trigger: "imperative-api",
1043
1180
  })
1181
+ ).toEqual([35, 20, 20, 25]);
1182
+
1183
+ expect(
1184
+ adjustLayoutByDelta({
1185
+ delta: 15,
1186
+ groupSizePixels: 100,
1187
+ layout: [25, 25, 25, 25],
1188
+ panelConstraints: [
1189
+ {},
1190
+ {
1191
+ collapsedSizePercentage: 5,
1192
+ collapsible: true,
1193
+ minSizePercentage: 20,
1194
+ },
1195
+ {
1196
+ collapsedSizePercentage: 5,
1197
+ collapsible: true,
1198
+ minSizePercentage: 20,
1199
+ },
1200
+ {
1201
+ collapsedSizePercentage: 5,
1202
+ collapsible: true,
1203
+ minSizePercentage: 20,
1204
+ },
1205
+ ],
1206
+ pivotIndices: [0, 1],
1207
+ trigger: "imperative-api",
1208
+ })
1044
1209
  ).toEqual([45, 5, 25, 25]);
1045
1210
  });
1046
1211
 
@@ -1153,7 +1318,7 @@ describe("adjustLayoutByDelta", () => {
1153
1318
  pivotIndices: [0, 1],
1154
1319
  trigger: "imperative-api",
1155
1320
  })
1156
- ).toEqual([15, 35, 25, 25]);
1321
+ ).toEqual([0, 35, 40, 25]);
1157
1322
  });
1158
1323
 
1159
1324
  it("[1--,2,3,4]", () => {
@@ -1175,6 +1340,26 @@ describe("adjustLayoutByDelta", () => {
1175
1340
  pivotIndices: [0, 1],
1176
1341
  trigger: "imperative-api",
1177
1342
  })
1343
+ ).toEqual([20, 30, 25, 25]);
1344
+
1345
+ expect(
1346
+ adjustLayoutByDelta({
1347
+ delta: -15,
1348
+ groupSizePixels: 100,
1349
+ layout: [25, 25, 25, 25],
1350
+ panelConstraints: [
1351
+ {
1352
+ collapsedSizePercentage: 5,
1353
+ collapsible: true,
1354
+ minSizePercentage: 20,
1355
+ },
1356
+ {},
1357
+ {},
1358
+ {},
1359
+ ],
1360
+ pivotIndices: [0, 1],
1361
+ trigger: "imperative-api",
1362
+ })
1178
1363
  ).toEqual([5, 45, 25, 25]);
1179
1364
  });
1180
1365
 
@@ -1197,6 +1382,26 @@ describe("adjustLayoutByDelta", () => {
1197
1382
  pivotIndices: [0, 1],
1198
1383
  trigger: "imperative-api",
1199
1384
  })
1385
+ ).toEqual([20, 30, 25, 25]);
1386
+
1387
+ expect(
1388
+ adjustLayoutByDelta({
1389
+ delta: -15,
1390
+ groupSizePixels: 100,
1391
+ layout: [25, 25, 25, 25],
1392
+ panelConstraints: [
1393
+ {
1394
+ collapsedSizePercentage: 5,
1395
+ collapsible: true,
1396
+ minSizePercentage: 20,
1397
+ },
1398
+ { maxSizePercentage: 35 },
1399
+ {},
1400
+ {},
1401
+ ],
1402
+ pivotIndices: [0, 1],
1403
+ trigger: "imperative-api",
1404
+ })
1200
1405
  ).toEqual([5, 35, 35, 25]);
1201
1406
  });
1202
1407
 
@@ -1256,7 +1461,9 @@ describe("adjustLayoutByDelta", () => {
1256
1461
  ).toEqual([20, 30, 25, 25]);
1257
1462
  });
1258
1463
 
1464
+ // Edge case (issues/210)
1259
1465
  it("[1--,2,3,4]", () => {
1466
+ // If the size doesn't drop below the halfway point, the panel should not collapse
1260
1467
  expect(
1261
1468
  adjustLayoutByDelta({
1262
1469
  delta: -10,
@@ -1275,6 +1482,29 @@ describe("adjustLayoutByDelta", () => {
1275
1482
  pivotIndices: [0, 1],
1276
1483
  trigger: "imperative-api",
1277
1484
  })
1485
+ ).toEqual([20, 30, 25, 25]);
1486
+
1487
+ // If the size drops below the halfway point, the panel should collapse
1488
+ // In this case it needs to add sizes to multiple other panels in order to collapse
1489
+ // because the nearest neighbor panel's max size constraints won't allow it to expand to cover all of the difference
1490
+ expect(
1491
+ adjustLayoutByDelta({
1492
+ delta: -20,
1493
+ groupSizePixels: 100,
1494
+ layout: [25, 25, 25, 25],
1495
+ panelConstraints: [
1496
+ {
1497
+ collapsedSizePercentage: 5,
1498
+ collapsible: true,
1499
+ minSizePercentage: 20,
1500
+ },
1501
+ { maxSizePercentage: 35 },
1502
+ { maxSizePercentage: 35 },
1503
+ { maxSizePercentage: 35 },
1504
+ ],
1505
+ pivotIndices: [0, 1],
1506
+ trigger: "imperative-api",
1507
+ })
1278
1508
  ).toEqual([5, 35, 35, 25]);
1279
1509
  });
1280
1510
 
@@ -1327,7 +1557,7 @@ describe("adjustLayoutByDelta", () => {
1327
1557
  pivotIndices: [1, 2],
1328
1558
  trigger: "imperative-api",
1329
1559
  })
1330
- ).toEqual([25, 35, 15, 25]);
1560
+ ).toEqual([65, 35, 0, 0]);
1331
1561
  });
1332
1562
 
1333
1563
  it("[1,2++,3,4]", () => {
@@ -1581,7 +1811,7 @@ describe("adjustLayoutByDelta", () => {
1581
1811
  pivotIndices: [2, 3],
1582
1812
  trigger: "imperative-api",
1583
1813
  })
1584
- ).toEqual([25, 25, 40, 10]);
1814
+ ).toEqual([25, 35, 40, 0]);
1585
1815
  });
1586
1816
 
1587
1817
  it("[1,2,3++,4]", () => {
@@ -1,9 +1,8 @@
1
+ import { PanelConstraints } from "../Panel";
1
2
  import { computePercentagePanelConstraints } from "./computePercentagePanelConstraints";
3
+ import { fuzzyCompareNumbers } from "./numbers/fuzzyCompareNumbers";
2
4
  import { fuzzyNumbersEqual } from "./numbers/fuzzyNumbersEqual";
3
5
  import { resizePanel } from "./resizePanel";
4
- import { PanelConstraints } from "../Panel";
5
-
6
- let isCheckingForInfiniteLoop = false;
7
6
 
8
7
  // All units must be in percentages; pixel values should be pre-converted
9
8
  export function adjustLayoutByDelta({
@@ -29,59 +28,124 @@ export function adjustLayoutByDelta({
29
28
 
30
29
  let deltaApplied = 0;
31
30
 
31
+ //const DEBUG = [];
32
+ //DEBUG.push(`adjustLayoutByDelta() ${prevLayout.join(", ")}`);
33
+ //DEBUG.push(` delta: ${delta}`);
34
+ //DEBUG.push(` pivotIndices: ${pivotIndices.join(", ")}`);
35
+ //DEBUG.push(` trigger: ${trigger}`);
36
+ //DEBUG.push("");
37
+
32
38
  // A resizing panel affects the panels before or after it.
33
39
  //
34
- // A negative delta means the panel immediately after the resizer should grow/expand by decreasing its offset.
40
+ // A negative delta means the panel(s) immediately after the resize handle should grow/expand by decreasing its offset.
35
41
  // Other panels may also need to shrink/contract (and shift) to make room, depending on the min weights.
36
42
  //
37
- // A positive delta means the panel immediately before the resizer should "expand".
38
- // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resizer.
43
+ // A positive delta means the panel(s) immediately before the resize handle should "expand".
44
+ // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resize handle.
39
45
 
40
- // First, check the panel we're pivoting around;
41
- // We should only expand or contract by as much as its constraints allow
42
46
  {
43
- const pivotIndex = delta < 0 ? pivotIndices[1]! : pivotIndices[0]!;
44
- const initialSize = nextLayout[pivotIndex]!;
47
+ // If this is a resize triggered by a keyboard event, our logic for expanding/collapsing is different.
48
+ // We no longer check the halfway threshold because this may prevent the panel from expanding at all.
49
+ if (trigger === "keyboard") {
50
+ {
51
+ // Check if we should expand a collapsed panel
52
+ const index = delta < 0 ? pivotIndices[1]! : pivotIndices[0]!;
53
+ const constraints = panelConstraints[index]!;
54
+ //DEBUG.push(`edge case check 1: ${index}`);
55
+ //DEBUG.push(` -> collapsible? ${constraints.collapsible}`);
56
+ if (constraints.collapsible) {
57
+ const prevSize = prevLayout[index]!;
58
+ const { collapsedSizePercentage, minSizePercentage } =
59
+ computePercentagePanelConstraints(
60
+ panelConstraints,
61
+ index,
62
+ groupSizePixels
63
+ );
64
+
65
+ if (fuzzyNumbersEqual(prevSize, collapsedSizePercentage)) {
66
+ const localDelta = minSizePercentage - prevSize;
67
+ //DEBUG.push(` -> expand delta: ${localDelta}`);
68
+
69
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
70
+ delta = delta < 0 ? 0 - localDelta : localDelta;
71
+ //DEBUG.push(` -> delta: ${delta}`);
72
+ }
73
+ }
74
+ }
75
+ }
45
76
 
46
- const { collapsible } = panelConstraints[pivotIndex]!;
47
- const { collapsedSizePercentage, minSizePercentage } =
48
- computePercentagePanelConstraints(
49
- panelConstraints,
50
- pivotIndex,
51
- groupSizePixels
52
- );
53
-
54
- const isCollapsed =
55
- collapsible && fuzzyNumbersEqual(initialSize, collapsedSizePercentage);
56
-
57
- let unsafeSize = initialSize + Math.abs(delta);
58
- if (isCollapsed) {
59
- switch (trigger) {
60
- case "keyboard":
61
- if (minSizePercentage > unsafeSize) {
62
- unsafeSize = minSizePercentage;
77
+ {
78
+ // Check if we should collapse a panel at its minimum size
79
+ const index = delta < 0 ? pivotIndices[0]! : pivotIndices[1]!;
80
+ const constraints = panelConstraints[index]!;
81
+ //DEBUG.push(`edge case check 2: ${index}`);
82
+ //DEBUG.push(` -> collapsible? ${constraints.collapsible}`);
83
+ if (constraints.collapsible) {
84
+ const prevSize = prevLayout[index]!;
85
+ const { collapsedSizePercentage, minSizePercentage } =
86
+ computePercentagePanelConstraints(
87
+ panelConstraints,
88
+ index,
89
+ groupSizePixels
90
+ );
91
+
92
+ if (fuzzyNumbersEqual(prevSize, minSizePercentage)) {
93
+ const localDelta = prevSize - collapsedSizePercentage;
94
+ //DEBUG.push(` -> expand delta: ${localDelta}`);
95
+
96
+ if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
97
+ delta = delta < 0 ? 0 - localDelta : localDelta;
98
+ //DEBUG.push(` -> delta: ${delta}`);
99
+ }
63
100
  }
101
+ }
64
102
  }
65
103
  }
104
+ //DEBUG.push("");
105
+ }
66
106
 
67
- const safeSize = resizePanel({
68
- groupSizePixels,
69
- panelConstraints,
70
- panelIndex: pivotIndex,
71
- size: unsafeSize,
72
- });
107
+ {
108
+ // Pre-calculate max available delta in the opposite direction of our pivot.
109
+ // This will be the maximum amount we're allowed to expand/contract the panels in the primary direction.
110
+ // If this amount is less than the requested delta, adjust the requested delta.
111
+ // If this amount is greater than the requested delta, that's useful information too–
112
+ // as an expanding panel might change from collapsed to min size.
113
+
114
+ const increment = delta < 0 ? 1 : -1;
115
+
116
+ let index = delta < 0 ? pivotIndices[1]! : pivotIndices[0]!;
117
+ let maxAvailableDelta = 0;
73
118
 
74
- if (fuzzyNumbersEqual(initialSize, safeSize)) {
75
- // If there's no room for the pivot panel to grow, we should ignore this change
76
- return nextLayout;
77
- } else {
78
- delta = delta < 0 ? initialSize - safeSize : safeSize - initialSize;
119
+ //DEBUG.push("pre calc...");
120
+ while (true) {
121
+ const prevSize = prevLayout[index];
122
+ const maxSafeSize = resizePanel({
123
+ groupSizePixels,
124
+ panelConstraints,
125
+ panelIndex: index,
126
+ size: 100,
127
+ });
128
+ const delta = maxSafeSize - prevSize;
129
+ //DEBUG.push(` ${index}: ${prevSize} -> ${maxSafeSize}`);
130
+
131
+ maxAvailableDelta += delta;
132
+ index += increment;
133
+
134
+ if (index < 0 || index >= panelConstraints.length) {
135
+ break;
136
+ }
79
137
  }
138
+
139
+ //DEBUG.push(` -> max available delta: ${maxAvailableDelta}`);
140
+ const minAbsDelta = Math.min(Math.abs(delta), Math.abs(maxAvailableDelta));
141
+ delta = delta < 0 ? 0 - minAbsDelta : minAbsDelta;
142
+ //DEBUG.push(` -> adjusted delta: ${delta}`);
143
+ //DEBUG.push("");
80
144
  }
81
145
 
82
- // Delta added to a panel needs to be subtracted from other panels
83
- // within the constraints that those panels allow
84
146
  {
147
+ // Delta added to a panel needs to be subtracted from other panels (within the constraints that those panels allow).
148
+
85
149
  const pivotIndex = delta < 0 ? pivotIndices[0]! : pivotIndices[1]!;
86
150
  let index = pivotIndex;
87
151
  while (index >= 0 && index < panelConstraints.length) {
@@ -89,8 +153,7 @@ export function adjustLayoutByDelta({
89
153
 
90
154
  const prevSize = prevLayout[index]!;
91
155
  const unsafeSize = prevSize - deltaRemaining;
92
-
93
- let safeSize = resizePanel({
156
+ const safeSize = resizePanel({
94
157
  groupSizePixels,
95
158
  panelConstraints,
96
159
  panelIndex: index,
@@ -120,14 +183,19 @@ export function adjustLayoutByDelta({
120
183
  }
121
184
  }
122
185
  }
186
+ //DEBUG.push(`after 1: ${nextLayout.join(", ")}`);
187
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
188
+ //DEBUG.push("");
123
189
 
124
190
  // If we were unable to resize any of the panels panels, return the previous state.
125
191
  // This will essentially bailout and ignore e.g. drags past a panel's boundaries
126
192
  if (fuzzyNumbersEqual(deltaApplied, 0)) {
193
+ //console.log(DEBUG.join("\n"));
127
194
  return prevLayout;
128
195
  }
129
196
 
130
197
  {
198
+ // Now distribute the applied delta to the panels in the other direction
131
199
  const pivotIndex = delta < 0 ? pivotIndices[1]! : pivotIndices[0]!;
132
200
 
133
201
  const unsafeSize = prevLayout[pivotIndex]! + deltaApplied;
@@ -173,38 +241,20 @@ export function adjustLayoutByDelta({
173
241
  index++;
174
242
  }
175
243
  }
244
+ }
245
+ }
246
+ //DEBUG.push(`after 2: ${nextLayout.join(", ")}`);
247
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
248
+ //DEBUG.push("");
176
249
 
177
- // If we can't redistribute, this layout is invalid;
178
- // There may be an incremental layout that is valid though
179
- if (!fuzzyNumbersEqual(deltaRemaining, 0)) {
180
- let didSetInfiniteLoopCheckCounter = false;
181
- if (isCheckingForInfiniteLoop === null) {
182
- didSetInfiniteLoopCheckCounter = true;
183
- isCheckingForInfiniteLoop = true;
184
- }
250
+ const totalSize = nextLayout.reduce((total, size) => size + total, 0);
251
+ deltaApplied = 100 - totalSize;
252
+ //DEBUG.push(`total size: ${totalSize}`);
253
+ //DEBUG.push(` deltaApplied: ${deltaApplied}`);
254
+ //console.log(DEBUG.join("\n"));
185
255
 
186
- try {
187
- return adjustLayoutByDelta({
188
- delta: delta < 0 ? delta + 1 : delta - 1,
189
- groupSizePixels,
190
- layout: prevLayout,
191
- panelConstraints,
192
- pivotIndices,
193
- trigger,
194
- });
195
- } catch (error) {
196
- if (error instanceof RangeError) {
197
- console.error(`Could not apply delta ${delta} to layout`);
198
-
199
- return prevLayout;
200
- }
201
- } finally {
202
- if (didSetInfiniteLoopCheckCounter) {
203
- isCheckingForInfiniteLoop = false;
204
- }
205
- }
206
- }
207
- }
256
+ if (!fuzzyNumbersEqual(totalSize, 100)) {
257
+ return prevLayout;
208
258
  }
209
259
 
210
260
  return nextLayout;