react-resizable-panels 0.0.27 → 0.0.29
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 +8 -0
- package/README.md +8 -0
- package/dist/react-resizable-panels.d.ts +12 -4
- package/dist/react-resizable-panels.d.ts.map +1 -1
- package/dist/react-resizable-panels.js +158 -27
- package/dist/react-resizable-panels.js.map +1 -1
- package/dist/react-resizable-panels.module.js +157 -26
- package/dist/react-resizable-panels.module.js.map +1 -1
- package/package.json +1 -1
- package/src/Panel.ts +44 -9
- package/src/PanelContexts.ts +3 -0
- package/src/PanelGroup.ts +175 -24
- package/src/PanelResizeHandle.ts +1 -1
- package/src/hooks/useIsomorphicEffect.ts +13 -0
- package/src/index.ts +18 -4
- package/src/utils/group.ts +46 -0
package/src/PanelGroup.ts
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
ReactNode,
|
|
6
6
|
useCallback,
|
|
7
7
|
useEffect,
|
|
8
|
-
useLayoutEffect,
|
|
9
8
|
useMemo,
|
|
10
9
|
useRef,
|
|
11
10
|
useState,
|
|
@@ -17,13 +16,16 @@ import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
|
|
|
17
16
|
import { getDragOffset, getMovement } from "./utils/coordinates";
|
|
18
17
|
import {
|
|
19
18
|
adjustByDelta,
|
|
19
|
+
callPanelCallbacks,
|
|
20
|
+
getBeforeAndAfterIds,
|
|
20
21
|
getFlexGrow,
|
|
21
22
|
getPanelGroup,
|
|
22
23
|
getResizeHandlePanelIds,
|
|
23
24
|
panelsMapToSortedArray,
|
|
24
25
|
} from "./utils/group";
|
|
25
|
-
import
|
|
26
|
+
import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
|
|
26
27
|
import useUniqueId from "./hooks/useUniqueId";
|
|
28
|
+
import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterBehavior";
|
|
27
29
|
import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
|
|
28
30
|
|
|
29
31
|
export type CommittedValues = {
|
|
@@ -48,7 +50,7 @@ export type PanelGroupProps = {
|
|
|
48
50
|
tagName?: ElementType;
|
|
49
51
|
};
|
|
50
52
|
|
|
51
|
-
export
|
|
53
|
+
export function PanelGroup({
|
|
52
54
|
autoSaveId,
|
|
53
55
|
children = null,
|
|
54
56
|
className: classNameFromProps = "",
|
|
@@ -67,6 +69,9 @@ export default function PanelGroup({
|
|
|
67
69
|
|
|
68
70
|
const dragOffsetRef = useRef<number>(0);
|
|
69
71
|
|
|
72
|
+
// Used to support imperative collapse/expand API.
|
|
73
|
+
const panelSizeBeforeCollapse = useRef<Map<string, number>>(new Map());
|
|
74
|
+
|
|
70
75
|
// Store committed values to avoid unnecessarily re-running memoization/effects functions.
|
|
71
76
|
const committedValuesRef = useRef<CommittedValues>({
|
|
72
77
|
direction,
|
|
@@ -74,7 +79,7 @@ export default function PanelGroup({
|
|
|
74
79
|
sizes,
|
|
75
80
|
});
|
|
76
81
|
|
|
77
|
-
|
|
82
|
+
useIsomorphicLayoutEffect(() => {
|
|
78
83
|
committedValuesRef.current.direction = direction;
|
|
79
84
|
committedValuesRef.current.panels = panels;
|
|
80
85
|
committedValuesRef.current.sizes = sizes;
|
|
@@ -91,7 +96,7 @@ export default function PanelGroup({
|
|
|
91
96
|
// Once all panels have registered themselves,
|
|
92
97
|
// Compute the initial sizes based on default weights.
|
|
93
98
|
// This assumes that panels register during initial mount (no conditional rendering)!
|
|
94
|
-
|
|
99
|
+
useIsomorphicLayoutEffect(() => {
|
|
95
100
|
const sizes = committedValuesRef.current.sizes;
|
|
96
101
|
if (sizes.length === panels.size) {
|
|
97
102
|
// Only compute (or restore) default sizes once per panel configuration.
|
|
@@ -168,6 +173,20 @@ export default function PanelGroup({
|
|
|
168
173
|
(id: string): CSSProperties => {
|
|
169
174
|
const { panels } = committedValuesRef.current;
|
|
170
175
|
|
|
176
|
+
// Before mounting, Panels will not yet have registered themselves.
|
|
177
|
+
// This includes server rendering.
|
|
178
|
+
// At this point the best we can do is render everything with the same size.
|
|
179
|
+
if (panels.size === 0) {
|
|
180
|
+
return {
|
|
181
|
+
flexBasis: "auto",
|
|
182
|
+
flexGrow: 1,
|
|
183
|
+
flexShrink: 1,
|
|
184
|
+
|
|
185
|
+
// Without this, Panel sizes may be unintentionally overridden by their content.
|
|
186
|
+
overflow: "hidden",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
171
190
|
const flexGrow = getFlexGrow(panels, id, sizes);
|
|
172
191
|
|
|
173
192
|
return {
|
|
@@ -177,9 +196,13 @@ export default function PanelGroup({
|
|
|
177
196
|
|
|
178
197
|
// Without this, Panel sizes may be unintentionally overridden by their content.
|
|
179
198
|
overflow: "hidden",
|
|
199
|
+
|
|
200
|
+
// Disable pointer events inside of a panel during resize.
|
|
201
|
+
// This avoid edge cases like nested iframes.
|
|
202
|
+
pointerEvents: activeHandleId !== null ? "none" : undefined,
|
|
180
203
|
};
|
|
181
204
|
},
|
|
182
|
-
[direction, sizes]
|
|
205
|
+
[activeHandleId, direction, sizes]
|
|
183
206
|
);
|
|
184
207
|
|
|
185
208
|
const registerPanel = useCallback((id: string, panel: PanelData) => {
|
|
@@ -261,24 +284,7 @@ export default function PanelGroup({
|
|
|
261
284
|
setGlobalCursorStyle(isHorizontal ? "horizontal" : "vertical");
|
|
262
285
|
|
|
263
286
|
// If resize change handlers have been declared, this is the time to call them.
|
|
264
|
-
|
|
265
|
-
const prevSize = prevSizes[index];
|
|
266
|
-
if (prevSize !== nextSize) {
|
|
267
|
-
const { onCollapse, onResize } =
|
|
268
|
-
panelsArray[index].callbacksRef.current;
|
|
269
|
-
if (onResize) {
|
|
270
|
-
onResize(nextSize);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (onCollapse) {
|
|
274
|
-
if (prevSize === 0 && nextSize !== 0) {
|
|
275
|
-
onCollapse(false);
|
|
276
|
-
} else if (prevSize !== 0 && nextSize === 0) {
|
|
277
|
-
onCollapse(true);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
});
|
|
287
|
+
callPanelCallbacks(panelsArray, prevSizes, nextSizes);
|
|
282
288
|
|
|
283
289
|
setSizes(nextSizes);
|
|
284
290
|
}
|
|
@@ -302,14 +308,156 @@ export default function PanelGroup({
|
|
|
302
308
|
});
|
|
303
309
|
}, []);
|
|
304
310
|
|
|
311
|
+
const collapsePanel = useCallback((id: string) => {
|
|
312
|
+
const { panels, sizes: prevSizes } = committedValuesRef.current;
|
|
313
|
+
|
|
314
|
+
const panel = panels.get(id);
|
|
315
|
+
if (panel == null || !panel.collapsible) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const panelsArray = panelsMapToSortedArray(panels);
|
|
320
|
+
|
|
321
|
+
const index = panelsArray.indexOf(panel);
|
|
322
|
+
if (index < 0) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const currentSize = prevSizes[index];
|
|
327
|
+
if (currentSize === 0) {
|
|
328
|
+
// Panel is already collapsed.
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
panelSizeBeforeCollapse.current.set(id, currentSize);
|
|
333
|
+
|
|
334
|
+
const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
|
|
335
|
+
if (idBefore == null || idAfter == null) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const isLastPanel = index === panelsArray.length - 1;
|
|
340
|
+
const delta = isLastPanel ? currentSize : 0 - currentSize;
|
|
341
|
+
|
|
342
|
+
const nextSizes = adjustByDelta(
|
|
343
|
+
panels,
|
|
344
|
+
idBefore,
|
|
345
|
+
idAfter,
|
|
346
|
+
delta,
|
|
347
|
+
prevSizes
|
|
348
|
+
);
|
|
349
|
+
if (prevSizes !== nextSizes) {
|
|
350
|
+
// If resize change handlers have been declared, this is the time to call them.
|
|
351
|
+
callPanelCallbacks(panelsArray, prevSizes, nextSizes);
|
|
352
|
+
|
|
353
|
+
setSizes(nextSizes);
|
|
354
|
+
}
|
|
355
|
+
}, []);
|
|
356
|
+
|
|
357
|
+
const expandPanel = useCallback((id: string) => {
|
|
358
|
+
const { panels, sizes: prevSizes } = committedValuesRef.current;
|
|
359
|
+
|
|
360
|
+
const panel = panels.get(id);
|
|
361
|
+
if (panel == null) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const sizeBeforeCollapse =
|
|
366
|
+
panelSizeBeforeCollapse.current.get(id) || panel.minSize;
|
|
367
|
+
if (!sizeBeforeCollapse) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const panelsArray = panelsMapToSortedArray(panels);
|
|
372
|
+
|
|
373
|
+
const index = panelsArray.indexOf(panel);
|
|
374
|
+
if (index < 0) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const currentSize = prevSizes[index];
|
|
379
|
+
if (currentSize !== 0) {
|
|
380
|
+
// Panel is already expanded.
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
|
|
385
|
+
if (idBefore == null || idAfter == null) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const isLastPanel = index === panelsArray.length - 1;
|
|
390
|
+
const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
|
|
391
|
+
|
|
392
|
+
const nextSizes = adjustByDelta(
|
|
393
|
+
panels,
|
|
394
|
+
idBefore,
|
|
395
|
+
idAfter,
|
|
396
|
+
delta,
|
|
397
|
+
prevSizes
|
|
398
|
+
);
|
|
399
|
+
if (prevSizes !== nextSizes) {
|
|
400
|
+
// If resize change handlers have been declared, this is the time to call them.
|
|
401
|
+
callPanelCallbacks(panelsArray, prevSizes, nextSizes);
|
|
402
|
+
|
|
403
|
+
setSizes(nextSizes);
|
|
404
|
+
}
|
|
405
|
+
}, []);
|
|
406
|
+
|
|
407
|
+
const resizePanel = useCallback((id: string, nextSize: number) => {
|
|
408
|
+
const { panels, sizes: prevSizes } = committedValuesRef.current;
|
|
409
|
+
|
|
410
|
+
const panel = panels.get(id);
|
|
411
|
+
if (panel == null) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const panelsArray = panelsMapToSortedArray(panels);
|
|
416
|
+
|
|
417
|
+
const index = panelsArray.indexOf(panel);
|
|
418
|
+
if (index < 0) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const currentSize = prevSizes[index];
|
|
423
|
+
if (currentSize === nextSize) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
|
|
428
|
+
if (idBefore == null || idAfter == null) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const isLastPanel = index === panelsArray.length - 1;
|
|
433
|
+
const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
|
|
434
|
+
|
|
435
|
+
const nextSizes = adjustByDelta(
|
|
436
|
+
panels,
|
|
437
|
+
idBefore,
|
|
438
|
+
idAfter,
|
|
439
|
+
delta,
|
|
440
|
+
prevSizes
|
|
441
|
+
);
|
|
442
|
+
if (prevSizes !== nextSizes) {
|
|
443
|
+
// If resize change handlers have been declared, this is the time to call them.
|
|
444
|
+
callPanelCallbacks(panelsArray, prevSizes, nextSizes);
|
|
445
|
+
|
|
446
|
+
setSizes(nextSizes);
|
|
447
|
+
}
|
|
448
|
+
}, []);
|
|
449
|
+
|
|
305
450
|
const context = useMemo(
|
|
306
451
|
() => ({
|
|
307
452
|
activeHandleId,
|
|
453
|
+
collapsePanel,
|
|
308
454
|
direction,
|
|
455
|
+
expandPanel,
|
|
309
456
|
getPanelStyle,
|
|
310
457
|
groupId,
|
|
311
458
|
registerPanel,
|
|
312
459
|
registerResizeHandle,
|
|
460
|
+
resizePanel,
|
|
313
461
|
startDragging: (id: string, event: ResizeEvent) => {
|
|
314
462
|
setActiveHandleId(id);
|
|
315
463
|
|
|
@@ -323,11 +471,14 @@ export default function PanelGroup({
|
|
|
323
471
|
}),
|
|
324
472
|
[
|
|
325
473
|
activeHandleId,
|
|
474
|
+
collapsePanel,
|
|
326
475
|
direction,
|
|
476
|
+
expandPanel,
|
|
327
477
|
getPanelStyle,
|
|
328
478
|
groupId,
|
|
329
479
|
registerPanel,
|
|
330
480
|
registerResizeHandle,
|
|
481
|
+
resizePanel,
|
|
331
482
|
unregisterPanel,
|
|
332
483
|
]
|
|
333
484
|
);
|
package/src/PanelResizeHandle.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect } from "react";
|
|
2
|
+
|
|
3
|
+
const canUseEffectHooks = !!(
|
|
4
|
+
typeof window !== "undefined" &&
|
|
5
|
+
typeof window.document !== "undefined" &&
|
|
6
|
+
typeof window.document.createElement !== "undefined"
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
const useIsomorphicLayoutEffect = canUseEffectHooks
|
|
10
|
+
? useLayoutEffect
|
|
11
|
+
: () => {};
|
|
12
|
+
|
|
13
|
+
export default useIsomorphicLayoutEffect;
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import Panel from "./Panel";
|
|
2
|
-
import PanelGroup from "./PanelGroup";
|
|
3
|
-
import PanelResizeHandle from "./PanelResizeHandle";
|
|
1
|
+
import { Panel } from "./Panel";
|
|
2
|
+
import { PanelGroup } from "./PanelGroup";
|
|
3
|
+
import { PanelResizeHandle } from "./PanelResizeHandle";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import type { ImperativePanelHandle, PanelProps } from "./Panel";
|
|
6
|
+
import type { PanelGroupProps } from "./PanelGroup";
|
|
7
|
+
import type { PanelResizeHandleProps } from "./PanelResizeHandle";
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
Panel,
|
|
11
|
+
PanelGroup,
|
|
12
|
+
PanelResizeHandle,
|
|
13
|
+
|
|
14
|
+
// TypeScript types
|
|
15
|
+
ImperativePanelHandle,
|
|
16
|
+
PanelGroupProps,
|
|
17
|
+
PanelProps,
|
|
18
|
+
PanelResizeHandleProps,
|
|
19
|
+
};
|
package/src/utils/group.ts
CHANGED
|
@@ -83,6 +83,52 @@ export function adjustByDelta(
|
|
|
83
83
|
return nextSizes;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
export function callPanelCallbacks(
|
|
87
|
+
panelsArray: PanelData[],
|
|
88
|
+
prevSizes: number[],
|
|
89
|
+
nextSizes: number[]
|
|
90
|
+
) {
|
|
91
|
+
nextSizes.forEach((nextSize, index) => {
|
|
92
|
+
const prevSize = prevSizes[index];
|
|
93
|
+
if (prevSize !== nextSize) {
|
|
94
|
+
const { callbacksRef } = panelsArray[index];
|
|
95
|
+
const { onCollapse, onResize } = callbacksRef.current;
|
|
96
|
+
|
|
97
|
+
if (onResize) {
|
|
98
|
+
onResize(nextSize);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (onCollapse) {
|
|
102
|
+
if (prevSize === 0 && nextSize !== 0) {
|
|
103
|
+
onCollapse(false);
|
|
104
|
+
} else if (prevSize !== 0 && nextSize === 0) {
|
|
105
|
+
onCollapse(true);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function getBeforeAndAfterIds(
|
|
113
|
+
id: string,
|
|
114
|
+
panelsArray: PanelData[]
|
|
115
|
+
): [idBefore: string | null, idAFter: string | null] {
|
|
116
|
+
if (panelsArray.length < 2) {
|
|
117
|
+
return [null, null];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const index = panelsArray.findIndex((panel) => panel.id === id);
|
|
121
|
+
if (index < 0) {
|
|
122
|
+
return [null, null];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const isLastPanel = index === panelsArray.length - 1;
|
|
126
|
+
const idBefore = isLastPanel ? panelsArray[index - 1].id : id;
|
|
127
|
+
const idAfter = isLastPanel ? id : panelsArray[index + 1].id;
|
|
128
|
+
|
|
129
|
+
return [idBefore, idAfter];
|
|
130
|
+
}
|
|
131
|
+
|
|
86
132
|
// This method returns a number between 1 and 100 representing
|
|
87
133
|
// the % of the group's overall space this panel should occupy.
|
|
88
134
|
export function getFlexGrow(
|