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