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