react-resizable-panels 1.0.2 → 1.0.4
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 +9 -1
- package/dist/react-resizable-panels.browser.cjs.js +34 -22
- package/dist/react-resizable-panels.browser.development.cjs.js +34 -22
- package/dist/react-resizable-panels.browser.development.esm.js +34 -22
- package/dist/react-resizable-panels.browser.esm.js +34 -22
- package/dist/react-resizable-panels.cjs.js +34 -22
- package/dist/react-resizable-panels.development.cjs.js +34 -22
- package/dist/react-resizable-panels.development.esm.js +34 -22
- package/dist/react-resizable-panels.development.node.cjs.js +24 -13
- package/dist/react-resizable-panels.development.node.esm.js +24 -13
- package/dist/react-resizable-panels.esm.js +34 -22
- package/dist/react-resizable-panels.node.cjs.js +24 -13
- package/dist/react-resizable-panels.node.esm.js +24 -13
- package/package.json +1 -1
- package/src/Panel.test.tsx +12 -0
- package/src/PanelGroup.ts +31 -13
- package/src/utils/serialization.ts +33 -18
|
@@ -800,11 +800,15 @@ function initializeDefaultStorage(storageObject) {
|
|
|
800
800
|
}
|
|
801
801
|
}
|
|
802
802
|
|
|
803
|
+
function getPanelGroupKey(autoSaveId) {
|
|
804
|
+
return `react-resizable-panels:${autoSaveId}`;
|
|
805
|
+
}
|
|
806
|
+
|
|
803
807
|
// Note that Panel ids might be user-provided (stable) or useId generated (non-deterministic)
|
|
804
808
|
// so they should not be used as part of the serialization key.
|
|
805
809
|
// Using the min/max size attributes should work well enough as a backup.
|
|
806
810
|
// Pre-sorting by minSize allows remembering layouts even if panels are re-ordered/dragged.
|
|
807
|
-
function
|
|
811
|
+
function getPanelKey(panels) {
|
|
808
812
|
return panels.map(panel => {
|
|
809
813
|
const {
|
|
810
814
|
constraints,
|
|
@@ -821,7 +825,8 @@ function getSerializationKey(panels) {
|
|
|
821
825
|
}
|
|
822
826
|
function loadSerializedPanelGroupState(autoSaveId, storage) {
|
|
823
827
|
try {
|
|
824
|
-
const
|
|
828
|
+
const panelGroupKey = getPanelGroupKey(autoSaveId);
|
|
829
|
+
const serialized = storage.getItem(panelGroupKey);
|
|
825
830
|
if (serialized) {
|
|
826
831
|
const parsed = JSON.parse(serialized);
|
|
827
832
|
if (typeof parsed === "object" && parsed != null) {
|
|
@@ -831,12 +836,17 @@ function loadSerializedPanelGroupState(autoSaveId, storage) {
|
|
|
831
836
|
} catch (error) {}
|
|
832
837
|
return null;
|
|
833
838
|
}
|
|
834
|
-
function
|
|
835
|
-
|
|
836
|
-
const
|
|
837
|
-
|
|
839
|
+
function savePanelGroupState(autoSaveId, panels, panelSizesBeforeCollapse, sizes, storage) {
|
|
840
|
+
var _loadSerializedPanelG2;
|
|
841
|
+
const panelGroupKey = getPanelGroupKey(autoSaveId);
|
|
842
|
+
const panelKey = getPanelKey(panels);
|
|
843
|
+
const state = (_loadSerializedPanelG2 = loadSerializedPanelGroupState(autoSaveId, storage)) !== null && _loadSerializedPanelG2 !== void 0 ? _loadSerializedPanelG2 : {};
|
|
844
|
+
state[panelKey] = {
|
|
845
|
+
expandToSizes: Object.fromEntries(panelSizesBeforeCollapse.entries()),
|
|
846
|
+
layout: sizes
|
|
847
|
+
};
|
|
838
848
|
try {
|
|
839
|
-
storage.setItem(
|
|
849
|
+
storage.setItem(panelGroupKey, JSON.stringify(state));
|
|
840
850
|
} catch (error) {
|
|
841
851
|
console.error(error);
|
|
842
852
|
}
|
|
@@ -933,7 +943,6 @@ function PanelGroupWithForwardedRef({
|
|
|
933
943
|
const groupId = useUniqueId(idFromProps);
|
|
934
944
|
const [dragState, setDragState] = useState(null);
|
|
935
945
|
const [layout, setLayout] = useState([]);
|
|
936
|
-
useState([]);
|
|
937
946
|
const panelIdToLastNotifiedSizeMapRef = useRef({});
|
|
938
947
|
const panelSizeBeforeCollapseRef = useRef(new Map());
|
|
939
948
|
const prevDeltaRef = useRef(0);
|
|
@@ -1008,13 +1017,15 @@ function PanelGroupWithForwardedRef({
|
|
|
1008
1017
|
|
|
1009
1018
|
// Limit the frequency of localStorage updates.
|
|
1010
1019
|
if (debouncedSave == null) {
|
|
1011
|
-
debouncedSave = debounce(
|
|
1020
|
+
debouncedSave = debounce(savePanelGroupState, LOCAL_STORAGE_DEBOUNCE_INTERVAL);
|
|
1012
1021
|
debounceMap[autoSaveId] = debouncedSave;
|
|
1013
1022
|
}
|
|
1014
1023
|
|
|
1015
|
-
// Clone
|
|
1016
|
-
//
|
|
1017
|
-
|
|
1024
|
+
// Clone mutable data before passing to the debounced function,
|
|
1025
|
+
// else we run the risk of saving an incorrect combination of mutable and immutable values to state.
|
|
1026
|
+
const clonedPanelDataArray = [...panelDataArray];
|
|
1027
|
+
const clonedPanelSizesBeforeCollapse = new Map(panelSizeBeforeCollapseRef.current);
|
|
1028
|
+
debouncedSave(autoSaveId, clonedPanelDataArray, clonedPanelSizesBeforeCollapse, layout, storage);
|
|
1018
1029
|
}
|
|
1019
1030
|
}, [autoSaveId, layout, storage]);
|
|
1020
1031
|
|
|
@@ -1140,7 +1151,7 @@ function PanelGroupWithForwardedRef({
|
|
|
1140
1151
|
panelDataArray
|
|
1141
1152
|
} = eagerValuesRef.current;
|
|
1142
1153
|
const {
|
|
1143
|
-
collapsedSize,
|
|
1154
|
+
collapsedSize = 0,
|
|
1144
1155
|
collapsible,
|
|
1145
1156
|
panelSize
|
|
1146
1157
|
} = panelDataHelper(panelDataArray, panelData, layout);
|
package/package.json
CHANGED
package/src/Panel.test.tsx
CHANGED
|
@@ -89,13 +89,19 @@ describe("PanelGroup", () => {
|
|
|
89
89
|
assert(mostRecentLayout);
|
|
90
90
|
|
|
91
91
|
verifyExpandedPanelGroupLayout(mostRecentLayout, [50, 50]);
|
|
92
|
+
expect(leftPanelRef.current?.isCollapsed()).toBe(false);
|
|
93
|
+
expect(rightPanelRef.current?.isCollapsed()).toBe(false);
|
|
92
94
|
act(() => {
|
|
93
95
|
leftPanelRef.current?.collapse();
|
|
94
96
|
});
|
|
97
|
+
expect(leftPanelRef.current?.isCollapsed()).toBe(true);
|
|
98
|
+
expect(rightPanelRef.current?.isCollapsed()).toBe(false);
|
|
95
99
|
verifyExpandedPanelGroupLayout(mostRecentLayout, [0, 100]);
|
|
96
100
|
act(() => {
|
|
97
101
|
leftPanelRef.current?.expand();
|
|
98
102
|
});
|
|
103
|
+
expect(leftPanelRef.current?.isCollapsed()).toBe(false);
|
|
104
|
+
expect(rightPanelRef.current?.isCollapsed()).toBe(false);
|
|
99
105
|
verifyExpandedPanelGroupLayout(mostRecentLayout, [50, 50]);
|
|
100
106
|
});
|
|
101
107
|
|
|
@@ -103,14 +109,20 @@ describe("PanelGroup", () => {
|
|
|
103
109
|
assert(mostRecentLayout);
|
|
104
110
|
|
|
105
111
|
verifyExpandedPanelGroupLayout(mostRecentLayout, [50, 50]);
|
|
112
|
+
expect(leftPanelRef.current?.isCollapsed()).toBe(false);
|
|
113
|
+
expect(rightPanelRef.current?.isCollapsed()).toBe(false);
|
|
106
114
|
act(() => {
|
|
107
115
|
rightPanelRef.current?.collapse();
|
|
108
116
|
});
|
|
109
117
|
verifyExpandedPanelGroupLayout(mostRecentLayout, [100, 0]);
|
|
118
|
+
expect(leftPanelRef.current?.isCollapsed()).toBe(false);
|
|
119
|
+
expect(rightPanelRef.current?.isCollapsed()).toBe(true);
|
|
110
120
|
act(() => {
|
|
111
121
|
rightPanelRef.current?.expand();
|
|
112
122
|
});
|
|
113
123
|
verifyExpandedPanelGroupLayout(mostRecentLayout, [50, 50]);
|
|
124
|
+
expect(leftPanelRef.current?.isCollapsed()).toBe(false);
|
|
125
|
+
expect(rightPanelRef.current?.isCollapsed()).toBe(false);
|
|
114
126
|
});
|
|
115
127
|
|
|
116
128
|
it("should re-expand to the most recent size before collapsing", () => {
|
package/src/PanelGroup.ts
CHANGED
|
@@ -20,7 +20,10 @@ import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
|
|
|
20
20
|
import { isKeyDown, isMouseEvent, isTouchEvent } from "./utils/events";
|
|
21
21
|
import { getResizeEventCursorPosition } from "./utils/getResizeEventCursorPosition";
|
|
22
22
|
import { initializeDefaultStorage } from "./utils/initializeDefaultStorage";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
loadPanelGroupState,
|
|
25
|
+
savePanelGroupState,
|
|
26
|
+
} from "./utils/serialization";
|
|
24
27
|
import { validatePanelConstraints } from "./utils/validatePanelConstraints";
|
|
25
28
|
import { validatePanelGroupLayout } from "./utils/validatePanelGroupLayout";
|
|
26
29
|
import {
|
|
@@ -79,7 +82,7 @@ export type PanelGroupProps = Omit<HTMLAttributes<ElementType>, "id"> &
|
|
|
79
82
|
}>;
|
|
80
83
|
|
|
81
84
|
const debounceMap: {
|
|
82
|
-
[key: string]: typeof
|
|
85
|
+
[key: string]: typeof savePanelGroupState;
|
|
83
86
|
} = {};
|
|
84
87
|
|
|
85
88
|
function PanelGroupWithForwardedRef({
|
|
@@ -102,7 +105,6 @@ function PanelGroupWithForwardedRef({
|
|
|
102
105
|
|
|
103
106
|
const [dragState, setDragState] = useState<DragState | null>(null);
|
|
104
107
|
const [layout, setLayout] = useState<number[]>([]);
|
|
105
|
-
const [panelDataArray, setPanelDataArray] = useState<PanelData[]>([]);
|
|
106
108
|
|
|
107
109
|
const panelIdToLastNotifiedSizeMapRef = useRef<Record<string, number>>({});
|
|
108
110
|
const panelSizeBeforeCollapseRef = useRef<Map<string, number>>(new Map());
|
|
@@ -218,16 +220,26 @@ function PanelGroupWithForwardedRef({
|
|
|
218
220
|
// Limit the frequency of localStorage updates.
|
|
219
221
|
if (debouncedSave == null) {
|
|
220
222
|
debouncedSave = debounce(
|
|
221
|
-
|
|
223
|
+
savePanelGroupState,
|
|
222
224
|
LOCAL_STORAGE_DEBOUNCE_INTERVAL
|
|
223
225
|
);
|
|
224
226
|
|
|
225
227
|
debounceMap[autoSaveId] = debouncedSave;
|
|
226
228
|
}
|
|
227
229
|
|
|
228
|
-
// Clone
|
|
229
|
-
//
|
|
230
|
-
|
|
230
|
+
// Clone mutable data before passing to the debounced function,
|
|
231
|
+
// else we run the risk of saving an incorrect combination of mutable and immutable values to state.
|
|
232
|
+
const clonedPanelDataArray = [...panelDataArray];
|
|
233
|
+
const clonedPanelSizesBeforeCollapse = new Map(
|
|
234
|
+
panelSizeBeforeCollapseRef.current
|
|
235
|
+
);
|
|
236
|
+
debouncedSave(
|
|
237
|
+
autoSaveId,
|
|
238
|
+
clonedPanelDataArray,
|
|
239
|
+
clonedPanelSizesBeforeCollapse,
|
|
240
|
+
layout,
|
|
241
|
+
storage
|
|
242
|
+
);
|
|
231
243
|
}
|
|
232
244
|
}, [autoSaveId, layout, storage]);
|
|
233
245
|
|
|
@@ -442,11 +454,11 @@ function PanelGroupWithForwardedRef({
|
|
|
442
454
|
const isPanelCollapsed = useCallback((panelData: PanelData) => {
|
|
443
455
|
const { layout, panelDataArray } = eagerValuesRef.current;
|
|
444
456
|
|
|
445
|
-
const {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
);
|
|
457
|
+
const {
|
|
458
|
+
collapsedSize = 0,
|
|
459
|
+
collapsible,
|
|
460
|
+
panelSize,
|
|
461
|
+
} = panelDataHelper(panelDataArray, panelData, layout);
|
|
450
462
|
|
|
451
463
|
return collapsible === true && panelSize === collapsedSize;
|
|
452
464
|
}, []);
|
|
@@ -500,7 +512,13 @@ function PanelGroupWithForwardedRef({
|
|
|
500
512
|
// default size should be restored from local storage if possible.
|
|
501
513
|
let unsafeLayout: number[] | null = null;
|
|
502
514
|
if (autoSaveId) {
|
|
503
|
-
|
|
515
|
+
const state = loadPanelGroupState(autoSaveId, panelDataArray, storage);
|
|
516
|
+
if (state) {
|
|
517
|
+
panelSizeBeforeCollapseRef.current = new Map(
|
|
518
|
+
Object.entries(state.expandToSizes)
|
|
519
|
+
);
|
|
520
|
+
unsafeLayout = state.layout;
|
|
521
|
+
}
|
|
504
522
|
}
|
|
505
523
|
|
|
506
524
|
if (unsafeLayout == null) {
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { PanelData } from "../Panel";
|
|
2
2
|
import { PanelGroupStorage } from "../PanelGroup";
|
|
3
3
|
|
|
4
|
-
type
|
|
4
|
+
export type PanelConfigurationState = {
|
|
5
|
+
expandToSizes: {
|
|
6
|
+
[panelId: string]: number;
|
|
7
|
+
};
|
|
8
|
+
layout: number[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type SerializedPanelGroupState = {
|
|
12
|
+
[panelIds: string]: PanelConfigurationState;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function getPanelGroupKey(autoSaveId: string): string {
|
|
16
|
+
return `react-resizable-panels:${autoSaveId}`;
|
|
17
|
+
}
|
|
5
18
|
|
|
6
19
|
// Note that Panel ids might be user-provided (stable) or useId generated (non-deterministic)
|
|
7
20
|
// so they should not be used as part of the serialization key.
|
|
8
21
|
// Using the min/max size attributes should work well enough as a backup.
|
|
9
22
|
// Pre-sorting by minSize allows remembering layouts even if panels are re-ordered/dragged.
|
|
10
|
-
function
|
|
23
|
+
function getPanelKey(panels: PanelData[]): string {
|
|
11
24
|
return panels
|
|
12
25
|
.map((panel) => {
|
|
13
26
|
const { constraints, id, idIsFromProps, order } = panel;
|
|
@@ -28,11 +41,12 @@ function loadSerializedPanelGroupState(
|
|
|
28
41
|
storage: PanelGroupStorage
|
|
29
42
|
): SerializedPanelGroupState | null {
|
|
30
43
|
try {
|
|
31
|
-
const
|
|
44
|
+
const panelGroupKey = getPanelGroupKey(autoSaveId);
|
|
45
|
+
const serialized = storage.getItem(panelGroupKey);
|
|
32
46
|
if (serialized) {
|
|
33
47
|
const parsed = JSON.parse(serialized);
|
|
34
48
|
if (typeof parsed === "object" && parsed != null) {
|
|
35
|
-
return parsed;
|
|
49
|
+
return parsed as SerializedPanelGroupState;
|
|
36
50
|
}
|
|
37
51
|
}
|
|
38
52
|
} catch (error) {}
|
|
@@ -40,32 +54,33 @@ function loadSerializedPanelGroupState(
|
|
|
40
54
|
return null;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
|
-
export function
|
|
57
|
+
export function loadPanelGroupState(
|
|
44
58
|
autoSaveId: string,
|
|
45
59
|
panels: PanelData[],
|
|
46
60
|
storage: PanelGroupStorage
|
|
47
|
-
):
|
|
48
|
-
const state = loadSerializedPanelGroupState(autoSaveId, storage);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return state[key] ?? null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return null;
|
|
61
|
+
): PanelConfigurationState | null {
|
|
62
|
+
const state = loadSerializedPanelGroupState(autoSaveId, storage) ?? {};
|
|
63
|
+
const panelKey = getPanelKey(panels);
|
|
64
|
+
return state[panelKey] ?? null;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
export function
|
|
67
|
+
export function savePanelGroupState(
|
|
58
68
|
autoSaveId: string,
|
|
59
69
|
panels: PanelData[],
|
|
70
|
+
panelSizesBeforeCollapse: Map<string, number>,
|
|
60
71
|
sizes: number[],
|
|
61
72
|
storage: PanelGroupStorage
|
|
62
73
|
): void {
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
state
|
|
74
|
+
const panelGroupKey = getPanelGroupKey(autoSaveId);
|
|
75
|
+
const panelKey = getPanelKey(panels);
|
|
76
|
+
const state = loadSerializedPanelGroupState(autoSaveId, storage) ?? {};
|
|
77
|
+
state[panelKey] = {
|
|
78
|
+
expandToSizes: Object.fromEntries(panelSizesBeforeCollapse.entries()),
|
|
79
|
+
layout: sizes,
|
|
80
|
+
};
|
|
66
81
|
|
|
67
82
|
try {
|
|
68
|
-
storage.setItem(
|
|
83
|
+
storage.setItem(panelGroupKey, JSON.stringify(state));
|
|
69
84
|
} catch (error) {
|
|
70
85
|
console.error(error);
|
|
71
86
|
}
|