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
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PanelData } from "./Panel";
|
|
2
|
+
import { MixedSizes } from "./types";
|
|
3
|
+
import { CSSProperties, createContext } from "./vendor/react";
|
|
4
|
+
|
|
5
|
+
export type ResizeEvent = KeyboardEvent | MouseEvent | TouchEvent;
|
|
6
|
+
export type ResizeHandler = (event: ResizeEvent) => void;
|
|
7
|
+
|
|
8
|
+
export type DragState = {
|
|
9
|
+
dragHandleId: string;
|
|
10
|
+
dragHandleRect: DOMRect;
|
|
11
|
+
initialCursorPosition: number;
|
|
12
|
+
initialLayout: number[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const PanelGroupContext = createContext<{
|
|
16
|
+
collapsePanel: (panelData: PanelData) => void;
|
|
17
|
+
direction: "horizontal" | "vertical";
|
|
18
|
+
dragState: DragState | null;
|
|
19
|
+
expandPanel: (panelData: PanelData) => void;
|
|
20
|
+
getPanelSize: (panelData: PanelData) => MixedSizes;
|
|
21
|
+
getPanelStyle: (panelData: PanelData) => CSSProperties;
|
|
22
|
+
groupId: string;
|
|
23
|
+
isPanelCollapsed: (panelData: PanelData) => boolean;
|
|
24
|
+
isPanelExpanded: (panelData: PanelData) => boolean;
|
|
25
|
+
registerPanel: (panelData: PanelData) => void;
|
|
26
|
+
registerResizeHandle: (dragHandleId: string) => ResizeHandler;
|
|
27
|
+
resizePanel: (panelData: PanelData, mixedSizes: Partial<MixedSizes>) => void;
|
|
28
|
+
startDragging: (dragHandleId: string, event: ResizeEvent) => void;
|
|
29
|
+
stopDragging: () => void;
|
|
30
|
+
unregisterPanel: (panelData: PanelData) => void;
|
|
31
|
+
} | null>(null);
|
|
32
|
+
|
|
33
|
+
PanelGroupContext.displayName = "PanelGroupContext";
|
package/src/PanelResizeHandle.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import useUniqueId from "./hooks/useUniqueId";
|
|
1
2
|
import {
|
|
2
3
|
createElement,
|
|
3
4
|
CSSProperties,
|
|
@@ -11,17 +12,17 @@ import {
|
|
|
11
12
|
useRef,
|
|
12
13
|
useState,
|
|
13
14
|
} from "./vendor/react";
|
|
14
|
-
import useUniqueId from "./hooks/useUniqueId";
|
|
15
15
|
|
|
16
16
|
import { useWindowSplitterResizeHandlerBehavior } from "./hooks/useWindowSplitterBehavior";
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
ResizeHandler,
|
|
17
|
+
import {
|
|
18
|
+
PanelGroupContext,
|
|
20
19
|
ResizeEvent,
|
|
21
|
-
|
|
22
|
-
} from "./
|
|
20
|
+
ResizeHandler,
|
|
21
|
+
} from "./PanelGroupContext";
|
|
23
22
|
import { getCursorStyle } from "./utils/cursor";
|
|
24
23
|
|
|
24
|
+
export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
|
|
25
|
+
|
|
25
26
|
export type PanelResizeHandleProps = {
|
|
26
27
|
children?: ReactNode;
|
|
27
28
|
className?: string;
|
|
@@ -59,8 +60,8 @@ export function PanelResizeHandle({
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
const {
|
|
62
|
-
activeHandleId,
|
|
63
63
|
direction,
|
|
64
|
+
dragState,
|
|
64
65
|
groupId,
|
|
65
66
|
registerResizeHandle,
|
|
66
67
|
startDragging,
|
|
@@ -68,7 +69,7 @@ export function PanelResizeHandle({
|
|
|
68
69
|
} = panelGroupContext;
|
|
69
70
|
|
|
70
71
|
const resizeHandleId = useUniqueId(idFromProps);
|
|
71
|
-
const isDragging =
|
|
72
|
+
const isDragging = dragState?.dragHandleId === resizeHandleId;
|
|
72
73
|
|
|
73
74
|
const [isFocused, setIsFocused] = useState(false);
|
|
74
75
|
|
|
@@ -150,15 +151,6 @@ export function PanelResizeHandle({
|
|
|
150
151
|
return createElement(Type, {
|
|
151
152
|
children,
|
|
152
153
|
className: classNameFromProps,
|
|
153
|
-
"data-resize-handle-active": isDragging
|
|
154
|
-
? "pointer"
|
|
155
|
-
: isFocused
|
|
156
|
-
? "keyboard"
|
|
157
|
-
: undefined,
|
|
158
|
-
"data-panel-group-direction": direction,
|
|
159
|
-
"data-panel-group-id": groupId,
|
|
160
|
-
"data-panel-resize-handle-enabled": !disabled,
|
|
161
|
-
"data-panel-resize-handle-id": resizeHandleId,
|
|
162
154
|
onBlur: () => setIsFocused(false),
|
|
163
155
|
onFocus: () => setIsFocused(true),
|
|
164
156
|
onMouseDown: (event: ReactMouseEvent) => {
|
|
@@ -187,6 +179,18 @@ export function PanelResizeHandle({
|
|
|
187
179
|
...styleFromProps,
|
|
188
180
|
},
|
|
189
181
|
tabIndex: 0,
|
|
182
|
+
|
|
183
|
+
// CSS selectors
|
|
184
|
+
"data-panel-group-direction": direction,
|
|
185
|
+
"data-panel-group-id": groupId,
|
|
186
|
+
"data-resize-handle": "",
|
|
187
|
+
"data-resize-handle-active": isDragging
|
|
188
|
+
? "pointer"
|
|
189
|
+
: isFocused
|
|
190
|
+
? "keyboard"
|
|
191
|
+
: undefined,
|
|
192
|
+
"data-panel-resize-handle-enabled": !disabled,
|
|
193
|
+
"data-panel-resize-handle-id": resizeHandleId,
|
|
190
194
|
});
|
|
191
195
|
}
|
|
192
196
|
|
package/src/hooks/useUniqueId.ts
CHANGED
|
@@ -1,169 +1,12 @@
|
|
|
1
|
-
import { RefObject, useEffect } from "../vendor/react";
|
|
2
|
-
import { PRECISION } from "../constants";
|
|
3
|
-
|
|
4
|
-
import { CommittedValues, PanelDataMap } from "../PanelGroup";
|
|
5
1
|
import { ResizeHandler } from "../types";
|
|
6
|
-
import {
|
|
7
|
-
adjustByDelta,
|
|
8
|
-
getPanel,
|
|
9
|
-
getPanelGroup,
|
|
10
|
-
getResizeHandle,
|
|
11
|
-
getResizeHandleIndex,
|
|
12
|
-
getResizeHandlePanelIds,
|
|
13
|
-
getResizeHandles,
|
|
14
|
-
getResizeHandlesForGroup,
|
|
15
|
-
getFlexGrow,
|
|
16
|
-
panelsMapToSortedArray,
|
|
17
|
-
} from "../utils/group";
|
|
18
2
|
import { assert } from "../utils/assert";
|
|
3
|
+
import { getResizeHandleElement } from "../utils/dom/getResizeHandleElement";
|
|
4
|
+
import { getResizeHandleElementIndex } from "../utils/dom/getResizeHandleElementIndex";
|
|
5
|
+
import { getResizeHandleElementsForGroup } from "../utils/dom/getResizeHandleElementsForGroup";
|
|
6
|
+
import { useEffect } from "../vendor/react";
|
|
19
7
|
|
|
20
8
|
// https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
|
|
21
9
|
|
|
22
|
-
export function useWindowSplitterPanelGroupBehavior({
|
|
23
|
-
committedValuesRef,
|
|
24
|
-
groupId,
|
|
25
|
-
panels,
|
|
26
|
-
setSizes,
|
|
27
|
-
sizes,
|
|
28
|
-
panelSizeBeforeCollapse,
|
|
29
|
-
}: {
|
|
30
|
-
committedValuesRef: RefObject<CommittedValues>;
|
|
31
|
-
groupId: string;
|
|
32
|
-
panels: PanelDataMap;
|
|
33
|
-
setSizes: (sizes: number[]) => void;
|
|
34
|
-
sizes: number[];
|
|
35
|
-
panelSizeBeforeCollapse: RefObject<Map<string, number>>;
|
|
36
|
-
}): void {
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
const { direction, panels } = committedValuesRef.current!;
|
|
39
|
-
|
|
40
|
-
const groupElement = getPanelGroup(groupId);
|
|
41
|
-
assert(groupElement != null, `No group found for id "${groupId}"`);
|
|
42
|
-
|
|
43
|
-
const { height, width } = groupElement.getBoundingClientRect();
|
|
44
|
-
|
|
45
|
-
const handles = getResizeHandlesForGroup(groupId);
|
|
46
|
-
const cleanupFunctions = handles.map((handle) => {
|
|
47
|
-
const handleId = handle.getAttribute("data-panel-resize-handle-id")!;
|
|
48
|
-
const panelsArray = panelsMapToSortedArray(panels);
|
|
49
|
-
|
|
50
|
-
const [idBefore, idAfter] = getResizeHandlePanelIds(
|
|
51
|
-
groupId,
|
|
52
|
-
handleId,
|
|
53
|
-
panelsArray
|
|
54
|
-
);
|
|
55
|
-
if (idBefore == null || idAfter == null) {
|
|
56
|
-
return () => {};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let currentMinSize = 0;
|
|
60
|
-
let currentMaxSize = 100;
|
|
61
|
-
let totalMinSize = 0;
|
|
62
|
-
let totalMaxSize = 0;
|
|
63
|
-
|
|
64
|
-
// A panel's effective min/max sizes also need to account for other panel's sizes.
|
|
65
|
-
panelsArray.forEach((panelData) => {
|
|
66
|
-
const { id, maxSize, minSize } = panelData.current;
|
|
67
|
-
if (id === idBefore) {
|
|
68
|
-
currentMinSize = minSize;
|
|
69
|
-
currentMaxSize = maxSize != null ? maxSize : 100;
|
|
70
|
-
} else {
|
|
71
|
-
totalMinSize += minSize;
|
|
72
|
-
totalMaxSize += maxSize != null ? maxSize : 100;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const ariaValueMax = Math.min(currentMaxSize, 100 - totalMinSize);
|
|
77
|
-
const ariaValueMin = Math.max(
|
|
78
|
-
currentMinSize,
|
|
79
|
-
(panelsArray.length - 1) * 100 - totalMaxSize
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const flexGrow = getFlexGrow(panels, idBefore, sizes);
|
|
83
|
-
|
|
84
|
-
handle.setAttribute("aria-valuemax", "" + Math.round(ariaValueMax));
|
|
85
|
-
handle.setAttribute("aria-valuemin", "" + Math.round(ariaValueMin));
|
|
86
|
-
handle.setAttribute("aria-valuenow", "" + Math.round(parseInt(flexGrow)));
|
|
87
|
-
|
|
88
|
-
const onKeyDown = (event: KeyboardEvent) => {
|
|
89
|
-
if (event.defaultPrevented) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
switch (event.key) {
|
|
94
|
-
case "Enter": {
|
|
95
|
-
event.preventDefault();
|
|
96
|
-
|
|
97
|
-
const index = panelsArray.findIndex(
|
|
98
|
-
(panel) => panel.current.id === idBefore
|
|
99
|
-
);
|
|
100
|
-
if (index >= 0) {
|
|
101
|
-
const panelData = panelsArray[index];
|
|
102
|
-
const size = sizes[index];
|
|
103
|
-
if (size != null) {
|
|
104
|
-
let delta = 0;
|
|
105
|
-
if (
|
|
106
|
-
size.toPrecision(PRECISION) <=
|
|
107
|
-
panelData.current.minSize.toPrecision(PRECISION)
|
|
108
|
-
) {
|
|
109
|
-
delta = direction === "horizontal" ? width : height;
|
|
110
|
-
} else {
|
|
111
|
-
delta = -(direction === "horizontal" ? width : height);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const nextSizes = adjustByDelta(
|
|
115
|
-
event,
|
|
116
|
-
committedValuesRef.current!,
|
|
117
|
-
idBefore,
|
|
118
|
-
idAfter,
|
|
119
|
-
delta,
|
|
120
|
-
sizes,
|
|
121
|
-
panelSizeBeforeCollapse.current!,
|
|
122
|
-
null
|
|
123
|
-
);
|
|
124
|
-
if (sizes !== nextSizes) {
|
|
125
|
-
setSizes(nextSizes);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
handle.addEventListener("keydown", onKeyDown);
|
|
135
|
-
|
|
136
|
-
const panelBefore = getPanel(idBefore);
|
|
137
|
-
if (panelBefore != null) {
|
|
138
|
-
handle.setAttribute("aria-controls", panelBefore.id);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return () => {
|
|
142
|
-
handle.removeAttribute("aria-valuemax");
|
|
143
|
-
handle.removeAttribute("aria-valuemin");
|
|
144
|
-
handle.removeAttribute("aria-valuenow");
|
|
145
|
-
|
|
146
|
-
handle.removeEventListener("keydown", onKeyDown);
|
|
147
|
-
|
|
148
|
-
if (panelBefore != null) {
|
|
149
|
-
handle.removeAttribute("aria-controls");
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return () => {
|
|
155
|
-
cleanupFunctions.forEach((cleanupFunction) => cleanupFunction());
|
|
156
|
-
};
|
|
157
|
-
}, [
|
|
158
|
-
committedValuesRef,
|
|
159
|
-
groupId,
|
|
160
|
-
panels,
|
|
161
|
-
panelSizeBeforeCollapse,
|
|
162
|
-
setSizes,
|
|
163
|
-
sizes,
|
|
164
|
-
]);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
10
|
export function useWindowSplitterResizeHandlerBehavior({
|
|
168
11
|
disabled,
|
|
169
12
|
handleId,
|
|
@@ -178,7 +21,7 @@ export function useWindowSplitterResizeHandlerBehavior({
|
|
|
178
21
|
return;
|
|
179
22
|
}
|
|
180
23
|
|
|
181
|
-
const handleElement =
|
|
24
|
+
const handleElement = getResizeHandleElement(handleId);
|
|
182
25
|
if (handleElement == null) {
|
|
183
26
|
return;
|
|
184
27
|
}
|
|
@@ -203,8 +46,10 @@ export function useWindowSplitterResizeHandlerBehavior({
|
|
|
203
46
|
case "F6": {
|
|
204
47
|
event.preventDefault();
|
|
205
48
|
|
|
206
|
-
const
|
|
207
|
-
|
|
49
|
+
const groupId = handleElement.getAttribute("data-panel-group-id")!;
|
|
50
|
+
|
|
51
|
+
const handles = getResizeHandleElementsForGroup(groupId);
|
|
52
|
+
const index = getResizeHandleElementIndex(groupId, handleId);
|
|
208
53
|
|
|
209
54
|
assert(index !== null);
|
|
210
55
|
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { isDevelopment } from "#is-development";
|
|
2
|
+
import { PanelData } from "../Panel";
|
|
3
|
+
import { PRECISION } from "../constants";
|
|
4
|
+
import { Direction } from "../types";
|
|
5
|
+
import { adjustLayoutByDelta } from "../utils/adjustLayoutByDelta";
|
|
6
|
+
import { assert } from "../utils/assert";
|
|
7
|
+
import { calculateAriaValues } from "../utils/calculateAriaValues";
|
|
8
|
+
import { determinePivotIndices } from "../utils/determinePivotIndices";
|
|
9
|
+
import { calculateAvailablePanelSizeInPixels } from "../utils/dom/calculateAvailablePanelSizeInPixels";
|
|
10
|
+
import { getAvailableGroupSizePixels } from "../utils/dom/getAvailableGroupSizePixels";
|
|
11
|
+
import { getPanelGroupElement } from "../utils/dom/getPanelGroupElement";
|
|
12
|
+
import { getResizeHandleElementsForGroup } from "../utils/dom/getResizeHandleElementsForGroup";
|
|
13
|
+
import { getResizeHandlePanelIds } from "../utils/dom/getResizeHandlePanelIds";
|
|
14
|
+
import { getPercentageSizeFromMixedSizes } from "../utils/getPercentageSizeFromMixedSizes";
|
|
15
|
+
import { RefObject, useEffect, useRef } from "../vendor/react";
|
|
16
|
+
import useIsomorphicLayoutEffect from "./useIsomorphicEffect";
|
|
17
|
+
|
|
18
|
+
// https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
|
|
19
|
+
|
|
20
|
+
export function useWindowSplitterPanelGroupBehavior({
|
|
21
|
+
committedValuesRef,
|
|
22
|
+
groupId,
|
|
23
|
+
layout,
|
|
24
|
+
panelDataArray,
|
|
25
|
+
setLayout,
|
|
26
|
+
}: {
|
|
27
|
+
committedValuesRef: RefObject<{
|
|
28
|
+
direction: Direction;
|
|
29
|
+
panelDataArray: PanelData[];
|
|
30
|
+
}>;
|
|
31
|
+
groupId: string;
|
|
32
|
+
layout: number[];
|
|
33
|
+
panelDataArray: PanelData[];
|
|
34
|
+
setLayout: (sizes: number[]) => void;
|
|
35
|
+
}): void {
|
|
36
|
+
const devWarningsRef = useRef<{
|
|
37
|
+
didWarnAboutMissingResizeHandle: boolean;
|
|
38
|
+
}>({
|
|
39
|
+
didWarnAboutMissingResizeHandle: false,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
useIsomorphicLayoutEffect(() => {
|
|
43
|
+
const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
|
|
44
|
+
const resizeHandleElements = getResizeHandleElementsForGroup(groupId);
|
|
45
|
+
|
|
46
|
+
for (let index = 0; index < panelDataArray.length - 1; index++) {
|
|
47
|
+
const { valueMax, valueMin, valueNow } = calculateAriaValues({
|
|
48
|
+
groupSizePixels,
|
|
49
|
+
layout,
|
|
50
|
+
panelsArray: panelDataArray,
|
|
51
|
+
pivotIndices: [index, index + 1],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const resizeHandleElement = resizeHandleElements[index];
|
|
55
|
+
if (resizeHandleElement == null) {
|
|
56
|
+
if (isDevelopment) {
|
|
57
|
+
const { didWarnAboutMissingResizeHandle } = devWarningsRef.current;
|
|
58
|
+
|
|
59
|
+
if (!didWarnAboutMissingResizeHandle) {
|
|
60
|
+
devWarningsRef.current.didWarnAboutMissingResizeHandle = true;
|
|
61
|
+
|
|
62
|
+
console.warn(
|
|
63
|
+
`WARNING: Missing resize handle for PanelGroup "${groupId}"`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
resizeHandleElement.setAttribute(
|
|
69
|
+
"aria-controls",
|
|
70
|
+
panelDataArray[index].id
|
|
71
|
+
);
|
|
72
|
+
resizeHandleElement.setAttribute(
|
|
73
|
+
"aria-valuemax",
|
|
74
|
+
"" + Math.round(valueMax)
|
|
75
|
+
);
|
|
76
|
+
resizeHandleElement.setAttribute(
|
|
77
|
+
"aria-valuemin",
|
|
78
|
+
"" + Math.round(valueMin)
|
|
79
|
+
);
|
|
80
|
+
resizeHandleElement.setAttribute(
|
|
81
|
+
"aria-valuenow",
|
|
82
|
+
"" + Math.round(valueNow)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return () => {
|
|
88
|
+
resizeHandleElements.forEach((resizeHandleElement, index) => {
|
|
89
|
+
resizeHandleElement.removeAttribute("aria-controls");
|
|
90
|
+
resizeHandleElement.removeAttribute("aria-valuemax");
|
|
91
|
+
resizeHandleElement.removeAttribute("aria-valuemin");
|
|
92
|
+
resizeHandleElement.removeAttribute("aria-valuenow");
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
}, [groupId, layout, panelDataArray]);
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const { direction, panelDataArray } = committedValuesRef.current!;
|
|
99
|
+
|
|
100
|
+
const groupElement = getPanelGroupElement(groupId);
|
|
101
|
+
assert(groupElement != null, `No group found for id "${groupId}"`);
|
|
102
|
+
|
|
103
|
+
const { height, width } = groupElement.getBoundingClientRect();
|
|
104
|
+
|
|
105
|
+
const handles = getResizeHandleElementsForGroup(groupId);
|
|
106
|
+
const cleanupFunctions = handles.map((handle) => {
|
|
107
|
+
const handleId = handle.getAttribute("data-panel-resize-handle-id")!;
|
|
108
|
+
|
|
109
|
+
const [idBefore, idAfter] = getResizeHandlePanelIds(
|
|
110
|
+
groupId,
|
|
111
|
+
handleId,
|
|
112
|
+
panelDataArray
|
|
113
|
+
);
|
|
114
|
+
if (idBefore == null || idAfter == null) {
|
|
115
|
+
return () => {};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
119
|
+
if (event.defaultPrevented) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
switch (event.key) {
|
|
124
|
+
case "Enter": {
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
|
|
127
|
+
const index = panelDataArray.findIndex(
|
|
128
|
+
(panelData) => panelData.id === idBefore
|
|
129
|
+
);
|
|
130
|
+
if (index >= 0) {
|
|
131
|
+
const panelData = panelDataArray[index];
|
|
132
|
+
const size = layout[index];
|
|
133
|
+
if (size != null) {
|
|
134
|
+
const groupSizePixels = getAvailableGroupSizePixels(groupId);
|
|
135
|
+
|
|
136
|
+
const minSize =
|
|
137
|
+
getPercentageSizeFromMixedSizes(
|
|
138
|
+
{
|
|
139
|
+
sizePercentage: panelData.constraints.minSizePercentage,
|
|
140
|
+
sizePixels: panelData.constraints.minSizePixels,
|
|
141
|
+
},
|
|
142
|
+
groupSizePixels
|
|
143
|
+
) ?? 0;
|
|
144
|
+
|
|
145
|
+
let delta = 0;
|
|
146
|
+
if (
|
|
147
|
+
size.toPrecision(PRECISION) <= minSize.toPrecision(PRECISION)
|
|
148
|
+
) {
|
|
149
|
+
delta = direction === "horizontal" ? width : height;
|
|
150
|
+
} else {
|
|
151
|
+
delta = -(direction === "horizontal" ? width : height);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const nextLayout = adjustLayoutByDelta({
|
|
155
|
+
delta,
|
|
156
|
+
groupSizePixels,
|
|
157
|
+
layout,
|
|
158
|
+
panelConstraints: panelDataArray.map(
|
|
159
|
+
(panelData) => panelData.constraints
|
|
160
|
+
),
|
|
161
|
+
pivotIndices: determinePivotIndices(groupId, handleId),
|
|
162
|
+
trigger: "keyboard",
|
|
163
|
+
});
|
|
164
|
+
if (layout !== nextLayout) {
|
|
165
|
+
setLayout(nextLayout);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
handle.addEventListener("keydown", onKeyDown);
|
|
175
|
+
|
|
176
|
+
return () => {
|
|
177
|
+
handle.removeEventListener("keydown", onKeyDown);
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
return () => {
|
|
182
|
+
cleanupFunctions.forEach((cleanupFunction) => cleanupFunction());
|
|
183
|
+
};
|
|
184
|
+
}, [committedValuesRef, groupId, layout, panelDataArray, setLayout]);
|
|
185
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,38 +2,43 @@ import { Panel } from "./Panel";
|
|
|
2
2
|
import { PanelGroup } from "./PanelGroup";
|
|
3
3
|
import { PanelResizeHandle } from "./PanelResizeHandle";
|
|
4
4
|
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
import type { PanelResizeHandleProps } from "./PanelResizeHandle";
|
|
8
|
-
import { getAvailableGroupSizePixels } from "./utils/group";
|
|
5
|
+
import type { MixedSizes } from "./types";
|
|
6
|
+
|
|
9
7
|
import type {
|
|
10
|
-
|
|
11
|
-
PanelGroupStorage,
|
|
8
|
+
ImperativePanelHandle,
|
|
12
9
|
PanelOnCollapse,
|
|
10
|
+
PanelOnExpand,
|
|
13
11
|
PanelOnResize,
|
|
12
|
+
PanelProps,
|
|
13
|
+
} from "./Panel";
|
|
14
|
+
import type {
|
|
15
|
+
ImperativePanelGroupHandle,
|
|
16
|
+
PanelGroupOnLayout,
|
|
17
|
+
PanelGroupProps,
|
|
18
|
+
PanelGroupStorage,
|
|
19
|
+
} from "./PanelGroup";
|
|
20
|
+
import type {
|
|
14
21
|
PanelResizeHandleOnDragging,
|
|
15
|
-
|
|
16
|
-
} from "./
|
|
22
|
+
PanelResizeHandleProps,
|
|
23
|
+
} from "./PanelResizeHandle";
|
|
17
24
|
|
|
18
25
|
export {
|
|
19
26
|
// TypeScript types
|
|
20
27
|
ImperativePanelGroupHandle,
|
|
21
28
|
ImperativePanelHandle,
|
|
22
|
-
|
|
23
|
-
PanelOnResize,
|
|
29
|
+
MixedSizes,
|
|
24
30
|
PanelGroupOnLayout,
|
|
25
31
|
PanelGroupProps,
|
|
26
32
|
PanelGroupStorage,
|
|
33
|
+
PanelOnCollapse,
|
|
34
|
+
PanelOnExpand,
|
|
35
|
+
PanelOnResize,
|
|
27
36
|
PanelProps,
|
|
28
37
|
PanelResizeHandleOnDragging,
|
|
29
38
|
PanelResizeHandleProps,
|
|
30
|
-
Units,
|
|
31
39
|
|
|
32
40
|
// React components
|
|
33
41
|
Panel,
|
|
34
42
|
PanelGroup,
|
|
35
43
|
PanelResizeHandle,
|
|
36
|
-
|
|
37
|
-
// Utility methods
|
|
38
|
-
getAvailableGroupSizePixels,
|
|
39
44
|
};
|
package/src/types.ts
CHANGED
|
@@ -1,35 +1,8 @@
|
|
|
1
|
-
import { RefObject } from "./vendor/react";
|
|
2
|
-
|
|
3
1
|
export type Direction = "horizontal" | "vertical";
|
|
4
|
-
export type Units = "percentages" | "pixels";
|
|
5
|
-
|
|
6
|
-
export type PanelGroupStorage = {
|
|
7
|
-
getItem(name: string): string | null;
|
|
8
|
-
setItem(name: string, value: string): void;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export type PanelGroupOnLayout = (sizes: number[]) => void;
|
|
12
|
-
export type PanelOnCollapse = (collapsed: boolean) => void;
|
|
13
|
-
export type PanelOnResize = (size: number, prevSize: number) => void;
|
|
14
|
-
export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
|
|
15
|
-
|
|
16
|
-
export type PanelCallbackRef = RefObject<{
|
|
17
|
-
onCollapse: PanelOnCollapse | null;
|
|
18
|
-
onResize: PanelOnResize | null;
|
|
19
|
-
}>;
|
|
20
2
|
|
|
21
|
-
export type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
collapsedSize: number;
|
|
25
|
-
collapsible: boolean;
|
|
26
|
-
defaultSize: number | null;
|
|
27
|
-
id: string;
|
|
28
|
-
idWasAutoGenerated: boolean;
|
|
29
|
-
maxSize: number | null;
|
|
30
|
-
minSize: number;
|
|
31
|
-
order: number | null;
|
|
32
|
-
};
|
|
3
|
+
export type MixedSizes = {
|
|
4
|
+
sizePercentage: number;
|
|
5
|
+
sizePixels: number;
|
|
33
6
|
};
|
|
34
7
|
|
|
35
8
|
export type ResizeEvent = KeyboardEvent | MouseEvent | TouchEvent;
|