react-resizable-panels 0.0.55 → 0.0.57
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 +238 -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 +1276 -1043
- package/dist/react-resizable-panels.browser.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.cjs.js +1410 -1097
- package/dist/react-resizable-panels.browser.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.esm.js +1411 -1097
- package/dist/react-resizable-panels.browser.esm.js +1277 -1043
- package/dist/react-resizable-panels.cjs.js +1276 -1043
- 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 +1415 -1102
- package/dist/react-resizable-panels.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.esm.js +1416 -1102
- package/dist/react-resizable-panels.development.node.cjs.js +1179 -947
- package/dist/react-resizable-panels.development.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.node.esm.js +1180 -947
- package/dist/react-resizable-panels.esm.js +1277 -1043
- package/dist/react-resizable-panels.esm.js.map +1 -1
- package/dist/react-resizable-panels.node.cjs.js +1068 -910
- package/dist/react-resizable-panels.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.node.esm.js +1069 -910
- package/jest.config.js +10 -0
- package/package.json +5 -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 -667
- package/src/PanelGroupContext.ts +33 -0
- package/src/PanelResizeHandle.ts +21 -17
- 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 +98 -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.test.ts +47 -0
- package/src/utils/convertPixelConstraintsToPercentages.ts +72 -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 +9 -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.test.ts +45 -0
- package/src/utils/resizePanel.ts +60 -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
|
@@ -7,6 +7,7 @@ import * as React from 'react';
|
|
|
7
7
|
const {
|
|
8
8
|
createElement,
|
|
9
9
|
createContext,
|
|
10
|
+
createRef,
|
|
10
11
|
forwardRef,
|
|
11
12
|
useCallback,
|
|
12
13
|
useContext,
|
|
@@ -21,6 +22,9 @@ const {
|
|
|
21
22
|
// `toString()` prevents bundlers from trying to `import { useId } from 'react'`
|
|
22
23
|
const useId = React["useId".toString()];
|
|
23
24
|
|
|
25
|
+
const PanelGroupContext = createContext(null);
|
|
26
|
+
PanelGroupContext.displayName = "PanelGroupContext";
|
|
27
|
+
|
|
24
28
|
const useIsomorphicLayoutEffect = useLayoutEffect ;
|
|
25
29
|
|
|
26
30
|
const wrappedUseId = typeof useId === "function" ? useId : () => null;
|
|
@@ -31,121 +35,134 @@ function useUniqueId(idFromParams = null) {
|
|
|
31
35
|
if (idRef.current === null) {
|
|
32
36
|
idRef.current = "" + counter++;
|
|
33
37
|
}
|
|
34
|
-
return idRef.current;
|
|
38
|
+
return idFromParams !== null && idFromParams !== void 0 ? idFromParams : idRef.current;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
const PanelGroupContext = createContext(null);
|
|
38
|
-
PanelGroupContext.displayName = "PanelGroupContext";
|
|
39
|
-
|
|
40
41
|
function PanelWithForwardedRef({
|
|
41
|
-
children
|
|
42
|
+
children,
|
|
42
43
|
className: classNameFromProps = "",
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
collapsedSizePercentage,
|
|
45
|
+
collapsedSizePixels,
|
|
46
|
+
collapsible,
|
|
47
|
+
defaultSizePercentage,
|
|
48
|
+
defaultSizePixels,
|
|
46
49
|
forwardedRef,
|
|
47
|
-
id: idFromProps
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
id: idFromProps,
|
|
51
|
+
maxSizePercentage,
|
|
52
|
+
maxSizePixels,
|
|
53
|
+
minSizePercentage,
|
|
54
|
+
minSizePixels,
|
|
55
|
+
onCollapse,
|
|
56
|
+
onExpand,
|
|
57
|
+
onResize,
|
|
58
|
+
order,
|
|
59
|
+
style: styleFromProps,
|
|
54
60
|
tagName: Type = "div"
|
|
55
61
|
}) {
|
|
56
62
|
const context = useContext(PanelGroupContext);
|
|
57
63
|
if (context === null) {
|
|
58
64
|
throw Error(`Panel components must be rendered within a PanelGroup container`);
|
|
59
65
|
}
|
|
60
|
-
const panelId = useUniqueId(idFromProps);
|
|
61
66
|
const {
|
|
62
67
|
collapsePanel,
|
|
63
68
|
expandPanel,
|
|
64
69
|
getPanelSize,
|
|
65
70
|
getPanelStyle,
|
|
71
|
+
isPanelCollapsed,
|
|
66
72
|
registerPanel,
|
|
67
73
|
resizePanel,
|
|
68
|
-
units,
|
|
69
74
|
unregisterPanel
|
|
70
75
|
} = context;
|
|
71
|
-
|
|
72
|
-
if (units === "percentages") {
|
|
73
|
-
// Mimics legacy default value for percentage based panel groups
|
|
74
|
-
minSize = 10;
|
|
75
|
-
} else {
|
|
76
|
-
// There is no meaningful minimum pixel default we can provide
|
|
77
|
-
minSize = 0;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Use a ref to guard against users passing inline props
|
|
82
|
-
const callbacksRef = useRef({
|
|
83
|
-
onCollapse,
|
|
84
|
-
onResize
|
|
85
|
-
});
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
callbacksRef.current.onCollapse = onCollapse;
|
|
88
|
-
callbacksRef.current.onResize = onResize;
|
|
89
|
-
});
|
|
90
|
-
const style = getPanelStyle(panelId, defaultSize);
|
|
91
|
-
const committedValuesRef = useRef({
|
|
92
|
-
size: parseSizeFromStyle(style)
|
|
93
|
-
});
|
|
76
|
+
const panelId = useUniqueId(idFromProps);
|
|
94
77
|
const panelDataRef = useRef({
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
78
|
+
callbacks: {
|
|
79
|
+
onCollapse,
|
|
80
|
+
onExpand,
|
|
81
|
+
onResize
|
|
82
|
+
},
|
|
83
|
+
constraints: {
|
|
84
|
+
collapsedSizePercentage,
|
|
85
|
+
collapsedSizePixels,
|
|
86
|
+
collapsible,
|
|
87
|
+
defaultSizePercentage,
|
|
88
|
+
defaultSizePixels,
|
|
89
|
+
maxSizePercentage,
|
|
90
|
+
maxSizePixels,
|
|
91
|
+
minSizePercentage,
|
|
92
|
+
minSizePixels
|
|
93
|
+
},
|
|
99
94
|
id: panelId,
|
|
100
|
-
|
|
101
|
-
maxSize,
|
|
102
|
-
minSize,
|
|
95
|
+
idIsFromProps: idFromProps !== undefined,
|
|
103
96
|
order
|
|
104
97
|
});
|
|
98
|
+
useRef({
|
|
99
|
+
didLogMissingDefaultSizeWarning: false
|
|
100
|
+
});
|
|
105
101
|
useIsomorphicLayoutEffect(() => {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
panelDataRef.current
|
|
110
|
-
panelDataRef.current.defaultSize = defaultSize;
|
|
102
|
+
const {
|
|
103
|
+
callbacks,
|
|
104
|
+
constraints
|
|
105
|
+
} = panelDataRef.current;
|
|
111
106
|
panelDataRef.current.id = panelId;
|
|
112
|
-
panelDataRef.current.
|
|
113
|
-
panelDataRef.current.maxSize = maxSize;
|
|
114
|
-
panelDataRef.current.minSize = minSize;
|
|
107
|
+
panelDataRef.current.idIsFromProps = idFromProps !== undefined;
|
|
115
108
|
panelDataRef.current.order = order;
|
|
109
|
+
callbacks.onCollapse = onCollapse;
|
|
110
|
+
callbacks.onExpand = onExpand;
|
|
111
|
+
callbacks.onResize = onResize;
|
|
112
|
+
constraints.collapsedSizePercentage = collapsedSizePercentage;
|
|
113
|
+
constraints.collapsedSizePixels = collapsedSizePixels;
|
|
114
|
+
constraints.collapsible = collapsible;
|
|
115
|
+
constraints.defaultSizePercentage = defaultSizePercentage;
|
|
116
|
+
constraints.defaultSizePixels = defaultSizePixels;
|
|
117
|
+
constraints.maxSizePercentage = maxSizePercentage;
|
|
118
|
+
constraints.maxSizePixels = maxSizePixels;
|
|
119
|
+
constraints.minSizePercentage = minSizePercentage;
|
|
120
|
+
constraints.minSizePixels = minSizePixels;
|
|
116
121
|
});
|
|
117
122
|
useIsomorphicLayoutEffect(() => {
|
|
118
|
-
|
|
123
|
+
const panelData = panelDataRef.current;
|
|
124
|
+
registerPanel(panelData);
|
|
119
125
|
return () => {
|
|
120
|
-
unregisterPanel(
|
|
126
|
+
unregisterPanel(panelData);
|
|
121
127
|
};
|
|
122
128
|
}, [order, panelId, registerPanel, unregisterPanel]);
|
|
123
129
|
useImperativeHandle(forwardedRef, () => ({
|
|
124
|
-
collapse: () =>
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
collapse: () => {
|
|
131
|
+
collapsePanel(panelDataRef.current);
|
|
132
|
+
},
|
|
133
|
+
expand: () => {
|
|
134
|
+
expandPanel(panelDataRef.current);
|
|
128
135
|
},
|
|
129
136
|
getId() {
|
|
130
137
|
return panelId;
|
|
131
138
|
},
|
|
132
|
-
getSize(
|
|
133
|
-
return getPanelSize(
|
|
139
|
+
getSize() {
|
|
140
|
+
return getPanelSize(panelDataRef.current);
|
|
141
|
+
},
|
|
142
|
+
isCollapsed() {
|
|
143
|
+
return isPanelCollapsed(panelDataRef.current);
|
|
134
144
|
},
|
|
135
|
-
|
|
136
|
-
|
|
145
|
+
isExpanded() {
|
|
146
|
+
return !isPanelCollapsed(panelDataRef.current);
|
|
147
|
+
},
|
|
148
|
+
resize: mixedSizes => {
|
|
149
|
+
resizePanel(panelDataRef.current, mixedSizes);
|
|
150
|
+
}
|
|
151
|
+
}), [collapsePanel, expandPanel, getPanelSize, isPanelCollapsed, panelId, resizePanel]);
|
|
152
|
+
const style = getPanelStyle(panelDataRef.current);
|
|
137
153
|
return createElement(Type, {
|
|
138
154
|
children,
|
|
139
155
|
className: classNameFromProps,
|
|
140
|
-
"data-panel": "",
|
|
141
|
-
"data-panel-collapsible": collapsible || undefined,
|
|
142
|
-
"data-panel-id": panelId,
|
|
143
|
-
"data-panel-size": parseFloat("" + style.flexGrow).toFixed(1),
|
|
144
|
-
id: `data-panel-id-${panelId}`,
|
|
145
156
|
style: {
|
|
146
157
|
...style,
|
|
147
158
|
...styleFromProps
|
|
148
|
-
}
|
|
159
|
+
},
|
|
160
|
+
// CSS selectors
|
|
161
|
+
"data-panel": "",
|
|
162
|
+
"data-panel-id": panelId,
|
|
163
|
+
// e2e test attributes
|
|
164
|
+
"data-panel-collapsible": undefined,
|
|
165
|
+
"data-panel-size": undefined
|
|
149
166
|
});
|
|
150
167
|
}
|
|
151
168
|
const Panel = forwardRef((props, ref) => createElement(PanelWithForwardedRef, {
|
|
@@ -155,36 +172,153 @@ const Panel = forwardRef((props, ref) => createElement(PanelWithForwardedRef, {
|
|
|
155
172
|
PanelWithForwardedRef.displayName = "Panel";
|
|
156
173
|
Panel.displayName = "forwardRef(Panel)";
|
|
157
174
|
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
const PRECISION = 10;
|
|
176
|
+
|
|
177
|
+
function convertPixelsToPercentage(pixels, groupSizePixels) {
|
|
178
|
+
return pixels / groupSizePixels * 100;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function convertPixelConstraintsToPercentages(panelConstraints, groupSizePixels) {
|
|
182
|
+
let {
|
|
183
|
+
collapsedSizePercentage = 0,
|
|
184
|
+
collapsedSizePixels,
|
|
185
|
+
defaultSizePercentage,
|
|
186
|
+
defaultSizePixels,
|
|
187
|
+
maxSizePercentage = 100,
|
|
188
|
+
maxSizePixels,
|
|
189
|
+
minSizePercentage = 0,
|
|
190
|
+
minSizePixels
|
|
191
|
+
} = panelConstraints;
|
|
192
|
+
const hasPixelConstraints = collapsedSizePixels != null || defaultSizePixels != null || minSizePixels != null || maxSizePixels != null;
|
|
193
|
+
if (hasPixelConstraints && groupSizePixels <= 0) {
|
|
194
|
+
console.warn(`WARNING: Invalid group size: ${groupSizePixels}px`);
|
|
195
|
+
return {
|
|
196
|
+
collapsedSizePercentage: 0,
|
|
197
|
+
defaultSizePercentage,
|
|
198
|
+
maxSizePercentage: 0,
|
|
199
|
+
minSizePercentage: 0
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (collapsedSizePixels != null) {
|
|
203
|
+
collapsedSizePercentage = convertPixelsToPercentage(collapsedSizePixels, groupSizePixels);
|
|
204
|
+
}
|
|
205
|
+
if (defaultSizePixels != null) {
|
|
206
|
+
defaultSizePercentage = convertPixelsToPercentage(defaultSizePixels, groupSizePixels);
|
|
207
|
+
}
|
|
208
|
+
if (minSizePixels != null) {
|
|
209
|
+
minSizePercentage = convertPixelsToPercentage(minSizePixels, groupSizePixels);
|
|
210
|
+
}
|
|
211
|
+
if (maxSizePixels != null) {
|
|
212
|
+
maxSizePercentage = convertPixelsToPercentage(maxSizePixels, groupSizePixels);
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
collapsedSizePercentage,
|
|
216
|
+
defaultSizePercentage,
|
|
217
|
+
maxSizePercentage,
|
|
218
|
+
minSizePercentage
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function computePercentagePanelConstraints(panelConstraintsArray, panelIndex, groupSizePixels) {
|
|
223
|
+
// All panel constraints, excluding the current one
|
|
224
|
+
let totalMinConstraints = 0;
|
|
225
|
+
let totalMaxConstraints = 0;
|
|
226
|
+
for (let index = 0; index < panelConstraintsArray.length; index++) {
|
|
227
|
+
if (index !== panelIndex) {
|
|
228
|
+
const {
|
|
229
|
+
collapsible
|
|
230
|
+
} = panelConstraintsArray[index];
|
|
231
|
+
const {
|
|
232
|
+
collapsedSizePercentage,
|
|
233
|
+
maxSizePercentage,
|
|
234
|
+
minSizePercentage
|
|
235
|
+
} = convertPixelConstraintsToPercentages(panelConstraintsArray[index], groupSizePixels);
|
|
236
|
+
totalMaxConstraints += maxSizePercentage;
|
|
237
|
+
totalMinConstraints += collapsible ? collapsedSizePercentage : minSizePercentage;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
160
240
|
const {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
241
|
+
collapsedSizePercentage,
|
|
242
|
+
defaultSizePercentage,
|
|
243
|
+
maxSizePercentage,
|
|
244
|
+
minSizePercentage
|
|
245
|
+
} = convertPixelConstraintsToPercentages(panelConstraintsArray[panelIndex], groupSizePixels);
|
|
246
|
+
return {
|
|
247
|
+
collapsedSizePercentage,
|
|
248
|
+
defaultSizePercentage,
|
|
249
|
+
maxSizePercentage: panelConstraintsArray.length > 1 ? Math.min(maxSizePercentage, 100 - totalMinConstraints) : maxSizePercentage,
|
|
250
|
+
minSizePercentage: panelConstraintsArray.length > 1 ? Math.max(minSizePercentage, 100 - totalMaxConstraints) : minSizePercentage
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function fuzzyCompareNumbers(actual, expected, fractionDigits = PRECISION) {
|
|
255
|
+
actual = parseFloat(actual.toFixed(fractionDigits));
|
|
256
|
+
expected = parseFloat(expected.toFixed(fractionDigits));
|
|
257
|
+
const delta = actual - expected;
|
|
258
|
+
if (delta === 0) {
|
|
259
|
+
return 0;
|
|
165
260
|
} else {
|
|
166
|
-
return
|
|
261
|
+
return delta > 0 ? 1 : -1;
|
|
167
262
|
}
|
|
168
263
|
}
|
|
169
264
|
|
|
170
|
-
|
|
265
|
+
function fuzzyNumbersEqual(actual, expected, fractionDigits) {
|
|
266
|
+
return fuzzyCompareNumbers(actual, expected, fractionDigits) === 0;
|
|
267
|
+
}
|
|
171
268
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
269
|
+
// Panel size must be in percentages; pixel values should be pre-converted
|
|
270
|
+
function resizePanel({
|
|
271
|
+
groupSizePixels,
|
|
272
|
+
panelConstraints,
|
|
273
|
+
panelIndex,
|
|
274
|
+
size
|
|
275
|
+
}) {
|
|
276
|
+
const hasPixelConstraints = panelConstraints.some(({
|
|
277
|
+
collapsedSizePixels,
|
|
278
|
+
defaultSizePixels,
|
|
279
|
+
minSizePixels,
|
|
280
|
+
maxSizePixels
|
|
281
|
+
}) => collapsedSizePixels != null || defaultSizePixels != null || minSizePixels != null || maxSizePixels != null);
|
|
282
|
+
if (hasPixelConstraints && groupSizePixels <= 0) {
|
|
283
|
+
console.warn(`WARNING: Invalid group size: ${groupSizePixels}px`);
|
|
284
|
+
return 0;
|
|
285
|
+
}
|
|
286
|
+
let {
|
|
287
|
+
collapsible
|
|
288
|
+
} = panelConstraints[panelIndex];
|
|
179
289
|
const {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
290
|
+
collapsedSizePercentage,
|
|
291
|
+
maxSizePercentage,
|
|
292
|
+
minSizePercentage
|
|
293
|
+
} = computePercentagePanelConstraints(panelConstraints, panelIndex, groupSizePixels);
|
|
294
|
+
if (minSizePercentage != null) {
|
|
295
|
+
if (fuzzyCompareNumbers(size, minSizePercentage) < 0) {
|
|
296
|
+
if (collapsible) {
|
|
297
|
+
size = collapsedSizePercentage;
|
|
298
|
+
} else {
|
|
299
|
+
size = minSizePercentage;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (maxSizePercentage != null) {
|
|
304
|
+
size = Math.min(maxSizePercentage, size);
|
|
305
|
+
}
|
|
306
|
+
return size;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// All units must be in percentages; pixel values should be pre-converted
|
|
310
|
+
function adjustLayoutByDelta({
|
|
311
|
+
delta,
|
|
312
|
+
groupSizePixels,
|
|
313
|
+
layout: prevLayout,
|
|
314
|
+
panelConstraints,
|
|
315
|
+
pivotIndices,
|
|
316
|
+
trigger
|
|
317
|
+
}) {
|
|
318
|
+
if (fuzzyNumbersEqual(delta, 0)) {
|
|
319
|
+
return prevLayout;
|
|
320
|
+
}
|
|
321
|
+
const nextLayout = [...prevLayout];
|
|
188
322
|
let deltaApplied = 0;
|
|
189
323
|
|
|
190
324
|
// A resizing panel affects the panels before or after it.
|
|
@@ -195,202 +329,243 @@ function adjustByDelta(event, committedValues, idBefore, idAfter, deltaPixels, p
|
|
|
195
329
|
// A positive delta means the panel immediately before the resizer should "expand".
|
|
196
330
|
// This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resizer.
|
|
197
331
|
|
|
198
|
-
//
|
|
332
|
+
// First, check the panel we're pivoting around;
|
|
333
|
+
// We should only expand or contract by as much as its constraints allow
|
|
199
334
|
{
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
335
|
+
const pivotIndex = delta < 0 ? pivotIndices[1] : pivotIndices[0];
|
|
336
|
+
const initialSize = nextLayout[pivotIndex];
|
|
337
|
+
const {
|
|
338
|
+
collapsible
|
|
339
|
+
} = panelConstraints[pivotIndex];
|
|
340
|
+
const {
|
|
341
|
+
collapsedSizePercentage,
|
|
342
|
+
minSizePercentage
|
|
343
|
+
} = computePercentagePanelConstraints(panelConstraints, pivotIndex, groupSizePixels);
|
|
344
|
+
const isCollapsed = collapsible && fuzzyNumbersEqual(initialSize, collapsedSizePercentage);
|
|
345
|
+
let unsafeSize = initialSize + Math.abs(delta);
|
|
346
|
+
if (isCollapsed) {
|
|
347
|
+
switch (trigger) {
|
|
348
|
+
case "keyboard":
|
|
349
|
+
if (minSizePercentage > unsafeSize) {
|
|
350
|
+
unsafeSize = minSizePercentage;
|
|
351
|
+
}
|
|
211
352
|
}
|
|
212
|
-
deltaPixels = deltaPixels < 0 ? baseSize - nextSize : nextSize - baseSize;
|
|
213
353
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
deltaApplied += baseSize - nextSize;
|
|
227
|
-
nextSizes[index] = nextSize;
|
|
228
|
-
if (deltaApplied.toPrecision(PRECISION).localeCompare(Math.abs(deltaPixels).toPrecision(PRECISION), undefined, {
|
|
229
|
-
numeric: true
|
|
230
|
-
}) >= 0) {
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
354
|
+
const safeSize = resizePanel({
|
|
355
|
+
groupSizePixels,
|
|
356
|
+
panelConstraints,
|
|
357
|
+
panelIndex: pivotIndex,
|
|
358
|
+
size: unsafeSize
|
|
359
|
+
});
|
|
360
|
+
if (fuzzyNumbersEqual(initialSize, safeSize)) {
|
|
361
|
+
// If there's no room for the pivot panel to grow, we should ignore this change
|
|
362
|
+
return nextLayout;
|
|
363
|
+
} else {
|
|
364
|
+
delta = delta < 0 ? initialSize - safeSize : safeSize - initialSize;
|
|
233
365
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Delta added to a panel needs to be subtracted from other panels
|
|
369
|
+
// within the constraints that those panels allow
|
|
370
|
+
{
|
|
371
|
+
const pivotIndex = delta < 0 ? pivotIndices[0] : pivotIndices[1];
|
|
372
|
+
let index = pivotIndex;
|
|
373
|
+
while (index >= 0 && index < panelConstraints.length) {
|
|
374
|
+
const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
|
|
375
|
+
const prevSize = prevLayout[index];
|
|
376
|
+
const unsafeSize = prevSize - deltaRemaining;
|
|
377
|
+
let safeSize = resizePanel({
|
|
378
|
+
groupSizePixels,
|
|
379
|
+
panelConstraints,
|
|
380
|
+
panelIndex: index,
|
|
381
|
+
size: unsafeSize
|
|
382
|
+
});
|
|
383
|
+
if (!fuzzyNumbersEqual(prevSize, safeSize)) {
|
|
384
|
+
deltaApplied += prevSize - safeSize;
|
|
385
|
+
nextLayout[index] = safeSize;
|
|
386
|
+
if (deltaApplied.toPrecision(3).localeCompare(Math.abs(delta).toPrecision(3), undefined, {
|
|
387
|
+
numeric: true
|
|
388
|
+
}) >= 0) {
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
237
391
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
392
|
+
if (delta < 0) {
|
|
393
|
+
index--;
|
|
394
|
+
} else {
|
|
395
|
+
index++;
|
|
241
396
|
}
|
|
242
397
|
}
|
|
243
398
|
}
|
|
244
399
|
|
|
245
400
|
// If we were unable to resize any of the panels panels, return the previous state.
|
|
246
|
-
// This will essentially bailout and ignore
|
|
247
|
-
if (deltaApplied
|
|
248
|
-
return
|
|
401
|
+
// This will essentially bailout and ignore e.g. drags past a panel's boundaries
|
|
402
|
+
if (fuzzyNumbersEqual(deltaApplied, 0)) {
|
|
403
|
+
return prevLayout;
|
|
249
404
|
}
|
|
405
|
+
{
|
|
406
|
+
const pivotIndex = delta < 0 ? pivotIndices[1] : pivotIndices[0];
|
|
407
|
+
const unsafeSize = prevLayout[pivotIndex] + deltaApplied;
|
|
408
|
+
const safeSize = resizePanel({
|
|
409
|
+
groupSizePixels,
|
|
410
|
+
panelConstraints,
|
|
411
|
+
panelIndex: pivotIndex,
|
|
412
|
+
size: unsafeSize
|
|
413
|
+
});
|
|
250
414
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
415
|
+
// Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
|
|
416
|
+
nextLayout[pivotIndex] = safeSize;
|
|
417
|
+
|
|
418
|
+
// Edge case where expanding or contracting one panel caused another one to change collapsed state
|
|
419
|
+
if (!fuzzyNumbersEqual(safeSize, unsafeSize)) {
|
|
420
|
+
let deltaRemaining = unsafeSize - safeSize;
|
|
421
|
+
const pivotIndex = delta < 0 ? pivotIndices[1] : pivotIndices[0];
|
|
422
|
+
let index = pivotIndex;
|
|
423
|
+
while (index >= 0 && index < panelConstraints.length) {
|
|
424
|
+
const prevSize = nextLayout[index];
|
|
425
|
+
const unsafeSize = prevSize + deltaRemaining;
|
|
426
|
+
const safeSize = resizePanel({
|
|
427
|
+
groupSizePixels,
|
|
428
|
+
panelConstraints,
|
|
429
|
+
panelIndex: index,
|
|
430
|
+
size: unsafeSize
|
|
431
|
+
});
|
|
432
|
+
if (!fuzzyNumbersEqual(prevSize, safeSize)) {
|
|
433
|
+
deltaRemaining -= safeSize - prevSize;
|
|
434
|
+
nextLayout[index] = safeSize;
|
|
435
|
+
}
|
|
436
|
+
if (fuzzyNumbersEqual(deltaRemaining, 0)) {
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
if (delta > 0) {
|
|
440
|
+
index--;
|
|
441
|
+
} else {
|
|
442
|
+
index++;
|
|
443
|
+
}
|
|
280
444
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
445
|
+
|
|
446
|
+
// If we can't redistribute, this layout is invalid;
|
|
447
|
+
// There may be an incremental layout that is valid though
|
|
448
|
+
if (!fuzzyNumbersEqual(deltaRemaining, 0)) {
|
|
449
|
+
try {
|
|
450
|
+
return adjustLayoutByDelta({
|
|
451
|
+
delta: delta < 0 ? delta + 1 : delta - 1,
|
|
452
|
+
groupSizePixels,
|
|
453
|
+
layout: prevLayout,
|
|
454
|
+
panelConstraints,
|
|
455
|
+
pivotIndices,
|
|
456
|
+
trigger
|
|
457
|
+
});
|
|
458
|
+
} catch (error) {
|
|
459
|
+
if (error instanceof RangeError) {
|
|
460
|
+
console.error(`Could not apply delta ${delta} to layout`);
|
|
461
|
+
return prevLayout;
|
|
462
|
+
}
|
|
463
|
+
} finally {
|
|
286
464
|
}
|
|
287
465
|
}
|
|
288
466
|
}
|
|
289
|
-
}
|
|
467
|
+
}
|
|
468
|
+
return nextLayout;
|
|
290
469
|
}
|
|
291
|
-
function calculateDefaultLayout({
|
|
292
|
-
groupId,
|
|
293
|
-
panels,
|
|
294
|
-
units
|
|
295
|
-
}) {
|
|
296
|
-
const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
|
|
297
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
298
|
-
const sizes = Array(panelsArray.length);
|
|
299
|
-
let numPanelsWithSizes = 0;
|
|
300
|
-
let remainingSize = 100;
|
|
301
470
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const {
|
|
307
|
-
defaultSize
|
|
308
|
-
} = panel.current;
|
|
309
|
-
if (defaultSize != null) {
|
|
310
|
-
numPanelsWithSizes++;
|
|
311
|
-
sizes[index] = units === "pixels" ? defaultSize / groupSizePixels * 100 : defaultSize;
|
|
312
|
-
remainingSize -= sizes[index];
|
|
313
|
-
}
|
|
471
|
+
function assert(expectedCondition, message = "Assertion failed!") {
|
|
472
|
+
if (!expectedCondition) {
|
|
473
|
+
console.error(message);
|
|
474
|
+
throw Error(message);
|
|
314
475
|
}
|
|
476
|
+
}
|
|
315
477
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
minSize
|
|
325
|
-
} = panel.current;
|
|
326
|
-
if (defaultSize != null) {
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
if (units === "pixels") {
|
|
330
|
-
minSize = minSize / groupSizePixels * 100;
|
|
331
|
-
if (maxSize != null) {
|
|
332
|
-
maxSize = maxSize / groupSizePixels * 100;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
const remainingPanels = panelsArray.length - numPanelsWithSizes;
|
|
336
|
-
const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, remainingSize / remainingPanels));
|
|
337
|
-
sizes[index] = size;
|
|
338
|
-
numPanelsWithSizes++;
|
|
339
|
-
remainingSize -= size;
|
|
478
|
+
function getPercentageSizeFromMixedSizes({
|
|
479
|
+
sizePercentage,
|
|
480
|
+
sizePixels
|
|
481
|
+
}, groupSizePixels) {
|
|
482
|
+
if (sizePercentage != null) {
|
|
483
|
+
return sizePercentage;
|
|
484
|
+
} else if (sizePixels != null) {
|
|
485
|
+
return convertPixelsToPercentage(sizePixels, groupSizePixels);
|
|
340
486
|
}
|
|
487
|
+
return undefined;
|
|
488
|
+
}
|
|
341
489
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
490
|
+
function calculateAriaValues({
|
|
491
|
+
groupSizePixels,
|
|
492
|
+
layout,
|
|
493
|
+
panelsArray,
|
|
494
|
+
pivotIndices
|
|
495
|
+
}) {
|
|
496
|
+
let currentMinSize = 0;
|
|
497
|
+
let currentMaxSize = 100;
|
|
498
|
+
let totalMinSize = 0;
|
|
499
|
+
let totalMaxSize = 0;
|
|
500
|
+
|
|
501
|
+
// A panel's effective min/max sizes also need to account for other panel's sizes.
|
|
502
|
+
panelsArray.forEach((panelData, index) => {
|
|
503
|
+
var _getPercentageSizeFro, _getPercentageSizeFro2;
|
|
504
|
+
const {
|
|
505
|
+
constraints
|
|
506
|
+
} = panelData;
|
|
507
|
+
const {
|
|
508
|
+
maxSizePercentage,
|
|
509
|
+
maxSizePixels,
|
|
510
|
+
minSizePercentage,
|
|
511
|
+
minSizePixels
|
|
512
|
+
} = constraints;
|
|
513
|
+
const minSize = (_getPercentageSizeFro = getPercentageSizeFromMixedSizes({
|
|
514
|
+
sizePercentage: minSizePercentage,
|
|
515
|
+
sizePixels: minSizePixels
|
|
516
|
+
}, groupSizePixels)) !== null && _getPercentageSizeFro !== void 0 ? _getPercentageSizeFro : 0;
|
|
517
|
+
const maxSize = (_getPercentageSizeFro2 = getPercentageSizeFromMixedSizes({
|
|
518
|
+
sizePercentage: maxSizePercentage,
|
|
519
|
+
sizePixels: maxSizePixels
|
|
520
|
+
}, groupSizePixels)) !== null && _getPercentageSizeFro2 !== void 0 ? _getPercentageSizeFro2 : 100;
|
|
521
|
+
if (index === pivotIndices[0]) {
|
|
522
|
+
currentMinSize = minSize;
|
|
523
|
+
currentMaxSize = maxSize;
|
|
524
|
+
} else {
|
|
525
|
+
totalMinSize += minSize;
|
|
526
|
+
totalMaxSize += maxSize;
|
|
367
527
|
}
|
|
368
|
-
}
|
|
528
|
+
});
|
|
529
|
+
const valueMax = Math.min(currentMaxSize, 100 - totalMinSize);
|
|
530
|
+
const valueMin = Math.max(currentMinSize, 100 - totalMaxSize);
|
|
531
|
+
const valueNow = layout[pivotIndices[0]];
|
|
532
|
+
return {
|
|
533
|
+
valueMax,
|
|
534
|
+
valueMin,
|
|
535
|
+
valueNow
|
|
536
|
+
};
|
|
537
|
+
}
|
|
369
538
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return sizes;
|
|
539
|
+
function getResizeHandleElementsForGroup(groupId) {
|
|
540
|
+
return Array.from(document.querySelectorAll(`[data-panel-resize-handle-id][data-panel-group-id="${groupId}"]`));
|
|
373
541
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
542
|
+
|
|
543
|
+
function getResizeHandleElementIndex(groupId, id) {
|
|
544
|
+
const handles = getResizeHandleElementsForGroup(groupId);
|
|
545
|
+
const index = handles.findIndex(handle => handle.getAttribute("data-panel-resize-handle-id") === id);
|
|
546
|
+
return index !== null && index !== void 0 ? index : null;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function determinePivotIndices(groupId, dragHandleId) {
|
|
550
|
+
const index = getResizeHandleElementIndex(groupId, dragHandleId);
|
|
551
|
+
return index != null ? [index, index + 1] : [-1, -1];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function getPanelGroupElement(id) {
|
|
555
|
+
const element = document.querySelector(`[data-panel-group][data-panel-group-id="${id}"]`);
|
|
556
|
+
if (element) {
|
|
557
|
+
return element;
|
|
381
558
|
}
|
|
382
|
-
|
|
383
|
-
const idBefore = isLastPanel ? panelsArray[index - 1].current.id : id;
|
|
384
|
-
const idAfter = isLastPanel ? id : panelsArray[index + 1].current.id;
|
|
385
|
-
return [idBefore, idAfter];
|
|
559
|
+
return null;
|
|
386
560
|
}
|
|
387
|
-
|
|
388
|
-
|
|
561
|
+
|
|
562
|
+
function calculateAvailablePanelSizeInPixels(groupId) {
|
|
563
|
+
const panelGroupElement = getPanelGroupElement(groupId);
|
|
389
564
|
if (panelGroupElement == null) {
|
|
390
565
|
return NaN;
|
|
391
566
|
}
|
|
392
567
|
const direction = panelGroupElement.getAttribute("data-panel-group-direction");
|
|
393
|
-
const resizeHandles =
|
|
568
|
+
const resizeHandles = getResizeHandleElementsForGroup(groupId);
|
|
394
569
|
if (direction === "horizontal") {
|
|
395
570
|
return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
|
|
396
571
|
return accumulated + handle.offsetWidth;
|
|
@@ -402,256 +577,103 @@ function getAvailableGroupSizePixels(groupId) {
|
|
|
402
577
|
}
|
|
403
578
|
}
|
|
404
579
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
return "100";
|
|
410
|
-
}
|
|
411
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
412
|
-
const index = panelsArray.findIndex(panel => panel.current.id === id);
|
|
413
|
-
const size = sizes[index];
|
|
414
|
-
if (size == null) {
|
|
415
|
-
return "0";
|
|
416
|
-
}
|
|
417
|
-
return size.toPrecision(PRECISION);
|
|
418
|
-
}
|
|
419
|
-
function getPanel(id) {
|
|
420
|
-
const element = document.querySelector(`[data-panel-id="${id}"]`);
|
|
421
|
-
if (element) {
|
|
422
|
-
return element;
|
|
580
|
+
function getAvailableGroupSizePixels(groupId) {
|
|
581
|
+
const panelGroupElement = getPanelGroupElement(groupId);
|
|
582
|
+
if (panelGroupElement == null) {
|
|
583
|
+
return NaN;
|
|
423
584
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
585
|
+
const direction = panelGroupElement.getAttribute("data-panel-group-direction");
|
|
586
|
+
const resizeHandles = getResizeHandleElementsForGroup(groupId);
|
|
587
|
+
if (direction === "horizontal") {
|
|
588
|
+
return panelGroupElement.offsetWidth - resizeHandles.reduce((accumulated, handle) => {
|
|
589
|
+
return accumulated + handle.offsetWidth;
|
|
590
|
+
}, 0);
|
|
591
|
+
} else {
|
|
592
|
+
return panelGroupElement.offsetHeight - resizeHandles.reduce((accumulated, handle) => {
|
|
593
|
+
return accumulated + handle.offsetHeight;
|
|
594
|
+
}, 0);
|
|
430
595
|
}
|
|
431
|
-
return null;
|
|
432
596
|
}
|
|
433
|
-
|
|
597
|
+
|
|
598
|
+
function getResizeHandleElement(id) {
|
|
434
599
|
const element = document.querySelector(`[data-panel-resize-handle-id="${id}"]`);
|
|
435
600
|
if (element) {
|
|
436
601
|
return element;
|
|
437
602
|
}
|
|
438
603
|
return null;
|
|
439
604
|
}
|
|
440
|
-
|
|
441
|
-
const handles = getResizeHandles();
|
|
442
|
-
const index = handles.findIndex(handle => handle.getAttribute("data-panel-resize-handle-id") === id);
|
|
443
|
-
return index ?? null;
|
|
444
|
-
}
|
|
445
|
-
function getResizeHandles() {
|
|
446
|
-
return Array.from(document.querySelectorAll(`[data-panel-resize-handle-id]`));
|
|
447
|
-
}
|
|
448
|
-
function getResizeHandlesForGroup(groupId) {
|
|
449
|
-
return Array.from(document.querySelectorAll(`[data-panel-resize-handle-id][data-panel-group-id="${groupId}"]`));
|
|
450
|
-
}
|
|
605
|
+
|
|
451
606
|
function getResizeHandlePanelIds(groupId, handleId, panelsArray) {
|
|
452
|
-
|
|
453
|
-
const
|
|
607
|
+
var _panelsArray$index$id, _panelsArray$index, _panelsArray$id, _panelsArray;
|
|
608
|
+
const handle = getResizeHandleElement(handleId);
|
|
609
|
+
const handles = getResizeHandleElementsForGroup(groupId);
|
|
454
610
|
const index = handle ? handles.indexOf(handle) : -1;
|
|
455
|
-
const idBefore = panelsArray[index]
|
|
456
|
-
const idAfter = panelsArray[index + 1]
|
|
611
|
+
const idBefore = (_panelsArray$index$id = (_panelsArray$index = panelsArray[index]) === null || _panelsArray$index === void 0 ? void 0 : _panelsArray$index.id) !== null && _panelsArray$index$id !== void 0 ? _panelsArray$index$id : null;
|
|
612
|
+
const idAfter = (_panelsArray$id = (_panelsArray = panelsArray[index + 1]) === null || _panelsArray === void 0 ? void 0 : _panelsArray.id) !== null && _panelsArray$id !== void 0 ? _panelsArray$id : null;
|
|
457
613
|
return [idBefore, idAfter];
|
|
458
614
|
}
|
|
459
|
-
function panelsMapToSortedArray(panels) {
|
|
460
|
-
return Array.from(panels.values()).sort((panelA, panelB) => {
|
|
461
|
-
const orderA = panelA.current.order;
|
|
462
|
-
const orderB = panelB.current.order;
|
|
463
|
-
if (orderA == null && orderB == null) {
|
|
464
|
-
return 0;
|
|
465
|
-
} else if (orderA == null) {
|
|
466
|
-
return -1;
|
|
467
|
-
} else if (orderB == null) {
|
|
468
|
-
return 1;
|
|
469
|
-
} else {
|
|
470
|
-
return orderA - orderB;
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
function safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize, event = null) {
|
|
475
|
-
let {
|
|
476
|
-
collapsedSize,
|
|
477
|
-
collapsible,
|
|
478
|
-
maxSize,
|
|
479
|
-
minSize
|
|
480
|
-
} = panel.current;
|
|
481
|
-
if (units === "pixels") {
|
|
482
|
-
collapsedSize = collapsedSize / groupSizePixels * 100;
|
|
483
|
-
if (maxSize != null) {
|
|
484
|
-
maxSize = maxSize / groupSizePixels * 100;
|
|
485
|
-
}
|
|
486
|
-
minSize = minSize / groupSizePixels * 100;
|
|
487
|
-
}
|
|
488
|
-
if (collapsible) {
|
|
489
|
-
if (prevSize > collapsedSize) {
|
|
490
|
-
// Mimic VS COde behavior; collapse a panel if it's smaller than half of its min-size
|
|
491
|
-
if (nextSize <= minSize / 2 + collapsedSize) {
|
|
492
|
-
return collapsedSize;
|
|
493
|
-
}
|
|
494
|
-
} else {
|
|
495
|
-
const isKeyboardEvent = event?.type?.startsWith("key");
|
|
496
|
-
if (!isKeyboardEvent) {
|
|
497
|
-
// Keyboard events should expand a collapsed panel to the min size,
|
|
498
|
-
// but mouse events should wait until the panel has reached its min size
|
|
499
|
-
// to avoid a visual flickering when dragging between collapsed and min size.
|
|
500
|
-
if (nextSize < minSize) {
|
|
501
|
-
return collapsedSize;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
return Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
|
|
507
|
-
}
|
|
508
|
-
function validatePanelProps(units, panelData) {
|
|
509
|
-
const {
|
|
510
|
-
collapsible,
|
|
511
|
-
defaultSize,
|
|
512
|
-
maxSize,
|
|
513
|
-
minSize
|
|
514
|
-
} = panelData.current;
|
|
515
|
-
|
|
516
|
-
// Basic props validation
|
|
517
|
-
if (minSize < 0 || units === "percentages" && minSize > 100) {
|
|
518
|
-
panelData.current.minSize = 0;
|
|
519
|
-
}
|
|
520
|
-
if (maxSize != null) {
|
|
521
|
-
if (maxSize < 0 || units === "percentages" && maxSize > 100) {
|
|
522
|
-
panelData.current.maxSize = null;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
if (defaultSize !== null) {
|
|
526
|
-
if (defaultSize < 0 || units === "percentages" && defaultSize > 100) {
|
|
527
|
-
panelData.current.defaultSize = null;
|
|
528
|
-
} else if (defaultSize < minSize && !collapsible) {
|
|
529
|
-
panelData.current.defaultSize = minSize;
|
|
530
|
-
} else if (maxSize != null && defaultSize > maxSize) {
|
|
531
|
-
panelData.current.defaultSize = maxSize;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
function validatePanelGroupLayout({
|
|
536
|
-
groupId,
|
|
537
|
-
panels,
|
|
538
|
-
nextSizes,
|
|
539
|
-
prevSizes,
|
|
540
|
-
units
|
|
541
|
-
}) {
|
|
542
|
-
// Clone because this method modifies
|
|
543
|
-
nextSizes = [...nextSizes];
|
|
544
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
545
|
-
const groupSizePixels = units === "pixels" ? getAvailableGroupSizePixels(groupId) : NaN;
|
|
546
|
-
let remainingSize = 0;
|
|
547
|
-
|
|
548
|
-
// First, check all of the proposed sizes against the min/max constraints
|
|
549
|
-
for (let index = 0; index < panelsArray.length; index++) {
|
|
550
|
-
const panel = panelsArray[index];
|
|
551
|
-
const prevSize = prevSizes[index];
|
|
552
|
-
const nextSize = nextSizes[index];
|
|
553
|
-
const safeNextSize = safeResizePanel(units, groupSizePixels, panel, prevSize, nextSize);
|
|
554
|
-
if (nextSize != safeNextSize) {
|
|
555
|
-
remainingSize += nextSize - safeNextSize;
|
|
556
|
-
nextSizes[index] = safeNextSize;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// If there is additional, left over space, assign it to any panel(s) that permits it
|
|
561
|
-
// (It's not worth taking multiple additional passes to evenly distribute)
|
|
562
|
-
if (remainingSize.toFixed(3) !== "0.000") {
|
|
563
|
-
for (let index = 0; index < panelsArray.length; index++) {
|
|
564
|
-
const panel = panelsArray[index];
|
|
565
|
-
let {
|
|
566
|
-
maxSize,
|
|
567
|
-
minSize
|
|
568
|
-
} = panel.current;
|
|
569
|
-
if (units === "pixels") {
|
|
570
|
-
minSize = minSize / groupSizePixels * 100;
|
|
571
|
-
if (maxSize != null) {
|
|
572
|
-
maxSize = maxSize / groupSizePixels * 100;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
const size = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSizes[index] + remainingSize));
|
|
576
|
-
if (size !== nextSizes[index]) {
|
|
577
|
-
remainingSize -= size - nextSizes[index];
|
|
578
|
-
nextSizes[index] = size;
|
|
579
|
-
|
|
580
|
-
// Fuzzy comparison to account for imprecise floating point math
|
|
581
|
-
if (Math.abs(remainingSize).toFixed(3) === "0.000") {
|
|
582
|
-
break;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// If we still have remainder, the requested layout wasn't valid and we should warn about it
|
|
589
|
-
if (remainingSize.toFixed(3) !== "0.000") ;
|
|
590
|
-
return nextSizes;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function assert(expectedCondition, message = "Assertion failed!") {
|
|
594
|
-
if (!expectedCondition) {
|
|
595
|
-
console.error(message);
|
|
596
|
-
throw Error(message);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
615
|
|
|
600
616
|
// https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
|
|
601
617
|
|
|
602
618
|
function useWindowSplitterPanelGroupBehavior({
|
|
603
619
|
committedValuesRef,
|
|
604
620
|
groupId,
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
panelSizeBeforeCollapse
|
|
621
|
+
layout,
|
|
622
|
+
panelDataArray,
|
|
623
|
+
setLayout
|
|
609
624
|
}) {
|
|
625
|
+
useRef({
|
|
626
|
+
didWarnAboutMissingResizeHandle: false
|
|
627
|
+
});
|
|
628
|
+
useIsomorphicLayoutEffect(() => {
|
|
629
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
630
|
+
const resizeHandleElements = getResizeHandleElementsForGroup(groupId);
|
|
631
|
+
for (let index = 0; index < panelDataArray.length - 1; index++) {
|
|
632
|
+
const {
|
|
633
|
+
valueMax,
|
|
634
|
+
valueMin,
|
|
635
|
+
valueNow
|
|
636
|
+
} = calculateAriaValues({
|
|
637
|
+
groupSizePixels,
|
|
638
|
+
layout,
|
|
639
|
+
panelsArray: panelDataArray,
|
|
640
|
+
pivotIndices: [index, index + 1]
|
|
641
|
+
});
|
|
642
|
+
const resizeHandleElement = resizeHandleElements[index];
|
|
643
|
+
if (resizeHandleElement == null) ; else {
|
|
644
|
+
resizeHandleElement.setAttribute("aria-controls", panelDataArray[index].id);
|
|
645
|
+
resizeHandleElement.setAttribute("aria-valuemax", "" + Math.round(valueMax));
|
|
646
|
+
resizeHandleElement.setAttribute("aria-valuemin", "" + Math.round(valueMin));
|
|
647
|
+
resizeHandleElement.setAttribute("aria-valuenow", "" + Math.round(valueNow));
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return () => {
|
|
651
|
+
resizeHandleElements.forEach((resizeHandleElement, index) => {
|
|
652
|
+
resizeHandleElement.removeAttribute("aria-controls");
|
|
653
|
+
resizeHandleElement.removeAttribute("aria-valuemax");
|
|
654
|
+
resizeHandleElement.removeAttribute("aria-valuemin");
|
|
655
|
+
resizeHandleElement.removeAttribute("aria-valuenow");
|
|
656
|
+
});
|
|
657
|
+
};
|
|
658
|
+
}, [groupId, layout, panelDataArray]);
|
|
610
659
|
useEffect(() => {
|
|
611
660
|
const {
|
|
612
661
|
direction,
|
|
613
|
-
|
|
662
|
+
panelDataArray
|
|
614
663
|
} = committedValuesRef.current;
|
|
615
|
-
const groupElement =
|
|
664
|
+
const groupElement = getPanelGroupElement(groupId);
|
|
616
665
|
assert(groupElement != null, `No group found for id "${groupId}"`);
|
|
617
666
|
const {
|
|
618
667
|
height,
|
|
619
668
|
width
|
|
620
669
|
} = groupElement.getBoundingClientRect();
|
|
621
|
-
const handles =
|
|
670
|
+
const handles = getResizeHandleElementsForGroup(groupId);
|
|
622
671
|
const cleanupFunctions = handles.map(handle => {
|
|
623
672
|
const handleId = handle.getAttribute("data-panel-resize-handle-id");
|
|
624
|
-
const
|
|
625
|
-
const [idBefore, idAfter] = getResizeHandlePanelIds(groupId, handleId, panelsArray);
|
|
673
|
+
const [idBefore, idAfter] = getResizeHandlePanelIds(groupId, handleId, panelDataArray);
|
|
626
674
|
if (idBefore == null || idAfter == null) {
|
|
627
675
|
return () => {};
|
|
628
676
|
}
|
|
629
|
-
let currentMinSize = 0;
|
|
630
|
-
let currentMaxSize = 100;
|
|
631
|
-
let totalMinSize = 0;
|
|
632
|
-
let totalMaxSize = 0;
|
|
633
|
-
|
|
634
|
-
// A panel's effective min/max sizes also need to account for other panel's sizes.
|
|
635
|
-
panelsArray.forEach(panelData => {
|
|
636
|
-
const {
|
|
637
|
-
id,
|
|
638
|
-
maxSize,
|
|
639
|
-
minSize
|
|
640
|
-
} = panelData.current;
|
|
641
|
-
if (id === idBefore) {
|
|
642
|
-
currentMinSize = minSize;
|
|
643
|
-
currentMaxSize = maxSize != null ? maxSize : 100;
|
|
644
|
-
} else {
|
|
645
|
-
totalMinSize += minSize;
|
|
646
|
-
totalMaxSize += maxSize != null ? maxSize : 100;
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
const ariaValueMax = Math.min(currentMaxSize, 100 - totalMinSize);
|
|
650
|
-
const ariaValueMin = Math.max(currentMinSize, (panelsArray.length - 1) * 100 - totalMaxSize);
|
|
651
|
-
const flexGrow = getFlexGrow(panels, idBefore, sizes);
|
|
652
|
-
handle.setAttribute("aria-valuemax", "" + Math.round(ariaValueMax));
|
|
653
|
-
handle.setAttribute("aria-valuemin", "" + Math.round(ariaValueMin));
|
|
654
|
-
handle.setAttribute("aria-valuenow", "" + Math.round(parseInt(flexGrow)));
|
|
655
677
|
const onKeyDown = event => {
|
|
656
678
|
if (event.defaultPrevented) {
|
|
657
679
|
return;
|
|
@@ -660,20 +682,33 @@ function useWindowSplitterPanelGroupBehavior({
|
|
|
660
682
|
case "Enter":
|
|
661
683
|
{
|
|
662
684
|
event.preventDefault();
|
|
663
|
-
const index =
|
|
685
|
+
const index = panelDataArray.findIndex(panelData => panelData.id === idBefore);
|
|
664
686
|
if (index >= 0) {
|
|
665
|
-
const panelData =
|
|
666
|
-
const size =
|
|
687
|
+
const panelData = panelDataArray[index];
|
|
688
|
+
const size = layout[index];
|
|
667
689
|
if (size != null) {
|
|
690
|
+
var _getPercentageSizeFro;
|
|
691
|
+
const groupSizePixels = getAvailableGroupSizePixels(groupId);
|
|
692
|
+
const minSize = (_getPercentageSizeFro = getPercentageSizeFromMixedSizes({
|
|
693
|
+
sizePercentage: panelData.constraints.minSizePercentage,
|
|
694
|
+
sizePixels: panelData.constraints.minSizePixels
|
|
695
|
+
}, groupSizePixels)) !== null && _getPercentageSizeFro !== void 0 ? _getPercentageSizeFro : 0;
|
|
668
696
|
let delta = 0;
|
|
669
|
-
if (size.toPrecision(PRECISION) <=
|
|
697
|
+
if (size.toPrecision(PRECISION) <= minSize.toPrecision(PRECISION)) {
|
|
670
698
|
delta = direction === "horizontal" ? width : height;
|
|
671
699
|
} else {
|
|
672
700
|
delta = -(direction === "horizontal" ? width : height);
|
|
673
701
|
}
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
702
|
+
const nextLayout = adjustLayoutByDelta({
|
|
703
|
+
delta,
|
|
704
|
+
groupSizePixels,
|
|
705
|
+
layout,
|
|
706
|
+
panelConstraints: panelDataArray.map(panelData => panelData.constraints),
|
|
707
|
+
pivotIndices: determinePivotIndices(groupId, handleId),
|
|
708
|
+
trigger: "keyboard"
|
|
709
|
+
});
|
|
710
|
+
if (layout !== nextLayout) {
|
|
711
|
+
setLayout(nextLayout);
|
|
677
712
|
}
|
|
678
713
|
}
|
|
679
714
|
}
|
|
@@ -682,72 +717,14 @@ function useWindowSplitterPanelGroupBehavior({
|
|
|
682
717
|
}
|
|
683
718
|
};
|
|
684
719
|
handle.addEventListener("keydown", onKeyDown);
|
|
685
|
-
const panelBefore = getPanel(idBefore);
|
|
686
|
-
if (panelBefore != null) {
|
|
687
|
-
handle.setAttribute("aria-controls", panelBefore.id);
|
|
688
|
-
}
|
|
689
720
|
return () => {
|
|
690
|
-
handle.removeAttribute("aria-valuemax");
|
|
691
|
-
handle.removeAttribute("aria-valuemin");
|
|
692
|
-
handle.removeAttribute("aria-valuenow");
|
|
693
721
|
handle.removeEventListener("keydown", onKeyDown);
|
|
694
|
-
if (panelBefore != null) {
|
|
695
|
-
handle.removeAttribute("aria-controls");
|
|
696
|
-
}
|
|
697
722
|
};
|
|
698
723
|
});
|
|
699
724
|
return () => {
|
|
700
725
|
cleanupFunctions.forEach(cleanupFunction => cleanupFunction());
|
|
701
726
|
};
|
|
702
|
-
}, [committedValuesRef, groupId,
|
|
703
|
-
}
|
|
704
|
-
function useWindowSplitterResizeHandlerBehavior({
|
|
705
|
-
disabled,
|
|
706
|
-
handleId,
|
|
707
|
-
resizeHandler
|
|
708
|
-
}) {
|
|
709
|
-
useEffect(() => {
|
|
710
|
-
if (disabled || resizeHandler == null) {
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
const handleElement = getResizeHandle(handleId);
|
|
714
|
-
if (handleElement == null) {
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
const onKeyDown = event => {
|
|
718
|
-
if (event.defaultPrevented) {
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
switch (event.key) {
|
|
722
|
-
case "ArrowDown":
|
|
723
|
-
case "ArrowLeft":
|
|
724
|
-
case "ArrowRight":
|
|
725
|
-
case "ArrowUp":
|
|
726
|
-
case "End":
|
|
727
|
-
case "Home":
|
|
728
|
-
{
|
|
729
|
-
event.preventDefault();
|
|
730
|
-
resizeHandler(event);
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
case "F6":
|
|
734
|
-
{
|
|
735
|
-
event.preventDefault();
|
|
736
|
-
const handles = getResizeHandles();
|
|
737
|
-
const index = getResizeHandleIndex(handleId);
|
|
738
|
-
assert(index !== null);
|
|
739
|
-
const nextIndex = event.shiftKey ? index > 0 ? index - 1 : handles.length - 1 : index + 1 < handles.length ? index + 1 : 0;
|
|
740
|
-
const nextHandle = handles[nextIndex];
|
|
741
|
-
nextHandle.focus();
|
|
742
|
-
break;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
};
|
|
746
|
-
handleElement.addEventListener("keydown", onKeyDown);
|
|
747
|
-
return () => {
|
|
748
|
-
handleElement.removeEventListener("keydown", onKeyDown);
|
|
749
|
-
};
|
|
750
|
-
}, [disabled, handleId, resizeHandler]);
|
|
727
|
+
}, [committedValuesRef, groupId, layout, panelDataArray, setLayout]);
|
|
751
728
|
}
|
|
752
729
|
|
|
753
730
|
function areEqual(arrayA, arrayB) {
|
|
@@ -762,41 +739,61 @@ function areEqual(arrayA, arrayB) {
|
|
|
762
739
|
return true;
|
|
763
740
|
}
|
|
764
741
|
|
|
765
|
-
function
|
|
742
|
+
function isKeyDown(event) {
|
|
743
|
+
return event.type === "keydown";
|
|
744
|
+
}
|
|
745
|
+
function isMouseEvent(event) {
|
|
746
|
+
return event.type.startsWith("mouse");
|
|
747
|
+
}
|
|
748
|
+
function isTouchEvent(event) {
|
|
749
|
+
return event.type.startsWith("touch");
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function getResizeEventCursorPosition(direction, event) {
|
|
766
753
|
const isHorizontal = direction === "horizontal";
|
|
767
|
-
let pointerOffset = 0;
|
|
768
754
|
if (isMouseEvent(event)) {
|
|
769
|
-
|
|
755
|
+
return isHorizontal ? event.clientX : event.clientY;
|
|
770
756
|
} else if (isTouchEvent(event)) {
|
|
771
757
|
const firstTouch = event.touches[0];
|
|
772
|
-
|
|
758
|
+
return isHorizontal ? firstTouch.screenX : firstTouch.screenY;
|
|
773
759
|
} else {
|
|
774
|
-
|
|
760
|
+
throw Error(`Unsupported event type "${event.type}"`);
|
|
775
761
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState) {
|
|
765
|
+
const isHorizontal = direction === "horizontal";
|
|
766
|
+
const handleElement = getResizeHandleElement(dragHandleId);
|
|
767
|
+
const groupId = handleElement.getAttribute("data-panel-group-id");
|
|
768
|
+
let {
|
|
769
|
+
initialCursorPosition
|
|
770
|
+
} = initialDragState;
|
|
771
|
+
const cursorPosition = getResizeEventCursorPosition(direction, event);
|
|
772
|
+
const groupElement = getPanelGroupElement(groupId);
|
|
773
|
+
const groupRect = groupElement.getBoundingClientRect();
|
|
774
|
+
const groupSizeInPixels = isHorizontal ? groupRect.width : groupRect.height;
|
|
775
|
+
const offsetPixels = cursorPosition - initialCursorPosition;
|
|
776
|
+
const offsetPercentage = offsetPixels / groupSizeInPixels * 100;
|
|
777
|
+
return offsetPercentage;
|
|
780
778
|
}
|
|
781
779
|
|
|
782
780
|
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX
|
|
783
|
-
function
|
|
784
|
-
const {
|
|
785
|
-
dragOffset = 0,
|
|
786
|
-
dragHandleRect,
|
|
787
|
-
sizes: initialSizes
|
|
788
|
-
} = initialDragState || {};
|
|
789
|
-
|
|
790
|
-
// If we're resizing by mouse or touch, use the initial sizes as a base.
|
|
791
|
-
// This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
|
|
792
|
-
const baseSizes = initialSizes || prevSizes;
|
|
781
|
+
function calculateDeltaPercentage(event, groupId, dragHandleId, direction, initialDragState, keyboardResizeByOptions) {
|
|
793
782
|
if (isKeyDown(event)) {
|
|
794
783
|
const isHorizontal = direction === "horizontal";
|
|
795
|
-
const groupElement =
|
|
784
|
+
const groupElement = getPanelGroupElement(groupId);
|
|
796
785
|
const rect = groupElement.getBoundingClientRect();
|
|
797
786
|
const groupSizeInPixels = isHorizontal ? rect.width : rect.height;
|
|
798
|
-
|
|
799
|
-
|
|
787
|
+
let delta = 0;
|
|
788
|
+
if (event.shiftKey) {
|
|
789
|
+
delta = 100;
|
|
790
|
+
} else if (keyboardResizeByOptions.percentage != null) {
|
|
791
|
+
delta = keyboardResizeByOptions.percentage;
|
|
792
|
+
} else if (keyboardResizeByOptions.pixels != null) {
|
|
793
|
+
delta = keyboardResizeByOptions.pixels / groupSizeInPixels;
|
|
794
|
+
} else {
|
|
795
|
+
delta = 10;
|
|
796
|
+
}
|
|
800
797
|
let movement = 0;
|
|
801
798
|
switch (event.key) {
|
|
802
799
|
case "ArrowDown":
|
|
@@ -812,40 +809,153 @@ function getMovement(event, groupId, handleId, panelsArray, direction, prevSizes
|
|
|
812
809
|
movement = isHorizontal ? 0 : -delta;
|
|
813
810
|
break;
|
|
814
811
|
case "End":
|
|
815
|
-
movement =
|
|
812
|
+
movement = 100;
|
|
816
813
|
break;
|
|
817
814
|
case "Home":
|
|
818
|
-
movement = -
|
|
815
|
+
movement = -100;
|
|
819
816
|
break;
|
|
820
817
|
}
|
|
821
|
-
|
|
822
|
-
// If the Panel being resized is collapsible,
|
|
823
|
-
// we need to special case resizing around the minSize boundary.
|
|
824
|
-
// If contracting, Panels should shrink to their minSize and then snap to fully collapsed.
|
|
825
|
-
// If expanding from collapsed, they should snap back to their minSize.
|
|
826
|
-
const [idBefore, idAfter] = getResizeHandlePanelIds(groupId, handleId, panelsArray);
|
|
827
|
-
const targetPanelId = movement < 0 ? idBefore : idAfter;
|
|
828
|
-
const targetPanelIndex = panelsArray.findIndex(panel => panel.current.id === targetPanelId);
|
|
829
|
-
const targetPanel = panelsArray[targetPanelIndex];
|
|
830
|
-
if (targetPanel.current.collapsible) {
|
|
831
|
-
const baseSize = baseSizes[targetPanelIndex];
|
|
832
|
-
if (baseSize === 0 || baseSize.toPrecision(PRECISION) === targetPanel.current.minSize.toPrecision(PRECISION)) {
|
|
833
|
-
movement = movement < 0 ? -targetPanel.current.minSize * groupSizeInPixels : targetPanel.current.minSize * groupSizeInPixels;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
818
|
return movement;
|
|
837
819
|
} else {
|
|
838
|
-
return
|
|
820
|
+
return calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState);
|
|
839
821
|
}
|
|
840
822
|
}
|
|
841
|
-
|
|
842
|
-
|
|
823
|
+
|
|
824
|
+
function calculateUnsafeDefaultLayout({
|
|
825
|
+
groupSizePixels,
|
|
826
|
+
panelDataArray
|
|
827
|
+
}) {
|
|
828
|
+
const layout = Array(panelDataArray.length);
|
|
829
|
+
const panelDataConstraints = panelDataArray.map(panelData => panelData.constraints);
|
|
830
|
+
let numPanelsWithSizes = 0;
|
|
831
|
+
let remainingSize = 100;
|
|
832
|
+
|
|
833
|
+
// Distribute default sizes first
|
|
834
|
+
for (let index = 0; index < panelDataArray.length; index++) {
|
|
835
|
+
const {
|
|
836
|
+
defaultSizePercentage
|
|
837
|
+
} = computePercentagePanelConstraints(panelDataConstraints, index, groupSizePixels);
|
|
838
|
+
if (defaultSizePercentage != null) {
|
|
839
|
+
numPanelsWithSizes++;
|
|
840
|
+
layout[index] = defaultSizePercentage;
|
|
841
|
+
remainingSize -= defaultSizePercentage;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Remaining size should be distributed evenly between panels without default sizes
|
|
846
|
+
for (let index = 0; index < panelDataArray.length; index++) {
|
|
847
|
+
const {
|
|
848
|
+
defaultSizePercentage
|
|
849
|
+
} = computePercentagePanelConstraints(panelDataConstraints, index, groupSizePixels);
|
|
850
|
+
if (defaultSizePercentage != null) {
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
const numRemainingPanels = panelDataArray.length - numPanelsWithSizes;
|
|
854
|
+
const size = remainingSize / numRemainingPanels;
|
|
855
|
+
numPanelsWithSizes++;
|
|
856
|
+
layout[index] = size;
|
|
857
|
+
remainingSize -= size;
|
|
858
|
+
}
|
|
859
|
+
return layout;
|
|
843
860
|
}
|
|
844
|
-
|
|
845
|
-
|
|
861
|
+
|
|
862
|
+
function convertPercentageToPixels(percentage, groupSizePixels) {
|
|
863
|
+
return percentage / 100 * groupSizePixels;
|
|
846
864
|
}
|
|
847
|
-
|
|
848
|
-
|
|
865
|
+
|
|
866
|
+
// Layout should be pre-converted into percentages
|
|
867
|
+
function callPanelCallbacks(groupId, panelsArray, layout, panelIdToLastNotifiedMixedSizesMap) {
|
|
868
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
869
|
+
layout.forEach((sizePercentage, index) => {
|
|
870
|
+
const panelData = panelsArray[index];
|
|
871
|
+
if (!panelData) {
|
|
872
|
+
// Handle initial mount (when panels are registered too late to be in the panels array)
|
|
873
|
+
// The subsequent render+effects will handle the resize notification
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
const {
|
|
877
|
+
callbacks,
|
|
878
|
+
constraints,
|
|
879
|
+
id: panelId
|
|
880
|
+
} = panelData;
|
|
881
|
+
const {
|
|
882
|
+
collapsible
|
|
883
|
+
} = constraints;
|
|
884
|
+
const mixedSizes = {
|
|
885
|
+
sizePercentage,
|
|
886
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
887
|
+
};
|
|
888
|
+
const lastNotifiedMixedSizes = panelIdToLastNotifiedMixedSizesMap[panelId];
|
|
889
|
+
if (lastNotifiedMixedSizes == null || mixedSizes.sizePercentage !== lastNotifiedMixedSizes.sizePercentage || mixedSizes.sizePixels !== lastNotifiedMixedSizes.sizePixels) {
|
|
890
|
+
panelIdToLastNotifiedMixedSizesMap[panelId] = mixedSizes;
|
|
891
|
+
const {
|
|
892
|
+
onCollapse,
|
|
893
|
+
onExpand,
|
|
894
|
+
onResize
|
|
895
|
+
} = callbacks;
|
|
896
|
+
if (onResize) {
|
|
897
|
+
onResize(mixedSizes, lastNotifiedMixedSizes);
|
|
898
|
+
}
|
|
899
|
+
if (collapsible && (onCollapse || onExpand)) {
|
|
900
|
+
var _getPercentageSizeFro;
|
|
901
|
+
const collapsedSize = (_getPercentageSizeFro = getPercentageSizeFromMixedSizes({
|
|
902
|
+
sizePercentage: constraints.collapsedSizePercentage,
|
|
903
|
+
sizePixels: constraints.collapsedSizePixels
|
|
904
|
+
}, groupSizePixels)) !== null && _getPercentageSizeFro !== void 0 ? _getPercentageSizeFro : 0;
|
|
905
|
+
const size = getPercentageSizeFromMixedSizes(mixedSizes, groupSizePixels);
|
|
906
|
+
if (onExpand && (lastNotifiedMixedSizes == null || lastNotifiedMixedSizes.sizePercentage === collapsedSize) && size !== collapsedSize) {
|
|
907
|
+
onExpand();
|
|
908
|
+
}
|
|
909
|
+
if (onCollapse && (lastNotifiedMixedSizes == null || lastNotifiedMixedSizes.sizePercentage !== collapsedSize) && size === collapsedSize) {
|
|
910
|
+
onCollapse();
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function compareLayouts(a, b) {
|
|
918
|
+
if (a.length !== b.length) {
|
|
919
|
+
return false;
|
|
920
|
+
} else {
|
|
921
|
+
for (let index = 0; index < a.length; index++) {
|
|
922
|
+
if (a[index] != b[index]) {
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// This method returns a number between 1 and 100 representing
|
|
931
|
+
|
|
932
|
+
// the % of the group's overall space this panel should occupy.
|
|
933
|
+
function computePanelFlexBoxStyle({
|
|
934
|
+
dragState,
|
|
935
|
+
layout,
|
|
936
|
+
panelData,
|
|
937
|
+
panelIndex,
|
|
938
|
+
precision = 3
|
|
939
|
+
}) {
|
|
940
|
+
const size = layout[panelIndex];
|
|
941
|
+
let flexGrow;
|
|
942
|
+
if (panelData.length === 1) {
|
|
943
|
+
flexGrow = "100";
|
|
944
|
+
} else if (size == null) {
|
|
945
|
+
flexGrow = "0";
|
|
946
|
+
} else {
|
|
947
|
+
flexGrow = size.toPrecision(precision);
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
flexBasis: 0,
|
|
951
|
+
flexGrow,
|
|
952
|
+
flexShrink: 1,
|
|
953
|
+
// Without this, Panel sizes may be unintentionally overridden by their content
|
|
954
|
+
overflow: "hidden",
|
|
955
|
+
// Disable pointer events inside of a panel during resize
|
|
956
|
+
// This avoid edge cases like nested iframes
|
|
957
|
+
pointerEvents: dragState !== null ? "none" : undefined
|
|
958
|
+
};
|
|
849
959
|
}
|
|
850
960
|
|
|
851
961
|
let currentState = null;
|
|
@@ -899,17 +1009,47 @@ function debounce(callback, durationMs = 10) {
|
|
|
899
1009
|
return callable;
|
|
900
1010
|
}
|
|
901
1011
|
|
|
1012
|
+
// PanelGroup might be rendering in a server-side environment where localStorage is not available
|
|
1013
|
+
// or on a browser with cookies/storage disabled.
|
|
1014
|
+
// In either case, this function avoids accessing localStorage until needed,
|
|
1015
|
+
// and avoids throwing user-visible errors.
|
|
1016
|
+
function initializeDefaultStorage(storageObject) {
|
|
1017
|
+
try {
|
|
1018
|
+
if (typeof localStorage !== "undefined") {
|
|
1019
|
+
// Bypass this check for future calls
|
|
1020
|
+
storageObject.getItem = name => {
|
|
1021
|
+
return localStorage.getItem(name);
|
|
1022
|
+
};
|
|
1023
|
+
storageObject.setItem = (name, value) => {
|
|
1024
|
+
localStorage.setItem(name, value);
|
|
1025
|
+
};
|
|
1026
|
+
} else {
|
|
1027
|
+
throw new Error("localStorage not supported in this environment");
|
|
1028
|
+
}
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
console.error(error);
|
|
1031
|
+
storageObject.getItem = () => null;
|
|
1032
|
+
storageObject.setItem = () => {};
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
902
1036
|
// Note that Panel ids might be user-provided (stable) or useId generated (non-deterministic)
|
|
903
1037
|
// so they should not be used as part of the serialization key.
|
|
904
|
-
// Using
|
|
1038
|
+
// Using the min/max size attributes should work well enough as a backup.
|
|
905
1039
|
// Pre-sorting by minSize allows remembering layouts even if panels are re-ordered/dragged.
|
|
906
1040
|
function getSerializationKey(panels) {
|
|
907
1041
|
return panels.map(panel => {
|
|
908
1042
|
const {
|
|
909
|
-
|
|
1043
|
+
constraints,
|
|
1044
|
+
id,
|
|
1045
|
+
idIsFromProps,
|
|
910
1046
|
order
|
|
911
|
-
} = panel
|
|
912
|
-
|
|
1047
|
+
} = panel;
|
|
1048
|
+
if (idIsFromProps) {
|
|
1049
|
+
return id;
|
|
1050
|
+
} else {
|
|
1051
|
+
return `${order}:${JSON.stringify(constraints)}`;
|
|
1052
|
+
}
|
|
913
1053
|
}).sort((a, b) => a.localeCompare(b)).join(",");
|
|
914
1054
|
}
|
|
915
1055
|
function loadSerializedPanelGroupState(autoSaveId, storage) {
|
|
@@ -927,8 +1067,9 @@ function loadSerializedPanelGroupState(autoSaveId, storage) {
|
|
|
927
1067
|
function loadPanelLayout(autoSaveId, panels, storage) {
|
|
928
1068
|
const state = loadSerializedPanelGroupState(autoSaveId, storage);
|
|
929
1069
|
if (state) {
|
|
1070
|
+
var _state$key;
|
|
930
1071
|
const key = getSerializationKey(panels);
|
|
931
|
-
return state[key]
|
|
1072
|
+
return (_state$key = state[key]) !== null && _state$key !== void 0 ? _state$key : null;
|
|
932
1073
|
}
|
|
933
1074
|
return null;
|
|
934
1075
|
}
|
|
@@ -943,31 +1084,68 @@ function savePanelGroupLayout(autoSaveId, panels, sizes, storage) {
|
|
|
943
1084
|
}
|
|
944
1085
|
}
|
|
945
1086
|
|
|
946
|
-
|
|
1087
|
+
function shouldMonitorPixelBasedConstraints(constraints) {
|
|
1088
|
+
return constraints.some(constraints => {
|
|
1089
|
+
return constraints.collapsedSizePixels !== undefined || constraints.maxSizePixels !== undefined || constraints.minSizePixels !== undefined;
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
947
1092
|
|
|
948
|
-
//
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1093
|
+
// All units must be in percentages; pixel values should be pre-converted
|
|
1094
|
+
function validatePanelGroupLayout({
|
|
1095
|
+
groupSizePixels,
|
|
1096
|
+
layout: prevLayout,
|
|
1097
|
+
panelConstraints
|
|
1098
|
+
}) {
|
|
1099
|
+
const nextLayout = [...prevLayout];
|
|
1100
|
+
|
|
1101
|
+
// Validate layout expectations
|
|
1102
|
+
if (nextLayout.length !== panelConstraints.length) {
|
|
1103
|
+
throw Error(`Invalid ${panelConstraints.length} panel layout: ${nextLayout.map(size => `${size}%`).join(", ")}`);
|
|
1104
|
+
} else if (!fuzzyNumbersEqual(nextLayout.reduce((accumulated, current) => accumulated + current, 0), 100)) ;
|
|
1105
|
+
let remainingSize = 0;
|
|
1106
|
+
|
|
1107
|
+
// First pass: Validate the proposed layout given each panel's constraints
|
|
1108
|
+
for (let index = 0; index < panelConstraints.length; index++) {
|
|
1109
|
+
const unsafeSize = nextLayout[index];
|
|
1110
|
+
const safeSize = resizePanel({
|
|
1111
|
+
groupSizePixels,
|
|
1112
|
+
panelConstraints,
|
|
1113
|
+
panelIndex: index,
|
|
1114
|
+
size: unsafeSize
|
|
1115
|
+
});
|
|
1116
|
+
if (unsafeSize != safeSize) {
|
|
1117
|
+
remainingSize += unsafeSize - safeSize;
|
|
1118
|
+
nextLayout[index] = safeSize;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// If there is additional, left over space, assign it to any panel(s) that permits it
|
|
1123
|
+
// (It's not worth taking multiple additional passes to evenly distribute)
|
|
1124
|
+
if (!fuzzyNumbersEqual(remainingSize, 0)) {
|
|
1125
|
+
for (let index = 0; index < panelConstraints.length; index++) {
|
|
1126
|
+
const prevSize = nextLayout[index];
|
|
1127
|
+
const unsafeSize = prevSize + remainingSize;
|
|
1128
|
+
const safeSize = resizePanel({
|
|
1129
|
+
groupSizePixels,
|
|
1130
|
+
panelConstraints,
|
|
1131
|
+
panelIndex: index,
|
|
1132
|
+
size: unsafeSize
|
|
1133
|
+
});
|
|
1134
|
+
if (prevSize !== safeSize) {
|
|
1135
|
+
remainingSize -= safeSize - prevSize;
|
|
1136
|
+
nextLayout[index] = safeSize;
|
|
1137
|
+
|
|
1138
|
+
// Once we've used up the remainder, bail
|
|
1139
|
+
if (fuzzyNumbersEqual(remainingSize, 0)) {
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
964
1143
|
}
|
|
965
|
-
} catch (error) {
|
|
966
|
-
console.error(error);
|
|
967
|
-
storageObject.getItem = () => null;
|
|
968
|
-
storageObject.setItem = () => {};
|
|
969
1144
|
}
|
|
1145
|
+
return nextLayout;
|
|
970
1146
|
}
|
|
1147
|
+
|
|
1148
|
+
const LOCAL_STORAGE_DEBOUNCE_INTERVAL = 100;
|
|
971
1149
|
const defaultStorage = {
|
|
972
1150
|
getItem: name => {
|
|
973
1151
|
initializeDefaultStorage(defaultStorage);
|
|
@@ -978,150 +1156,113 @@ const defaultStorage = {
|
|
|
978
1156
|
defaultStorage.setItem(name, value);
|
|
979
1157
|
}
|
|
980
1158
|
};
|
|
981
|
-
|
|
982
|
-
// Initial drag state serves a few purposes:
|
|
983
|
-
// * dragOffset:
|
|
984
|
-
// Resize is calculated by the distance between the current pointer event and the resize handle being "dragged"
|
|
985
|
-
// This value accounts for the initial offset when the touch/click starts, so the handle doesn't appear to "jump"
|
|
986
|
-
// * dragHandleRect, sizes:
|
|
987
|
-
// When resizing is done via mouse/touch event– some initial state is stored
|
|
988
|
-
// so that any panels that contract will also expand if drag direction is reversed.
|
|
1159
|
+
const debounceMap = {};
|
|
989
1160
|
function PanelGroupWithForwardedRef({
|
|
990
1161
|
autoSaveId,
|
|
991
|
-
children
|
|
1162
|
+
children,
|
|
992
1163
|
className: classNameFromProps = "",
|
|
993
1164
|
direction,
|
|
994
|
-
disablePointerEventsDuringResize = false,
|
|
995
1165
|
forwardedRef,
|
|
996
|
-
id: idFromProps
|
|
997
|
-
onLayout,
|
|
1166
|
+
id: idFromProps,
|
|
1167
|
+
onLayout = null,
|
|
1168
|
+
keyboardResizeByPercentage = null,
|
|
1169
|
+
keyboardResizeByPixels = null,
|
|
998
1170
|
storage = defaultStorage,
|
|
999
|
-
style: styleFromProps
|
|
1000
|
-
tagName: Type = "div"
|
|
1001
|
-
units = "percentages"
|
|
1171
|
+
style: styleFromProps,
|
|
1172
|
+
tagName: Type = "div"
|
|
1002
1173
|
}) {
|
|
1003
1174
|
const groupId = useUniqueId(idFromProps);
|
|
1004
|
-
const [
|
|
1005
|
-
const [
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
// This has the benefit of causing force-collapsed panels to spring back open if drag is reversed.
|
|
1010
|
-
const initialDragStateRef = useRef(null);
|
|
1011
|
-
useRef({
|
|
1012
|
-
didLogDefaultSizeWarning: false,
|
|
1013
|
-
didLogIdAndOrderWarning: false,
|
|
1014
|
-
didLogInvalidLayoutWarning: false,
|
|
1015
|
-
prevPanelIds: []
|
|
1016
|
-
});
|
|
1017
|
-
|
|
1018
|
-
// Use a ref to guard against users passing inline props
|
|
1019
|
-
const callbacksRef = useRef({
|
|
1020
|
-
onLayout
|
|
1021
|
-
});
|
|
1022
|
-
useEffect(() => {
|
|
1023
|
-
callbacksRef.current.onLayout = onLayout;
|
|
1024
|
-
});
|
|
1025
|
-
const panelIdToLastNotifiedSizeMapRef = useRef({});
|
|
1026
|
-
|
|
1027
|
-
// 0-1 values representing the relative size of each panel.
|
|
1028
|
-
const [sizes, setSizes] = useState([]);
|
|
1029
|
-
|
|
1030
|
-
// Used to support imperative collapse/expand API.
|
|
1031
|
-
const panelSizeBeforeCollapse = useRef(new Map());
|
|
1175
|
+
const [dragState, setDragState] = useState(null);
|
|
1176
|
+
const [layout, setLayout] = useState([]);
|
|
1177
|
+
const [panelDataArray, setPanelDataArray] = useState([]);
|
|
1178
|
+
const panelIdToLastNotifiedMixedSizesMapRef = useRef({});
|
|
1179
|
+
const panelSizeBeforeCollapseRef = useRef(new Map());
|
|
1032
1180
|
const prevDeltaRef = useRef(0);
|
|
1033
|
-
|
|
1034
|
-
// Store committed values to avoid unnecessarily re-running memoization/effects functions.
|
|
1035
1181
|
const committedValuesRef = useRef({
|
|
1036
1182
|
direction,
|
|
1183
|
+
dragState,
|
|
1037
1184
|
id: groupId,
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1185
|
+
keyboardResizeByPercentage,
|
|
1186
|
+
keyboardResizeByPixels,
|
|
1187
|
+
layout,
|
|
1188
|
+
onLayout,
|
|
1189
|
+
panelDataArray
|
|
1190
|
+
});
|
|
1191
|
+
useRef({
|
|
1192
|
+
didLogIdAndOrderWarning: false,
|
|
1193
|
+
didLogPanelConstraintsWarning: false,
|
|
1194
|
+
prevPanelIds: []
|
|
1041
1195
|
});
|
|
1042
1196
|
useImperativeHandle(forwardedRef, () => ({
|
|
1043
|
-
getId: () =>
|
|
1044
|
-
getLayout:
|
|
1197
|
+
getId: () => committedValuesRef.current.id,
|
|
1198
|
+
getLayout: () => {
|
|
1045
1199
|
const {
|
|
1046
|
-
|
|
1047
|
-
|
|
1200
|
+
id: groupId,
|
|
1201
|
+
layout
|
|
1048
1202
|
} = committedValuesRef.current;
|
|
1049
|
-
const
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1203
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
1204
|
+
return layout.map(sizePercentage => {
|
|
1205
|
+
return {
|
|
1206
|
+
sizePercentage,
|
|
1207
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1208
|
+
};
|
|
1209
|
+
});
|
|
1056
1210
|
},
|
|
1057
|
-
setLayout:
|
|
1211
|
+
setLayout: mixedSizes => {
|
|
1058
1212
|
const {
|
|
1059
1213
|
id: groupId,
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1214
|
+
layout: prevLayout,
|
|
1215
|
+
onLayout,
|
|
1216
|
+
panelDataArray
|
|
1063
1217
|
} = committedValuesRef.current;
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const nextSizes = validatePanelGroupLayout({
|
|
1071
|
-
groupId,
|
|
1072
|
-
panels,
|
|
1073
|
-
nextSizes: sizes,
|
|
1074
|
-
prevSizes,
|
|
1075
|
-
units
|
|
1218
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
1219
|
+
const unsafeLayout = mixedSizes.map(mixedSize => getPercentageSizeFromMixedSizes(mixedSize, groupSizePixels));
|
|
1220
|
+
const safeLayout = validatePanelGroupLayout({
|
|
1221
|
+
groupSizePixels,
|
|
1222
|
+
layout: unsafeLayout,
|
|
1223
|
+
panelConstraints: panelDataArray.map(panelData => panelData.constraints)
|
|
1076
1224
|
});
|
|
1077
|
-
if (!areEqual(
|
|
1078
|
-
|
|
1079
|
-
|
|
1225
|
+
if (!areEqual(prevLayout, safeLayout)) {
|
|
1226
|
+
setLayout(safeLayout);
|
|
1227
|
+
if (onLayout) {
|
|
1228
|
+
onLayout(safeLayout.map(sizePercentage => ({
|
|
1229
|
+
sizePercentage,
|
|
1230
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1231
|
+
})));
|
|
1232
|
+
}
|
|
1233
|
+
callPanelCallbacks(groupId, panelDataArray, safeLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
|
|
1080
1234
|
}
|
|
1081
1235
|
}
|
|
1082
|
-
}), [
|
|
1236
|
+
}), []);
|
|
1083
1237
|
useIsomorphicLayoutEffect(() => {
|
|
1084
1238
|
committedValuesRef.current.direction = direction;
|
|
1239
|
+
committedValuesRef.current.dragState = dragState;
|
|
1085
1240
|
committedValuesRef.current.id = groupId;
|
|
1086
|
-
committedValuesRef.current.
|
|
1087
|
-
committedValuesRef.current.
|
|
1088
|
-
committedValuesRef.current.
|
|
1241
|
+
committedValuesRef.current.layout = layout;
|
|
1242
|
+
committedValuesRef.current.onLayout = onLayout;
|
|
1243
|
+
committedValuesRef.current.panelDataArray = panelDataArray;
|
|
1089
1244
|
});
|
|
1090
1245
|
useWindowSplitterPanelGroupBehavior({
|
|
1091
1246
|
committedValuesRef,
|
|
1092
1247
|
groupId,
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
panelSizeBeforeCollapse
|
|
1248
|
+
layout,
|
|
1249
|
+
panelDataArray,
|
|
1250
|
+
setLayout
|
|
1097
1251
|
});
|
|
1098
|
-
|
|
1099
|
-
// Notify external code when sizes have changed.
|
|
1100
1252
|
useEffect(() => {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
sizes
|
|
1107
|
-
} = committedValuesRef.current;
|
|
1253
|
+
// If this panel has been configured to persist sizing information, save sizes to local storage.
|
|
1254
|
+
if (autoSaveId) {
|
|
1255
|
+
if (layout.length === 0 || layout.length !== panelDataArray.length) {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1108
1258
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
onLayout(sizes);
|
|
1259
|
+
// Limit the frequency of localStorage updates.
|
|
1260
|
+
if (!debounceMap[autoSaveId]) {
|
|
1261
|
+
debounceMap[autoSaveId] = debounce(savePanelGroupLayout, LOCAL_STORAGE_DEBOUNCE_INTERVAL);
|
|
1113
1262
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
// When possible, we notify before the next render so that rendering work can be batched together.
|
|
1117
|
-
// Some cases are difficult to detect though,
|
|
1118
|
-
// for example– panels that are conditionally rendered can affect the size of neighboring panels.
|
|
1119
|
-
// In this case, the best we can do is notify on commit.
|
|
1120
|
-
// The callPanelCallbacks() uses its own memoization to avoid notifying panels twice in these cases.
|
|
1121
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
1122
|
-
callPanelCallbacks(panelsArray, sizes, panelIdToLastNotifiedSizeMap);
|
|
1263
|
+
debounceMap[autoSaveId](autoSaveId, panelDataArray, layout, storage);
|
|
1123
1264
|
}
|
|
1124
|
-
}, [
|
|
1265
|
+
}, [autoSaveId, layout, panelDataArray, storage]);
|
|
1125
1266
|
|
|
1126
1267
|
// Once all panels have registered themselves,
|
|
1127
1268
|
// Compute the initial sizes based on default weights.
|
|
@@ -1129,189 +1270,312 @@ function PanelGroupWithForwardedRef({
|
|
|
1129
1270
|
useIsomorphicLayoutEffect(() => {
|
|
1130
1271
|
const {
|
|
1131
1272
|
id: groupId,
|
|
1132
|
-
|
|
1133
|
-
|
|
1273
|
+
layout,
|
|
1274
|
+
onLayout
|
|
1134
1275
|
} = committedValuesRef.current;
|
|
1135
|
-
if (
|
|
1136
|
-
// Only compute (or restore) default
|
|
1276
|
+
if (layout.length === panelDataArray.length) {
|
|
1277
|
+
// Only compute (or restore) default layout once per panel configuration.
|
|
1137
1278
|
return;
|
|
1138
1279
|
}
|
|
1139
1280
|
|
|
1140
1281
|
// If this panel has been configured to persist sizing information,
|
|
1141
1282
|
// default size should be restored from local storage if possible.
|
|
1142
|
-
let
|
|
1283
|
+
let unsafeLayout = null;
|
|
1143
1284
|
if (autoSaveId) {
|
|
1144
|
-
|
|
1145
|
-
defaultSizes = loadPanelLayout(autoSaveId, panelsArray, storage);
|
|
1285
|
+
unsafeLayout = loadPanelLayout(autoSaveId, panelDataArray, storage);
|
|
1146
1286
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
//
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
});
|
|
1157
|
-
setSizes(validatedSizes);
|
|
1158
|
-
} else {
|
|
1159
|
-
const sizes = calculateDefaultLayout({
|
|
1160
|
-
groupId,
|
|
1161
|
-
panels,
|
|
1162
|
-
units
|
|
1287
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
1288
|
+
if (groupSizePixels <= 0) {
|
|
1289
|
+
// Wait until the group has rendered a non-zero size before computing layout.
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
if (unsafeLayout == null) {
|
|
1293
|
+
unsafeLayout = calculateUnsafeDefaultLayout({
|
|
1294
|
+
groupSizePixels,
|
|
1295
|
+
panelDataArray
|
|
1163
1296
|
});
|
|
1164
|
-
setSizes(sizes);
|
|
1165
1297
|
}
|
|
1166
|
-
}, [autoSaveId, panels, storage]);
|
|
1167
|
-
useEffect(() => {
|
|
1168
|
-
// If this panel has been configured to persist sizing information, save sizes to local storage.
|
|
1169
|
-
if (autoSaveId) {
|
|
1170
|
-
if (sizes.length === 0 || sizes.length !== panels.size) {
|
|
1171
|
-
return;
|
|
1172
|
-
}
|
|
1173
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
1174
1298
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1299
|
+
// Validate even saved layouts in case something has changed since last render
|
|
1300
|
+
// e.g. for pixel groups, this could be the size of the window
|
|
1301
|
+
const validatedLayout = validatePanelGroupLayout({
|
|
1302
|
+
groupSizePixels,
|
|
1303
|
+
layout: unsafeLayout,
|
|
1304
|
+
panelConstraints: panelDataArray.map(panelData => panelData.constraints)
|
|
1305
|
+
});
|
|
1306
|
+
if (!areEqual(layout, validatedLayout)) {
|
|
1307
|
+
setLayout(validatedLayout);
|
|
1180
1308
|
}
|
|
1181
|
-
|
|
1309
|
+
if (onLayout) {
|
|
1310
|
+
onLayout(validatedLayout.map(sizePercentage => ({
|
|
1311
|
+
sizePercentage,
|
|
1312
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1313
|
+
})));
|
|
1314
|
+
}
|
|
1315
|
+
callPanelCallbacks(groupId, panelDataArray, validatedLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
|
|
1316
|
+
}, [autoSaveId, layout, panelDataArray, storage]);
|
|
1182
1317
|
useIsomorphicLayoutEffect(() => {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1318
|
+
const constraints = panelDataArray.map(({
|
|
1319
|
+
constraints
|
|
1320
|
+
}) => constraints);
|
|
1321
|
+
if (!shouldMonitorPixelBasedConstraints(constraints)) {
|
|
1322
|
+
// Avoid the overhead of ResizeObserver if no pixel constraints require monitoring
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
if (typeof ResizeObserver === "undefined") {
|
|
1326
|
+
console.warn(`WARNING: Pixel based constraints require ResizeObserver but it is not supported by the current browser.`);
|
|
1327
|
+
} else {
|
|
1186
1328
|
const resizeObserver = new ResizeObserver(() => {
|
|
1329
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
1187
1330
|
const {
|
|
1188
|
-
|
|
1189
|
-
|
|
1331
|
+
layout: prevLayout,
|
|
1332
|
+
onLayout
|
|
1190
1333
|
} = committedValuesRef.current;
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
prevSizes,
|
|
1196
|
-
units
|
|
1334
|
+
const nextLayout = validatePanelGroupLayout({
|
|
1335
|
+
groupSizePixels,
|
|
1336
|
+
layout: prevLayout,
|
|
1337
|
+
panelConstraints: panelDataArray.map(panelData => panelData.constraints)
|
|
1197
1338
|
});
|
|
1198
|
-
if (!areEqual(
|
|
1199
|
-
|
|
1339
|
+
if (!areEqual(prevLayout, nextLayout)) {
|
|
1340
|
+
setLayout(nextLayout);
|
|
1341
|
+
if (onLayout) {
|
|
1342
|
+
onLayout(nextLayout.map(sizePercentage => ({
|
|
1343
|
+
sizePercentage,
|
|
1344
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1345
|
+
})));
|
|
1346
|
+
}
|
|
1347
|
+
callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
|
|
1200
1348
|
}
|
|
1201
1349
|
});
|
|
1202
|
-
resizeObserver.observe(
|
|
1350
|
+
resizeObserver.observe(getPanelGroupElement(groupId));
|
|
1203
1351
|
return () => {
|
|
1204
1352
|
resizeObserver.disconnect();
|
|
1205
1353
|
};
|
|
1206
1354
|
}
|
|
1207
|
-
}, [groupId,
|
|
1208
|
-
|
|
1355
|
+
}, [groupId, panelDataArray]);
|
|
1356
|
+
|
|
1357
|
+
// DEV warnings
|
|
1358
|
+
useEffect(() => {
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
// External APIs are safe to memoize via committed values ref
|
|
1362
|
+
const collapsePanel = useCallback(panelData => {
|
|
1209
1363
|
const {
|
|
1210
|
-
|
|
1211
|
-
|
|
1364
|
+
layout: prevLayout,
|
|
1365
|
+
onLayout,
|
|
1366
|
+
panelDataArray
|
|
1212
1367
|
} = committedValuesRef.current;
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1368
|
+
if (panelData.constraints.collapsible) {
|
|
1369
|
+
const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
|
|
1370
|
+
const {
|
|
1371
|
+
collapsedSizePercentage,
|
|
1372
|
+
panelSizePercentage,
|
|
1373
|
+
pivotIndices,
|
|
1374
|
+
groupSizePixels
|
|
1375
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, prevLayout);
|
|
1376
|
+
if (panelSizePercentage !== collapsedSizePercentage) {
|
|
1377
|
+
// Store size before collapse;
|
|
1378
|
+
// This is the size that gets restored if the expand() API is used.
|
|
1379
|
+
panelSizeBeforeCollapseRef.current.set(panelData.id, panelSizePercentage);
|
|
1380
|
+
const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
|
|
1381
|
+
const delta = isLastPanel ? panelSizePercentage - collapsedSizePercentage : collapsedSizePercentage - panelSizePercentage;
|
|
1382
|
+
const nextLayout = adjustLayoutByDelta({
|
|
1383
|
+
delta,
|
|
1384
|
+
groupSizePixels,
|
|
1385
|
+
layout: prevLayout,
|
|
1386
|
+
panelConstraints: panelConstraintsArray,
|
|
1387
|
+
pivotIndices,
|
|
1388
|
+
trigger: "imperative-api"
|
|
1389
|
+
});
|
|
1390
|
+
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
1391
|
+
setLayout(nextLayout);
|
|
1392
|
+
if (onLayout) {
|
|
1393
|
+
onLayout(nextLayout.map(sizePercentage => ({
|
|
1394
|
+
sizePercentage,
|
|
1395
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1396
|
+
})));
|
|
1397
|
+
}
|
|
1398
|
+
callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1222
1401
|
}
|
|
1223
|
-
}, [groupId
|
|
1224
|
-
|
|
1402
|
+
}, [groupId]);
|
|
1403
|
+
|
|
1404
|
+
// External APIs are safe to memoize via committed values ref
|
|
1405
|
+
const expandPanel = useCallback(panelData => {
|
|
1225
1406
|
const {
|
|
1226
|
-
|
|
1407
|
+
layout: prevLayout,
|
|
1408
|
+
onLayout,
|
|
1409
|
+
panelDataArray
|
|
1227
1410
|
} = committedValuesRef.current;
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1411
|
+
if (panelData.constraints.collapsible) {
|
|
1412
|
+
const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
|
|
1413
|
+
const {
|
|
1414
|
+
collapsedSizePercentage,
|
|
1415
|
+
panelSizePercentage,
|
|
1416
|
+
minSizePercentage,
|
|
1417
|
+
pivotIndices,
|
|
1418
|
+
groupSizePixels
|
|
1419
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, prevLayout);
|
|
1420
|
+
if (panelSizePercentage === collapsedSizePercentage) {
|
|
1421
|
+
// Restore this panel to the size it was before it was collapsed, if possible.
|
|
1422
|
+
const prevPanelSizePercentage = panelSizeBeforeCollapseRef.current.get(panelData.id);
|
|
1423
|
+
const baseSizePercentage = prevPanelSizePercentage != null ? prevPanelSizePercentage : minSizePercentage;
|
|
1424
|
+
const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
|
|
1425
|
+
const delta = isLastPanel ? panelSizePercentage - baseSizePercentage : baseSizePercentage - panelSizePercentage;
|
|
1426
|
+
const nextLayout = adjustLayoutByDelta({
|
|
1427
|
+
delta,
|
|
1428
|
+
groupSizePixels,
|
|
1429
|
+
layout: prevLayout,
|
|
1430
|
+
panelConstraints: panelConstraintsArray,
|
|
1431
|
+
pivotIndices,
|
|
1432
|
+
trigger: "imperative-api"
|
|
1433
|
+
});
|
|
1434
|
+
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
1435
|
+
setLayout(nextLayout);
|
|
1436
|
+
if (onLayout) {
|
|
1437
|
+
onLayout(nextLayout.map(sizePercentage => ({
|
|
1438
|
+
sizePercentage,
|
|
1439
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1440
|
+
})));
|
|
1441
|
+
}
|
|
1442
|
+
callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1240
1445
|
}
|
|
1241
|
-
|
|
1446
|
+
}, [groupId]);
|
|
1447
|
+
|
|
1448
|
+
// External APIs are safe to memoize via committed values ref
|
|
1449
|
+
const getPanelSize = useCallback(panelData => {
|
|
1450
|
+
const {
|
|
1451
|
+
layout,
|
|
1452
|
+
panelDataArray
|
|
1453
|
+
} = committedValuesRef.current;
|
|
1454
|
+
const {
|
|
1455
|
+
panelSizePercentage,
|
|
1456
|
+
panelSizePixels
|
|
1457
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, layout);
|
|
1242
1458
|
return {
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
flexShrink: 1,
|
|
1246
|
-
// Without this, Panel sizes may be unintentionally overridden by their content.
|
|
1247
|
-
overflow: "hidden",
|
|
1248
|
-
// Disable pointer events inside of a panel during resize.
|
|
1249
|
-
// This avoid edge cases like nested iframes.
|
|
1250
|
-
pointerEvents: disablePointerEventsDuringResize && activeHandleId !== null ? "none" : undefined
|
|
1459
|
+
sizePercentage: panelSizePercentage,
|
|
1460
|
+
sizePixels: panelSizePixels
|
|
1251
1461
|
};
|
|
1252
|
-
}, [
|
|
1253
|
-
|
|
1462
|
+
}, [groupId]);
|
|
1463
|
+
|
|
1464
|
+
// This API should never read from committedValuesRef
|
|
1465
|
+
const getPanelStyle = useCallback(panelData => {
|
|
1466
|
+
const panelIndex = panelDataArray.indexOf(panelData);
|
|
1467
|
+
return computePanelFlexBoxStyle({
|
|
1468
|
+
dragState,
|
|
1469
|
+
layout,
|
|
1470
|
+
panelData: panelDataArray,
|
|
1471
|
+
panelIndex
|
|
1472
|
+
});
|
|
1473
|
+
}, [dragState, layout, panelDataArray]);
|
|
1474
|
+
|
|
1475
|
+
// External APIs are safe to memoize via committed values ref
|
|
1476
|
+
const isPanelCollapsed = useCallback(panelData => {
|
|
1254
1477
|
const {
|
|
1255
|
-
|
|
1478
|
+
layout,
|
|
1479
|
+
panelDataArray
|
|
1256
1480
|
} = committedValuesRef.current;
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1481
|
+
const {
|
|
1482
|
+
collapsedSizePercentage,
|
|
1483
|
+
collapsible,
|
|
1484
|
+
panelSizePercentage
|
|
1485
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, layout);
|
|
1486
|
+
return collapsible === true && panelSizePercentage === collapsedSizePercentage;
|
|
1487
|
+
}, [groupId]);
|
|
1488
|
+
|
|
1489
|
+
// External APIs are safe to memoize via committed values ref
|
|
1490
|
+
const isPanelExpanded = useCallback(panelData => {
|
|
1491
|
+
const {
|
|
1492
|
+
layout,
|
|
1493
|
+
panelDataArray
|
|
1494
|
+
} = committedValuesRef.current;
|
|
1495
|
+
const {
|
|
1496
|
+
collapsedSizePercentage,
|
|
1497
|
+
collapsible,
|
|
1498
|
+
panelSizePercentage
|
|
1499
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, layout);
|
|
1500
|
+
return !collapsible || panelSizePercentage > collapsedSizePercentage;
|
|
1501
|
+
}, [groupId]);
|
|
1502
|
+
const registerPanel = useCallback(panelData => {
|
|
1503
|
+
setPanelDataArray(prevPanelDataArray => {
|
|
1504
|
+
const nextPanelDataArray = [...prevPanelDataArray, panelData];
|
|
1505
|
+
return nextPanelDataArray.sort((panelA, panelB) => {
|
|
1506
|
+
const orderA = panelA.order;
|
|
1507
|
+
const orderB = panelB.order;
|
|
1508
|
+
if (orderA == null && orderB == null) {
|
|
1509
|
+
return 0;
|
|
1510
|
+
} else if (orderA == null) {
|
|
1511
|
+
return -1;
|
|
1512
|
+
} else if (orderB == null) {
|
|
1513
|
+
return 1;
|
|
1514
|
+
} else {
|
|
1515
|
+
return orderA - orderB;
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1265
1518
|
});
|
|
1266
1519
|
}, []);
|
|
1267
|
-
const registerResizeHandle = useCallback(
|
|
1268
|
-
|
|
1520
|
+
const registerResizeHandle = useCallback(dragHandleId => {
|
|
1521
|
+
return function resizeHandler(event) {
|
|
1269
1522
|
event.preventDefault();
|
|
1270
1523
|
const {
|
|
1271
1524
|
direction,
|
|
1272
|
-
|
|
1273
|
-
|
|
1525
|
+
dragState,
|
|
1526
|
+
id: groupId,
|
|
1527
|
+
keyboardResizeByPercentage,
|
|
1528
|
+
keyboardResizeByPixels,
|
|
1529
|
+
onLayout,
|
|
1530
|
+
panelDataArray,
|
|
1531
|
+
layout: prevLayout
|
|
1274
1532
|
} = committedValuesRef.current;
|
|
1275
|
-
const
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1533
|
+
const {
|
|
1534
|
+
initialLayout
|
|
1535
|
+
} = dragState !== null && dragState !== void 0 ? dragState : {};
|
|
1536
|
+
const pivotIndices = determinePivotIndices(groupId, dragHandleId);
|
|
1537
|
+
let delta = calculateDeltaPercentage(event, groupId, dragHandleId, direction, dragState, {
|
|
1538
|
+
percentage: keyboardResizeByPercentage,
|
|
1539
|
+
pixels: keyboardResizeByPixels
|
|
1540
|
+
});
|
|
1541
|
+
if (delta === 0) {
|
|
1282
1542
|
return;
|
|
1283
1543
|
}
|
|
1284
|
-
const groupElement = getPanelGroup(groupId);
|
|
1285
|
-
const rect = groupElement.getBoundingClientRect();
|
|
1286
|
-
const isHorizontal = direction === "horizontal";
|
|
1287
1544
|
|
|
1288
1545
|
// Support RTL layouts
|
|
1546
|
+
const isHorizontal = direction === "horizontal";
|
|
1289
1547
|
if (document.dir === "rtl" && isHorizontal) {
|
|
1290
|
-
|
|
1548
|
+
delta = -delta;
|
|
1291
1549
|
}
|
|
1292
|
-
const
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1550
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
1551
|
+
const panelConstraints = panelDataArray.map(panelData => panelData.constraints);
|
|
1552
|
+
const nextLayout = adjustLayoutByDelta({
|
|
1553
|
+
delta,
|
|
1554
|
+
groupSizePixels,
|
|
1555
|
+
layout: initialLayout !== null && initialLayout !== void 0 ? initialLayout : prevLayout,
|
|
1556
|
+
panelConstraints,
|
|
1557
|
+
pivotIndices,
|
|
1558
|
+
trigger: isKeyDown(event) ? "keyboard" : "mouse-or-touch"
|
|
1559
|
+
});
|
|
1560
|
+
const layoutChanged = !compareLayouts(prevLayout, nextLayout);
|
|
1299
1561
|
|
|
1300
|
-
//
|
|
1562
|
+
// Only update the cursor for layout changes triggered by touch/mouse events (not keyboard)
|
|
1563
|
+
// Update the cursor even if the layout hasn't changed (we may need to show an invalid cursor state)
|
|
1301
1564
|
if (isMouseEvent(event) || isTouchEvent(event)) {
|
|
1302
1565
|
// Watch for multiple subsequent deltas; this might occur for tiny cursor movements.
|
|
1303
1566
|
// In this case, Panel sizes might not change–
|
|
1304
1567
|
// but updating cursor in this scenario would cause a flicker.
|
|
1305
1568
|
if (prevDeltaRef.current != delta) {
|
|
1306
|
-
|
|
1569
|
+
prevDeltaRef.current = delta;
|
|
1570
|
+
if (!layoutChanged) {
|
|
1307
1571
|
// If the pointer has moved too far to resize the panel any further,
|
|
1308
1572
|
// update the cursor style for a visual clue.
|
|
1309
1573
|
// This mimics VS Code behavior.
|
|
1310
1574
|
|
|
1311
1575
|
if (isHorizontal) {
|
|
1312
|
-
setGlobalCursorStyle(
|
|
1576
|
+
setGlobalCursorStyle(delta < 0 ? "horizontal-min" : "horizontal-max");
|
|
1313
1577
|
} else {
|
|
1314
|
-
setGlobalCursorStyle(
|
|
1578
|
+
setGlobalCursorStyle(delta < 0 ? "vertical-min" : "vertical-max");
|
|
1315
1579
|
}
|
|
1316
1580
|
} else {
|
|
1317
1581
|
// Reset the cursor style to the the normal resize cursor.
|
|
@@ -1319,202 +1583,100 @@ function PanelGroupWithForwardedRef({
|
|
|
1319
1583
|
}
|
|
1320
1584
|
}
|
|
1321
1585
|
}
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
callPanelCallbacks(
|
|
1586
|
+
if (layoutChanged) {
|
|
1587
|
+
setLayout(nextLayout);
|
|
1588
|
+
if (onLayout) {
|
|
1589
|
+
onLayout(nextLayout.map(sizePercentage => ({
|
|
1590
|
+
sizePercentage,
|
|
1591
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1592
|
+
})));
|
|
1593
|
+
}
|
|
1594
|
+
callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
|
|
1331
1595
|
}
|
|
1332
|
-
prevDeltaRef.current = delta;
|
|
1333
1596
|
};
|
|
1334
|
-
return resizeHandler;
|
|
1335
|
-
}, [groupId]);
|
|
1336
|
-
const unregisterPanel = useCallback(id => {
|
|
1337
|
-
setPanels(prevPanels => {
|
|
1338
|
-
if (!prevPanels.has(id)) {
|
|
1339
|
-
return prevPanels;
|
|
1340
|
-
}
|
|
1341
|
-
const nextPanels = new Map(prevPanels);
|
|
1342
|
-
nextPanels.delete(id);
|
|
1343
|
-
return nextPanels;
|
|
1344
|
-
});
|
|
1345
1597
|
}, []);
|
|
1346
|
-
|
|
1598
|
+
|
|
1599
|
+
// External APIs are safe to memoize via committed values ref
|
|
1600
|
+
const resizePanel = useCallback((panelData, mixedSizes) => {
|
|
1347
1601
|
const {
|
|
1348
|
-
|
|
1349
|
-
|
|
1602
|
+
layout: prevLayout,
|
|
1603
|
+
onLayout,
|
|
1604
|
+
panelDataArray
|
|
1350
1605
|
} = committedValuesRef.current;
|
|
1351
|
-
const
|
|
1352
|
-
if (panel == null) {
|
|
1353
|
-
return;
|
|
1354
|
-
}
|
|
1606
|
+
const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
|
|
1355
1607
|
const {
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
const
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
if (prevSizes !== nextSizes) {
|
|
1381
|
-
const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
|
|
1382
|
-
setSizes(nextSizes);
|
|
1383
|
-
|
|
1384
|
-
// If resize change handlers have been declared, this is the time to call them.
|
|
1385
|
-
// Trigger user callbacks after updating state, so that user code can override the sizes.
|
|
1386
|
-
callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
|
|
1608
|
+
groupSizePixels,
|
|
1609
|
+
panelSizePercentage,
|
|
1610
|
+
pivotIndices
|
|
1611
|
+
} = panelDataHelper(groupId, panelDataArray, panelData, prevLayout);
|
|
1612
|
+
const sizePercentage = getPercentageSizeFromMixedSizes(mixedSizes, groupSizePixels);
|
|
1613
|
+
const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
|
|
1614
|
+
const delta = isLastPanel ? panelSizePercentage - sizePercentage : sizePercentage - panelSizePercentage;
|
|
1615
|
+
const nextLayout = adjustLayoutByDelta({
|
|
1616
|
+
delta,
|
|
1617
|
+
groupSizePixels,
|
|
1618
|
+
layout: prevLayout,
|
|
1619
|
+
panelConstraints: panelConstraintsArray,
|
|
1620
|
+
pivotIndices,
|
|
1621
|
+
trigger: "imperative-api"
|
|
1622
|
+
});
|
|
1623
|
+
if (!compareLayouts(prevLayout, nextLayout)) {
|
|
1624
|
+
setLayout(nextLayout);
|
|
1625
|
+
if (onLayout) {
|
|
1626
|
+
onLayout(nextLayout.map(sizePercentage => ({
|
|
1627
|
+
sizePercentage,
|
|
1628
|
+
sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels)
|
|
1629
|
+
})));
|
|
1630
|
+
}
|
|
1631
|
+
callPanelCallbacks(groupId, panelDataArray, nextLayout, panelIdToLastNotifiedMixedSizesMapRef.current);
|
|
1387
1632
|
}
|
|
1388
|
-
}, []);
|
|
1389
|
-
const
|
|
1633
|
+
}, [groupId]);
|
|
1634
|
+
const startDragging = useCallback((dragHandleId, event) => {
|
|
1390
1635
|
const {
|
|
1391
|
-
|
|
1392
|
-
|
|
1636
|
+
direction,
|
|
1637
|
+
layout
|
|
1393
1638
|
} = committedValuesRef.current;
|
|
1394
|
-
const
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
}
|
|
1402
|
-
const sizeBeforeCollapse = panelSizeBeforeCollapse.current.get(id) || minSize;
|
|
1403
|
-
if (!sizeBeforeCollapse) {
|
|
1404
|
-
return;
|
|
1405
|
-
}
|
|
1406
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
1407
|
-
const index = panelsArray.indexOf(panel);
|
|
1408
|
-
if (index < 0) {
|
|
1409
|
-
return;
|
|
1410
|
-
}
|
|
1411
|
-
const currentSize = prevSizes[index];
|
|
1412
|
-
if (currentSize !== collapsedSize) {
|
|
1413
|
-
// Panel is already expanded.
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
|
|
1417
|
-
if (idBefore == null || idAfter == null) {
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
const isLastPanel = index === panelsArray.length - 1;
|
|
1421
|
-
const delta = isLastPanel ? collapsedSize - sizeBeforeCollapse : sizeBeforeCollapse;
|
|
1422
|
-
const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
|
|
1423
|
-
if (prevSizes !== nextSizes) {
|
|
1424
|
-
const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
|
|
1425
|
-
setSizes(nextSizes);
|
|
1426
|
-
|
|
1427
|
-
// If resize change handlers have been declared, this is the time to call them.
|
|
1428
|
-
// Trigger user callbacks after updating state, so that user code can override the sizes.
|
|
1429
|
-
callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
|
|
1430
|
-
}
|
|
1639
|
+
const handleElement = getResizeHandleElement(dragHandleId);
|
|
1640
|
+
const initialCursorPosition = getResizeEventCursorPosition(direction, event);
|
|
1641
|
+
setDragState({
|
|
1642
|
+
dragHandleId,
|
|
1643
|
+
dragHandleRect: handleElement.getBoundingClientRect(),
|
|
1644
|
+
initialCursorPosition,
|
|
1645
|
+
initialLayout: layout
|
|
1646
|
+
});
|
|
1431
1647
|
}, []);
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
const panel = panels.get(id);
|
|
1444
|
-
if (panel == null) {
|
|
1445
|
-
return;
|
|
1446
|
-
}
|
|
1447
|
-
let {
|
|
1448
|
-
collapsedSize,
|
|
1449
|
-
collapsible,
|
|
1450
|
-
maxSize,
|
|
1451
|
-
minSize
|
|
1452
|
-
} = panel.current;
|
|
1453
|
-
if (units === "pixels") {
|
|
1454
|
-
const groupSizePixels = getAvailableGroupSizePixels(groupId);
|
|
1455
|
-
minSize = minSize / groupSizePixels * 100;
|
|
1456
|
-
if (maxSize != null) {
|
|
1457
|
-
maxSize = maxSize / groupSizePixels * 100;
|
|
1648
|
+
const stopDragging = useCallback(() => {
|
|
1649
|
+
resetGlobalCursorStyle();
|
|
1650
|
+
setDragState(null);
|
|
1651
|
+
}, []);
|
|
1652
|
+
const unregisterPanel = useCallback(panelData => {
|
|
1653
|
+
delete panelIdToLastNotifiedMixedSizesMapRef.current[panelData.id];
|
|
1654
|
+
setPanelDataArray(panelDataArray => {
|
|
1655
|
+
const index = panelDataArray.indexOf(panelData);
|
|
1656
|
+
if (index >= 0) {
|
|
1657
|
+
panelDataArray = [...panelDataArray];
|
|
1658
|
+
panelDataArray.splice(index, 1);
|
|
1458
1659
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
const index = panelsArray.indexOf(panel);
|
|
1462
|
-
if (index < 0) {
|
|
1463
|
-
return;
|
|
1464
|
-
}
|
|
1465
|
-
const currentSize = prevSizes[index];
|
|
1466
|
-
if (currentSize === nextSize) {
|
|
1467
|
-
return;
|
|
1468
|
-
}
|
|
1469
|
-
if (collapsible && nextSize === collapsedSize) ; else {
|
|
1470
|
-
nextSize = Math.min(maxSize != null ? maxSize : 100, Math.max(minSize, nextSize));
|
|
1471
|
-
}
|
|
1472
|
-
const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
|
|
1473
|
-
if (idBefore == null || idAfter == null) {
|
|
1474
|
-
return;
|
|
1475
|
-
}
|
|
1476
|
-
const isLastPanel = index === panelsArray.length - 1;
|
|
1477
|
-
const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
|
|
1478
|
-
const nextSizes = adjustByDelta(null, committedValuesRef.current, idBefore, idAfter, delta, prevSizes, panelSizeBeforeCollapse.current, null);
|
|
1479
|
-
if (prevSizes !== nextSizes) {
|
|
1480
|
-
const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
|
|
1481
|
-
setSizes(nextSizes);
|
|
1482
|
-
|
|
1483
|
-
// If resize change handlers have been declared, this is the time to call them.
|
|
1484
|
-
// Trigger user callbacks after updating state, so that user code can override the sizes.
|
|
1485
|
-
callPanelCallbacks(panelsArray, nextSizes, panelIdToLastNotifiedSizeMap);
|
|
1486
|
-
}
|
|
1660
|
+
return panelDataArray;
|
|
1661
|
+
});
|
|
1487
1662
|
}, []);
|
|
1488
1663
|
const context = useMemo(() => ({
|
|
1489
|
-
activeHandleId,
|
|
1490
1664
|
collapsePanel,
|
|
1491
1665
|
direction,
|
|
1666
|
+
dragState,
|
|
1492
1667
|
expandPanel,
|
|
1493
1668
|
getPanelSize,
|
|
1494
1669
|
getPanelStyle,
|
|
1495
1670
|
groupId,
|
|
1671
|
+
isPanelCollapsed,
|
|
1672
|
+
isPanelExpanded,
|
|
1496
1673
|
registerPanel,
|
|
1497
1674
|
registerResizeHandle,
|
|
1498
1675
|
resizePanel,
|
|
1499
|
-
startDragging
|
|
1500
|
-
|
|
1501
|
-
if (isMouseEvent(event) || isTouchEvent(event)) {
|
|
1502
|
-
const handleElement = getResizeHandle(id);
|
|
1503
|
-
initialDragStateRef.current = {
|
|
1504
|
-
dragHandleRect: handleElement.getBoundingClientRect(),
|
|
1505
|
-
dragOffset: getDragOffset(event, id, direction),
|
|
1506
|
-
sizes: committedValuesRef.current.sizes
|
|
1507
|
-
};
|
|
1508
|
-
}
|
|
1509
|
-
},
|
|
1510
|
-
stopDragging: () => {
|
|
1511
|
-
resetGlobalCursorStyle();
|
|
1512
|
-
setActiveHandleId(null);
|
|
1513
|
-
initialDragStateRef.current = null;
|
|
1514
|
-
},
|
|
1515
|
-
units,
|
|
1676
|
+
startDragging,
|
|
1677
|
+
stopDragging,
|
|
1516
1678
|
unregisterPanel
|
|
1517
|
-
}), [
|
|
1679
|
+
}), [collapsePanel, dragState, direction, expandPanel, getPanelSize, getPanelStyle, groupId, isPanelCollapsed, isPanelExpanded, registerPanel, registerResizeHandle, resizePanel, startDragging, stopDragging, unregisterPanel]);
|
|
1518
1680
|
const style = {
|
|
1519
1681
|
display: "flex",
|
|
1520
1682
|
flexDirection: direction === "horizontal" ? "row" : "column",
|
|
@@ -1523,20 +1685,19 @@ function PanelGroupWithForwardedRef({
|
|
|
1523
1685
|
width: "100%"
|
|
1524
1686
|
};
|
|
1525
1687
|
return createElement(PanelGroupContext.Provider, {
|
|
1526
|
-
children: createElement(Type, {
|
|
1527
|
-
children,
|
|
1528
|
-
className: classNameFromProps,
|
|
1529
|
-
"data-panel-group": "",
|
|
1530
|
-
"data-panel-group-direction": direction,
|
|
1531
|
-
"data-panel-group-id": groupId,
|
|
1532
|
-
"data-panel-group-units": units,
|
|
1533
|
-
style: {
|
|
1534
|
-
...style,
|
|
1535
|
-
...styleFromProps
|
|
1536
|
-
}
|
|
1537
|
-
}),
|
|
1538
1688
|
value: context
|
|
1539
|
-
}
|
|
1689
|
+
}, createElement(Type, {
|
|
1690
|
+
children,
|
|
1691
|
+
className: classNameFromProps,
|
|
1692
|
+
style: {
|
|
1693
|
+
...style,
|
|
1694
|
+
...styleFromProps
|
|
1695
|
+
},
|
|
1696
|
+
// CSS selectors
|
|
1697
|
+
"data-panel-group": "",
|
|
1698
|
+
"data-panel-group-direction": direction,
|
|
1699
|
+
"data-panel-group-id": groupId
|
|
1700
|
+
}));
|
|
1540
1701
|
}
|
|
1541
1702
|
const PanelGroup = forwardRef((props, ref) => createElement(PanelGroupWithForwardedRef, {
|
|
1542
1703
|
...props,
|
|
@@ -1544,6 +1705,77 @@ const PanelGroup = forwardRef((props, ref) => createElement(PanelGroupWithForwar
|
|
|
1544
1705
|
}));
|
|
1545
1706
|
PanelGroupWithForwardedRef.displayName = "PanelGroup";
|
|
1546
1707
|
PanelGroup.displayName = "forwardRef(PanelGroup)";
|
|
1708
|
+
function panelDataHelper(groupId, panelDataArray, panelData, layout) {
|
|
1709
|
+
const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
|
|
1710
|
+
const panelIndex = panelDataArray.indexOf(panelData);
|
|
1711
|
+
const panelConstraints = panelConstraintsArray[panelIndex];
|
|
1712
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
1713
|
+
const percentagePanelConstraints = computePercentagePanelConstraints(panelConstraintsArray, panelIndex, groupSizePixels);
|
|
1714
|
+
const isLastPanel = panelIndex === panelDataArray.length - 1;
|
|
1715
|
+
const pivotIndices = isLastPanel ? [panelIndex - 1, panelIndex] : [panelIndex, panelIndex + 1];
|
|
1716
|
+
const panelSizePercentage = layout[panelIndex];
|
|
1717
|
+
const panelSizePixels = convertPercentageToPixels(panelSizePercentage, groupSizePixels);
|
|
1718
|
+
return {
|
|
1719
|
+
...percentagePanelConstraints,
|
|
1720
|
+
collapsible: panelConstraints.collapsible,
|
|
1721
|
+
panelSizePercentage,
|
|
1722
|
+
panelSizePixels,
|
|
1723
|
+
groupSizePixels,
|
|
1724
|
+
pivotIndices
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
|
|
1729
|
+
|
|
1730
|
+
function useWindowSplitterResizeHandlerBehavior({
|
|
1731
|
+
disabled,
|
|
1732
|
+
handleId,
|
|
1733
|
+
resizeHandler
|
|
1734
|
+
}) {
|
|
1735
|
+
useEffect(() => {
|
|
1736
|
+
if (disabled || resizeHandler == null) {
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
const handleElement = getResizeHandleElement(handleId);
|
|
1740
|
+
if (handleElement == null) {
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
const onKeyDown = event => {
|
|
1744
|
+
if (event.defaultPrevented) {
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
switch (event.key) {
|
|
1748
|
+
case "ArrowDown":
|
|
1749
|
+
case "ArrowLeft":
|
|
1750
|
+
case "ArrowRight":
|
|
1751
|
+
case "ArrowUp":
|
|
1752
|
+
case "End":
|
|
1753
|
+
case "Home":
|
|
1754
|
+
{
|
|
1755
|
+
event.preventDefault();
|
|
1756
|
+
resizeHandler(event);
|
|
1757
|
+
break;
|
|
1758
|
+
}
|
|
1759
|
+
case "F6":
|
|
1760
|
+
{
|
|
1761
|
+
event.preventDefault();
|
|
1762
|
+
const groupId = handleElement.getAttribute("data-panel-group-id");
|
|
1763
|
+
const handles = getResizeHandleElementsForGroup(groupId);
|
|
1764
|
+
const index = getResizeHandleElementIndex(groupId, handleId);
|
|
1765
|
+
assert(index !== null);
|
|
1766
|
+
const nextIndex = event.shiftKey ? index > 0 ? index - 1 : handles.length - 1 : index + 1 < handles.length ? index + 1 : 0;
|
|
1767
|
+
const nextHandle = handles[nextIndex];
|
|
1768
|
+
nextHandle.focus();
|
|
1769
|
+
break;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
};
|
|
1773
|
+
handleElement.addEventListener("keydown", onKeyDown);
|
|
1774
|
+
return () => {
|
|
1775
|
+
handleElement.removeEventListener("keydown", onKeyDown);
|
|
1776
|
+
};
|
|
1777
|
+
}, [disabled, handleId, resizeHandler]);
|
|
1778
|
+
}
|
|
1547
1779
|
|
|
1548
1780
|
function PanelResizeHandle({
|
|
1549
1781
|
children = null,
|
|
@@ -1568,15 +1800,15 @@ function PanelResizeHandle({
|
|
|
1568
1800
|
throw Error(`PanelResizeHandle components must be rendered within a PanelGroup container`);
|
|
1569
1801
|
}
|
|
1570
1802
|
const {
|
|
1571
|
-
activeHandleId,
|
|
1572
1803
|
direction,
|
|
1804
|
+
dragState,
|
|
1573
1805
|
groupId,
|
|
1574
1806
|
registerResizeHandle,
|
|
1575
1807
|
startDragging,
|
|
1576
1808
|
stopDragging
|
|
1577
1809
|
} = panelGroupContext;
|
|
1578
1810
|
const resizeHandleId = useUniqueId(idFromProps);
|
|
1579
|
-
const isDragging =
|
|
1811
|
+
const isDragging = (dragState === null || dragState === void 0 ? void 0 : dragState.dragHandleId) === resizeHandleId;
|
|
1580
1812
|
const [isFocused, setIsFocused] = useState(false);
|
|
1581
1813
|
const [resizeHandler, setResizeHandler] = useState(null);
|
|
1582
1814
|
const stopDraggingAndBlur = useCallback(() => {
|
|
@@ -1640,11 +1872,6 @@ function PanelResizeHandle({
|
|
|
1640
1872
|
return createElement(Type, {
|
|
1641
1873
|
children,
|
|
1642
1874
|
className: classNameFromProps,
|
|
1643
|
-
"data-resize-handle-active": isDragging ? "pointer" : isFocused ? "keyboard" : undefined,
|
|
1644
|
-
"data-panel-group-direction": direction,
|
|
1645
|
-
"data-panel-group-id": groupId,
|
|
1646
|
-
"data-panel-resize-handle-enabled": !disabled,
|
|
1647
|
-
"data-panel-resize-handle-id": resizeHandleId,
|
|
1648
1875
|
onBlur: () => setIsFocused(false),
|
|
1649
1876
|
onFocus: () => setIsFocused(true),
|
|
1650
1877
|
onMouseDown: event => {
|
|
@@ -1674,9 +1901,16 @@ function PanelResizeHandle({
|
|
|
1674
1901
|
...style,
|
|
1675
1902
|
...styleFromProps
|
|
1676
1903
|
},
|
|
1677
|
-
tabIndex: 0
|
|
1904
|
+
tabIndex: 0,
|
|
1905
|
+
// CSS selectors
|
|
1906
|
+
"data-panel-group-direction": direction,
|
|
1907
|
+
"data-panel-group-id": groupId,
|
|
1908
|
+
"data-resize-handle": "",
|
|
1909
|
+
"data-resize-handle-active": isDragging ? "pointer" : isFocused ? "keyboard" : undefined,
|
|
1910
|
+
"data-panel-resize-handle-enabled": !disabled,
|
|
1911
|
+
"data-panel-resize-handle-id": resizeHandleId
|
|
1678
1912
|
});
|
|
1679
1913
|
}
|
|
1680
1914
|
PanelResizeHandle.displayName = "PanelResizeHandle";
|
|
1681
1915
|
|
|
1682
|
-
export { Panel, PanelGroup, PanelResizeHandle
|
|
1916
|
+
export { Panel, PanelGroup, PanelResizeHandle };
|