react-resizable-panels 0.0.54 → 0.0.56
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 +253 -80
- package/README.md +55 -49
- package/dist/declarations/src/Panel.d.ts +76 -20
- package/dist/declarations/src/PanelGroup.d.ts +29 -21
- package/dist/declarations/src/PanelResizeHandle.d.ts +1 -1
- package/dist/declarations/src/index.d.ts +5 -5
- package/dist/declarations/src/types.d.ts +3 -25
- package/dist/declarations/src/vendor/react.d.ts +4 -4
- package/dist/react-resizable-panels.browser.cjs.js +1279 -796
- package/dist/react-resizable-panels.browser.development.cjs.js +1404 -809
- package/dist/react-resizable-panels.browser.development.esm.js +1398 -803
- package/dist/react-resizable-panels.browser.esm.js +1279 -796
- package/dist/react-resizable-panels.cjs.js +1279 -796
- package/dist/react-resizable-panels.cjs.js.map +1 -0
- package/dist/react-resizable-panels.development.cjs.js +1399 -804
- package/dist/react-resizable-panels.development.esm.js +1400 -805
- package/dist/react-resizable-panels.development.node.cjs.js +1172 -755
- package/dist/react-resizable-panels.development.node.esm.js +1173 -756
- package/dist/react-resizable-panels.esm.js +1279 -796
- package/dist/react-resizable-panels.esm.js.map +1 -0
- package/dist/react-resizable-panels.node.cjs.js +1064 -749
- package/dist/react-resizable-panels.node.esm.js +1065 -750
- package/jest.config.js +10 -0
- package/package.json +3 -1
- package/src/Panel.test.tsx +308 -0
- package/src/Panel.ts +179 -127
- package/src/PanelGroup.test.tsx +210 -0
- package/src/PanelGroup.ts +751 -580
- package/src/PanelGroupContext.ts +33 -0
- package/src/PanelResizeHandle.ts +13 -8
- package/src/hooks/useUniqueId.ts +1 -1
- package/src/hooks/useWindowSplitterBehavior.ts +9 -161
- package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +185 -0
- package/src/index.ts +24 -11
- package/src/types.ts +3 -29
- 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 +71 -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.ts +55 -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 +7 -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.ts +41 -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/src/PanelContexts.ts +0 -20
- package/src/utils/coordinates.ts +0 -149
- package/src/utils/group.ts +0 -315
package/src/PanelGroup.ts
CHANGED
|
@@ -1,12 +1,40 @@
|
|
|
1
|
-
import { isBrowser } from "#is-browser";
|
|
2
1
|
import { isDevelopment } from "#is-development";
|
|
2
|
+
import { PanelData } from "./Panel";
|
|
3
|
+
import { DragState, PanelGroupContext, ResizeEvent } from "./PanelGroupContext";
|
|
4
|
+
import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
|
|
5
|
+
import useUniqueId from "./hooks/useUniqueId";
|
|
6
|
+
import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterPanelGroupBehavior";
|
|
7
|
+
import { Direction, MixedSizes } from "./types";
|
|
8
|
+
import { adjustLayoutByDelta } from "./utils/adjustLayoutByDelta";
|
|
9
|
+
import { areEqual } from "./utils/arrays";
|
|
10
|
+
import { calculateDeltaPercentage } from "./utils/calculateDeltaPercentage";
|
|
11
|
+
import { calculateUnsafeDefaultLayout } from "./utils/calculateUnsafeDefaultLayout";
|
|
12
|
+
import { callPanelCallbacks } from "./utils/callPanelCallbacks";
|
|
13
|
+
import { compareLayouts } from "./utils/compareLayouts";
|
|
14
|
+
import { computePanelFlexBoxStyle } from "./utils/computePanelFlexBoxStyle";
|
|
15
|
+
import { computePercentagePanelConstraints } from "./utils/computePercentagePanelConstraints";
|
|
16
|
+
import { convertPercentageToPixels } from "./utils/convertPercentageToPixels";
|
|
17
|
+
import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
|
|
18
|
+
import debounce from "./utils/debounce";
|
|
19
|
+
import { determinePivotIndices } from "./utils/determinePivotIndices";
|
|
20
|
+
import { calculateAvailablePanelSizeInPixels } from "./utils/dom/calculateAvailablePanelSizeInPixels";
|
|
21
|
+
import { getPanelGroupElement } from "./utils/dom/getPanelGroupElement";
|
|
22
|
+
import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
|
|
23
|
+
import { isKeyDown, isMouseEvent, isTouchEvent } from "./utils/events";
|
|
24
|
+
import { getPercentageSizeFromMixedSizes } from "./utils/getPercentageSizeFromMixedSizes";
|
|
25
|
+
import { getResizeEventCursorPosition } from "./utils/getResizeEventCursorPosition";
|
|
26
|
+
import { initializeDefaultStorage } from "./utils/initializeDefaultStorage";
|
|
27
|
+
import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
|
|
28
|
+
import { shouldMonitorPixelBasedConstraints } from "./utils/shouldMonitorPixelBasedConstraints";
|
|
29
|
+
import { validatePanelConstraints } from "./utils/validatePanelConstraints";
|
|
30
|
+
import { validatePanelGroupLayout } from "./utils/validatePanelGroupLayout";
|
|
3
31
|
import {
|
|
4
|
-
createElement,
|
|
5
32
|
CSSProperties,
|
|
6
33
|
ElementType,
|
|
7
34
|
ForwardedRef,
|
|
35
|
+
PropsWithChildren,
|
|
36
|
+
createElement,
|
|
8
37
|
forwardRef,
|
|
9
|
-
ReactNode,
|
|
10
38
|
useCallback,
|
|
11
39
|
useEffect,
|
|
12
40
|
useImperativeHandle,
|
|
@@ -15,72 +43,20 @@ import {
|
|
|
15
43
|
useState,
|
|
16
44
|
} from "./vendor/react";
|
|
17
45
|
|
|
18
|
-
|
|
19
|
-
import useUniqueId from "./hooks/useUniqueId";
|
|
20
|
-
import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterBehavior";
|
|
21
|
-
import { PanelGroupContext } from "./PanelContexts";
|
|
22
|
-
import {
|
|
23
|
-
Direction,
|
|
24
|
-
PanelData,
|
|
25
|
-
PanelGroupOnLayout,
|
|
26
|
-
PanelGroupStorage,
|
|
27
|
-
ResizeEvent,
|
|
28
|
-
} from "./types";
|
|
29
|
-
import { areEqual } from "./utils/arrays";
|
|
30
|
-
import { assert } from "./utils/assert";
|
|
31
|
-
import {
|
|
32
|
-
getDragOffset,
|
|
33
|
-
getMovement,
|
|
34
|
-
isMouseEvent,
|
|
35
|
-
isTouchEvent,
|
|
36
|
-
} from "./utils/coordinates";
|
|
37
|
-
import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
|
|
38
|
-
import debounce from "./utils/debounce";
|
|
39
|
-
import {
|
|
40
|
-
adjustByDelta,
|
|
41
|
-
callPanelCallbacks,
|
|
42
|
-
getBeforeAndAfterIds,
|
|
43
|
-
getFlexGrow,
|
|
44
|
-
getPanelGroup,
|
|
45
|
-
getResizeHandle,
|
|
46
|
-
getResizeHandlePanelIds,
|
|
47
|
-
panelsMapToSortedArray,
|
|
48
|
-
} from "./utils/group";
|
|
49
|
-
import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
|
|
46
|
+
const LOCAL_STORAGE_DEBOUNCE_INTERVAL = 100;
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
storage: PanelGroupStorage
|
|
57
|
-
) => void;
|
|
58
|
-
} = {};
|
|
48
|
+
export type ImperativePanelGroupHandle = {
|
|
49
|
+
getId: () => string;
|
|
50
|
+
getLayout: () => MixedSizes[];
|
|
51
|
+
setLayout: (layout: Partial<MixedSizes>[]) => void;
|
|
52
|
+
};
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
function initializeDefaultStorage(storageObject: PanelGroupStorage) {
|
|
65
|
-
try {
|
|
66
|
-
if (typeof localStorage !== "undefined") {
|
|
67
|
-
// Bypass this check for future calls
|
|
68
|
-
storageObject.getItem = (name: string) => {
|
|
69
|
-
return localStorage.getItem(name);
|
|
70
|
-
};
|
|
71
|
-
storageObject.setItem = (name: string, value: string) => {
|
|
72
|
-
localStorage.setItem(name, value);
|
|
73
|
-
};
|
|
74
|
-
} else {
|
|
75
|
-
throw new Error("localStorage not supported in this environment");
|
|
76
|
-
}
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.error(error);
|
|
54
|
+
export type PanelGroupStorage = {
|
|
55
|
+
getItem(name: string): string | null;
|
|
56
|
+
setItem(name: string, value: string): void;
|
|
57
|
+
};
|
|
79
58
|
|
|
80
|
-
|
|
81
|
-
storageObject.setItem = () => {};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
59
|
+
export type PanelGroupOnLayout = (layout: MixedSizes[]) => void;
|
|
84
60
|
|
|
85
61
|
const defaultStorage: PanelGroupStorage = {
|
|
86
62
|
getItem: (name: string) => {
|
|
@@ -93,132 +69,145 @@ const defaultStorage: PanelGroupStorage = {
|
|
|
93
69
|
},
|
|
94
70
|
};
|
|
95
71
|
|
|
96
|
-
export type
|
|
97
|
-
direction: Direction;
|
|
98
|
-
panels: Map<string, PanelData>;
|
|
99
|
-
sizes: number[];
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
export type PanelDataMap = Map<string, PanelData>;
|
|
103
|
-
|
|
104
|
-
// Initial drag state serves a few purposes:
|
|
105
|
-
// * dragOffset:
|
|
106
|
-
// Resize is calculated by the distance between the current pointer event and the resize handle being "dragged"
|
|
107
|
-
// This value accounts for the initial offset when the touch/click starts, so the handle doesn't appear to "jump"
|
|
108
|
-
// * dragHandleRect, sizes:
|
|
109
|
-
// When resizing is done via mouse/touch event– some initial state is stored
|
|
110
|
-
// so that any panels that contract will also expand if drag direction is reversed.
|
|
111
|
-
export type InitialDragState = {
|
|
112
|
-
dragHandleRect: DOMRect;
|
|
113
|
-
dragOffset: number;
|
|
114
|
-
sizes: number[];
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
// TODO
|
|
118
|
-
// Within an active drag, remember original positions to refine more easily on expand.
|
|
119
|
-
// Look at what the Chrome devtools Sources does.
|
|
120
|
-
|
|
121
|
-
export type PanelGroupProps = {
|
|
72
|
+
export type PanelGroupProps = PropsWithChildren<{
|
|
122
73
|
autoSaveId?: string;
|
|
123
|
-
children?: ReactNode;
|
|
124
74
|
className?: string;
|
|
125
75
|
direction: Direction;
|
|
126
|
-
disablePointerEventsDuringResize?: boolean;
|
|
127
76
|
id?: string | null;
|
|
128
|
-
|
|
77
|
+
keyboardResizeByPercentage?: number | null;
|
|
78
|
+
keyboardResizeByPixels?: number | null;
|
|
79
|
+
onLayout?: PanelGroupOnLayout | null;
|
|
129
80
|
storage?: PanelGroupStorage;
|
|
130
81
|
style?: CSSProperties;
|
|
131
82
|
tagName?: ElementType;
|
|
132
|
-
}
|
|
83
|
+
}>;
|
|
133
84
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
};
|
|
85
|
+
const debounceMap: {
|
|
86
|
+
[key: string]: typeof savePanelGroupLayout;
|
|
87
|
+
} = {};
|
|
138
88
|
|
|
139
89
|
function PanelGroupWithForwardedRef({
|
|
140
90
|
autoSaveId,
|
|
141
|
-
children
|
|
91
|
+
children,
|
|
142
92
|
className: classNameFromProps = "",
|
|
143
93
|
direction,
|
|
144
|
-
disablePointerEventsDuringResize = false,
|
|
145
94
|
forwardedRef,
|
|
146
|
-
id: idFromProps
|
|
147
|
-
onLayout,
|
|
95
|
+
id: idFromProps,
|
|
96
|
+
onLayout = null,
|
|
97
|
+
keyboardResizeByPercentage = null,
|
|
98
|
+
keyboardResizeByPixels = null,
|
|
148
99
|
storage = defaultStorage,
|
|
149
|
-
style: styleFromProps
|
|
100
|
+
style: styleFromProps,
|
|
150
101
|
tagName: Type = "div",
|
|
151
102
|
}: PanelGroupProps & {
|
|
152
103
|
forwardedRef: ForwardedRef<ImperativePanelGroupHandle>;
|
|
153
104
|
}) {
|
|
154
105
|
const groupId = useUniqueId(idFromProps);
|
|
155
106
|
|
|
156
|
-
const [
|
|
157
|
-
const [
|
|
107
|
+
const [dragState, setDragState] = useState<DragState | null>(null);
|
|
108
|
+
const [layout, setLayout] = useState<number[]>([]);
|
|
109
|
+
const [panelDataArray, setPanelDataArray] = useState<PanelData[]>([]);
|
|
110
|
+
|
|
111
|
+
const panelIdToLastNotifiedMixedSizesMapRef = useRef<
|
|
112
|
+
Record<string, MixedSizes>
|
|
113
|
+
>({});
|
|
114
|
+
const panelSizeBeforeCollapseRef = useRef<Map<string, number>>(new Map());
|
|
115
|
+
const prevDeltaRef = useRef<number>(0);
|
|
158
116
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
117
|
+
const committedValuesRef = useRef<{
|
|
118
|
+
direction: Direction;
|
|
119
|
+
dragState: DragState | null;
|
|
120
|
+
id: string;
|
|
121
|
+
keyboardResizeByPercentage: number | null;
|
|
122
|
+
keyboardResizeByPixels: number | null;
|
|
123
|
+
layout: number[];
|
|
124
|
+
onLayout: PanelGroupOnLayout | null;
|
|
125
|
+
panelDataArray: PanelData[];
|
|
126
|
+
}>({
|
|
127
|
+
direction,
|
|
128
|
+
dragState,
|
|
129
|
+
id: groupId,
|
|
130
|
+
keyboardResizeByPercentage,
|
|
131
|
+
keyboardResizeByPixels,
|
|
132
|
+
layout,
|
|
133
|
+
onLayout,
|
|
134
|
+
panelDataArray,
|
|
135
|
+
});
|
|
163
136
|
|
|
164
137
|
const devWarningsRef = useRef<{
|
|
165
|
-
didLogDefaultSizeWarning: boolean;
|
|
166
138
|
didLogIdAndOrderWarning: boolean;
|
|
139
|
+
didLogPanelConstraintsWarning: boolean;
|
|
167
140
|
prevPanelIds: string[];
|
|
168
141
|
}>({
|
|
169
|
-
didLogDefaultSizeWarning: false,
|
|
170
142
|
didLogIdAndOrderWarning: false,
|
|
143
|
+
didLogPanelConstraintsWarning: false,
|
|
171
144
|
prevPanelIds: [],
|
|
172
145
|
});
|
|
173
146
|
|
|
174
|
-
// Use a ref to guard against users passing inline props
|
|
175
|
-
const callbacksRef = useRef<{
|
|
176
|
-
onLayout: PanelGroupOnLayout | undefined;
|
|
177
|
-
}>({ onLayout });
|
|
178
|
-
useEffect(() => {
|
|
179
|
-
callbacksRef.current.onLayout = onLayout;
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
const panelIdToLastNotifiedSizeMapRef = useRef<Record<string, number>>({});
|
|
183
|
-
|
|
184
|
-
// 0-1 values representing the relative size of each panel.
|
|
185
|
-
const [sizes, setSizes] = useState<number[]>([]);
|
|
186
|
-
|
|
187
|
-
// Used to support imperative collapse/expand API.
|
|
188
|
-
const panelSizeBeforeCollapse = useRef<Map<string, number>>(new Map());
|
|
189
|
-
|
|
190
|
-
const prevDeltaRef = useRef<number>(0);
|
|
191
|
-
|
|
192
|
-
// Store committed values to avoid unnecessarily re-running memoization/effects functions.
|
|
193
|
-
const committedValuesRef = useRef<CommittedValues>({
|
|
194
|
-
direction,
|
|
195
|
-
panels,
|
|
196
|
-
sizes,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
147
|
useImperativeHandle(
|
|
200
148
|
forwardedRef,
|
|
201
149
|
() => ({
|
|
150
|
+
getId: () => committedValuesRef.current.id,
|
|
202
151
|
getLayout: () => {
|
|
203
|
-
const {
|
|
204
|
-
|
|
152
|
+
const { id: groupId, layout } = committedValuesRef.current;
|
|
153
|
+
|
|
154
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
155
|
+
|
|
156
|
+
return layout.map((sizePercentage) => {
|
|
157
|
+
return {
|
|
158
|
+
sizePercentage,
|
|
159
|
+
sizePixels: convertPercentageToPixels(
|
|
160
|
+
sizePercentage,
|
|
161
|
+
groupSizePixels
|
|
162
|
+
),
|
|
163
|
+
};
|
|
164
|
+
});
|
|
205
165
|
},
|
|
206
|
-
setLayout: (
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
166
|
+
setLayout: (mixedSizes: Partial<MixedSizes>[]) => {
|
|
167
|
+
const {
|
|
168
|
+
id: groupId,
|
|
169
|
+
layout: prevLayout,
|
|
170
|
+
onLayout,
|
|
171
|
+
panelDataArray,
|
|
172
|
+
} = committedValuesRef.current;
|
|
211
173
|
|
|
212
|
-
|
|
174
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
213
175
|
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
176
|
+
const unsafeLayout = mixedSizes.map(
|
|
177
|
+
(mixedSize) =>
|
|
178
|
+
getPercentageSizeFromMixedSizes(mixedSize, groupSizePixels)!
|
|
179
|
+
);
|
|
218
180
|
|
|
219
|
-
|
|
181
|
+
const safeLayout = validatePanelGroupLayout({
|
|
182
|
+
groupSizePixels,
|
|
183
|
+
layout: unsafeLayout,
|
|
184
|
+
panelConstraints: panelDataArray.map(
|
|
185
|
+
(panelData) => panelData.constraints
|
|
186
|
+
),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
if (!areEqual(prevLayout, safeLayout)) {
|
|
190
|
+
setLayout(safeLayout);
|
|
191
|
+
|
|
192
|
+
if (onLayout) {
|
|
193
|
+
onLayout(
|
|
194
|
+
safeLayout.map((sizePercentage) => ({
|
|
195
|
+
sizePercentage,
|
|
196
|
+
sizePixels: convertPercentageToPixels(
|
|
197
|
+
sizePercentage,
|
|
198
|
+
groupSizePixels
|
|
199
|
+
),
|
|
200
|
+
}))
|
|
201
|
+
);
|
|
202
|
+
}
|
|
220
203
|
|
|
221
|
-
|
|
204
|
+
callPanelCallbacks(
|
|
205
|
+
groupId,
|
|
206
|
+
panelDataArray,
|
|
207
|
+
safeLayout,
|
|
208
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
209
|
+
);
|
|
210
|
+
}
|
|
222
211
|
},
|
|
223
212
|
}),
|
|
224
213
|
[]
|
|
@@ -226,131 +215,169 @@ function PanelGroupWithForwardedRef({
|
|
|
226
215
|
|
|
227
216
|
useIsomorphicLayoutEffect(() => {
|
|
228
217
|
committedValuesRef.current.direction = direction;
|
|
229
|
-
committedValuesRef.current.
|
|
230
|
-
committedValuesRef.current.
|
|
218
|
+
committedValuesRef.current.dragState = dragState;
|
|
219
|
+
committedValuesRef.current.id = groupId;
|
|
220
|
+
committedValuesRef.current.layout = layout;
|
|
221
|
+
committedValuesRef.current.onLayout = onLayout;
|
|
222
|
+
committedValuesRef.current.panelDataArray = panelDataArray;
|
|
231
223
|
});
|
|
232
224
|
|
|
233
225
|
useWindowSplitterPanelGroupBehavior({
|
|
234
226
|
committedValuesRef,
|
|
235
227
|
groupId,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
panelSizeBeforeCollapse,
|
|
228
|
+
layout,
|
|
229
|
+
panelDataArray,
|
|
230
|
+
setLayout,
|
|
240
231
|
});
|
|
241
232
|
|
|
242
|
-
// Notify external code when sizes have changed.
|
|
243
233
|
useEffect(() => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (sizes.length > 0) {
|
|
249
|
-
if (onLayout) {
|
|
250
|
-
onLayout(sizes);
|
|
234
|
+
// If this panel has been configured to persist sizing information, save sizes to local storage.
|
|
235
|
+
if (autoSaveId) {
|
|
236
|
+
if (layout.length === 0 || layout.length !== panelDataArray.length) {
|
|
237
|
+
return;
|
|
251
238
|
}
|
|
252
239
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
262
|
-
callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
|
|
240
|
+
// Limit the frequency of localStorage updates.
|
|
241
|
+
if (!debounceMap[autoSaveId]) {
|
|
242
|
+
debounceMap[autoSaveId] = debounce(
|
|
243
|
+
savePanelGroupLayout,
|
|
244
|
+
LOCAL_STORAGE_DEBOUNCE_INTERVAL
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
debounceMap[autoSaveId](autoSaveId, panelDataArray, layout, storage);
|
|
263
248
|
}
|
|
264
|
-
}, [
|
|
249
|
+
}, [autoSaveId, layout, panelDataArray, storage]);
|
|
265
250
|
|
|
266
251
|
// Once all panels have registered themselves,
|
|
267
252
|
// Compute the initial sizes based on default weights.
|
|
268
253
|
// This assumes that panels register during initial mount (no conditional rendering)!
|
|
269
254
|
useIsomorphicLayoutEffect(() => {
|
|
270
|
-
const
|
|
271
|
-
if (
|
|
272
|
-
// Only compute (or restore) default
|
|
255
|
+
const { id: groupId, layout, onLayout } = committedValuesRef.current;
|
|
256
|
+
if (layout.length === panelDataArray.length) {
|
|
257
|
+
// Only compute (or restore) default layout once per panel configuration.
|
|
273
258
|
return;
|
|
274
259
|
}
|
|
275
260
|
|
|
276
261
|
// If this panel has been configured to persist sizing information,
|
|
277
262
|
// default size should be restored from local storage if possible.
|
|
278
|
-
let
|
|
263
|
+
let unsafeLayout: number[] | null = null;
|
|
279
264
|
if (autoSaveId) {
|
|
280
|
-
|
|
281
|
-
defaultSizes = loadPanelLayout(autoSaveId, panelsArray, storage);
|
|
265
|
+
unsafeLayout = loadPanelLayout(autoSaveId, panelDataArray, storage);
|
|
282
266
|
}
|
|
283
267
|
|
|
284
|
-
|
|
285
|
-
setSizes(defaultSizes);
|
|
286
|
-
} else {
|
|
287
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
268
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
288
269
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
270
|
+
if (unsafeLayout == null) {
|
|
271
|
+
unsafeLayout = calculateUnsafeDefaultLayout({
|
|
272
|
+
groupSizePixels,
|
|
273
|
+
panelDataArray,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
292
276
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
277
|
+
// Validate even saved layouts in case something has changed since last render
|
|
278
|
+
// e.g. for pixel groups, this could be the size of the window
|
|
279
|
+
const validatedLayout = validatePanelGroupLayout({
|
|
280
|
+
groupSizePixels,
|
|
281
|
+
layout: unsafeLayout,
|
|
282
|
+
panelConstraints: panelDataArray.map(
|
|
283
|
+
(panelData) => panelData.constraints
|
|
284
|
+
),
|
|
285
|
+
});
|
|
297
286
|
|
|
298
|
-
|
|
299
|
-
|
|
287
|
+
if (!areEqual(layout, validatedLayout)) {
|
|
288
|
+
setLayout(validatedLayout);
|
|
289
|
+
}
|
|
300
290
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
291
|
+
if (onLayout) {
|
|
292
|
+
onLayout(
|
|
293
|
+
validatedLayout.map((sizePercentage) => ({
|
|
294
|
+
sizePercentage,
|
|
295
|
+
sizePixels: convertPercentageToPixels(
|
|
296
|
+
sizePercentage,
|
|
297
|
+
groupSizePixels
|
|
298
|
+
),
|
|
299
|
+
}))
|
|
300
|
+
);
|
|
301
|
+
}
|
|
307
302
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
throw new Error(`Invalid default sizes specified for panels`);
|
|
316
|
-
} else if (totalMinSize > 100) {
|
|
317
|
-
throw new Error(`Minimum panel sizes cannot exceed 100%`);
|
|
318
|
-
}
|
|
303
|
+
callPanelCallbacks(
|
|
304
|
+
groupId,
|
|
305
|
+
panelDataArray,
|
|
306
|
+
validatedLayout,
|
|
307
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
308
|
+
);
|
|
309
|
+
}, [autoSaveId, layout, panelDataArray, storage]);
|
|
319
310
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
311
|
+
useIsomorphicLayoutEffect(() => {
|
|
312
|
+
const constraints = panelDataArray.map(({ constraints }) => constraints);
|
|
313
|
+
if (!shouldMonitorPixelBasedConstraints(constraints)) {
|
|
314
|
+
// Avoid the overhead of ResizeObserver if no pixel constraints require monitoring
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
325
317
|
|
|
326
|
-
|
|
327
|
-
|
|
318
|
+
if (typeof ResizeObserver === "undefined") {
|
|
319
|
+
console.warn(
|
|
320
|
+
`WARNING: Pixel based constraints require ResizeObserver but it is not supported by the current browser.`
|
|
328
321
|
);
|
|
329
|
-
}
|
|
330
|
-
|
|
322
|
+
} else {
|
|
323
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
324
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
325
|
+
|
|
326
|
+
const { layout: prevLayout, onLayout } = committedValuesRef.current;
|
|
327
|
+
|
|
328
|
+
const nextLayout = validatePanelGroupLayout({
|
|
329
|
+
groupSizePixels,
|
|
330
|
+
layout: prevLayout,
|
|
331
|
+
panelConstraints: panelDataArray.map(
|
|
332
|
+
(panelData) => panelData.constraints
|
|
333
|
+
),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
if (!areEqual(prevLayout, nextLayout)) {
|
|
337
|
+
setLayout(nextLayout);
|
|
338
|
+
|
|
339
|
+
if (onLayout) {
|
|
340
|
+
onLayout(
|
|
341
|
+
nextLayout.map((sizePercentage) => ({
|
|
342
|
+
sizePercentage,
|
|
343
|
+
sizePixels: convertPercentageToPixels(
|
|
344
|
+
sizePercentage,
|
|
345
|
+
groupSizePixels
|
|
346
|
+
),
|
|
347
|
+
}))
|
|
348
|
+
);
|
|
349
|
+
}
|
|
331
350
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
351
|
+
callPanelCallbacks(
|
|
352
|
+
groupId,
|
|
353
|
+
panelDataArray,
|
|
354
|
+
nextLayout,
|
|
355
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
338
359
|
|
|
339
|
-
|
|
360
|
+
resizeObserver.observe(getPanelGroupElement(groupId)!);
|
|
340
361
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
debounceMap[autoSaveId](autoSaveId, panelsArray, sizes, storage);
|
|
362
|
+
return () => {
|
|
363
|
+
resizeObserver.disconnect();
|
|
364
|
+
};
|
|
346
365
|
}
|
|
366
|
+
}, [groupId, panelDataArray]);
|
|
347
367
|
|
|
368
|
+
// DEV warnings
|
|
369
|
+
useEffect(() => {
|
|
348
370
|
if (isDevelopment) {
|
|
349
|
-
const {
|
|
371
|
+
const {
|
|
372
|
+
didLogIdAndOrderWarning,
|
|
373
|
+
didLogPanelConstraintsWarning,
|
|
374
|
+
prevPanelIds,
|
|
375
|
+
} = devWarningsRef.current;
|
|
376
|
+
|
|
350
377
|
if (!didLogIdAndOrderWarning) {
|
|
351
|
-
const {
|
|
378
|
+
const { panelDataArray } = committedValuesRef.current;
|
|
352
379
|
|
|
353
|
-
const panelIds =
|
|
380
|
+
const panelIds = panelDataArray.map(({ id }) => id);
|
|
354
381
|
|
|
355
382
|
devWarningsRef.current.prevPanelIds = panelIds;
|
|
356
383
|
|
|
@@ -358,9 +385,8 @@ function PanelGroupWithForwardedRef({
|
|
|
358
385
|
prevPanelIds.length > 0 && !areEqual(prevPanelIds, panelIds);
|
|
359
386
|
if (panelsHaveChanged) {
|
|
360
387
|
if (
|
|
361
|
-
|
|
362
|
-
(
|
|
363
|
-
panel.current.idWasAutoGenerated || panel.current.order == null
|
|
388
|
+
panelDataArray.find(
|
|
389
|
+
({ idIsFromProps, order }) => !idIsFromProps || order == null
|
|
364
390
|
)
|
|
365
391
|
) {
|
|
366
392
|
devWarningsRef.current.didLogIdAndOrderWarning = true;
|
|
@@ -371,414 +397,509 @@ function PanelGroupWithForwardedRef({
|
|
|
371
397
|
}
|
|
372
398
|
}
|
|
373
399
|
}
|
|
374
|
-
}
|
|
375
|
-
}, [autoSaveId, panels, sizes, storage]);
|
|
376
400
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
// Before mounting, Panels will not yet have registered themselves.
|
|
382
|
-
// This includes server rendering.
|
|
383
|
-
// At this point the best we can do is render everything with the same size.
|
|
384
|
-
if (panels.size === 0) {
|
|
385
|
-
if (isDevelopment) {
|
|
386
|
-
if (!devWarningsRef.current.didLogDefaultSizeWarning) {
|
|
387
|
-
if (!isBrowser && defaultSize == null) {
|
|
388
|
-
devWarningsRef.current.didLogDefaultSizeWarning = true;
|
|
389
|
-
console.warn(
|
|
390
|
-
`WARNING: Panel defaultSize prop recommended to avoid layout shift after server rendering`
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return {
|
|
397
|
-
flexBasis: 0,
|
|
398
|
-
flexGrow: defaultSize != null ? defaultSize : undefined,
|
|
399
|
-
flexShrink: 1,
|
|
401
|
+
if (!didLogPanelConstraintsWarning) {
|
|
402
|
+
const panelConstraints = panelDataArray.map(
|
|
403
|
+
(panelData) => panelData.constraints
|
|
404
|
+
);
|
|
400
405
|
|
|
401
|
-
|
|
402
|
-
overflow: "hidden",
|
|
403
|
-
};
|
|
404
|
-
}
|
|
406
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
405
407
|
|
|
406
|
-
|
|
408
|
+
for (
|
|
409
|
+
let panelIndex = 0;
|
|
410
|
+
panelIndex < panelConstraints.length;
|
|
411
|
+
panelIndex++
|
|
412
|
+
) {
|
|
413
|
+
const isValid = validatePanelConstraints({
|
|
414
|
+
groupSizePixels,
|
|
415
|
+
panelConstraints,
|
|
416
|
+
panelId: panelDataArray[panelIndex].id,
|
|
417
|
+
panelIndex,
|
|
418
|
+
});
|
|
407
419
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
flexGrow,
|
|
411
|
-
flexShrink: 1,
|
|
412
|
-
|
|
413
|
-
// Without this, Panel sizes may be unintentionally overridden by their content.
|
|
414
|
-
overflow: "hidden",
|
|
415
|
-
|
|
416
|
-
// Disable pointer events inside of a panel during resize.
|
|
417
|
-
// This avoid edge cases like nested iframes.
|
|
418
|
-
pointerEvents:
|
|
419
|
-
disablePointerEventsDuringResize && activeHandleId !== null
|
|
420
|
-
? "none"
|
|
421
|
-
: undefined,
|
|
422
|
-
};
|
|
423
|
-
},
|
|
424
|
-
[activeHandleId, disablePointerEventsDuringResize, sizes]
|
|
425
|
-
);
|
|
420
|
+
if (!isValid) {
|
|
421
|
+
devWarningsRef.current.didLogPanelConstraintsWarning = true;
|
|
426
422
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return prevPanels;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
431
426
|
}
|
|
427
|
+
}
|
|
428
|
+
});
|
|
432
429
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const {
|
|
446
|
-
direction,
|
|
447
|
-
panels,
|
|
448
|
-
sizes: prevSizes,
|
|
449
|
-
} = committedValuesRef.current;
|
|
450
|
-
|
|
451
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
452
|
-
|
|
453
|
-
const [idBefore, idAfter] = getResizeHandlePanelIds(
|
|
454
|
-
groupId,
|
|
455
|
-
handleId,
|
|
456
|
-
panelsArray
|
|
430
|
+
// External APIs are safe to memoize via committed values ref
|
|
431
|
+
const collapsePanel = useCallback(
|
|
432
|
+
(panelData: PanelData) => {
|
|
433
|
+
const {
|
|
434
|
+
layout: prevLayout,
|
|
435
|
+
onLayout,
|
|
436
|
+
panelDataArray,
|
|
437
|
+
} = committedValuesRef.current;
|
|
438
|
+
|
|
439
|
+
if (panelData.constraints.collapsible) {
|
|
440
|
+
const panelConstraintsArray = panelDataArray.map(
|
|
441
|
+
(panelData) => panelData.constraints
|
|
457
442
|
);
|
|
458
|
-
if (idBefore == null || idAfter == null) {
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
443
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
444
|
+
const {
|
|
445
|
+
collapsedSizePercentage,
|
|
446
|
+
panelSizePercentage,
|
|
447
|
+
pivotIndices,
|
|
448
|
+
groupSizePixels,
|
|
449
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, prevLayout);
|
|
450
|
+
|
|
451
|
+
if (panelSizePercentage !== collapsedSizePercentage) {
|
|
452
|
+
// Store size before collapse;
|
|
453
|
+
// This is the size that gets restored if the expand() API is used.
|
|
454
|
+
panelSizeBeforeCollapseRef.current.set(
|
|
455
|
+
panelData.id,
|
|
456
|
+
panelSizePercentage
|
|
457
|
+
);
|
|
474
458
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
459
|
+
const isLastPanel =
|
|
460
|
+
panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
|
|
461
|
+
const delta = isLastPanel
|
|
462
|
+
? panelSizePercentage - collapsedSizePercentage
|
|
463
|
+
: collapsedSizePercentage - panelSizePercentage;
|
|
464
|
+
|
|
465
|
+
const nextLayout = adjustLayoutByDelta({
|
|
466
|
+
delta,
|
|
467
|
+
groupSizePixels,
|
|
468
|
+
layout: prevLayout,
|
|
469
|
+
panelConstraints: panelConstraintsArray,
|
|
470
|
+
pivotIndices,
|
|
471
|
+
trigger: "imperative-api",
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
475
|
+
setLayout(nextLayout);
|
|
476
|
+
|
|
477
|
+
if (onLayout) {
|
|
478
|
+
onLayout(
|
|
479
|
+
nextLayout.map((sizePercentage) => ({
|
|
480
|
+
sizePercentage,
|
|
481
|
+
sizePixels: convertPercentageToPixels(
|
|
482
|
+
sizePercentage,
|
|
483
|
+
groupSizePixels
|
|
484
|
+
),
|
|
485
|
+
}))
|
|
486
|
+
);
|
|
487
|
+
}
|
|
478
488
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
489
|
+
callPanelCallbacks(
|
|
490
|
+
groupId,
|
|
491
|
+
panelDataArray,
|
|
492
|
+
nextLayout,
|
|
493
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
494
|
+
);
|
|
495
|
+
}
|
|
482
496
|
}
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
[groupId]
|
|
500
|
+
);
|
|
483
501
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
502
|
+
// External APIs are safe to memoize via committed values ref
|
|
503
|
+
const expandPanel = useCallback(
|
|
504
|
+
(panelData: PanelData) => {
|
|
505
|
+
const {
|
|
506
|
+
layout: prevLayout,
|
|
507
|
+
onLayout,
|
|
508
|
+
panelDataArray,
|
|
509
|
+
} = committedValuesRef.current;
|
|
510
|
+
|
|
511
|
+
if (panelData.constraints.collapsible) {
|
|
512
|
+
const panelConstraintsArray = panelDataArray.map(
|
|
513
|
+
(panelData) => panelData.constraints
|
|
496
514
|
);
|
|
497
515
|
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
516
|
+
const {
|
|
517
|
+
collapsedSizePercentage,
|
|
518
|
+
panelSizePercentage,
|
|
519
|
+
minSizePercentage,
|
|
520
|
+
pivotIndices,
|
|
521
|
+
groupSizePixels,
|
|
522
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, prevLayout);
|
|
523
|
+
|
|
524
|
+
if (panelSizePercentage === collapsedSizePercentage) {
|
|
525
|
+
// Restore this panel to the size it was before it was collapsed, if possible.
|
|
526
|
+
const prevPanelSizePercentage =
|
|
527
|
+
panelSizeBeforeCollapseRef.current.get(panelData.id);
|
|
528
|
+
|
|
529
|
+
const baseSizePercentage =
|
|
530
|
+
prevPanelSizePercentage != null
|
|
531
|
+
? prevPanelSizePercentage
|
|
532
|
+
: minSizePercentage;
|
|
533
|
+
|
|
534
|
+
const isLastPanel =
|
|
535
|
+
panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
|
|
536
|
+
const delta = isLastPanel
|
|
537
|
+
? panelSizePercentage - baseSizePercentage
|
|
538
|
+
: baseSizePercentage - panelSizePercentage;
|
|
539
|
+
|
|
540
|
+
const nextLayout = adjustLayoutByDelta({
|
|
541
|
+
delta,
|
|
542
|
+
groupSizePixels,
|
|
543
|
+
layout: prevLayout,
|
|
544
|
+
panelConstraints: panelConstraintsArray,
|
|
545
|
+
pivotIndices,
|
|
546
|
+
trigger: "imperative-api",
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
550
|
+
setLayout(nextLayout);
|
|
551
|
+
|
|
552
|
+
if (onLayout) {
|
|
553
|
+
onLayout(
|
|
554
|
+
nextLayout.map((sizePercentage) => ({
|
|
555
|
+
sizePercentage,
|
|
556
|
+
sizePixels: convertPercentageToPixels(
|
|
557
|
+
sizePercentage,
|
|
558
|
+
groupSizePixels
|
|
559
|
+
),
|
|
560
|
+
}))
|
|
561
|
+
);
|
|
523
562
|
}
|
|
563
|
+
|
|
564
|
+
callPanelCallbacks(
|
|
565
|
+
groupId,
|
|
566
|
+
panelDataArray,
|
|
567
|
+
nextLayout,
|
|
568
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
569
|
+
);
|
|
524
570
|
}
|
|
525
571
|
}
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
[groupId]
|
|
575
|
+
);
|
|
526
576
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
setSizes(nextSizes);
|
|
577
|
+
// External APIs are safe to memoize via committed values ref
|
|
578
|
+
const getPanelSize = useCallback(
|
|
579
|
+
(panelData: PanelData) => {
|
|
580
|
+
const { layout, panelDataArray } = committedValuesRef.current;
|
|
532
581
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
);
|
|
540
|
-
}
|
|
582
|
+
const { panelSizePercentage, panelSizePixels } = panelDataHelper(
|
|
583
|
+
groupId,
|
|
584
|
+
panelDataArray,
|
|
585
|
+
panelData,
|
|
586
|
+
layout
|
|
587
|
+
);
|
|
541
588
|
|
|
542
|
-
|
|
589
|
+
return {
|
|
590
|
+
sizePercentage: panelSizePercentage,
|
|
591
|
+
sizePixels: panelSizePixels,
|
|
543
592
|
};
|
|
544
|
-
|
|
545
|
-
return resizeHandler;
|
|
546
593
|
},
|
|
547
594
|
[groupId]
|
|
548
595
|
);
|
|
549
596
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const { panels, sizes: prevSizes } = committedValuesRef.current;
|
|
565
|
-
|
|
566
|
-
const panel = panels.get(id);
|
|
567
|
-
if (panel == null) {
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const { collapsedSize, collapsible } = panel.current;
|
|
572
|
-
if (!collapsible) {
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
597
|
+
// This API should never read from committedValuesRef
|
|
598
|
+
const getPanelStyle = useCallback(
|
|
599
|
+
(panelData: PanelData) => {
|
|
600
|
+
const panelIndex = panelDataArray.indexOf(panelData);
|
|
601
|
+
|
|
602
|
+
return computePanelFlexBoxStyle({
|
|
603
|
+
dragState,
|
|
604
|
+
layout,
|
|
605
|
+
panelData: panelDataArray,
|
|
606
|
+
panelIndex,
|
|
607
|
+
});
|
|
608
|
+
},
|
|
609
|
+
[dragState, layout, panelDataArray]
|
|
610
|
+
);
|
|
577
611
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
612
|
+
// External APIs are safe to memoize via committed values ref
|
|
613
|
+
const isPanelCollapsed = useCallback(
|
|
614
|
+
(panelData: PanelData) => {
|
|
615
|
+
const { layout, panelDataArray } = committedValuesRef.current;
|
|
582
616
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
// Panel is already collapsed.
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
617
|
+
const { collapsedSizePercentage, collapsible, panelSizePercentage } =
|
|
618
|
+
panelDataHelper(groupId, panelDataArray, panelData, layout);
|
|
588
619
|
|
|
589
|
-
|
|
620
|
+
return (
|
|
621
|
+
collapsible === true && panelSizePercentage === collapsedSizePercentage
|
|
622
|
+
);
|
|
623
|
+
},
|
|
624
|
+
[groupId]
|
|
625
|
+
);
|
|
590
626
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
627
|
+
// External APIs are safe to memoize via committed values ref
|
|
628
|
+
const isPanelExpanded = useCallback(
|
|
629
|
+
(panelData: PanelData) => {
|
|
630
|
+
const { layout, panelDataArray } = committedValuesRef.current;
|
|
595
631
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const nextSizes = adjustByDelta(
|
|
600
|
-
null,
|
|
601
|
-
panels,
|
|
602
|
-
idBefore,
|
|
603
|
-
idAfter,
|
|
604
|
-
delta,
|
|
605
|
-
prevSizes,
|
|
606
|
-
panelSizeBeforeCollapse.current,
|
|
607
|
-
null
|
|
608
|
-
);
|
|
609
|
-
if (prevSizes !== nextSizes) {
|
|
610
|
-
const panelIdToLastNotifiedSizeMap =
|
|
611
|
-
panelIdToLastNotifiedSizeMapRef.current;
|
|
632
|
+
const { collapsedSizePercentage, collapsible, panelSizePercentage } =
|
|
633
|
+
panelDataHelper(groupId, panelDataArray, panelData, layout);
|
|
612
634
|
|
|
613
|
-
|
|
635
|
+
return !collapsible || panelSizePercentage > collapsedSizePercentage;
|
|
636
|
+
},
|
|
637
|
+
[groupId]
|
|
638
|
+
);
|
|
614
639
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
640
|
+
const registerPanel = useCallback((panelData: PanelData) => {
|
|
641
|
+
setPanelDataArray((prevPanelDataArray) => {
|
|
642
|
+
const nextPanelDataArray = [...prevPanelDataArray, panelData];
|
|
643
|
+
return nextPanelDataArray.sort((panelA, panelB) => {
|
|
644
|
+
const orderA = panelA.order;
|
|
645
|
+
const orderB = panelB.order;
|
|
646
|
+
if (orderA == null && orderB == null) {
|
|
647
|
+
return 0;
|
|
648
|
+
} else if (orderA == null) {
|
|
649
|
+
return -1;
|
|
650
|
+
} else if (orderB == null) {
|
|
651
|
+
return 1;
|
|
652
|
+
} else {
|
|
653
|
+
return orderA - orderB;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
});
|
|
619
657
|
}, []);
|
|
620
658
|
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
659
|
+
const registerResizeHandle = useCallback((dragHandleId: string) => {
|
|
660
|
+
return function resizeHandler(event: ResizeEvent) {
|
|
661
|
+
event.preventDefault();
|
|
662
|
+
|
|
663
|
+
const {
|
|
664
|
+
direction,
|
|
665
|
+
dragState,
|
|
666
|
+
id: groupId,
|
|
667
|
+
keyboardResizeByPercentage,
|
|
668
|
+
keyboardResizeByPixels,
|
|
669
|
+
onLayout,
|
|
670
|
+
panelDataArray,
|
|
671
|
+
layout: prevLayout,
|
|
672
|
+
} = committedValuesRef.current;
|
|
673
|
+
|
|
674
|
+
const { initialLayout } = dragState ?? {};
|
|
675
|
+
|
|
676
|
+
const pivotIndices = determinePivotIndices(groupId, dragHandleId);
|
|
677
|
+
|
|
678
|
+
let delta = calculateDeltaPercentage(
|
|
679
|
+
event,
|
|
680
|
+
groupId,
|
|
681
|
+
dragHandleId,
|
|
682
|
+
direction,
|
|
683
|
+
dragState!,
|
|
684
|
+
{
|
|
685
|
+
percentage: keyboardResizeByPercentage,
|
|
686
|
+
pixels: keyboardResizeByPixels,
|
|
687
|
+
}
|
|
688
|
+
);
|
|
689
|
+
if (delta === 0) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
638
692
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
693
|
+
// Support RTL layouts
|
|
694
|
+
const isHorizontal = direction === "horizontal";
|
|
695
|
+
if (document.dir === "rtl" && isHorizontal) {
|
|
696
|
+
delta = -delta;
|
|
697
|
+
}
|
|
643
698
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
699
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
700
|
+
const panelConstraints = panelDataArray.map(
|
|
701
|
+
(panelData) => panelData.constraints
|
|
702
|
+
);
|
|
649
703
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
704
|
+
const nextLayout = adjustLayoutByDelta({
|
|
705
|
+
delta,
|
|
706
|
+
groupSizePixels,
|
|
707
|
+
layout: initialLayout ?? prevLayout,
|
|
708
|
+
panelConstraints,
|
|
709
|
+
pivotIndices,
|
|
710
|
+
trigger: isKeyDown(event) ? "keyboard" : "mouse-or-touch",
|
|
711
|
+
});
|
|
654
712
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
713
|
+
const layoutChanged = !compareLayouts(prevLayout, nextLayout);
|
|
714
|
+
|
|
715
|
+
// Only update the cursor for layout changes triggered by touch/mouse events (not keyboard)
|
|
716
|
+
// Update the cursor even if the layout hasn't changed (we may need to show an invalid cursor state)
|
|
717
|
+
if (isMouseEvent(event) || isTouchEvent(event)) {
|
|
718
|
+
// Watch for multiple subsequent deltas; this might occur for tiny cursor movements.
|
|
719
|
+
// In this case, Panel sizes might not change–
|
|
720
|
+
// but updating cursor in this scenario would cause a flicker.
|
|
721
|
+
if (prevDeltaRef.current != delta) {
|
|
722
|
+
prevDeltaRef.current = delta;
|
|
723
|
+
|
|
724
|
+
if (!layoutChanged) {
|
|
725
|
+
// If the pointer has moved too far to resize the panel any further,
|
|
726
|
+
// update the cursor style for a visual clue.
|
|
727
|
+
// This mimics VS Code behavior.
|
|
728
|
+
|
|
729
|
+
if (isHorizontal) {
|
|
730
|
+
setGlobalCursorStyle(
|
|
731
|
+
delta < 0 ? "horizontal-min" : "horizontal-max"
|
|
732
|
+
);
|
|
733
|
+
} else {
|
|
734
|
+
setGlobalCursorStyle(delta < 0 ? "vertical-min" : "vertical-max");
|
|
735
|
+
}
|
|
736
|
+
} else {
|
|
737
|
+
// Reset the cursor style to the the normal resize cursor.
|
|
738
|
+
setGlobalCursorStyle(isHorizontal ? "horizontal" : "vertical");
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
673
742
|
|
|
674
|
-
|
|
743
|
+
if (layoutChanged) {
|
|
744
|
+
setLayout(nextLayout);
|
|
745
|
+
|
|
746
|
+
if (onLayout) {
|
|
747
|
+
onLayout(
|
|
748
|
+
nextLayout.map((sizePercentage) => ({
|
|
749
|
+
sizePercentage,
|
|
750
|
+
sizePixels: convertPercentageToPixels(
|
|
751
|
+
sizePercentage,
|
|
752
|
+
groupSizePixels
|
|
753
|
+
),
|
|
754
|
+
}))
|
|
755
|
+
);
|
|
756
|
+
}
|
|
675
757
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
758
|
+
callPanelCallbacks(
|
|
759
|
+
groupId,
|
|
760
|
+
panelDataArray,
|
|
761
|
+
nextLayout,
|
|
762
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
};
|
|
680
766
|
}, []);
|
|
681
767
|
|
|
682
|
-
|
|
683
|
-
|
|
768
|
+
// External APIs are safe to memoize via committed values ref
|
|
769
|
+
const resizePanel = useCallback(
|
|
770
|
+
(panelData: PanelData, mixedSizes: Partial<MixedSizes>) => {
|
|
771
|
+
const {
|
|
772
|
+
layout: prevLayout,
|
|
773
|
+
onLayout,
|
|
774
|
+
panelDataArray,
|
|
775
|
+
} = committedValuesRef.current;
|
|
776
|
+
|
|
777
|
+
const panelConstraintsArray = panelDataArray.map(
|
|
778
|
+
(panelData) => panelData.constraints
|
|
779
|
+
);
|
|
684
780
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
781
|
+
const { groupSizePixels, panelSizePercentage, pivotIndices } =
|
|
782
|
+
panelDataHelper(groupId, panelDataArray, panelData, prevLayout);
|
|
783
|
+
|
|
784
|
+
const sizePercentage = getPercentageSizeFromMixedSizes(
|
|
785
|
+
mixedSizes,
|
|
786
|
+
groupSizePixels
|
|
787
|
+
)!;
|
|
788
|
+
|
|
789
|
+
const isLastPanel =
|
|
790
|
+
panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
|
|
791
|
+
const delta = isLastPanel
|
|
792
|
+
? panelSizePercentage - sizePercentage
|
|
793
|
+
: sizePercentage - panelSizePercentage;
|
|
794
|
+
|
|
795
|
+
const nextLayout = adjustLayoutByDelta({
|
|
796
|
+
delta,
|
|
797
|
+
groupSizePixels,
|
|
798
|
+
layout: prevLayout,
|
|
799
|
+
panelConstraints: panelConstraintsArray,
|
|
800
|
+
pivotIndices,
|
|
801
|
+
trigger: "imperative-api",
|
|
802
|
+
});
|
|
689
803
|
|
|
690
|
-
|
|
804
|
+
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
805
|
+
setLayout(nextLayout);
|
|
806
|
+
|
|
807
|
+
if (onLayout) {
|
|
808
|
+
onLayout(
|
|
809
|
+
nextLayout.map((sizePercentage) => ({
|
|
810
|
+
sizePercentage,
|
|
811
|
+
sizePixels: convertPercentageToPixels(
|
|
812
|
+
sizePercentage,
|
|
813
|
+
groupSizePixels
|
|
814
|
+
),
|
|
815
|
+
}))
|
|
816
|
+
);
|
|
817
|
+
}
|
|
691
818
|
|
|
692
|
-
|
|
819
|
+
callPanelCallbacks(
|
|
820
|
+
groupId,
|
|
821
|
+
panelDataArray,
|
|
822
|
+
nextLayout,
|
|
823
|
+
panelIdToLastNotifiedMixedSizesMapRef.current
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
[groupId]
|
|
828
|
+
);
|
|
693
829
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
830
|
+
const startDragging = useCallback(
|
|
831
|
+
(dragHandleId: string, event: ResizeEvent) => {
|
|
832
|
+
const { direction, layout } = committedValuesRef.current;
|
|
698
833
|
|
|
699
|
-
|
|
700
|
-
if (currentSize === nextSize) {
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
834
|
+
const handleElement = getResizeHandleElement(dragHandleId)!;
|
|
703
835
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
}
|
|
836
|
+
const initialCursorPosition = getResizeEventCursorPosition(
|
|
837
|
+
direction,
|
|
838
|
+
event
|
|
839
|
+
);
|
|
709
840
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
841
|
+
setDragState({
|
|
842
|
+
dragHandleId,
|
|
843
|
+
dragHandleRect: handleElement.getBoundingClientRect(),
|
|
844
|
+
initialCursorPosition,
|
|
845
|
+
initialLayout: layout,
|
|
846
|
+
});
|
|
847
|
+
},
|
|
848
|
+
[]
|
|
849
|
+
);
|
|
714
850
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
null,
|
|
720
|
-
panels,
|
|
721
|
-
idBefore,
|
|
722
|
-
idAfter,
|
|
723
|
-
delta,
|
|
724
|
-
prevSizes,
|
|
725
|
-
panelSizeBeforeCollapse.current,
|
|
726
|
-
null
|
|
727
|
-
);
|
|
728
|
-
if (prevSizes !== nextSizes) {
|
|
729
|
-
const panelIdToLastNotifiedSizeMap =
|
|
730
|
-
panelIdToLastNotifiedSizeMapRef.current;
|
|
851
|
+
const stopDragging = useCallback(() => {
|
|
852
|
+
resetGlobalCursorStyle();
|
|
853
|
+
setDragState(null);
|
|
854
|
+
}, []);
|
|
731
855
|
|
|
732
|
-
|
|
856
|
+
const unregisterPanel = useCallback((panelData: PanelData) => {
|
|
857
|
+
delete panelIdToLastNotifiedMixedSizesMapRef.current[panelData.id];
|
|
733
858
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
859
|
+
setPanelDataArray((panelDataArray) => {
|
|
860
|
+
const index = panelDataArray.indexOf(panelData);
|
|
861
|
+
if (index >= 0) {
|
|
862
|
+
panelDataArray = [...panelDataArray];
|
|
863
|
+
panelDataArray.splice(index, 1);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return panelDataArray;
|
|
867
|
+
});
|
|
738
868
|
}, []);
|
|
739
869
|
|
|
740
870
|
const context = useMemo(
|
|
741
871
|
() => ({
|
|
742
|
-
activeHandleId,
|
|
743
872
|
collapsePanel,
|
|
744
873
|
direction,
|
|
874
|
+
dragState,
|
|
745
875
|
expandPanel,
|
|
876
|
+
getPanelSize,
|
|
746
877
|
getPanelStyle,
|
|
747
878
|
groupId,
|
|
879
|
+
isPanelCollapsed,
|
|
880
|
+
isPanelExpanded,
|
|
748
881
|
registerPanel,
|
|
749
882
|
registerResizeHandle,
|
|
750
883
|
resizePanel,
|
|
751
|
-
startDragging
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
if (isMouseEvent(event) || isTouchEvent(event)) {
|
|
755
|
-
const handleElement = getResizeHandle(id)!;
|
|
756
|
-
|
|
757
|
-
initialDragStateRef.current = {
|
|
758
|
-
dragHandleRect: handleElement.getBoundingClientRect(),
|
|
759
|
-
dragOffset: getDragOffset(event, id, direction),
|
|
760
|
-
sizes: committedValuesRef.current.sizes,
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
},
|
|
764
|
-
stopDragging: () => {
|
|
765
|
-
resetGlobalCursorStyle();
|
|
766
|
-
setActiveHandleId(null);
|
|
767
|
-
|
|
768
|
-
initialDragStateRef.current = null;
|
|
769
|
-
},
|
|
884
|
+
startDragging,
|
|
885
|
+
stopDragging,
|
|
770
886
|
unregisterPanel,
|
|
771
887
|
}),
|
|
772
888
|
[
|
|
773
|
-
activeHandleId,
|
|
774
889
|
collapsePanel,
|
|
890
|
+
dragState,
|
|
775
891
|
direction,
|
|
776
892
|
expandPanel,
|
|
893
|
+
getPanelSize,
|
|
777
894
|
getPanelStyle,
|
|
778
895
|
groupId,
|
|
896
|
+
isPanelCollapsed,
|
|
897
|
+
isPanelExpanded,
|
|
779
898
|
registerPanel,
|
|
780
899
|
registerResizeHandle,
|
|
781
900
|
resizePanel,
|
|
901
|
+
startDragging,
|
|
902
|
+
stopDragging,
|
|
782
903
|
unregisterPanel,
|
|
783
904
|
]
|
|
784
905
|
);
|
|
@@ -791,17 +912,25 @@ function PanelGroupWithForwardedRef({
|
|
|
791
912
|
width: "100%",
|
|
792
913
|
};
|
|
793
914
|
|
|
794
|
-
return createElement(
|
|
795
|
-
|
|
915
|
+
return createElement(
|
|
916
|
+
PanelGroupContext.Provider,
|
|
917
|
+
{ value: context },
|
|
918
|
+
createElement(Type, {
|
|
796
919
|
children,
|
|
797
920
|
className: classNameFromProps,
|
|
921
|
+
style: {
|
|
922
|
+
...style,
|
|
923
|
+
...styleFromProps,
|
|
924
|
+
},
|
|
925
|
+
|
|
926
|
+
// CSS selectors
|
|
798
927
|
"data-panel-group": "",
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
928
|
+
|
|
929
|
+
// e2e test attributes
|
|
930
|
+
"data-panel-group-direction": isDevelopment ? direction : undefined,
|
|
931
|
+
"data-panel-group-id": isDevelopment ? groupId : undefined,
|
|
932
|
+
})
|
|
933
|
+
);
|
|
805
934
|
}
|
|
806
935
|
|
|
807
936
|
export const PanelGroup = forwardRef<
|
|
@@ -813,3 +942,45 @@ export const PanelGroup = forwardRef<
|
|
|
813
942
|
|
|
814
943
|
PanelGroupWithForwardedRef.displayName = "PanelGroup";
|
|
815
944
|
PanelGroup.displayName = "forwardRef(PanelGroup)";
|
|
945
|
+
|
|
946
|
+
function panelDataHelper(
|
|
947
|
+
groupId: string,
|
|
948
|
+
panelDataArray: PanelData[],
|
|
949
|
+
panelData: PanelData,
|
|
950
|
+
layout: number[]
|
|
951
|
+
) {
|
|
952
|
+
const panelConstraintsArray = panelDataArray.map(
|
|
953
|
+
(panelData) => panelData.constraints
|
|
954
|
+
);
|
|
955
|
+
|
|
956
|
+
const panelIndex = panelDataArray.indexOf(panelData);
|
|
957
|
+
const panelConstraints = panelConstraintsArray[panelIndex];
|
|
958
|
+
|
|
959
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
960
|
+
|
|
961
|
+
const percentagePanelConstraints = computePercentagePanelConstraints(
|
|
962
|
+
panelConstraintsArray,
|
|
963
|
+
panelIndex,
|
|
964
|
+
groupSizePixels
|
|
965
|
+
);
|
|
966
|
+
|
|
967
|
+
const isLastPanel = panelIndex === panelDataArray.length - 1;
|
|
968
|
+
const pivotIndices = isLastPanel
|
|
969
|
+
? [panelIndex - 1, panelIndex]
|
|
970
|
+
: [panelIndex, panelIndex + 1];
|
|
971
|
+
|
|
972
|
+
const panelSizePercentage = layout[panelIndex];
|
|
973
|
+
const panelSizePixels = convertPercentageToPixels(
|
|
974
|
+
panelSizePercentage,
|
|
975
|
+
groupSizePixels
|
|
976
|
+
);
|
|
977
|
+
|
|
978
|
+
return {
|
|
979
|
+
...percentagePanelConstraints,
|
|
980
|
+
collapsible: panelConstraints.collapsible,
|
|
981
|
+
panelSizePercentage,
|
|
982
|
+
panelSizePixels,
|
|
983
|
+
groupSizePixels,
|
|
984
|
+
pivotIndices,
|
|
985
|
+
};
|
|
986
|
+
}
|