react-resizable-panels 0.0.55 → 0.0.57
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 +238 -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 +1276 -1043
- package/dist/react-resizable-panels.browser.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.cjs.js +1410 -1097
- package/dist/react-resizable-panels.browser.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.esm.js +1411 -1097
- package/dist/react-resizable-panels.browser.esm.js +1277 -1043
- package/dist/react-resizable-panels.cjs.js +1276 -1043
- 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 +1415 -1102
- package/dist/react-resizable-panels.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.esm.js +1416 -1102
- package/dist/react-resizable-panels.development.node.cjs.js +1179 -947
- package/dist/react-resizable-panels.development.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.node.esm.js +1180 -947
- package/dist/react-resizable-panels.esm.js +1277 -1043
- package/dist/react-resizable-panels.esm.js.map +1 -1
- package/dist/react-resizable-panels.node.cjs.js +1068 -910
- package/dist/react-resizable-panels.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.node.esm.js +1069 -910
- package/jest.config.js +10 -0
- package/package.json +5 -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 -667
- package/src/PanelGroupContext.ts +33 -0
- package/src/PanelResizeHandle.ts +21 -17
- 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 +98 -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.test.ts +47 -0
- package/src/utils/convertPixelConstraintsToPercentages.ts +72 -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 +9 -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.test.ts +45 -0
- package/src/utils/resizePanel.ts +60 -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
package/src/utils/group.ts
DELETED
|
@@ -1,614 +0,0 @@
|
|
|
1
|
-
import { isDevelopment } from "#is-development";
|
|
2
|
-
import { CommittedValues, InitialDragState } from "../PanelGroup";
|
|
3
|
-
import { PRECISION } from "../constants";
|
|
4
|
-
import { PanelData, ResizeEvent, Units } from "../types";
|
|
5
|
-
|
|
6
|
-
export function adjustByDelta(
|
|
7
|
-
event: ResizeEvent | null,
|
|
8
|
-
committedValues: CommittedValues,
|
|
9
|
-
idBefore: string,
|
|
10
|
-
idAfter: string,
|
|
11
|
-
deltaPixels: number,
|
|
12
|
-
prevSizes: number[],
|
|
13
|
-
panelSizeBeforeCollapse: Map<string, number>,
|
|
14
|
-
initialDragState: InitialDragState | null
|
|
15
|
-
): number[] {
|
|
16
|
-
const { id: groupId, panels, units } = committedValues;
|
|
17
|
-
|
|
18
|
-
const groupSizePixels =
|
|
19
|
-
units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
|
|
20
|
-
|
|
21
|
-
const { sizes: initialSizes } = initialDragState || {};
|
|
22
|
-
|
|
23
|
-
// If we're resizing by mouse or touch, use the initial sizes as a base.
|
|
24
|
-
// This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
|
|
25
|
-
const baseSizes = initialSizes || prevSizes;
|
|
26
|
-
|
|
27
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
28
|
-
|
|
29
|
-
const nextSizes = baseSizes.concat();
|
|
30
|
-
|
|
31
|
-
let deltaApplied = 0;
|
|
32
|
-
|
|
33
|
-
// A resizing panel affects the panels before or after it.
|
|
34
|
-
//
|
|
35
|
-
// A negative delta means the panel immediately after the resizer should grow/expand by decreasing its offset.
|
|
36
|
-
// Other panels may also need to shrink/contract (and shift) to make room, depending on the min weights.
|
|
37
|
-
//
|
|
38
|
-
// A positive delta means the panel immediately before the resizer should "expand".
|
|
39
|
-
// This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resizer.
|
|
40
|
-
|
|
41
|
-
// Max-bounds check the panel being expanded first.
|
|
42
|
-
{
|
|
43
|
-
const pivotId = deltaPixels < 0 ? idAfter : idBefore;
|
|
44
|
-
const index = panelsArray.findIndex(
|
|
45
|
-
(panel) => panel.current.id === pivotId
|
|
46
|
-
);
|
|
47
|
-
const panel = panelsArray[index];
|
|
48
|
-
const baseSize = baseSizes[index];
|
|
49
|
-
|
|
50
|
-
const nextSize = safeResizePanel(
|
|
51
|
-
units,
|
|
52
|
-
groupSizePixels,
|
|
53
|
-
panel,
|
|
54
|
-
baseSize,
|
|
55
|
-
baseSize + Math.abs(deltaPixels),
|
|
56
|
-
event
|
|
57
|
-
);
|
|
58
|
-
if (baseSize === nextSize) {
|
|
59
|
-
// If there's no room for the pivot panel to grow, we can ignore this drag update.
|
|
60
|
-
return baseSizes;
|
|
61
|
-
} else {
|
|
62
|
-
if (nextSize === 0 && baseSize > 0) {
|
|
63
|
-
panelSizeBeforeCollapse.set(pivotId, baseSize);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
deltaPixels = deltaPixels < 0 ? baseSize - nextSize : nextSize - baseSize;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let pivotId = deltaPixels < 0 ? idBefore : idAfter;
|
|
71
|
-
let index = panelsArray.findIndex((panel) => panel.current.id === pivotId);
|
|
72
|
-
while (true) {
|
|
73
|
-
const panel = panelsArray[index];
|
|
74
|
-
const baseSize = baseSizes[index];
|
|
75
|
-
|
|
76
|
-
const deltaRemaining = Math.abs(deltaPixels) - Math.abs(deltaApplied);
|
|
77
|
-
|
|
78
|
-
const nextSize = safeResizePanel(
|
|
79
|
-
units,
|
|
80
|
-
groupSizePixels,
|
|
81
|
-
panel,
|
|
82
|
-
baseSize,
|
|
83
|
-
baseSize - deltaRemaining,
|
|
84
|
-
event
|
|
85
|
-
);
|
|
86
|
-
if (baseSize !== nextSize) {
|
|
87
|
-
if (nextSize === 0 && baseSize > 0) {
|
|
88
|
-
panelSizeBeforeCollapse.set(panel.current.id, baseSize);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
deltaApplied += baseSize - nextSize;
|
|
92
|
-
|
|
93
|
-
nextSizes[index] = nextSize;
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
deltaApplied
|
|
97
|
-
.toPrecision(PRECISION)
|
|
98
|
-
.localeCompare(
|
|
99
|
-
Math.abs(deltaPixels).toPrecision(PRECISION),
|
|
100
|
-
undefined,
|
|
101
|
-
{
|
|
102
|
-
numeric: true,
|
|
103
|
-
}
|
|
104
|
-
) >= 0
|
|
105
|
-
) {
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (deltaPixels < 0) {
|
|
111
|
-
if (--index < 0) {
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
if (++index >= panelsArray.length) {
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// If we were unable to resize any of the panels panels, return the previous state.
|
|
122
|
-
// This will essentially bailout and ignore the "mousemove" event.
|
|
123
|
-
if (deltaApplied === 0) {
|
|
124
|
-
return baseSizes;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
|
|
128
|
-
pivotId = deltaPixels < 0 ? idAfter : idBefore;
|
|
129
|
-
index = panelsArray.findIndex((panel) => panel.current.id === pivotId);
|
|
130
|
-
nextSizes[index] = baseSizes[index] + deltaApplied;
|
|
131
|
-
|
|
132
|
-
return nextSizes;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function callPanelCallbacks(
|
|
136
|
-
panelsArray: PanelData[],
|
|
137
|
-
sizes: number[],
|
|
138
|
-
panelIdToLastNotifiedSizeMap: Record<string, number>
|
|
139
|
-
) {
|
|
140
|
-
sizes.forEach((size, index) => {
|
|
141
|
-
const panelRef = panelsArray[index];
|
|
142
|
-
if (!panelRef) {
|
|
143
|
-
// Handle initial mount (when panels are registered too late to be in the panels array)
|
|
144
|
-
// The subsequent render+effects will handle the resize notification
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const { callbacksRef, collapsedSize, collapsible, id } = panelRef.current;
|
|
149
|
-
|
|
150
|
-
const lastNotifiedSize = panelIdToLastNotifiedSizeMap[id];
|
|
151
|
-
if (lastNotifiedSize !== size) {
|
|
152
|
-
panelIdToLastNotifiedSizeMap[id] = size;
|
|
153
|
-
|
|
154
|
-
const { onCollapse, onResize } = callbacksRef.current!;
|
|
155
|
-
|
|
156
|
-
if (onResize) {
|
|
157
|
-
onResize(size, lastNotifiedSize);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (collapsible && onCollapse) {
|
|
161
|
-
if (
|
|
162
|
-
(lastNotifiedSize == null || lastNotifiedSize === collapsedSize) &&
|
|
163
|
-
size !== collapsedSize
|
|
164
|
-
) {
|
|
165
|
-
onCollapse(false);
|
|
166
|
-
} else if (
|
|
167
|
-
lastNotifiedSize !== collapsedSize &&
|
|
168
|
-
size === collapsedSize
|
|
169
|
-
) {
|
|
170
|
-
onCollapse(true);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export function calculateDefaultLayout({
|
|
178
|
-
groupId,
|
|
179
|
-
panels,
|
|
180
|
-
units,
|
|
181
|
-
}: {
|
|
182
|
-
groupId: string;
|
|
183
|
-
panels: Map<string, PanelData>;
|
|
184
|
-
units: Units;
|
|
185
|
-
}): number[] {
|
|
186
|
-
const groupSizePixels =
|
|
187
|
-
units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
|
|
188
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
189
|
-
const sizes = Array<number>(panelsArray.length);
|
|
190
|
-
|
|
191
|
-
let numPanelsWithSizes = 0;
|
|
192
|
-
let remainingSize = 100;
|
|
193
|
-
|
|
194
|
-
// Assigning default sizes requires a couple of passes:
|
|
195
|
-
// First, all panels with defaultSize should be set as-is
|
|
196
|
-
for (let index = 0; index < panelsArray.length; index++) {
|
|
197
|
-
const panel = panelsArray[index];
|
|
198
|
-
const { defaultSize } = panel.current;
|
|
199
|
-
|
|
200
|
-
if (defaultSize != null) {
|
|
201
|
-
numPanelsWithSizes++;
|
|
202
|
-
|
|
203
|
-
sizes[index] =
|
|
204
|
-
units === "pixels"
|
|
205
|
-
? (defaultSize / groupSizePixels) * 100
|
|
206
|
-
: defaultSize;
|
|
207
|
-
|
|
208
|
-
remainingSize -= sizes[index];
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Remaining total size should be distributed evenly between panels
|
|
213
|
-
// This may require two passes, depending on min/max constraints
|
|
214
|
-
for (let index = 0; index < panelsArray.length; index++) {
|
|
215
|
-
const panel = panelsArray[index];
|
|
216
|
-
let { defaultSize, id, maxSize, minSize } = panel.current;
|
|
217
|
-
if (defaultSize != null) {
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (units === "pixels") {
|
|
222
|
-
minSize = (minSize / groupSizePixels) * 100;
|
|
223
|
-
if (maxSize != null) {
|
|
224
|
-
maxSize = (maxSize / groupSizePixels) * 100;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const remainingPanels = panelsArray.length - numPanelsWithSizes;
|
|
229
|
-
const size = Math.min(
|
|
230
|
-
maxSize != null ? maxSize : 100,
|
|
231
|
-
Math.max(minSize, remainingSize / remainingPanels)
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
sizes[index] = size;
|
|
235
|
-
numPanelsWithSizes++;
|
|
236
|
-
remainingSize -= size;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// If there is additional, left over space, assign it to any panel(s) that permits it
|
|
240
|
-
// (It's not worth taking multiple additional passes to evenly distribute)
|
|
241
|
-
if (remainingSize !== 0) {
|
|
242
|
-
for (let index = 0; index < panelsArray.length; index++) {
|
|
243
|
-
const panel = panelsArray[index];
|
|
244
|
-
let { maxSize, minSize } = panel.current;
|
|
245
|
-
|
|
246
|
-
if (units === "pixels") {
|
|
247
|
-
minSize = (minSize / groupSizePixels) * 100;
|
|
248
|
-
if (maxSize != null) {
|
|
249
|
-
maxSize = (maxSize / groupSizePixels) * 100;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const size = Math.min(
|
|
254
|
-
maxSize != null ? maxSize : 100,
|
|
255
|
-
Math.max(minSize, sizes[index] + remainingSize)
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
if (size !== sizes[index]) {
|
|
259
|
-
remainingSize -= size - sizes[index];
|
|
260
|
-
sizes[index] = size;
|
|
261
|
-
|
|
262
|
-
// Fuzzy comparison to account for imprecise floating point math
|
|
263
|
-
if (Math.abs(remainingSize).toFixed(3) === "0.000") {
|
|
264
|
-
break;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Finally, if there is still left-over size, log an error
|
|
271
|
-
if (Math.abs(remainingSize).toFixed(3) !== "0.000") {
|
|
272
|
-
if (isDevelopment) {
|
|
273
|
-
console.error(
|
|
274
|
-
`Invalid panel group configuration; default panel sizes should total 100% but was ${(
|
|
275
|
-
100 - remainingSize
|
|
276
|
-
).toFixed(
|
|
277
|
-
1
|
|
278
|
-
)}%. This can cause the cursor to become misaligned while dragging.`
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return sizes;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export function getBeforeAndAfterIds(
|
|
287
|
-
id: string,
|
|
288
|
-
panelsArray: PanelData[]
|
|
289
|
-
): [idBefore: string | null, idAFter: string | null] {
|
|
290
|
-
if (panelsArray.length < 2) {
|
|
291
|
-
return [null, null];
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const index = panelsArray.findIndex((panel) => panel.current.id === id);
|
|
295
|
-
if (index < 0) {
|
|
296
|
-
return [null, null];
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const isLastPanel = index === panelsArray.length - 1;
|
|
300
|
-
const idBefore = isLastPanel ? panelsArray[index - 1].current.id : id;
|
|
301
|
-
const idAfter = isLastPanel ? id : panelsArray[index + 1].current.id;
|
|
302
|
-
|
|
303
|
-
return [idBefore, idAfter];
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
export function getAvailableGroupSizePixels(groupId: string): number {
|
|
307
|
-
const panelGroupElement = getPanelGroup(groupId);
|
|
308
|
-
if (panelGroupElement == null) {
|
|
309
|
-
return NaN;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const direction = panelGroupElement.getAttribute(
|
|
313
|
-
"data-panel-group-direction"
|
|
314
|
-
);
|
|
315
|
-
const resizeHandles = getResizeHandlesForGroup(groupId);
|
|
316
|
-
if (direction === "horizontal") {
|
|
317
|
-
return (
|
|
318
|
-
panelGroupElement.offsetWidth -
|
|
319
|
-
resizeHandles.reduce((accumulated, handle) => {
|
|
320
|
-
return accumulated + handle.offsetWidth;
|
|
321
|
-
}, 0)
|
|
322
|
-
);
|
|
323
|
-
} else {
|
|
324
|
-
return (
|
|
325
|
-
panelGroupElement.offsetHeight -
|
|
326
|
-
resizeHandles.reduce((accumulated, handle) => {
|
|
327
|
-
return accumulated + handle.offsetHeight;
|
|
328
|
-
}, 0)
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// This method returns a number between 1 and 100 representing
|
|
334
|
-
// the % of the group's overall space this panel should occupy.
|
|
335
|
-
export function getFlexGrow(
|
|
336
|
-
panels: Map<string, PanelData>,
|
|
337
|
-
id: string,
|
|
338
|
-
sizes: number[]
|
|
339
|
-
): string {
|
|
340
|
-
if (panels.size === 1) {
|
|
341
|
-
return "100";
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
345
|
-
|
|
346
|
-
const index = panelsArray.findIndex((panel) => panel.current.id === id);
|
|
347
|
-
const size = sizes[index];
|
|
348
|
-
if (size == null) {
|
|
349
|
-
return "0";
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return size.toPrecision(PRECISION);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
export function getPanel(id: string): HTMLDivElement | null {
|
|
356
|
-
const element = document.querySelector(`[data-panel-id="${id}"]`);
|
|
357
|
-
if (element) {
|
|
358
|
-
return element as HTMLDivElement;
|
|
359
|
-
}
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
export function getPanelGroup(id: string): HTMLDivElement | null {
|
|
364
|
-
const element = document.querySelector(`[data-panel-group-id="${id}"]`);
|
|
365
|
-
if (element) {
|
|
366
|
-
return element as HTMLDivElement;
|
|
367
|
-
}
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
export function getResizeHandle(id: string): HTMLDivElement | null {
|
|
372
|
-
const element = document.querySelector(
|
|
373
|
-
`[data-panel-resize-handle-id="${id}"]`
|
|
374
|
-
);
|
|
375
|
-
if (element) {
|
|
376
|
-
return element as HTMLDivElement;
|
|
377
|
-
}
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
export function getResizeHandleIndex(id: string): number | null {
|
|
382
|
-
const handles = getResizeHandles();
|
|
383
|
-
const index = handles.findIndex(
|
|
384
|
-
(handle) => handle.getAttribute("data-panel-resize-handle-id") === id
|
|
385
|
-
);
|
|
386
|
-
return index ?? null;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export function getResizeHandles(): HTMLDivElement[] {
|
|
390
|
-
return Array.from(document.querySelectorAll(`[data-panel-resize-handle-id]`));
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
export function getResizeHandlesForGroup(groupId: string): HTMLDivElement[] {
|
|
394
|
-
return Array.from(
|
|
395
|
-
document.querySelectorAll(
|
|
396
|
-
`[data-panel-resize-handle-id][data-panel-group-id="${groupId}"]`
|
|
397
|
-
)
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
export function getResizeHandlePanelIds(
|
|
402
|
-
groupId: string,
|
|
403
|
-
handleId: string,
|
|
404
|
-
panelsArray: PanelData[]
|
|
405
|
-
): [idBefore: string | null, idAfter: string | null] {
|
|
406
|
-
const handle = getResizeHandle(handleId);
|
|
407
|
-
const handles = getResizeHandlesForGroup(groupId);
|
|
408
|
-
const index = handle ? handles.indexOf(handle) : -1;
|
|
409
|
-
|
|
410
|
-
const idBefore: string | null = panelsArray[index]?.current?.id ?? null;
|
|
411
|
-
const idAfter: string | null = panelsArray[index + 1]?.current?.id ?? null;
|
|
412
|
-
|
|
413
|
-
return [idBefore, idAfter];
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
export function panelsMapToSortedArray(
|
|
417
|
-
panels: Map<string, PanelData>
|
|
418
|
-
): PanelData[] {
|
|
419
|
-
return Array.from(panels.values()).sort((panelA, panelB) => {
|
|
420
|
-
const orderA = panelA.current.order;
|
|
421
|
-
const orderB = panelB.current.order;
|
|
422
|
-
if (orderA == null && orderB == null) {
|
|
423
|
-
return 0;
|
|
424
|
-
} else if (orderA == null) {
|
|
425
|
-
return -1;
|
|
426
|
-
} else if (orderB == null) {
|
|
427
|
-
return 1;
|
|
428
|
-
} else {
|
|
429
|
-
return orderA - orderB;
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
export function safeResizePanel(
|
|
435
|
-
units: Units,
|
|
436
|
-
groupSizePixels: number,
|
|
437
|
-
panel: PanelData,
|
|
438
|
-
prevSize: number,
|
|
439
|
-
nextSize: number,
|
|
440
|
-
event: ResizeEvent | null = null
|
|
441
|
-
): number {
|
|
442
|
-
let { collapsedSize, collapsible, maxSize, minSize } = panel.current;
|
|
443
|
-
|
|
444
|
-
if (units === "pixels") {
|
|
445
|
-
collapsedSize = (collapsedSize / groupSizePixels) * 100;
|
|
446
|
-
if (maxSize != null) {
|
|
447
|
-
maxSize = (maxSize / groupSizePixels) * 100;
|
|
448
|
-
}
|
|
449
|
-
minSize = (minSize / groupSizePixels) * 100;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (collapsible) {
|
|
453
|
-
if (prevSize > collapsedSize) {
|
|
454
|
-
// Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
|
|
455
|
-
if (nextSize <= minSize / 2 + collapsedSize) {
|
|
456
|
-
return collapsedSize;
|
|
457
|
-
}
|
|
458
|
-
} else {
|
|
459
|
-
const isKeyboardEvent = event?.type?.startsWith("key");
|
|
460
|
-
if (!isKeyboardEvent) {
|
|
461
|
-
// Keyboard events should expand a collapsed panel to the min size,
|
|
462
|
-
// but mouse events should wait until the panel has reached its min size
|
|
463
|
-
// to avoid a visual flickering when dragging between collapsed and min size.
|
|
464
|
-
if (nextSize < minSize) {
|
|
465
|
-
return collapsedSize;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
return Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
export function validatePanelProps(units: Units, panelData: PanelData) {
|
|
475
|
-
const { collapsible, defaultSize, maxSize, minSize } = panelData.current;
|
|
476
|
-
|
|
477
|
-
// Basic props validation
|
|
478
|
-
if (minSize < 0 || (units === "percentages" && minSize > 100)) {
|
|
479
|
-
if (isDevelopment) {
|
|
480
|
-
console.error(`Invalid Panel minSize provided, ${minSize}`);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
panelData.current.minSize = 0;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (maxSize != null) {
|
|
487
|
-
if (maxSize < 0 || (units === "percentages" && maxSize > 100)) {
|
|
488
|
-
if (isDevelopment) {
|
|
489
|
-
console.error(`Invalid Panel maxSize provided, ${maxSize}`);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
panelData.current.maxSize = null;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if (defaultSize !== null) {
|
|
497
|
-
if (defaultSize < 0 || (units === "percentages" && defaultSize > 100)) {
|
|
498
|
-
if (isDevelopment) {
|
|
499
|
-
console.error(`Invalid Panel defaultSize provided, ${defaultSize}`);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
panelData.current.defaultSize = null;
|
|
503
|
-
} else if (defaultSize < minSize && !collapsible) {
|
|
504
|
-
if (isDevelopment) {
|
|
505
|
-
console.error(
|
|
506
|
-
`Panel minSize (${minSize}) cannot be greater than defaultSize (${defaultSize})`
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
panelData.current.defaultSize = minSize;
|
|
511
|
-
} else if (maxSize != null && defaultSize > maxSize) {
|
|
512
|
-
if (isDevelopment) {
|
|
513
|
-
console.error(
|
|
514
|
-
`Panel maxSize (${maxSize}) cannot be less than defaultSize (${defaultSize})`
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
panelData.current.defaultSize = maxSize;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
export function validatePanelGroupLayout({
|
|
524
|
-
groupId,
|
|
525
|
-
panels,
|
|
526
|
-
nextSizes,
|
|
527
|
-
prevSizes,
|
|
528
|
-
units,
|
|
529
|
-
}: {
|
|
530
|
-
groupId: string;
|
|
531
|
-
panels: Map<string, PanelData>;
|
|
532
|
-
nextSizes: number[];
|
|
533
|
-
prevSizes: number[];
|
|
534
|
-
units: Units;
|
|
535
|
-
}): number[] {
|
|
536
|
-
// Clone because this method modifies
|
|
537
|
-
nextSizes = [...nextSizes];
|
|
538
|
-
|
|
539
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
540
|
-
|
|
541
|
-
const groupSizePixels =
|
|
542
|
-
units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
|
|
543
|
-
|
|
544
|
-
let remainingSize = 0;
|
|
545
|
-
|
|
546
|
-
// First, check all of the proposed sizes against the min/max constraints
|
|
547
|
-
for (let index = 0; index < panelsArray.length; index++) {
|
|
548
|
-
const panel = panelsArray[index];
|
|
549
|
-
const prevSize = prevSizes[index];
|
|
550
|
-
const nextSize = nextSizes[index];
|
|
551
|
-
const safeNextSize = safeResizePanel(
|
|
552
|
-
units,
|
|
553
|
-
groupSizePixels,
|
|
554
|
-
panel,
|
|
555
|
-
prevSize,
|
|
556
|
-
nextSize
|
|
557
|
-
);
|
|
558
|
-
if (nextSize != safeNextSize) {
|
|
559
|
-
remainingSize += nextSize - safeNextSize;
|
|
560
|
-
nextSizes[index] = safeNextSize;
|
|
561
|
-
|
|
562
|
-
if (isDevelopment) {
|
|
563
|
-
console.error(
|
|
564
|
-
`Invalid size (${nextSize}) specified for Panel "${panel.current.id}" given the panel's min/max size constraints`
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// If there is additional, left over space, assign it to any panel(s) that permits it
|
|
571
|
-
// (It's not worth taking multiple additional passes to evenly distribute)
|
|
572
|
-
if (remainingSize.toFixed(3) !== "0.000") {
|
|
573
|
-
for (let index = 0; index < panelsArray.length; index++) {
|
|
574
|
-
const panel = panelsArray[index];
|
|
575
|
-
|
|
576
|
-
let { maxSize, minSize } = panel.current;
|
|
577
|
-
|
|
578
|
-
if (units === "pixels") {
|
|
579
|
-
minSize = (minSize / groupSizePixels) * 100;
|
|
580
|
-
if (maxSize != null) {
|
|
581
|
-
maxSize = (maxSize / groupSizePixels) * 100;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
const size = Math.min(
|
|
586
|
-
maxSize != null ? maxSize : 100,
|
|
587
|
-
Math.max(minSize, nextSizes[index] + remainingSize)
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
if (size !== nextSizes[index]) {
|
|
591
|
-
remainingSize -= size - nextSizes[index];
|
|
592
|
-
nextSizes[index] = size;
|
|
593
|
-
|
|
594
|
-
// Fuzzy comparison to account for imprecise floating point math
|
|
595
|
-
if (Math.abs(remainingSize).toFixed(3) === "0.000") {
|
|
596
|
-
break;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// If we still have remainder, the requested layout wasn't valid and we should warn about it
|
|
603
|
-
if (remainingSize.toFixed(3) !== "0.000") {
|
|
604
|
-
if (isDevelopment) {
|
|
605
|
-
console.error(
|
|
606
|
-
`"Invalid panel group configuration; default panel sizes should total 100% but was ${
|
|
607
|
-
100 - remainingSize
|
|
608
|
-
}%`
|
|
609
|
-
);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
return nextSizes;
|
|
614
|
-
}
|