react-resizable-panels 0.0.59 → 0.0.61
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/CHANGELOG.md +10 -0
- package/dist/declarations/src/PanelGroup.d.ts +2 -2
- package/dist/react-resizable-panels.browser.cjs.js +226 -166
- package/dist/react-resizable-panels.browser.development.cjs.js +229 -169
- package/dist/react-resizable-panels.browser.development.esm.js +229 -169
- package/dist/react-resizable-panels.browser.esm.js +226 -166
- package/dist/react-resizable-panels.cjs.js +226 -166
- package/dist/react-resizable-panels.cjs.js.map +1 -1
- package/dist/react-resizable-panels.development.cjs.js +229 -169
- package/dist/react-resizable-panels.development.esm.js +229 -169
- package/dist/react-resizable-panels.development.node.cjs.js +273 -87
- package/dist/react-resizable-panels.development.node.esm.js +273 -87
- package/dist/react-resizable-panels.esm.js +226 -166
- package/dist/react-resizable-panels.esm.js.map +1 -1
- package/dist/react-resizable-panels.node.cjs.js +270 -84
- package/dist/react-resizable-panels.node.esm.js +270 -84
- package/package.json +1 -1
- package/src/Panel.ts +2 -0
- package/src/PanelGroup.ts +242 -208
- package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +12 -2
- package/src/utils/dom/getPanelElementsForGroup.ts +5 -0
package/src/PanelGroup.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
|
|
|
18
18
|
import debounce from "./utils/debounce";
|
|
19
19
|
import { determinePivotIndices } from "./utils/determinePivotIndices";
|
|
20
20
|
import { calculateAvailablePanelSizeInPixels } from "./utils/dom/calculateAvailablePanelSizeInPixels";
|
|
21
|
+
import { getPanelElementsForGroup } from "./utils/dom/getPanelElementsForGroup";
|
|
21
22
|
import { getPanelGroupElement } from "./utils/dom/getPanelGroupElement";
|
|
22
23
|
import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
|
|
23
24
|
import { isKeyDown, isMouseEvent, isTouchEvent } from "./utils/events";
|
|
@@ -70,7 +71,7 @@ const defaultStorage: PanelGroupStorage = {
|
|
|
70
71
|
};
|
|
71
72
|
|
|
72
73
|
export type PanelGroupProps = PropsWithChildren<{
|
|
73
|
-
autoSaveId?: string;
|
|
74
|
+
autoSaveId?: string | null;
|
|
74
75
|
className?: string;
|
|
75
76
|
dataAttributes?: DataAttributes;
|
|
76
77
|
direction: Direction;
|
|
@@ -83,18 +84,12 @@ export type PanelGroupProps = PropsWithChildren<{
|
|
|
83
84
|
tagName?: ElementType;
|
|
84
85
|
}>;
|
|
85
86
|
|
|
86
|
-
type ImperativeApiQueue = {
|
|
87
|
-
type: "collapse" | "expand" | "resize";
|
|
88
|
-
mixedSizes?: Partial<MixedSizes>;
|
|
89
|
-
panelData: PanelData;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
87
|
const debounceMap: {
|
|
93
88
|
[key: string]: typeof savePanelGroupLayout;
|
|
94
89
|
} = {};
|
|
95
90
|
|
|
96
91
|
function PanelGroupWithForwardedRef({
|
|
97
|
-
autoSaveId,
|
|
92
|
+
autoSaveId = null,
|
|
98
93
|
children,
|
|
99
94
|
className: classNameFromProps = "",
|
|
100
95
|
dataAttributes,
|
|
@@ -114,7 +109,6 @@ function PanelGroupWithForwardedRef({
|
|
|
114
109
|
|
|
115
110
|
const [dragState, setDragState] = useState<DragState | null>(null);
|
|
116
111
|
const [layout, setLayout] = useState<number[]>([]);
|
|
117
|
-
const [panelDataArray, setPanelDataArray] = useState<PanelData[]>([]);
|
|
118
112
|
|
|
119
113
|
const panelIdToLastNotifiedMixedSizesMapRef = useRef<
|
|
120
114
|
Record<string, MixedSizes>
|
|
@@ -122,28 +116,32 @@ function PanelGroupWithForwardedRef({
|
|
|
122
116
|
const panelSizeBeforeCollapseRef = useRef<Map<string, number>>(new Map());
|
|
123
117
|
const prevDeltaRef = useRef<number>(0);
|
|
124
118
|
|
|
125
|
-
const [imperativeApiQueue, setImperativeApiQueue] = useState<
|
|
126
|
-
ImperativeApiQueue[]
|
|
127
|
-
>([]);
|
|
128
|
-
|
|
129
119
|
const committedValuesRef = useRef<{
|
|
120
|
+
autoSaveId: string | null;
|
|
130
121
|
direction: Direction;
|
|
131
122
|
dragState: DragState | null;
|
|
132
123
|
id: string;
|
|
133
124
|
keyboardResizeByPercentage: number | null;
|
|
134
125
|
keyboardResizeByPixels: number | null;
|
|
135
|
-
layout: number[];
|
|
136
126
|
onLayout: PanelGroupOnLayout | null;
|
|
137
|
-
|
|
127
|
+
storage: PanelGroupStorage;
|
|
138
128
|
}>({
|
|
129
|
+
autoSaveId,
|
|
139
130
|
direction,
|
|
140
131
|
dragState,
|
|
141
132
|
id: groupId,
|
|
142
133
|
keyboardResizeByPercentage,
|
|
143
134
|
keyboardResizeByPixels,
|
|
144
|
-
layout,
|
|
145
135
|
onLayout,
|
|
146
|
-
|
|
136
|
+
storage,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const eagerValuesRef = useRef<{
|
|
140
|
+
layout: number[];
|
|
141
|
+
panelDataArray: PanelData[];
|
|
142
|
+
}>({
|
|
143
|
+
layout,
|
|
144
|
+
panelDataArray: [],
|
|
147
145
|
});
|
|
148
146
|
|
|
149
147
|
const devWarningsRef = useRef<{
|
|
@@ -161,7 +159,8 @@ function PanelGroupWithForwardedRef({
|
|
|
161
159
|
() => ({
|
|
162
160
|
getId: () => committedValuesRef.current.id,
|
|
163
161
|
getLayout: () => {
|
|
164
|
-
const { id: groupId
|
|
162
|
+
const { id: groupId } = committedValuesRef.current;
|
|
163
|
+
const { layout } = eagerValuesRef.current;
|
|
165
164
|
|
|
166
165
|
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
167
166
|
|
|
@@ -176,12 +175,8 @@ function PanelGroupWithForwardedRef({
|
|
|
176
175
|
});
|
|
177
176
|
},
|
|
178
177
|
setLayout: (mixedSizes: Partial<MixedSizes>[]) => {
|
|
179
|
-
const {
|
|
180
|
-
|
|
181
|
-
layout: prevLayout,
|
|
182
|
-
onLayout,
|
|
183
|
-
panelDataArray,
|
|
184
|
-
} = committedValuesRef.current;
|
|
178
|
+
const { id: groupId, onLayout } = committedValuesRef.current;
|
|
179
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
185
180
|
|
|
186
181
|
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
187
182
|
|
|
@@ -201,6 +196,8 @@ function PanelGroupWithForwardedRef({
|
|
|
201
196
|
if (!areEqual(prevLayout, safeLayout)) {
|
|
202
197
|
setLayout(safeLayout);
|
|
203
198
|
|
|
199
|
+
eagerValuesRef.current.layout = safeLayout;
|
|
200
|
+
|
|
204
201
|
if (onLayout) {
|
|
205
202
|
onLayout(
|
|
206
203
|
safeLayout.map((sizePercentage) => ({
|
|
@@ -226,23 +223,29 @@ function PanelGroupWithForwardedRef({
|
|
|
226
223
|
);
|
|
227
224
|
|
|
228
225
|
useIsomorphicLayoutEffect(() => {
|
|
226
|
+
committedValuesRef.current.autoSaveId = autoSaveId;
|
|
229
227
|
committedValuesRef.current.direction = direction;
|
|
230
228
|
committedValuesRef.current.dragState = dragState;
|
|
231
229
|
committedValuesRef.current.id = groupId;
|
|
232
|
-
committedValuesRef.current.layout = layout;
|
|
233
230
|
committedValuesRef.current.onLayout = onLayout;
|
|
234
|
-
committedValuesRef.current.
|
|
231
|
+
committedValuesRef.current.storage = storage;
|
|
232
|
+
|
|
233
|
+
// panelDataArray and layout are updated in-sync with scheduled state updates.
|
|
234
|
+
// TODO [217] Move these values into a separate ref
|
|
235
235
|
});
|
|
236
236
|
|
|
237
237
|
useWindowSplitterPanelGroupBehavior({
|
|
238
238
|
committedValuesRef,
|
|
239
|
+
eagerValuesRef,
|
|
239
240
|
groupId,
|
|
240
241
|
layout,
|
|
241
|
-
panelDataArray,
|
|
242
|
+
panelDataArray: eagerValuesRef.current.panelDataArray,
|
|
242
243
|
setLayout,
|
|
243
244
|
});
|
|
244
245
|
|
|
245
246
|
useEffect(() => {
|
|
247
|
+
const { panelDataArray } = eagerValuesRef.current;
|
|
248
|
+
|
|
246
249
|
// If this panel has been configured to persist sizing information, save sizes to local storage.
|
|
247
250
|
if (autoSaveId) {
|
|
248
251
|
if (layout.length === 0 || layout.length !== panelDataArray.length) {
|
|
@@ -258,79 +261,11 @@ function PanelGroupWithForwardedRef({
|
|
|
258
261
|
}
|
|
259
262
|
debounceMap[autoSaveId](autoSaveId, panelDataArray, layout, storage);
|
|
260
263
|
}
|
|
261
|
-
}, [autoSaveId, layout,
|
|
264
|
+
}, [autoSaveId, layout, storage]);
|
|
262
265
|
|
|
263
|
-
// Once all panels have registered themselves,
|
|
264
|
-
// Compute the initial sizes based on default weights.
|
|
265
|
-
// This assumes that panels register during initial mount (no conditional rendering)!
|
|
266
266
|
useIsomorphicLayoutEffect(() => {
|
|
267
|
-
const {
|
|
268
|
-
if (layout.length === panelDataArray.length) {
|
|
269
|
-
// Only compute (or restore) default layout once per panel configuration.
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
267
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
272
268
|
|
|
273
|
-
// If this panel has been configured to persist sizing information,
|
|
274
|
-
// default size should be restored from local storage if possible.
|
|
275
|
-
let unsafeLayout: number[] | null = null;
|
|
276
|
-
if (autoSaveId) {
|
|
277
|
-
unsafeLayout = loadPanelLayout(autoSaveId, panelDataArray, storage);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
281
|
-
if (groupSizePixels <= 0) {
|
|
282
|
-
if (
|
|
283
|
-
shouldMonitorPixelBasedConstraints(
|
|
284
|
-
panelDataArray.map(({ constraints }) => constraints)
|
|
285
|
-
)
|
|
286
|
-
) {
|
|
287
|
-
// Wait until the group has rendered a non-zero size before computing layout.
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (unsafeLayout == null) {
|
|
293
|
-
unsafeLayout = calculateUnsafeDefaultLayout({
|
|
294
|
-
groupSizePixels,
|
|
295
|
-
panelDataArray,
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Validate even saved layouts in case something has changed since last render
|
|
300
|
-
// e.g. for pixel groups, this could be the size of the window
|
|
301
|
-
const validatedLayout = validatePanelGroupLayout({
|
|
302
|
-
groupSizePixels,
|
|
303
|
-
layout: unsafeLayout,
|
|
304
|
-
panelConstraints: panelDataArray.map(
|
|
305
|
-
(panelData) => panelData.constraints
|
|
306
|
-
),
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
if (!areEqual(layout, validatedLayout)) {
|
|
310
|
-
setLayout(validatedLayout);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (onLayout) {
|
|
314
|
-
onLayout(
|
|
315
|
-
validatedLayout.map((sizePercentage) => ({
|
|
316
|
-
sizePercentage,
|
|
317
|
-
sizePixels: convertPercentageToPixels(
|
|
318
|
-
sizePercentage,
|
|
319
|
-
groupSizePixels
|
|
320
|
-
),
|
|
321
|
-
}))
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
callPanelCallbacks(
|
|
326
|
-
groupId,
|
|
327
|
-
panelDataArray,
|
|
328
|
-
validatedLayout,
|
|
329
|
-
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
330
|
-
);
|
|
331
|
-
}, [autoSaveId, layout, panelDataArray, storage]);
|
|
332
|
-
|
|
333
|
-
useIsomorphicLayoutEffect(() => {
|
|
334
269
|
const constraints = panelDataArray.map(({ constraints }) => constraints);
|
|
335
270
|
if (!shouldMonitorPixelBasedConstraints(constraints)) {
|
|
336
271
|
// Avoid the overhead of ResizeObserver if no pixel constraints require monitoring
|
|
@@ -345,7 +280,7 @@ function PanelGroupWithForwardedRef({
|
|
|
345
280
|
const resizeObserver = new ResizeObserver(() => {
|
|
346
281
|
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
347
282
|
|
|
348
|
-
const {
|
|
283
|
+
const { onLayout } = committedValuesRef.current;
|
|
349
284
|
|
|
350
285
|
const nextLayout = validatePanelGroupLayout({
|
|
351
286
|
groupSizePixels,
|
|
@@ -358,6 +293,8 @@ function PanelGroupWithForwardedRef({
|
|
|
358
293
|
if (!areEqual(prevLayout, nextLayout)) {
|
|
359
294
|
setLayout(nextLayout);
|
|
360
295
|
|
|
296
|
+
eagerValuesRef.current.layout = nextLayout;
|
|
297
|
+
|
|
361
298
|
if (onLayout) {
|
|
362
299
|
onLayout(
|
|
363
300
|
nextLayout.map((sizePercentage) => ({
|
|
@@ -385,11 +322,13 @@ function PanelGroupWithForwardedRef({
|
|
|
385
322
|
resizeObserver.disconnect();
|
|
386
323
|
};
|
|
387
324
|
}
|
|
388
|
-
}, [groupId
|
|
325
|
+
}, [groupId]);
|
|
389
326
|
|
|
390
327
|
// DEV warnings
|
|
391
328
|
useEffect(() => {
|
|
392
329
|
if (isDevelopment) {
|
|
330
|
+
const { panelDataArray } = eagerValuesRef.current;
|
|
331
|
+
|
|
393
332
|
const {
|
|
394
333
|
didLogIdAndOrderWarning,
|
|
395
334
|
didLogPanelConstraintsWarning,
|
|
@@ -397,8 +336,6 @@ function PanelGroupWithForwardedRef({
|
|
|
397
336
|
} = devWarningsRef.current;
|
|
398
337
|
|
|
399
338
|
if (!didLogIdAndOrderWarning) {
|
|
400
|
-
const { panelDataArray } = committedValuesRef.current;
|
|
401
|
-
|
|
402
339
|
const panelIds = panelDataArray.map(({ id }) => id);
|
|
403
340
|
|
|
404
341
|
devWarningsRef.current.prevPanelIds = panelIds;
|
|
@@ -452,23 +389,8 @@ function PanelGroupWithForwardedRef({
|
|
|
452
389
|
// External APIs are safe to memoize via committed values ref
|
|
453
390
|
const collapsePanel = useCallback(
|
|
454
391
|
(panelData: PanelData) => {
|
|
455
|
-
const {
|
|
456
|
-
|
|
457
|
-
onLayout,
|
|
458
|
-
panelDataArray,
|
|
459
|
-
} = committedValuesRef.current;
|
|
460
|
-
|
|
461
|
-
// See issues/211
|
|
462
|
-
if (panelDataArray.find(({ id }) => id === panelData.id) == null) {
|
|
463
|
-
setImperativeApiQueue((prev) => [
|
|
464
|
-
...prev,
|
|
465
|
-
{
|
|
466
|
-
panelData,
|
|
467
|
-
type: "collapse",
|
|
468
|
-
},
|
|
469
|
-
]);
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
392
|
+
const { onLayout } = committedValuesRef.current;
|
|
393
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
472
394
|
|
|
473
395
|
if (panelData.constraints.collapsible) {
|
|
474
396
|
const panelConstraintsArray = panelDataArray.map(
|
|
@@ -508,6 +430,8 @@ function PanelGroupWithForwardedRef({
|
|
|
508
430
|
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
509
431
|
setLayout(nextLayout);
|
|
510
432
|
|
|
433
|
+
eagerValuesRef.current.layout = nextLayout;
|
|
434
|
+
|
|
511
435
|
if (onLayout) {
|
|
512
436
|
onLayout(
|
|
513
437
|
nextLayout.map((sizePercentage) => ({
|
|
@@ -536,23 +460,8 @@ function PanelGroupWithForwardedRef({
|
|
|
536
460
|
// External APIs are safe to memoize via committed values ref
|
|
537
461
|
const expandPanel = useCallback(
|
|
538
462
|
(panelData: PanelData) => {
|
|
539
|
-
const {
|
|
540
|
-
|
|
541
|
-
onLayout,
|
|
542
|
-
panelDataArray,
|
|
543
|
-
} = committedValuesRef.current;
|
|
544
|
-
|
|
545
|
-
// See issues/211
|
|
546
|
-
if (panelDataArray.find(({ id }) => id === panelData.id) == null) {
|
|
547
|
-
setImperativeApiQueue((prev) => [
|
|
548
|
-
...prev,
|
|
549
|
-
{
|
|
550
|
-
panelData,
|
|
551
|
-
type: "expand",
|
|
552
|
-
},
|
|
553
|
-
]);
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
463
|
+
const { onLayout } = committedValuesRef.current;
|
|
464
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
556
465
|
|
|
557
466
|
if (panelData.constraints.collapsible) {
|
|
558
467
|
const panelConstraintsArray = panelDataArray.map(
|
|
@@ -595,6 +504,8 @@ function PanelGroupWithForwardedRef({
|
|
|
595
504
|
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
596
505
|
setLayout(nextLayout);
|
|
597
506
|
|
|
507
|
+
eagerValuesRef.current.layout = nextLayout;
|
|
508
|
+
|
|
598
509
|
if (onLayout) {
|
|
599
510
|
onLayout(
|
|
600
511
|
nextLayout.map((sizePercentage) => ({
|
|
@@ -623,7 +534,7 @@ function PanelGroupWithForwardedRef({
|
|
|
623
534
|
// External APIs are safe to memoize via committed values ref
|
|
624
535
|
const getPanelSize = useCallback(
|
|
625
536
|
(panelData: PanelData) => {
|
|
626
|
-
const { layout, panelDataArray } =
|
|
537
|
+
const { layout, panelDataArray } = eagerValuesRef.current;
|
|
627
538
|
|
|
628
539
|
const { panelSizePercentage, panelSizePixels } = panelDataHelper(
|
|
629
540
|
groupId,
|
|
@@ -643,6 +554,8 @@ function PanelGroupWithForwardedRef({
|
|
|
643
554
|
// This API should never read from committedValuesRef
|
|
644
555
|
const getPanelStyle = useCallback(
|
|
645
556
|
(panelData: PanelData) => {
|
|
557
|
+
const { panelDataArray } = eagerValuesRef.current;
|
|
558
|
+
|
|
646
559
|
const panelIndex = panelDataArray.indexOf(panelData);
|
|
647
560
|
|
|
648
561
|
return computePanelFlexBoxStyle({
|
|
@@ -652,13 +565,13 @@ function PanelGroupWithForwardedRef({
|
|
|
652
565
|
panelIndex,
|
|
653
566
|
});
|
|
654
567
|
},
|
|
655
|
-
[dragState, layout
|
|
568
|
+
[dragState, layout]
|
|
656
569
|
);
|
|
657
570
|
|
|
658
571
|
// External APIs are safe to memoize via committed values ref
|
|
659
572
|
const isPanelCollapsed = useCallback(
|
|
660
573
|
(panelData: PanelData) => {
|
|
661
|
-
const { layout, panelDataArray } =
|
|
574
|
+
const { layout, panelDataArray } = eagerValuesRef.current;
|
|
662
575
|
|
|
663
576
|
const { collapsedSizePercentage, collapsible, panelSizePercentage } =
|
|
664
577
|
panelDataHelper(groupId, panelDataArray, panelData, layout);
|
|
@@ -673,7 +586,7 @@ function PanelGroupWithForwardedRef({
|
|
|
673
586
|
// External APIs are safe to memoize via committed values ref
|
|
674
587
|
const isPanelExpanded = useCallback(
|
|
675
588
|
(panelData: PanelData) => {
|
|
676
|
-
const { layout, panelDataArray } =
|
|
589
|
+
const { layout, panelDataArray } = eagerValuesRef.current;
|
|
677
590
|
|
|
678
591
|
const { collapsedSizePercentage, collapsible, panelSizePercentage } =
|
|
679
592
|
panelDataHelper(groupId, panelDataArray, panelData, layout);
|
|
@@ -684,22 +597,99 @@ function PanelGroupWithForwardedRef({
|
|
|
684
597
|
);
|
|
685
598
|
|
|
686
599
|
const registerPanel = useCallback((panelData: PanelData) => {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
600
|
+
const {
|
|
601
|
+
autoSaveId,
|
|
602
|
+
id: groupId,
|
|
603
|
+
onLayout,
|
|
604
|
+
storage,
|
|
605
|
+
} = committedValuesRef.current;
|
|
606
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
607
|
+
|
|
608
|
+
panelDataArray.push(panelData);
|
|
609
|
+
panelDataArray.sort((panelA, panelB) => {
|
|
610
|
+
const orderA = panelA.order;
|
|
611
|
+
const orderB = panelB.order;
|
|
612
|
+
if (orderA == null && orderB == null) {
|
|
613
|
+
return 0;
|
|
614
|
+
} else if (orderA == null) {
|
|
615
|
+
return -1;
|
|
616
|
+
} else if (orderB == null) {
|
|
617
|
+
return 1;
|
|
618
|
+
} else {
|
|
619
|
+
return orderA - orderB;
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
// Wait until all panels have registered before we try to compute layout;
|
|
624
|
+
// doing it earlier is both wasteful and may trigger misleading warnings in development mode.
|
|
625
|
+
const panelElements = getPanelElementsForGroup(groupId);
|
|
626
|
+
if (panelElements.length !== panelDataArray.length) {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// If this panel has been configured to persist sizing information,
|
|
631
|
+
// default size should be restored from local storage if possible.
|
|
632
|
+
let unsafeLayout: number[] | null = null;
|
|
633
|
+
if (autoSaveId) {
|
|
634
|
+
unsafeLayout = loadPanelLayout(autoSaveId, panelDataArray, storage);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
638
|
+
if (groupSizePixels <= 0) {
|
|
639
|
+
if (
|
|
640
|
+
shouldMonitorPixelBasedConstraints(
|
|
641
|
+
panelDataArray.map(({ constraints }) => constraints)
|
|
642
|
+
)
|
|
643
|
+
) {
|
|
644
|
+
// Wait until the group has rendered a non-zero size before computing layout.
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (unsafeLayout == null) {
|
|
650
|
+
unsafeLayout = calculateUnsafeDefaultLayout({
|
|
651
|
+
groupSizePixels,
|
|
652
|
+
panelDataArray,
|
|
701
653
|
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Validate even saved layouts in case something has changed since last render
|
|
657
|
+
// e.g. for pixel groups, this could be the size of the window
|
|
658
|
+
const nextLayout = validatePanelGroupLayout({
|
|
659
|
+
groupSizePixels,
|
|
660
|
+
layout: unsafeLayout,
|
|
661
|
+
panelConstraints: panelDataArray.map(
|
|
662
|
+
(panelData) => panelData.constraints
|
|
663
|
+
),
|
|
702
664
|
});
|
|
665
|
+
|
|
666
|
+
// Offscreen mode makes this a bit weird;
|
|
667
|
+
// Panels unregister when hidden and re-register when shown again,
|
|
668
|
+
// but the overall layout doesn't change between these two cases.
|
|
669
|
+
setLayout(nextLayout);
|
|
670
|
+
|
|
671
|
+
eagerValuesRef.current.layout = nextLayout;
|
|
672
|
+
|
|
673
|
+
if (!areEqual(prevLayout, nextLayout)) {
|
|
674
|
+
if (onLayout) {
|
|
675
|
+
onLayout(
|
|
676
|
+
nextLayout.map((sizePercentage) => ({
|
|
677
|
+
sizePercentage,
|
|
678
|
+
sizePixels: convertPercentageToPixels(
|
|
679
|
+
sizePercentage,
|
|
680
|
+
groupSizePixels
|
|
681
|
+
),
|
|
682
|
+
}))
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
callPanelCallbacks(
|
|
687
|
+
groupId,
|
|
688
|
+
panelDataArray,
|
|
689
|
+
nextLayout,
|
|
690
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
691
|
+
);
|
|
692
|
+
}
|
|
703
693
|
}, []);
|
|
704
694
|
|
|
705
695
|
const registerResizeHandle = useCallback((dragHandleId: string) => {
|
|
@@ -713,9 +703,8 @@ function PanelGroupWithForwardedRef({
|
|
|
713
703
|
keyboardResizeByPercentage,
|
|
714
704
|
keyboardResizeByPixels,
|
|
715
705
|
onLayout,
|
|
716
|
-
panelDataArray,
|
|
717
|
-
layout: prevLayout,
|
|
718
706
|
} = committedValuesRef.current;
|
|
707
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
719
708
|
|
|
720
709
|
const { initialLayout } = dragState ?? {};
|
|
721
710
|
|
|
@@ -789,6 +778,8 @@ function PanelGroupWithForwardedRef({
|
|
|
789
778
|
if (layoutChanged) {
|
|
790
779
|
setLayout(nextLayout);
|
|
791
780
|
|
|
781
|
+
eagerValuesRef.current.layout = nextLayout;
|
|
782
|
+
|
|
792
783
|
if (onLayout) {
|
|
793
784
|
onLayout(
|
|
794
785
|
nextLayout.map((sizePercentage) => ({
|
|
@@ -814,24 +805,9 @@ function PanelGroupWithForwardedRef({
|
|
|
814
805
|
// External APIs are safe to memoize via committed values ref
|
|
815
806
|
const resizePanel = useCallback(
|
|
816
807
|
(panelData: PanelData, mixedSizes: Partial<MixedSizes>) => {
|
|
817
|
-
const {
|
|
818
|
-
layout: prevLayout,
|
|
819
|
-
onLayout,
|
|
820
|
-
panelDataArray,
|
|
821
|
-
} = committedValuesRef.current;
|
|
808
|
+
const { onLayout } = committedValuesRef.current;
|
|
822
809
|
|
|
823
|
-
|
|
824
|
-
if (panelDataArray.find(({ id }) => id === panelData.id) == null) {
|
|
825
|
-
setImperativeApiQueue((prev) => [
|
|
826
|
-
...prev,
|
|
827
|
-
{
|
|
828
|
-
panelData,
|
|
829
|
-
mixedSizes,
|
|
830
|
-
type: "resize",
|
|
831
|
-
},
|
|
832
|
-
]);
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
810
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
835
811
|
|
|
836
812
|
const panelConstraintsArray = panelDataArray.map(
|
|
837
813
|
(panelData) => panelData.constraints
|
|
@@ -863,6 +839,8 @@ function PanelGroupWithForwardedRef({
|
|
|
863
839
|
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
864
840
|
setLayout(nextLayout);
|
|
865
841
|
|
|
842
|
+
eagerValuesRef.current.layout = nextLayout;
|
|
843
|
+
|
|
866
844
|
if (onLayout) {
|
|
867
845
|
onLayout(
|
|
868
846
|
nextLayout.map((sizePercentage) => ({
|
|
@@ -888,7 +866,8 @@ function PanelGroupWithForwardedRef({
|
|
|
888
866
|
|
|
889
867
|
const startDragging = useCallback(
|
|
890
868
|
(dragHandleId: string, event: ResizeEvent) => {
|
|
891
|
-
const { direction
|
|
869
|
+
const { direction } = committedValuesRef.current;
|
|
870
|
+
const { layout } = eagerValuesRef.current;
|
|
892
871
|
|
|
893
872
|
const handleElement = getResizeHandleElement(dragHandleId)!;
|
|
894
873
|
|
|
@@ -912,48 +891,103 @@ function PanelGroupWithForwardedRef({
|
|
|
912
891
|
setDragState(null);
|
|
913
892
|
}, []);
|
|
914
893
|
|
|
894
|
+
const unregisterPanelRef = useRef<{
|
|
895
|
+
pendingPanelIds: Set<string>;
|
|
896
|
+
timeout: NodeJS.Timeout | null;
|
|
897
|
+
}>({
|
|
898
|
+
pendingPanelIds: new Set(),
|
|
899
|
+
timeout: null,
|
|
900
|
+
});
|
|
915
901
|
const unregisterPanel = useCallback((panelData: PanelData) => {
|
|
916
|
-
|
|
902
|
+
const { id: groupId, onLayout } = committedValuesRef.current;
|
|
903
|
+
const { layout: prevLayout, panelDataArray } = eagerValuesRef.current;
|
|
917
904
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
}
|
|
905
|
+
const index = panelDataArray.indexOf(panelData);
|
|
906
|
+
if (index >= 0) {
|
|
907
|
+
panelDataArray.splice(index, 1);
|
|
908
|
+
unregisterPanelRef.current.pendingPanelIds.add(panelData.id);
|
|
909
|
+
}
|
|
924
910
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
911
|
+
if (unregisterPanelRef.current.timeout != null) {
|
|
912
|
+
clearTimeout(unregisterPanelRef.current.timeout);
|
|
913
|
+
}
|
|
928
914
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
915
|
+
// Batch panel unmounts so that we only calculate layout once;
|
|
916
|
+
// This is more efficient and avoids misleading warnings in development mode.
|
|
917
|
+
// We can't check the DOM to detect this because Panel elements have not yet been removed.
|
|
918
|
+
unregisterPanelRef.current.timeout = setTimeout(() => {
|
|
919
|
+
const { pendingPanelIds } = unregisterPanelRef.current;
|
|
920
|
+
const map = panelIdToLastNotifiedMixedSizesMapRef.current;
|
|
921
|
+
|
|
922
|
+
// TRICKY
|
|
923
|
+
// Strict effects mode
|
|
924
|
+
let unmountDueToStrictMode = false;
|
|
925
|
+
pendingPanelIds.forEach((panelId) => {
|
|
926
|
+
pendingPanelIds.delete(panelId);
|
|
927
|
+
|
|
928
|
+
if (panelDataArray.find(({ id }) => id === panelId) == null) {
|
|
929
|
+
unmountDueToStrictMode = true;
|
|
930
|
+
|
|
931
|
+
// TRICKY
|
|
932
|
+
// When a panel is removed from the group, we should delete the most recent prev-size entry for it.
|
|
933
|
+
// If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
|
|
934
|
+
// Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
|
|
935
|
+
delete map[panelData.id];
|
|
942
936
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
if (!unmountDueToStrictMode) {
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if (panelDataArray.length === 0) {
|
|
944
|
+
// The group is unmounting; skip layout calculation.
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
949
|
+
|
|
950
|
+
let unsafeLayout: number[] = calculateUnsafeDefaultLayout({
|
|
951
|
+
groupSizePixels,
|
|
952
|
+
panelDataArray,
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
// Validate even saved layouts in case something has changed since last render
|
|
956
|
+
// e.g. for pixel groups, this could be the size of the window
|
|
957
|
+
const nextLayout = validatePanelGroupLayout({
|
|
958
|
+
groupSizePixels,
|
|
959
|
+
layout: unsafeLayout,
|
|
960
|
+
panelConstraints: panelDataArray.map(
|
|
961
|
+
(panelData) => panelData.constraints
|
|
962
|
+
),
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
if (!areEqual(prevLayout, nextLayout)) {
|
|
966
|
+
setLayout(nextLayout);
|
|
967
|
+
|
|
968
|
+
eagerValuesRef.current.layout = nextLayout;
|
|
969
|
+
|
|
970
|
+
if (onLayout) {
|
|
971
|
+
onLayout(
|
|
972
|
+
nextLayout.map((sizePercentage) => ({
|
|
973
|
+
sizePercentage,
|
|
974
|
+
sizePixels: convertPercentageToPixels(
|
|
975
|
+
sizePercentage,
|
|
976
|
+
groupSizePixels
|
|
977
|
+
),
|
|
978
|
+
}))
|
|
979
|
+
);
|
|
946
980
|
}
|
|
981
|
+
|
|
982
|
+
callPanelCallbacks(
|
|
983
|
+
groupId,
|
|
984
|
+
panelDataArray,
|
|
985
|
+
nextLayout,
|
|
986
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
987
|
+
);
|
|
947
988
|
}
|
|
948
|
-
}
|
|
949
|
-
}, [
|
|
950
|
-
collapsePanel,
|
|
951
|
-
expandPanel,
|
|
952
|
-
imperativeApiQueue,
|
|
953
|
-
layout,
|
|
954
|
-
panelDataArray,
|
|
955
|
-
resizePanel,
|
|
956
|
-
]);
|
|
989
|
+
}, 0);
|
|
990
|
+
}, []);
|
|
957
991
|
|
|
958
992
|
const context = useMemo(
|
|
959
993
|
() => ({
|