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.
Files changed (95) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/CHANGELOG.md +238 -90
  3. package/README.md +55 -49
  4. package/dist/declarations/src/Panel.d.ts +75 -20
  5. package/dist/declarations/src/PanelGroup.d.ts +29 -25
  6. package/dist/declarations/src/PanelResizeHandle.d.ts +1 -1
  7. package/dist/declarations/src/index.d.ts +5 -6
  8. package/dist/declarations/src/types.d.ts +3 -26
  9. package/dist/declarations/src/vendor/react.d.ts +4 -4
  10. package/dist/react-resizable-panels.browser.cjs.js +1276 -1043
  11. package/dist/react-resizable-panels.browser.cjs.mjs +1 -2
  12. package/dist/react-resizable-panels.browser.development.cjs.js +1410 -1097
  13. package/dist/react-resizable-panels.browser.development.cjs.mjs +1 -2
  14. package/dist/react-resizable-panels.browser.development.esm.js +1411 -1097
  15. package/dist/react-resizable-panels.browser.esm.js +1277 -1043
  16. package/dist/react-resizable-panels.cjs.js +1276 -1043
  17. package/dist/react-resizable-panels.cjs.js.map +1 -1
  18. package/dist/react-resizable-panels.cjs.mjs +1 -2
  19. package/dist/react-resizable-panels.development.cjs.js +1415 -1102
  20. package/dist/react-resizable-panels.development.cjs.mjs +1 -2
  21. package/dist/react-resizable-panels.development.esm.js +1416 -1102
  22. package/dist/react-resizable-panels.development.node.cjs.js +1179 -947
  23. package/dist/react-resizable-panels.development.node.cjs.mjs +1 -2
  24. package/dist/react-resizable-panels.development.node.esm.js +1180 -947
  25. package/dist/react-resizable-panels.esm.js +1277 -1043
  26. package/dist/react-resizable-panels.esm.js.map +1 -1
  27. package/dist/react-resizable-panels.node.cjs.js +1068 -910
  28. package/dist/react-resizable-panels.node.cjs.mjs +1 -2
  29. package/dist/react-resizable-panels.node.esm.js +1069 -910
  30. package/jest.config.js +10 -0
  31. package/package.json +5 -1
  32. package/src/Panel.test.tsx +308 -0
  33. package/src/Panel.ts +175 -123
  34. package/src/PanelGroup.test.tsx +210 -0
  35. package/src/PanelGroup.ts +730 -667
  36. package/src/PanelGroupContext.ts +33 -0
  37. package/src/PanelResizeHandle.ts +21 -17
  38. package/src/hooks/useUniqueId.ts +1 -1
  39. package/src/hooks/useWindowSplitterBehavior.ts +9 -164
  40. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +185 -0
  41. package/src/index.ts +19 -14
  42. package/src/types.ts +3 -30
  43. package/src/utils/adjustLayoutByDelta.test.ts +1808 -0
  44. package/src/utils/adjustLayoutByDelta.ts +211 -0
  45. package/src/utils/calculateAriaValues.test.ts +111 -0
  46. package/src/utils/calculateAriaValues.ts +67 -0
  47. package/src/utils/calculateDeltaPercentage.ts +68 -0
  48. package/src/utils/calculateDragOffsetPercentage.ts +30 -0
  49. package/src/utils/calculateUnsafeDefaultLayout.test.ts +92 -0
  50. package/src/utils/calculateUnsafeDefaultLayout.ts +55 -0
  51. package/src/utils/callPanelCallbacks.ts +81 -0
  52. package/src/utils/compareLayouts.test.ts +9 -0
  53. package/src/utils/compareLayouts.ts +12 -0
  54. package/src/utils/computePanelFlexBoxStyle.ts +44 -0
  55. package/src/utils/computePercentagePanelConstraints.test.ts +98 -0
  56. package/src/utils/computePercentagePanelConstraints.ts +56 -0
  57. package/src/utils/convertPercentageToPixels.test.ts +9 -0
  58. package/src/utils/convertPercentageToPixels.ts +6 -0
  59. package/src/utils/convertPixelConstraintsToPercentages.test.ts +47 -0
  60. package/src/utils/convertPixelConstraintsToPercentages.ts +72 -0
  61. package/src/utils/convertPixelsToPercentage.test.ts +9 -0
  62. package/src/utils/convertPixelsToPercentage.ts +6 -0
  63. package/src/utils/determinePivotIndices.ts +10 -0
  64. package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +29 -0
  65. package/src/utils/dom/getAvailableGroupSizePixels.ts +29 -0
  66. package/src/utils/dom/getPanelElement.ts +7 -0
  67. package/src/utils/dom/getPanelGroupElement.ts +9 -0
  68. package/src/utils/dom/getResizeHandleElement.ts +9 -0
  69. package/src/utils/dom/getResizeHandleElementIndex.ts +12 -0
  70. package/src/utils/dom/getResizeHandleElementsForGroup.ts +9 -0
  71. package/src/utils/dom/getResizeHandlePanelIds.ts +18 -0
  72. package/src/utils/events.ts +13 -0
  73. package/src/utils/getPercentageSizeFromMixedSizes.test.ts +47 -0
  74. package/src/utils/getPercentageSizeFromMixedSizes.ts +15 -0
  75. package/src/utils/getResizeEventCursorPosition.ts +19 -0
  76. package/src/utils/initializeDefaultStorage.ts +26 -0
  77. package/src/utils/numbers/fuzzyCompareNumbers.test.ts +16 -0
  78. package/src/utils/numbers/fuzzyCompareNumbers.ts +17 -0
  79. package/src/utils/numbers/fuzzyNumbersEqual.ts +9 -0
  80. package/src/utils/resizePanel.test.ts +45 -0
  81. package/src/utils/resizePanel.ts +60 -0
  82. package/src/utils/serialization.ts +9 -4
  83. package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +23 -0
  84. package/src/utils/shouldMonitorPixelBasedConstraints.ts +13 -0
  85. package/src/utils/test-utils.ts +136 -0
  86. package/src/utils/validatePanelConstraints.test.ts +151 -0
  87. package/src/utils/validatePanelConstraints.ts +103 -0
  88. package/src/utils/validatePanelGroupLayout.test.ts +233 -0
  89. package/src/utils/validatePanelGroupLayout.ts +88 -0
  90. package/src/vendor/react.ts +4 -0
  91. package/.eslintrc.json +0 -22
  92. package/dist/declarations/src/utils/group.d.ts +0 -29
  93. package/src/PanelContexts.ts +0 -22
  94. package/src/utils/coordinates.ts +0 -149
  95. 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";
@@ -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 { PanelGroupContext } from "./PanelContexts";
18
- import type {
19
- ResizeHandler,
17
+ import {
18
+ PanelGroupContext,
20
19
  ResizeEvent,
21
- PanelResizeHandleOnDragging,
22
- } from "./types";
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 = activeHandleId === resizeHandleId;
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
 
@@ -15,5 +15,5 @@ export default function useUniqueId(
15
15
  idRef.current = "" + counter++;
16
16
  }
17
17
 
18
- return idRef.current;
18
+ return idFromParams ?? idRef.current;
19
19
  }
@@ -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 = getResizeHandle(handleId);
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 handles = getResizeHandles();
207
- const index = getResizeHandleIndex(handleId);
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 { ImperativePanelHandle, PanelProps } from "./Panel";
6
- import type { ImperativePanelGroupHandle, PanelGroupProps } from "./PanelGroup";
7
- import type { PanelResizeHandleProps } from "./PanelResizeHandle";
8
- import { getAvailableGroupSizePixels } from "./utils/group";
5
+ import type { MixedSizes } from "./types";
6
+
9
7
  import type {
10
- PanelGroupOnLayout,
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
- Units,
16
- } from "./types";
22
+ PanelResizeHandleProps,
23
+ } from "./PanelResizeHandle";
17
24
 
18
25
  export {
19
26
  // TypeScript types
20
27
  ImperativePanelGroupHandle,
21
28
  ImperativePanelHandle,
22
- PanelOnCollapse,
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 PanelData = {
22
- current: {
23
- callbacksRef: PanelCallbackRef;
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;