react-resizable-panels 0.0.55 → 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.
- package/.eslintrc.cjs +26 -0
- package/CHANGELOG.md +234 -90
- package/README.md +55 -49
- package/dist/declarations/src/Panel.d.ts +75 -20
- package/dist/declarations/src/PanelGroup.d.ts +29 -25
- package/dist/declarations/src/PanelResizeHandle.d.ts +1 -1
- package/dist/declarations/src/index.d.ts +5 -6
- package/dist/declarations/src/types.d.ts +3 -26
- package/dist/declarations/src/vendor/react.d.ts +4 -4
- package/dist/react-resizable-panels.browser.cjs.js +1241 -1035
- package/dist/react-resizable-panels.browser.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.cjs.js +1367 -1081
- package/dist/react-resizable-panels.browser.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.esm.js +1368 -1081
- package/dist/react-resizable-panels.browser.esm.js +1242 -1035
- package/dist/react-resizable-panels.cjs.js +1241 -1035
- package/dist/react-resizable-panels.cjs.js.map +1 -1
- package/dist/react-resizable-panels.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.cjs.js +1370 -1084
- package/dist/react-resizable-panels.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.esm.js +1371 -1084
- package/dist/react-resizable-panels.development.node.cjs.js +1151 -940
- package/dist/react-resizable-panels.development.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.node.esm.js +1152 -940
- package/dist/react-resizable-panels.esm.js +1242 -1035
- package/dist/react-resizable-panels.esm.js.map +1 -1
- package/dist/react-resizable-panels.node.cjs.js +1049 -912
- package/dist/react-resizable-panels.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.node.esm.js +1050 -912
- package/jest.config.js +10 -0
- package/package.json +3 -1
- package/src/Panel.test.tsx +308 -0
- package/src/Panel.ts +175 -123
- package/src/PanelGroup.test.tsx +210 -0
- package/src/PanelGroup.ts +730 -669
- package/src/PanelGroupContext.ts +33 -0
- package/src/PanelResizeHandle.ts +13 -8
- package/src/hooks/useUniqueId.ts +1 -1
- package/src/hooks/useWindowSplitterBehavior.ts +9 -164
- package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +185 -0
- package/src/index.ts +19 -14
- package/src/types.ts +3 -30
- package/src/utils/adjustLayoutByDelta.test.ts +1808 -0
- package/src/utils/adjustLayoutByDelta.ts +211 -0
- package/src/utils/calculateAriaValues.test.ts +111 -0
- package/src/utils/calculateAriaValues.ts +67 -0
- package/src/utils/calculateDeltaPercentage.ts +68 -0
- package/src/utils/calculateDragOffsetPercentage.ts +30 -0
- package/src/utils/calculateUnsafeDefaultLayout.test.ts +92 -0
- package/src/utils/calculateUnsafeDefaultLayout.ts +55 -0
- package/src/utils/callPanelCallbacks.ts +81 -0
- package/src/utils/compareLayouts.test.ts +9 -0
- package/src/utils/compareLayouts.ts +12 -0
- package/src/utils/computePanelFlexBoxStyle.ts +44 -0
- package/src/utils/computePercentagePanelConstraints.test.ts +71 -0
- package/src/utils/computePercentagePanelConstraints.ts +56 -0
- package/src/utils/convertPercentageToPixels.test.ts +9 -0
- package/src/utils/convertPercentageToPixels.ts +6 -0
- package/src/utils/convertPixelConstraintsToPercentages.ts +55 -0
- package/src/utils/convertPixelsToPercentage.test.ts +9 -0
- package/src/utils/convertPixelsToPercentage.ts +6 -0
- package/src/utils/determinePivotIndices.ts +10 -0
- package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +29 -0
- package/src/utils/dom/getAvailableGroupSizePixels.ts +29 -0
- package/src/utils/dom/getPanelElement.ts +7 -0
- package/src/utils/dom/getPanelGroupElement.ts +7 -0
- package/src/utils/dom/getResizeHandleElement.ts +9 -0
- package/src/utils/dom/getResizeHandleElementIndex.ts +12 -0
- package/src/utils/dom/getResizeHandleElementsForGroup.ts +9 -0
- package/src/utils/dom/getResizeHandlePanelIds.ts +18 -0
- package/src/utils/events.ts +13 -0
- package/src/utils/getPercentageSizeFromMixedSizes.test.ts +47 -0
- package/src/utils/getPercentageSizeFromMixedSizes.ts +15 -0
- package/src/utils/getResizeEventCursorPosition.ts +19 -0
- package/src/utils/initializeDefaultStorage.ts +26 -0
- package/src/utils/numbers/fuzzyCompareNumbers.test.ts +16 -0
- package/src/utils/numbers/fuzzyCompareNumbers.ts +17 -0
- package/src/utils/numbers/fuzzyNumbersEqual.ts +9 -0
- package/src/utils/resizePanel.ts +41 -0
- package/src/utils/serialization.ts +9 -4
- package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +23 -0
- package/src/utils/shouldMonitorPixelBasedConstraints.ts +13 -0
- package/src/utils/test-utils.ts +136 -0
- package/src/utils/validatePanelConstraints.test.ts +151 -0
- package/src/utils/validatePanelConstraints.ts +103 -0
- package/src/utils/validatePanelGroupLayout.test.ts +233 -0
- package/src/utils/validatePanelGroupLayout.ts +88 -0
- package/src/vendor/react.ts +4 -0
- package/.eslintrc.json +0 -22
- package/dist/declarations/src/utils/group.d.ts +0 -29
- package/src/PanelContexts.ts +0 -22
- package/src/utils/coordinates.ts +0 -149
- package/src/utils/group.ts +0 -614
|
@@ -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
|
+
}
|