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