react-resizable-panels 0.0.54 → 0.0.56

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 (86) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/CHANGELOG.md +253 -80
  3. package/README.md +55 -49
  4. package/dist/declarations/src/Panel.d.ts +76 -20
  5. package/dist/declarations/src/PanelGroup.d.ts +29 -21
  6. package/dist/declarations/src/PanelResizeHandle.d.ts +1 -1
  7. package/dist/declarations/src/index.d.ts +5 -5
  8. package/dist/declarations/src/types.d.ts +3 -25
  9. package/dist/declarations/src/vendor/react.d.ts +4 -4
  10. package/dist/react-resizable-panels.browser.cjs.js +1279 -796
  11. package/dist/react-resizable-panels.browser.development.cjs.js +1404 -809
  12. package/dist/react-resizable-panels.browser.development.esm.js +1398 -803
  13. package/dist/react-resizable-panels.browser.esm.js +1279 -796
  14. package/dist/react-resizable-panels.cjs.js +1279 -796
  15. package/dist/react-resizable-panels.cjs.js.map +1 -0
  16. package/dist/react-resizable-panels.development.cjs.js +1399 -804
  17. package/dist/react-resizable-panels.development.esm.js +1400 -805
  18. package/dist/react-resizable-panels.development.node.cjs.js +1172 -755
  19. package/dist/react-resizable-panels.development.node.esm.js +1173 -756
  20. package/dist/react-resizable-panels.esm.js +1279 -796
  21. package/dist/react-resizable-panels.esm.js.map +1 -0
  22. package/dist/react-resizable-panels.node.cjs.js +1064 -749
  23. package/dist/react-resizable-panels.node.esm.js +1065 -750
  24. package/jest.config.js +10 -0
  25. package/package.json +3 -1
  26. package/src/Panel.test.tsx +308 -0
  27. package/src/Panel.ts +179 -127
  28. package/src/PanelGroup.test.tsx +210 -0
  29. package/src/PanelGroup.ts +751 -580
  30. package/src/PanelGroupContext.ts +33 -0
  31. package/src/PanelResizeHandle.ts +13 -8
  32. package/src/hooks/useUniqueId.ts +1 -1
  33. package/src/hooks/useWindowSplitterBehavior.ts +9 -161
  34. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +185 -0
  35. package/src/index.ts +24 -11
  36. package/src/types.ts +3 -29
  37. package/src/utils/adjustLayoutByDelta.test.ts +1808 -0
  38. package/src/utils/adjustLayoutByDelta.ts +211 -0
  39. package/src/utils/calculateAriaValues.test.ts +111 -0
  40. package/src/utils/calculateAriaValues.ts +67 -0
  41. package/src/utils/calculateDeltaPercentage.ts +68 -0
  42. package/src/utils/calculateDragOffsetPercentage.ts +30 -0
  43. package/src/utils/calculateUnsafeDefaultLayout.test.ts +92 -0
  44. package/src/utils/calculateUnsafeDefaultLayout.ts +55 -0
  45. package/src/utils/callPanelCallbacks.ts +81 -0
  46. package/src/utils/compareLayouts.test.ts +9 -0
  47. package/src/utils/compareLayouts.ts +12 -0
  48. package/src/utils/computePanelFlexBoxStyle.ts +44 -0
  49. package/src/utils/computePercentagePanelConstraints.test.ts +71 -0
  50. package/src/utils/computePercentagePanelConstraints.ts +56 -0
  51. package/src/utils/convertPercentageToPixels.test.ts +9 -0
  52. package/src/utils/convertPercentageToPixels.ts +6 -0
  53. package/src/utils/convertPixelConstraintsToPercentages.ts +55 -0
  54. package/src/utils/convertPixelsToPercentage.test.ts +9 -0
  55. package/src/utils/convertPixelsToPercentage.ts +6 -0
  56. package/src/utils/determinePivotIndices.ts +10 -0
  57. package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +29 -0
  58. package/src/utils/dom/getAvailableGroupSizePixels.ts +29 -0
  59. package/src/utils/dom/getPanelElement.ts +7 -0
  60. package/src/utils/dom/getPanelGroupElement.ts +7 -0
  61. package/src/utils/dom/getResizeHandleElement.ts +9 -0
  62. package/src/utils/dom/getResizeHandleElementIndex.ts +12 -0
  63. package/src/utils/dom/getResizeHandleElementsForGroup.ts +9 -0
  64. package/src/utils/dom/getResizeHandlePanelIds.ts +18 -0
  65. package/src/utils/events.ts +13 -0
  66. package/src/utils/getPercentageSizeFromMixedSizes.test.ts +47 -0
  67. package/src/utils/getPercentageSizeFromMixedSizes.ts +15 -0
  68. package/src/utils/getResizeEventCursorPosition.ts +19 -0
  69. package/src/utils/initializeDefaultStorage.ts +26 -0
  70. package/src/utils/numbers/fuzzyCompareNumbers.test.ts +16 -0
  71. package/src/utils/numbers/fuzzyCompareNumbers.ts +17 -0
  72. package/src/utils/numbers/fuzzyNumbersEqual.ts +9 -0
  73. package/src/utils/resizePanel.ts +41 -0
  74. package/src/utils/serialization.ts +9 -4
  75. package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +23 -0
  76. package/src/utils/shouldMonitorPixelBasedConstraints.ts +13 -0
  77. package/src/utils/test-utils.ts +136 -0
  78. package/src/utils/validatePanelConstraints.test.ts +151 -0
  79. package/src/utils/validatePanelConstraints.ts +103 -0
  80. package/src/utils/validatePanelGroupLayout.test.ts +233 -0
  81. package/src/utils/validatePanelGroupLayout.ts +88 -0
  82. package/src/vendor/react.ts +4 -0
  83. package/.eslintrc.json +0 -22
  84. package/src/PanelContexts.ts +0 -20
  85. package/src/utils/coordinates.ts +0 -149
  86. package/src/utils/group.ts +0 -315
@@ -0,0 +1,211 @@
1
+ import { computePercentagePanelConstraints } from "./computePercentagePanelConstraints";
2
+ import { fuzzyNumbersEqual } from "./numbers/fuzzyNumbersEqual";
3
+ import { resizePanel } from "./resizePanel";
4
+ import { PanelConstraints } from "../Panel";
5
+
6
+ let isCheckingForInfiniteLoop = false;
7
+
8
+ // All units must be in percentages; pixel values should be pre-converted
9
+ export function adjustLayoutByDelta({
10
+ delta,
11
+ groupSizePixels,
12
+ layout: prevLayout,
13
+ panelConstraints,
14
+ pivotIndices,
15
+ trigger,
16
+ }: {
17
+ delta: number;
18
+ groupSizePixels: number;
19
+ layout: number[];
20
+ panelConstraints: PanelConstraints[];
21
+ pivotIndices: number[];
22
+ trigger: "imperative-api" | "keyboard" | "mouse-or-touch";
23
+ }): number[] {
24
+ if (fuzzyNumbersEqual(delta, 0)) {
25
+ return prevLayout;
26
+ }
27
+
28
+ const nextLayout = [...prevLayout];
29
+
30
+ let deltaApplied = 0;
31
+
32
+ // A resizing panel affects the panels before or after it.
33
+ //
34
+ // A negative delta means the panel immediately after the resizer should grow/expand by decreasing its offset.
35
+ // Other panels may also need to shrink/contract (and shift) to make room, depending on the min weights.
36
+ //
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.
39
+
40
+ // First, check the panel we're pivoting around;
41
+ // We should only expand or contract by as much as its constraints allow
42
+ {
43
+ const pivotIndex = delta < 0 ? pivotIndices[1]! : pivotIndices[0]!;
44
+ const initialSize = nextLayout[pivotIndex]!;
45
+
46
+ const { collapsible } = panelConstraints[pivotIndex]!;
47
+ const { collapsedSizePercentage, maxSizePercentage, 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;
63
+ }
64
+ }
65
+ }
66
+
67
+ const safeSize = resizePanel({
68
+ groupSizePixels,
69
+ panelConstraints,
70
+ panelIndex: pivotIndex,
71
+ size: unsafeSize,
72
+ });
73
+
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;
79
+ }
80
+ }
81
+
82
+ // Delta added to a panel needs to be subtracted from other panels
83
+ // within the constraints that those panels allow
84
+ {
85
+ const pivotIndex = delta < 0 ? pivotIndices[0]! : pivotIndices[1]!;
86
+ let index = pivotIndex;
87
+ while (index >= 0 && index < panelConstraints.length) {
88
+ const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
89
+
90
+ const prevSize = prevLayout[index]!;
91
+ const unsafeSize = prevSize - deltaRemaining;
92
+
93
+ let safeSize = resizePanel({
94
+ groupSizePixels,
95
+ panelConstraints,
96
+ panelIndex: index,
97
+ size: unsafeSize,
98
+ });
99
+
100
+ if (!fuzzyNumbersEqual(prevSize, safeSize)) {
101
+ deltaApplied += prevSize - safeSize;
102
+
103
+ nextLayout[index] = safeSize;
104
+
105
+ if (
106
+ deltaApplied
107
+ .toPrecision(3)
108
+ .localeCompare(Math.abs(delta).toPrecision(3), undefined, {
109
+ numeric: true,
110
+ }) >= 0
111
+ ) {
112
+ break;
113
+ }
114
+ }
115
+
116
+ if (delta < 0) {
117
+ index--;
118
+ } else {
119
+ index++;
120
+ }
121
+ }
122
+ }
123
+
124
+ // If we were unable to resize any of the panels panels, return the previous state.
125
+ // This will essentially bailout and ignore e.g. drags past a panel's boundaries
126
+ if (fuzzyNumbersEqual(deltaApplied, 0)) {
127
+ return prevLayout;
128
+ }
129
+
130
+ {
131
+ const pivotIndex = delta < 0 ? pivotIndices[1]! : pivotIndices[0]!;
132
+
133
+ const unsafeSize = prevLayout[pivotIndex]! + deltaApplied;
134
+ const safeSize = resizePanel({
135
+ groupSizePixels,
136
+ panelConstraints,
137
+ panelIndex: pivotIndex,
138
+ size: unsafeSize,
139
+ });
140
+
141
+ // Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
142
+ nextLayout[pivotIndex] = safeSize;
143
+
144
+ // Edge case where expanding or contracting one panel caused another one to change collapsed state
145
+ if (!fuzzyNumbersEqual(safeSize, unsafeSize)) {
146
+ let deltaRemaining = unsafeSize - safeSize;
147
+
148
+ const pivotIndex = delta < 0 ? pivotIndices[1]! : pivotIndices[0]!;
149
+ let index = pivotIndex;
150
+ while (index >= 0 && index < panelConstraints.length) {
151
+ const prevSize = nextLayout[index]!;
152
+ const unsafeSize = prevSize + deltaRemaining;
153
+ const safeSize = resizePanel({
154
+ groupSizePixels,
155
+ panelConstraints,
156
+ panelIndex: index,
157
+ size: unsafeSize,
158
+ });
159
+
160
+ if (!fuzzyNumbersEqual(prevSize, safeSize)) {
161
+ deltaRemaining -= safeSize - prevSize;
162
+
163
+ nextLayout[index] = safeSize;
164
+ }
165
+
166
+ if (fuzzyNumbersEqual(deltaRemaining, 0)) {
167
+ break;
168
+ }
169
+
170
+ if (delta > 0) {
171
+ index--;
172
+ } else {
173
+ index++;
174
+ }
175
+ }
176
+
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
+ }
185
+
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
+ }
208
+ }
209
+
210
+ return nextLayout;
211
+ }
@@ -0,0 +1,111 @@
1
+ import { PanelConstraints, PanelData } from "../Panel";
2
+ import { calculateAriaValues } from "./calculateAriaValues";
3
+
4
+ describe("calculateAriaValues", () => {
5
+ let idCounter = 0;
6
+ let orderCounter = 0;
7
+
8
+ function createPanelData(constraints: PanelConstraints = {}): PanelData {
9
+ return {
10
+ callbacks: {
11
+ onCollapse: undefined,
12
+ onExpand: undefined,
13
+ onResize: undefined,
14
+ },
15
+ constraints,
16
+ id: `${idCounter++}`,
17
+ idIsFromProps: false,
18
+ order: orderCounter++,
19
+ };
20
+ }
21
+
22
+ beforeEach(() => {
23
+ idCounter = 0;
24
+ orderCounter = 0;
25
+ });
26
+
27
+ it("should work correctly for panels with no min/max constraints", () => {
28
+ expect(
29
+ calculateAriaValues({
30
+ groupSizePixels: 1_000,
31
+ layout: [50, 50],
32
+ panelsArray: [createPanelData(), createPanelData()],
33
+ pivotIndices: [0, 1],
34
+ })
35
+ ).toEqual({
36
+ valueMax: 100,
37
+ valueMin: 0,
38
+ valueNow: 50,
39
+ });
40
+
41
+ expect(
42
+ calculateAriaValues({
43
+ groupSizePixels: 1_000,
44
+ layout: [20, 50, 30],
45
+ panelsArray: [createPanelData(), createPanelData(), createPanelData()],
46
+ pivotIndices: [0, 1],
47
+ })
48
+ ).toEqual({
49
+ valueMax: 100,
50
+ valueMin: 0,
51
+ valueNow: 20,
52
+ });
53
+
54
+ expect(
55
+ calculateAriaValues({
56
+ groupSizePixels: 1_000,
57
+ layout: [20, 50, 30],
58
+ panelsArray: [createPanelData(), createPanelData(), createPanelData()],
59
+ pivotIndices: [1, 2],
60
+ })
61
+ ).toEqual({
62
+ valueMax: 100,
63
+ valueMin: 0,
64
+ valueNow: 50,
65
+ });
66
+ });
67
+
68
+ it("should work correctly for panels with min/max constraints", () => {
69
+ expect(
70
+ calculateAriaValues({
71
+ groupSizePixels: 1_000,
72
+ layout: [25, 75],
73
+ panelsArray: [
74
+ createPanelData({
75
+ maxSizePercentage: 35,
76
+ minSizePercentage: 10,
77
+ }),
78
+ createPanelData(),
79
+ ],
80
+ pivotIndices: [0, 1],
81
+ })
82
+ ).toEqual({
83
+ valueMax: 35,
84
+ valueMin: 10,
85
+ valueNow: 25,
86
+ });
87
+
88
+ expect(
89
+ calculateAriaValues({
90
+ groupSizePixels: 1_000,
91
+ layout: [25, 50, 25],
92
+ panelsArray: [
93
+ createPanelData({
94
+ maxSizePercentage: 35,
95
+ minSizePercentage: 10,
96
+ }),
97
+ createPanelData(),
98
+ createPanelData({
99
+ maxSizePercentage: 35,
100
+ minSizePercentage: 10,
101
+ }),
102
+ ],
103
+ pivotIndices: [1, 2],
104
+ })
105
+ ).toEqual({
106
+ valueMax: 80,
107
+ valueMin: 30,
108
+ valueNow: 50,
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,67 @@
1
+ import { PanelData } from "../Panel";
2
+ import { getPercentageSizeFromMixedSizes } from "./getPercentageSizeFromMixedSizes";
3
+
4
+ export function calculateAriaValues({
5
+ groupSizePixels,
6
+ layout,
7
+ panelsArray,
8
+ pivotIndices,
9
+ }: {
10
+ groupSizePixels: number;
11
+ layout: number[];
12
+ panelsArray: PanelData[];
13
+ pivotIndices: number[];
14
+ }) {
15
+ let currentMinSize = 0;
16
+ let currentMaxSize = 100;
17
+ let totalMinSize = 0;
18
+ let totalMaxSize = 0;
19
+
20
+ // A panel's effective min/max sizes also need to account for other panel's sizes.
21
+ panelsArray.forEach((panelData, index) => {
22
+ const { constraints } = panelData;
23
+ const {
24
+ maxSizePercentage,
25
+ maxSizePixels,
26
+ minSizePercentage,
27
+ minSizePixels,
28
+ } = constraints;
29
+
30
+ const minSize =
31
+ getPercentageSizeFromMixedSizes(
32
+ {
33
+ sizePercentage: minSizePercentage,
34
+ sizePixels: minSizePixels,
35
+ },
36
+ groupSizePixels
37
+ ) ?? 0;
38
+
39
+ const maxSize =
40
+ getPercentageSizeFromMixedSizes(
41
+ {
42
+ sizePercentage: maxSizePercentage,
43
+ sizePixels: maxSizePixels,
44
+ },
45
+ groupSizePixels
46
+ ) ?? 100;
47
+
48
+ if (index === pivotIndices[0]) {
49
+ currentMinSize = minSize;
50
+ currentMaxSize = maxSize;
51
+ } else {
52
+ totalMinSize += minSize;
53
+ totalMaxSize += maxSize;
54
+ }
55
+ });
56
+
57
+ const valueMax = Math.min(currentMaxSize, 100 - totalMinSize);
58
+ const valueMin = Math.max(currentMinSize, 100 - totalMaxSize);
59
+
60
+ const valueNow = layout[pivotIndices[0]];
61
+
62
+ return {
63
+ valueMax,
64
+ valueMin,
65
+ valueNow,
66
+ };
67
+ }
@@ -0,0 +1,68 @@
1
+ import { DragState, ResizeEvent } from "../PanelGroupContext";
2
+ import { Direction } from "../types";
3
+ import { getPanelGroupElement } from "../utils/dom/getPanelGroupElement";
4
+ import { calculateDragOffsetPercentage } from "./calculateDragOffsetPercentage";
5
+ import { isKeyDown } from "./events";
6
+
7
+ // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX
8
+ export function calculateDeltaPercentage(
9
+ event: ResizeEvent,
10
+ groupId: string,
11
+ dragHandleId: string,
12
+ direction: Direction,
13
+ initialDragState: DragState,
14
+ keyboardResizeByOptions: {
15
+ percentage: number | null;
16
+ pixels: number | null;
17
+ }
18
+ ): number {
19
+ if (isKeyDown(event)) {
20
+ const isHorizontal = direction === "horizontal";
21
+
22
+ const groupElement = getPanelGroupElement(groupId)!;
23
+ const rect = groupElement.getBoundingClientRect();
24
+ const groupSizeInPixels = isHorizontal ? rect.width : rect.height;
25
+
26
+ let delta = 0;
27
+ if (event.shiftKey) {
28
+ delta = 100;
29
+ } else if (keyboardResizeByOptions.percentage != null) {
30
+ delta = keyboardResizeByOptions.percentage;
31
+ } else if (keyboardResizeByOptions.pixels != null) {
32
+ delta = keyboardResizeByOptions.pixels / groupSizeInPixels;
33
+ } else {
34
+ delta = 10;
35
+ }
36
+
37
+ let movement = 0;
38
+ switch (event.key) {
39
+ case "ArrowDown":
40
+ movement = isHorizontal ? 0 : delta;
41
+ break;
42
+ case "ArrowLeft":
43
+ movement = isHorizontal ? -delta : 0;
44
+ break;
45
+ case "ArrowRight":
46
+ movement = isHorizontal ? delta : 0;
47
+ break;
48
+ case "ArrowUp":
49
+ movement = isHorizontal ? 0 : -delta;
50
+ break;
51
+ case "End":
52
+ movement = 100;
53
+ break;
54
+ case "Home":
55
+ movement = -100;
56
+ break;
57
+ }
58
+
59
+ return movement;
60
+ } else {
61
+ return calculateDragOffsetPercentage(
62
+ event,
63
+ dragHandleId,
64
+ direction,
65
+ initialDragState
66
+ );
67
+ }
68
+ }
@@ -0,0 +1,30 @@
1
+ import { DragState, ResizeEvent } from "../PanelGroupContext";
2
+ import { Direction } from "../types";
3
+ import { getPanelGroupElement } from "../utils/dom/getPanelGroupElement";
4
+ import { getResizeHandleElement } from "../utils/dom/getResizeHandleElement";
5
+ import { getResizeEventCursorPosition } from "./getResizeEventCursorPosition";
6
+
7
+ export function calculateDragOffsetPercentage(
8
+ event: ResizeEvent,
9
+ dragHandleId: string,
10
+ direction: Direction,
11
+ initialDragState: DragState
12
+ ): number {
13
+ const isHorizontal = direction === "horizontal";
14
+
15
+ const handleElement = getResizeHandleElement(dragHandleId)!;
16
+ const groupId = handleElement.getAttribute("data-panel-group-id")!;
17
+
18
+ let { initialCursorPosition } = initialDragState;
19
+
20
+ const cursorPosition = getResizeEventCursorPosition(direction, event);
21
+
22
+ const groupElement = getPanelGroupElement(groupId)!;
23
+ const groupRect = groupElement.getBoundingClientRect();
24
+ const groupSizeInPixels = isHorizontal ? groupRect.width : groupRect.height;
25
+
26
+ const offsetPixels = cursorPosition - initialCursorPosition;
27
+ const offsetPercentage = (offsetPixels / groupSizeInPixels) * 100;
28
+
29
+ return offsetPercentage;
30
+ }
@@ -0,0 +1,92 @@
1
+ import { PanelConstraints, PanelData } from "../Panel";
2
+ import { calculateUnsafeDefaultLayout } from "./calculateUnsafeDefaultLayout";
3
+ import { expectToBeCloseToArray } from "./test-utils";
4
+
5
+ describe("calculateUnsafeDefaultLayout", () => {
6
+ let idCounter = 0;
7
+ let orderCounter = 0;
8
+
9
+ function createPanelData(constraints: PanelConstraints = {}): PanelData {
10
+ return {
11
+ callbacks: {
12
+ onCollapse: undefined,
13
+ onExpand: undefined,
14
+ onResize: undefined,
15
+ },
16
+ constraints,
17
+ id: `${idCounter++}`,
18
+ idIsFromProps: false,
19
+ order: orderCounter++,
20
+ };
21
+ }
22
+
23
+ beforeEach(() => {
24
+ idCounter = 0;
25
+ orderCounter = 0;
26
+ });
27
+
28
+ it("should assign even sizes for every panel by default", () => {
29
+ expectToBeCloseToArray(
30
+ calculateUnsafeDefaultLayout({
31
+ groupSizePixels: 100_000,
32
+ panelDataArray: [createPanelData()],
33
+ }),
34
+ [100]
35
+ );
36
+
37
+ expectToBeCloseToArray(
38
+ calculateUnsafeDefaultLayout({
39
+ groupSizePixels: 100_000,
40
+ panelDataArray: [createPanelData(), createPanelData()],
41
+ }),
42
+ [50, 50]
43
+ );
44
+
45
+ expectToBeCloseToArray(
46
+ calculateUnsafeDefaultLayout({
47
+ groupSizePixels: 100_000,
48
+ panelDataArray: [
49
+ createPanelData(),
50
+ createPanelData(),
51
+ createPanelData(),
52
+ ],
53
+ }),
54
+ [33.3, 33.3, 33.3]
55
+ );
56
+ });
57
+
58
+ it("should respect default panel size constraints", () => {
59
+ expectToBeCloseToArray(
60
+ calculateUnsafeDefaultLayout({
61
+ groupSizePixels: 100_000,
62
+ panelDataArray: [
63
+ createPanelData({
64
+ defaultSizePercentage: 15,
65
+ }),
66
+ createPanelData({
67
+ defaultSizePercentage: 85,
68
+ }),
69
+ ],
70
+ }),
71
+ [15, 85]
72
+ );
73
+ });
74
+
75
+ it("should ignore min and max panel size constraints", () => {
76
+ expectToBeCloseToArray(
77
+ calculateUnsafeDefaultLayout({
78
+ groupSizePixels: 100_000,
79
+ panelDataArray: [
80
+ createPanelData({
81
+ minSizePercentage: 40,
82
+ }),
83
+ createPanelData(),
84
+ createPanelData({
85
+ maxSizePercentage: 10,
86
+ }),
87
+ ],
88
+ }),
89
+ [33.3, 33.3, 33.3]
90
+ );
91
+ });
92
+ });
@@ -0,0 +1,55 @@
1
+ import { PanelData } from "../Panel";
2
+ import { computePercentagePanelConstraints } from "./computePercentagePanelConstraints";
3
+
4
+ export function calculateUnsafeDefaultLayout({
5
+ groupSizePixels,
6
+ panelDataArray,
7
+ }: {
8
+ groupSizePixels: number;
9
+ panelDataArray: PanelData[];
10
+ }): number[] {
11
+ const layout = Array<number>(panelDataArray.length);
12
+
13
+ const panelDataConstraints = panelDataArray.map(
14
+ (panelData) => panelData.constraints
15
+ );
16
+
17
+ let numPanelsWithSizes = 0;
18
+ let remainingSize = 100;
19
+
20
+ // Distribute default sizes first
21
+ for (let index = 0; index < panelDataArray.length; index++) {
22
+ const { defaultSizePercentage } = computePercentagePanelConstraints(
23
+ panelDataConstraints,
24
+ index,
25
+ groupSizePixels
26
+ );
27
+
28
+ if (defaultSizePercentage != null) {
29
+ numPanelsWithSizes++;
30
+ layout[index] = defaultSizePercentage;
31
+ remainingSize -= defaultSizePercentage;
32
+ }
33
+ }
34
+
35
+ // Remaining size should be distributed evenly between panels without default sizes
36
+ for (let index = 0; index < panelDataArray.length; index++) {
37
+ const { defaultSizePercentage } = computePercentagePanelConstraints(
38
+ panelDataConstraints,
39
+ index,
40
+ groupSizePixels
41
+ );
42
+ if (defaultSizePercentage != null) {
43
+ continue;
44
+ }
45
+
46
+ const numRemainingPanels = panelDataArray.length - numPanelsWithSizes;
47
+ const size = remainingSize / numRemainingPanels;
48
+
49
+ numPanelsWithSizes++;
50
+ layout[index] = size;
51
+ remainingSize -= size;
52
+ }
53
+
54
+ return layout;
55
+ }