react-resizable-panels 0.0.33 → 0.0.35
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 -1
- package/dist/react-resizable-panels.d.ts +6 -1
- package/dist/react-resizable-panels.d.ts.map +1 -1
- package/dist/react-resizable-panels.js +264 -208
- package/dist/react-resizable-panels.js.map +1 -1
- package/dist/react-resizable-panels.module.js +262 -206
- package/dist/react-resizable-panels.module.js.map +1 -1
- package/package.json +5 -2
- package/src/PanelGroup.ts +112 -29
- package/src/hooks/useWindowSplitterBehavior.ts +2 -1
- package/src/index.ts +2 -0
- package/src/types.ts +5 -0
- package/src/utils/arrays.ts +13 -0
- package/src/utils/coordinates.ts +27 -8
- package/src/utils/group.ts +40 -23
- package/src/utils/serialization.ts +12 -12
package/src/PanelGroup.ts
CHANGED
|
@@ -11,15 +11,27 @@ import {
|
|
|
11
11
|
} from "react";
|
|
12
12
|
|
|
13
13
|
import { PanelGroupContext } from "./PanelContexts";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
Direction,
|
|
16
|
+
PanelData,
|
|
17
|
+
PanelGroupOnLayout,
|
|
18
|
+
ResizeEvent,
|
|
19
|
+
PanelGroupStorage,
|
|
20
|
+
} from "./types";
|
|
15
21
|
import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
|
|
16
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
getDragOffset,
|
|
24
|
+
getMovement,
|
|
25
|
+
isMouseEvent,
|
|
26
|
+
isTouchEvent,
|
|
27
|
+
} from "./utils/coordinates";
|
|
17
28
|
import {
|
|
18
29
|
adjustByDelta,
|
|
19
30
|
callPanelCallbacks,
|
|
20
31
|
getBeforeAndAfterIds,
|
|
21
32
|
getFlexGrow,
|
|
22
33
|
getPanelGroup,
|
|
34
|
+
getResizeHandle,
|
|
23
35
|
getResizeHandlePanelIds,
|
|
24
36
|
panelsMapToSortedArray,
|
|
25
37
|
} from "./utils/group";
|
|
@@ -28,10 +40,26 @@ import useUniqueId from "./hooks/useUniqueId";
|
|
|
28
40
|
import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterBehavior";
|
|
29
41
|
import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
|
|
30
42
|
import debounce from "./utils/debounce";
|
|
43
|
+
import { areEqual } from "./utils/arrays";
|
|
31
44
|
|
|
32
45
|
// Limit the frequency of localStorage updates.
|
|
33
46
|
const savePanelGroupLayoutDebounced = debounce(savePanelGroupLayout, 100);
|
|
34
47
|
|
|
48
|
+
function throwServerError() {
|
|
49
|
+
throw new Error('PanelGroup "storage" prop required for server rendering.');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const defaultStorage: PanelGroupStorage = {
|
|
53
|
+
getItem:
|
|
54
|
+
typeof localStorage !== "undefined"
|
|
55
|
+
? (name: string) => localStorage.getItem(name)
|
|
56
|
+
: (throwServerError as any),
|
|
57
|
+
setItem:
|
|
58
|
+
typeof localStorage !== "undefined"
|
|
59
|
+
? (name: string, value: string) => localStorage.setItem(name, value)
|
|
60
|
+
: (throwServerError as any),
|
|
61
|
+
};
|
|
62
|
+
|
|
35
63
|
export type CommittedValues = {
|
|
36
64
|
direction: Direction;
|
|
37
65
|
panels: Map<string, PanelData>;
|
|
@@ -40,6 +68,19 @@ export type CommittedValues = {
|
|
|
40
68
|
|
|
41
69
|
export type PanelDataMap = Map<string, PanelData>;
|
|
42
70
|
|
|
71
|
+
// Initial drag state serves a few purposes:
|
|
72
|
+
// * dragOffset:
|
|
73
|
+
// Resize is calculated by the distance between the current pointer event and the resize handle being "dragged"
|
|
74
|
+
// This value accounts for the initial offset when the touch/click starts, so the handle doesn't appear to "jump"
|
|
75
|
+
// * dragHandleRect, sizes:
|
|
76
|
+
// When resizing is done via mouse/touch event– some initial state is stored
|
|
77
|
+
// so that any panels that contract will also expand if drag direction is reversed.
|
|
78
|
+
export type InitialDragState = {
|
|
79
|
+
dragHandleRect: DOMRect;
|
|
80
|
+
dragOffset: number;
|
|
81
|
+
sizes: number[];
|
|
82
|
+
};
|
|
83
|
+
|
|
43
84
|
// TODO
|
|
44
85
|
// Within an active drag, remember original positions to refine more easily on expand.
|
|
45
86
|
// Look at what the Chrome devtools Sources does.
|
|
@@ -51,6 +92,7 @@ export type PanelGroupProps = {
|
|
|
51
92
|
direction: Direction;
|
|
52
93
|
id?: string | null;
|
|
53
94
|
onLayout?: PanelGroupOnLayout;
|
|
95
|
+
storage?: PanelGroupStorage;
|
|
54
96
|
style?: CSSProperties;
|
|
55
97
|
tagName?: ElementType;
|
|
56
98
|
};
|
|
@@ -62,6 +104,7 @@ export function PanelGroup({
|
|
|
62
104
|
direction,
|
|
63
105
|
id: idFromProps = null,
|
|
64
106
|
onLayout = null,
|
|
107
|
+
storage = defaultStorage,
|
|
65
108
|
style: styleFromProps = {},
|
|
66
109
|
tagName: Type = "div",
|
|
67
110
|
}: PanelGroupProps) {
|
|
@@ -70,6 +113,11 @@ export function PanelGroup({
|
|
|
70
113
|
const [activeHandleId, setActiveHandleId] = useState<string | null>(null);
|
|
71
114
|
const [panels, setPanels] = useState<PanelDataMap>(new Map());
|
|
72
115
|
|
|
116
|
+
// When resizing is done via mouse/touch event–
|
|
117
|
+
// We store the initial Panel sizes in this ref, and apply move deltas to them instead of to the current sizes.
|
|
118
|
+
// This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
|
|
119
|
+
const initialDragStateRef = useRef<InitialDragState | null>(null);
|
|
120
|
+
|
|
73
121
|
// Use a ref to guard against users passing inline props
|
|
74
122
|
const callbacksRef = useRef<{
|
|
75
123
|
onLayout: PanelGroupOnLayout | null;
|
|
@@ -81,13 +129,11 @@ export function PanelGroup({
|
|
|
81
129
|
// 0-1 values representing the relative size of each panel.
|
|
82
130
|
const [sizes, setSizes] = useState<number[]>([]);
|
|
83
131
|
|
|
84
|
-
// Resize is calculated by the distance between the current pointer event and the resize handle being "dragged"
|
|
85
|
-
// This value accounts for the initial offset when the touch/click starts, so the handle doesn't appear to "jump"
|
|
86
|
-
const dragOffsetRef = useRef<number>(0);
|
|
87
|
-
|
|
88
132
|
// Used to support imperative collapse/expand API.
|
|
89
133
|
const panelSizeBeforeCollapse = useRef<Map<string, number>>(new Map());
|
|
90
134
|
|
|
135
|
+
const prevDeltaRef = useRef<number>(0);
|
|
136
|
+
|
|
91
137
|
// Store committed values to avoid unnecessarily re-running memoization/effects functions.
|
|
92
138
|
const committedValuesRef = useRef<CommittedValues>({
|
|
93
139
|
direction,
|
|
@@ -155,7 +201,7 @@ export function PanelGroup({
|
|
|
155
201
|
let defaultSizes: number[] | undefined = undefined;
|
|
156
202
|
if (autoSaveId) {
|
|
157
203
|
const panelsArray = panelsMapToSortedArray(panels);
|
|
158
|
-
defaultSizes = loadPanelLayout(autoSaveId, panelsArray);
|
|
204
|
+
defaultSizes = loadPanelLayout(autoSaveId, panelsArray, storage);
|
|
159
205
|
}
|
|
160
206
|
|
|
161
207
|
if (defaultSizes != null) {
|
|
@@ -213,7 +259,7 @@ export function PanelGroup({
|
|
|
213
259
|
|
|
214
260
|
const panelsArray = panelsMapToSortedArray(panels);
|
|
215
261
|
|
|
216
|
-
savePanelGroupLayoutDebounced(autoSaveId, panelsArray, sizes);
|
|
262
|
+
savePanelGroupLayoutDebounced(autoSaveId, panelsArray, sizes, storage);
|
|
217
263
|
}
|
|
218
264
|
}, [autoSaveId, panels, sizes]);
|
|
219
265
|
|
|
@@ -295,7 +341,7 @@ export function PanelGroup({
|
|
|
295
341
|
panelsArray,
|
|
296
342
|
direction,
|
|
297
343
|
prevSizes,
|
|
298
|
-
|
|
344
|
+
initialDragStateRef.current
|
|
299
345
|
);
|
|
300
346
|
if (movement === 0) {
|
|
301
347
|
return;
|
|
@@ -314,30 +360,47 @@ export function PanelGroup({
|
|
|
314
360
|
idAfter,
|
|
315
361
|
delta,
|
|
316
362
|
prevSizes,
|
|
317
|
-
panelSizeBeforeCollapse.current
|
|
363
|
+
panelSizeBeforeCollapse.current,
|
|
364
|
+
initialDragStateRef.current
|
|
318
365
|
);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
366
|
+
|
|
367
|
+
const sizesChanged = !areEqual(prevSizes, nextSizes);
|
|
368
|
+
|
|
369
|
+
// Don't update cursor for resizes triggered by keyboard interactions.
|
|
370
|
+
if (isMouseEvent(event) || isTouchEvent(event)) {
|
|
371
|
+
// Watch for multiple subsequent deltas; this might occur for tiny cursor movements.
|
|
372
|
+
// In this case, Panel sizes might not change–
|
|
373
|
+
// but updating cursor in this scenario would cause a flicker.
|
|
374
|
+
if (prevDeltaRef.current != delta) {
|
|
375
|
+
if (!sizesChanged) {
|
|
376
|
+
// If the pointer has moved too far to resize the panel any further,
|
|
377
|
+
// update the cursor style for a visual clue.
|
|
378
|
+
// This mimics VS Code behavior.
|
|
379
|
+
|
|
380
|
+
if (isHorizontal) {
|
|
381
|
+
setGlobalCursorStyle(
|
|
382
|
+
movement < 0 ? "horizontal-min" : "horizontal-max"
|
|
383
|
+
);
|
|
384
|
+
} else {
|
|
385
|
+
setGlobalCursorStyle(
|
|
386
|
+
movement < 0 ? "vertical-min" : "vertical-max"
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
// Reset the cursor style to the the normal resize cursor.
|
|
391
|
+
setGlobalCursorStyle(isHorizontal ? "horizontal" : "vertical");
|
|
392
|
+
}
|
|
331
393
|
}
|
|
332
|
-
}
|
|
333
|
-
// Reset the cursor style to the the normal resize cursor.
|
|
334
|
-
setGlobalCursorStyle(isHorizontal ? "horizontal" : "vertical");
|
|
394
|
+
}
|
|
335
395
|
|
|
396
|
+
if (sizesChanged) {
|
|
336
397
|
// If resize change handlers have been declared, this is the time to call them.
|
|
337
398
|
callPanelCallbacks(panelsArray, prevSizes, nextSizes);
|
|
338
399
|
|
|
339
400
|
setSizes(nextSizes);
|
|
340
401
|
}
|
|
402
|
+
|
|
403
|
+
prevDeltaRef.current = delta;
|
|
341
404
|
};
|
|
342
405
|
|
|
343
406
|
return resizeHandler;
|
|
@@ -396,7 +459,8 @@ export function PanelGroup({
|
|
|
396
459
|
idAfter,
|
|
397
460
|
delta,
|
|
398
461
|
prevSizes,
|
|
399
|
-
panelSizeBeforeCollapse.current
|
|
462
|
+
panelSizeBeforeCollapse.current,
|
|
463
|
+
null
|
|
400
464
|
);
|
|
401
465
|
if (prevSizes !== nextSizes) {
|
|
402
466
|
// If resize change handlers have been declared, this is the time to call them.
|
|
@@ -448,7 +512,8 @@ export function PanelGroup({
|
|
|
448
512
|
idAfter,
|
|
449
513
|
delta,
|
|
450
514
|
prevSizes,
|
|
451
|
-
panelSizeBeforeCollapse.current
|
|
515
|
+
panelSizeBeforeCollapse.current,
|
|
516
|
+
null
|
|
452
517
|
);
|
|
453
518
|
if (prevSizes !== nextSizes) {
|
|
454
519
|
// If resize change handlers have been declared, this is the time to call them.
|
|
@@ -478,6 +543,12 @@ export function PanelGroup({
|
|
|
478
543
|
return;
|
|
479
544
|
}
|
|
480
545
|
|
|
546
|
+
if (panel.collapsible && nextSize === 0) {
|
|
547
|
+
// This is a valid resize state.
|
|
548
|
+
} else {
|
|
549
|
+
nextSize = Math.min(panel.maxSize, Math.max(panel.minSize, nextSize));
|
|
550
|
+
}
|
|
551
|
+
|
|
481
552
|
const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
|
|
482
553
|
if (idBefore == null || idAfter == null) {
|
|
483
554
|
return;
|
|
@@ -493,7 +564,8 @@ export function PanelGroup({
|
|
|
493
564
|
idAfter,
|
|
494
565
|
delta,
|
|
495
566
|
prevSizes,
|
|
496
|
-
panelSizeBeforeCollapse.current
|
|
567
|
+
panelSizeBeforeCollapse.current,
|
|
568
|
+
null
|
|
497
569
|
);
|
|
498
570
|
if (prevSizes !== nextSizes) {
|
|
499
571
|
// If resize change handlers have been declared, this is the time to call them.
|
|
@@ -517,11 +589,21 @@ export function PanelGroup({
|
|
|
517
589
|
startDragging: (id: string, event: ResizeEvent) => {
|
|
518
590
|
setActiveHandleId(id);
|
|
519
591
|
|
|
520
|
-
|
|
592
|
+
if (isMouseEvent(event) || isTouchEvent(event)) {
|
|
593
|
+
const handleElement = getResizeHandle(id);
|
|
594
|
+
|
|
595
|
+
initialDragStateRef.current = {
|
|
596
|
+
dragHandleRect: handleElement.getBoundingClientRect(),
|
|
597
|
+
dragOffset: getDragOffset(event, id, direction),
|
|
598
|
+
sizes: committedValuesRef.current.sizes,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
521
601
|
},
|
|
522
602
|
stopDragging: () => {
|
|
523
603
|
resetGlobalCursorStyle();
|
|
524
604
|
setActiveHandleId(null);
|
|
605
|
+
|
|
606
|
+
initialDragStateRef.current = null;
|
|
525
607
|
},
|
|
526
608
|
unregisterPanel,
|
|
527
609
|
}),
|
|
@@ -551,6 +633,7 @@ export function PanelGroup({
|
|
|
551
633
|
children: createElement(Type, {
|
|
552
634
|
children,
|
|
553
635
|
className: classNameFromProps,
|
|
636
|
+
"data-panel-group": "",
|
|
554
637
|
"data-panel-group-direction": direction,
|
|
555
638
|
"data-panel-group-id": groupId,
|
|
556
639
|
style: { ...style, ...styleFromProps },
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { PanelResizeHandle } from "./PanelResizeHandle";
|
|
|
5
5
|
import type { ImperativePanelHandle, PanelProps } from "./Panel";
|
|
6
6
|
import type { PanelGroupProps } from "./PanelGroup";
|
|
7
7
|
import type { PanelResizeHandleProps } from "./PanelResizeHandle";
|
|
8
|
+
import type { PanelGroupStorage } from "./types";
|
|
8
9
|
|
|
9
10
|
export {
|
|
10
11
|
Panel,
|
|
@@ -14,6 +15,7 @@ export {
|
|
|
14
15
|
// TypeScript types
|
|
15
16
|
ImperativePanelHandle,
|
|
16
17
|
PanelGroupProps,
|
|
18
|
+
PanelGroupStorage,
|
|
17
19
|
PanelProps,
|
|
18
20
|
PanelResizeHandleProps,
|
|
19
21
|
};
|
package/src/types.ts
CHANGED
|
@@ -2,6 +2,11 @@ import { RefObject } from "react";
|
|
|
2
2
|
|
|
3
3
|
export type Direction = "horizontal" | "vertical";
|
|
4
4
|
|
|
5
|
+
export type PanelGroupStorage = {
|
|
6
|
+
getItem(name: string): string | null;
|
|
7
|
+
setItem(name: string, value: string): void;
|
|
8
|
+
};
|
|
9
|
+
|
|
5
10
|
export type PanelGroupOnLayout = (sizes: number[]) => void;
|
|
6
11
|
export type PanelOnCollapse = (collapsed: boolean) => void;
|
|
7
12
|
export type PanelOnResize = (size: number) => void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function areEqual(arrayA: any[], arrayB: any[]): boolean {
|
|
2
|
+
if (arrayA.length !== arrayB.length) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
for (let index = 0; index < arrayA.length; index++) {
|
|
7
|
+
if (arrayA[index] !== arrayB[index]) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return true;
|
|
13
|
+
}
|
package/src/utils/coordinates.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PRECISION } from "../constants";
|
|
2
|
+
import { InitialDragState } from "../PanelGroup";
|
|
2
3
|
import { Direction, PanelData, ResizeEvent } from "../types";
|
|
3
4
|
import {
|
|
4
5
|
getPanelGroup,
|
|
@@ -20,7 +21,8 @@ export function getDragOffset(
|
|
|
20
21
|
event: ResizeEvent,
|
|
21
22
|
handleId: string,
|
|
22
23
|
direction: Direction,
|
|
23
|
-
initialOffset: number = 0
|
|
24
|
+
initialOffset: number = 0,
|
|
25
|
+
initialHandleElementRect: DOMRect | null = null
|
|
24
26
|
): number {
|
|
25
27
|
const isHorizontal = direction === "horizontal";
|
|
26
28
|
|
|
@@ -35,7 +37,8 @@ export function getDragOffset(
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
const handleElement = getResizeHandle(handleId);
|
|
38
|
-
const rect =
|
|
40
|
+
const rect =
|
|
41
|
+
initialHandleElementRect || handleElement.getBoundingClientRect();
|
|
39
42
|
const elementOffset = isHorizontal ? rect.left : rect.top;
|
|
40
43
|
|
|
41
44
|
return pointerOffset - elementOffset - initialOffset;
|
|
@@ -48,9 +51,19 @@ export function getMovement(
|
|
|
48
51
|
handleId: string,
|
|
49
52
|
panelsArray: PanelData[],
|
|
50
53
|
direction: Direction,
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
prevSizes: number[],
|
|
55
|
+
initialDragState: InitialDragState | null
|
|
53
56
|
): number {
|
|
57
|
+
const {
|
|
58
|
+
dragOffset = 0,
|
|
59
|
+
dragHandleRect,
|
|
60
|
+
sizes: initialSizes,
|
|
61
|
+
} = initialDragState || {};
|
|
62
|
+
|
|
63
|
+
// If we're resizing by mouse or touch, use the initial sizes as a base.
|
|
64
|
+
// This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
|
|
65
|
+
const baseSizes = initialSizes || prevSizes;
|
|
66
|
+
|
|
54
67
|
if (isKeyDown(event)) {
|
|
55
68
|
const isHorizontal = direction === "horizontal";
|
|
56
69
|
|
|
@@ -98,10 +111,10 @@ export function getMovement(
|
|
|
98
111
|
);
|
|
99
112
|
const targetPanel = panelsArray[targetPanelIndex];
|
|
100
113
|
if (targetPanel.collapsible) {
|
|
101
|
-
const
|
|
114
|
+
const baseSize = baseSizes[targetPanelIndex];
|
|
102
115
|
if (
|
|
103
|
-
|
|
104
|
-
|
|
116
|
+
baseSize === 0 ||
|
|
117
|
+
baseSize.toPrecision(PRECISION) ===
|
|
105
118
|
targetPanel.minSize.toPrecision(PRECISION)
|
|
106
119
|
) {
|
|
107
120
|
movement =
|
|
@@ -113,7 +126,13 @@ export function getMovement(
|
|
|
113
126
|
|
|
114
127
|
return movement;
|
|
115
128
|
} else {
|
|
116
|
-
return getDragOffset(
|
|
129
|
+
return getDragOffset(
|
|
130
|
+
event,
|
|
131
|
+
handleId,
|
|
132
|
+
direction,
|
|
133
|
+
dragOffset,
|
|
134
|
+
dragHandleRect
|
|
135
|
+
);
|
|
117
136
|
}
|
|
118
137
|
}
|
|
119
138
|
|
package/src/utils/group.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PRECISION } from "../constants";
|
|
2
|
+
import { InitialDragState } from "../PanelGroup";
|
|
2
3
|
import { PanelData, ResizeEvent } from "../types";
|
|
3
4
|
|
|
4
5
|
export function adjustByDelta(
|
|
@@ -8,15 +9,22 @@ export function adjustByDelta(
|
|
|
8
9
|
idAfter: string,
|
|
9
10
|
delta: number,
|
|
10
11
|
prevSizes: number[],
|
|
11
|
-
panelSizeBeforeCollapse: Map<string, number
|
|
12
|
+
panelSizeBeforeCollapse: Map<string, number>,
|
|
13
|
+
initialDragState: InitialDragState | null
|
|
12
14
|
): number[] {
|
|
15
|
+
const { sizes: initialSizes } = initialDragState || {};
|
|
16
|
+
|
|
17
|
+
// If we're resizing by mouse or touch, use the initial sizes as a base.
|
|
18
|
+
// This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
|
|
19
|
+
const baseSizes = initialSizes || prevSizes;
|
|
20
|
+
|
|
13
21
|
if (delta === 0) {
|
|
14
|
-
return
|
|
22
|
+
return baseSizes;
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
const panelsArray = panelsMapToSortedArray(panels);
|
|
18
26
|
|
|
19
|
-
const nextSizes =
|
|
27
|
+
const nextSizes = baseSizes.concat();
|
|
20
28
|
|
|
21
29
|
let deltaApplied = 0;
|
|
22
30
|
|
|
@@ -33,17 +41,18 @@ export function adjustByDelta(
|
|
|
33
41
|
const pivotId = delta < 0 ? idAfter : idBefore;
|
|
34
42
|
const index = panelsArray.findIndex((panel) => panel.id === pivotId);
|
|
35
43
|
const panel = panelsArray[index];
|
|
36
|
-
const
|
|
44
|
+
const baseSize = baseSizes[index];
|
|
37
45
|
|
|
38
|
-
const nextSize = safeResizePanel(panel, Math.abs(delta),
|
|
39
|
-
if (
|
|
40
|
-
|
|
46
|
+
const nextSize = safeResizePanel(panel, Math.abs(delta), baseSize, event);
|
|
47
|
+
if (baseSize === nextSize) {
|
|
48
|
+
// If there's no room for the pivot panel to grow, we can ignore this drag update.
|
|
49
|
+
return baseSizes;
|
|
41
50
|
} else {
|
|
42
|
-
if (nextSize === 0 &&
|
|
43
|
-
panelSizeBeforeCollapse.set(pivotId,
|
|
51
|
+
if (nextSize === 0 && baseSize > 0) {
|
|
52
|
+
panelSizeBeforeCollapse.set(pivotId, baseSize);
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
delta = delta < 0 ?
|
|
55
|
+
delta = delta < 0 ? baseSize - nextSize : nextSize - baseSize;
|
|
47
56
|
}
|
|
48
57
|
}
|
|
49
58
|
|
|
@@ -51,24 +60,32 @@ export function adjustByDelta(
|
|
|
51
60
|
let index = panelsArray.findIndex((panel) => panel.id === pivotId);
|
|
52
61
|
while (true) {
|
|
53
62
|
const panel = panelsArray[index];
|
|
54
|
-
const
|
|
63
|
+
const baseSize = baseSizes[index];
|
|
64
|
+
|
|
65
|
+
const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
|
|
55
66
|
|
|
56
67
|
const nextSize = safeResizePanel(
|
|
57
68
|
panel,
|
|
58
|
-
0 -
|
|
59
|
-
|
|
69
|
+
0 - deltaRemaining,
|
|
70
|
+
baseSize,
|
|
60
71
|
event
|
|
61
72
|
);
|
|
62
|
-
if (
|
|
63
|
-
if (nextSize === 0 &&
|
|
64
|
-
panelSizeBeforeCollapse.set(panel.id,
|
|
73
|
+
if (baseSize !== nextSize) {
|
|
74
|
+
if (nextSize === 0 && baseSize > 0) {
|
|
75
|
+
panelSizeBeforeCollapse.set(panel.id, baseSize);
|
|
65
76
|
}
|
|
66
77
|
|
|
67
|
-
deltaApplied +=
|
|
78
|
+
deltaApplied += baseSize - nextSize;
|
|
68
79
|
|
|
69
80
|
nextSizes[index] = nextSize;
|
|
70
81
|
|
|
71
|
-
if (
|
|
82
|
+
if (
|
|
83
|
+
deltaApplied
|
|
84
|
+
.toPrecision(PRECISION)
|
|
85
|
+
.localeCompare(Math.abs(delta).toPrecision(PRECISION), undefined, {
|
|
86
|
+
numeric: true,
|
|
87
|
+
}) >= 0
|
|
88
|
+
) {
|
|
72
89
|
break;
|
|
73
90
|
}
|
|
74
91
|
}
|
|
@@ -87,13 +104,13 @@ export function adjustByDelta(
|
|
|
87
104
|
// If we were unable to resize any of the panels panels, return the previous state.
|
|
88
105
|
// This will essentially bailout and ignore the "mousemove" event.
|
|
89
106
|
if (deltaApplied === 0) {
|
|
90
|
-
return
|
|
107
|
+
return baseSizes;
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
// Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
|
|
94
111
|
pivotId = delta < 0 ? idAfter : idBefore;
|
|
95
112
|
index = panelsArray.findIndex((panel) => panel.id === pivotId);
|
|
96
|
-
nextSizes[index] =
|
|
113
|
+
nextSizes[index] = baseSizes[index] + deltaApplied;
|
|
97
114
|
|
|
98
115
|
return nextSizes;
|
|
99
116
|
}
|
|
@@ -199,7 +216,7 @@ export function getResizeHandleIndex(id: string): number | null {
|
|
|
199
216
|
const index = handles.findIndex(
|
|
200
217
|
(handle) => handle.getAttribute("data-panel-resize-handle-id") === id
|
|
201
218
|
);
|
|
202
|
-
return index
|
|
219
|
+
return index ?? null;
|
|
203
220
|
}
|
|
204
221
|
|
|
205
222
|
export function getResizeHandles(): HTMLDivElement[] {
|
|
@@ -223,8 +240,8 @@ export function getResizeHandlePanelIds(
|
|
|
223
240
|
const handles = getResizeHandlesForGroup(groupId);
|
|
224
241
|
const index = handles.indexOf(handle);
|
|
225
242
|
|
|
226
|
-
const idBefore: string | null = panelsArray[index]?.id
|
|
227
|
-
const idAfter: string | null = panelsArray[index + 1]?.id
|
|
243
|
+
const idBefore: string | null = panelsArray[index]?.id ?? null;
|
|
244
|
+
const idAfter: string | null = panelsArray[index + 1]?.id ?? null;
|
|
228
245
|
|
|
229
246
|
return [idBefore, idAfter];
|
|
230
247
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PanelData } from "../types";
|
|
1
|
+
import { PanelData, PanelGroupStorage } from "../types";
|
|
2
2
|
|
|
3
3
|
type SerializedPanelGroupState = { [panelIds: string]: number[] };
|
|
4
4
|
|
|
@@ -17,10 +17,11 @@ function getSerializationKey(panels: PanelData[]): string {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function loadSerializedPanelGroupState(
|
|
20
|
-
autoSaveId: string
|
|
20
|
+
autoSaveId: string,
|
|
21
|
+
storage: PanelGroupStorage
|
|
21
22
|
): SerializedPanelGroupState | null {
|
|
22
23
|
try {
|
|
23
|
-
const serialized =
|
|
24
|
+
const serialized = storage.getItem(`PanelGroup:sizes:${autoSaveId}`);
|
|
24
25
|
if (serialized) {
|
|
25
26
|
const parsed = JSON.parse(serialized);
|
|
26
27
|
if (typeof parsed === "object" && parsed != null) {
|
|
@@ -34,12 +35,13 @@ function loadSerializedPanelGroupState(
|
|
|
34
35
|
|
|
35
36
|
export function loadPanelLayout(
|
|
36
37
|
autoSaveId: string,
|
|
37
|
-
panels: PanelData[]
|
|
38
|
+
panels: PanelData[],
|
|
39
|
+
storage: PanelGroupStorage
|
|
38
40
|
): number[] | null {
|
|
39
|
-
const state = loadSerializedPanelGroupState(autoSaveId);
|
|
41
|
+
const state = loadSerializedPanelGroupState(autoSaveId, storage);
|
|
40
42
|
if (state) {
|
|
41
43
|
const key = getSerializationKey(panels);
|
|
42
|
-
return state[key]
|
|
44
|
+
return state[key] ?? null;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
return null;
|
|
@@ -48,17 +50,15 @@ export function loadPanelLayout(
|
|
|
48
50
|
export function savePanelGroupLayout(
|
|
49
51
|
autoSaveId: string,
|
|
50
52
|
panels: PanelData[],
|
|
51
|
-
sizes: number[]
|
|
53
|
+
sizes: number[],
|
|
54
|
+
storage: PanelGroupStorage
|
|
52
55
|
): void {
|
|
53
56
|
const key = getSerializationKey(panels);
|
|
54
|
-
const state = loadSerializedPanelGroupState(autoSaveId) || {};
|
|
57
|
+
const state = loadSerializedPanelGroupState(autoSaveId, storage) || {};
|
|
55
58
|
state[key] = sizes;
|
|
56
59
|
|
|
57
60
|
try {
|
|
58
|
-
|
|
59
|
-
`PanelGroup:sizes:${autoSaveId}`,
|
|
60
|
-
JSON.stringify(state)
|
|
61
|
-
);
|
|
61
|
+
storage.setItem(`PanelGroup:sizes:${autoSaveId}`, JSON.stringify(state));
|
|
62
62
|
} catch (error) {
|
|
63
63
|
console.error(error);
|
|
64
64
|
}
|