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