react-resizable-panels 0.0.63 → 1.0.0-rc.2

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 (74) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/CHANGELOG.md +5 -0
  3. package/dist/declarations/src/Panel.d.ts +19 -34
  4. package/dist/declarations/src/PanelGroup.d.ts +9 -13
  5. package/dist/declarations/src/PanelResizeHandle.d.ts +5 -7
  6. package/dist/declarations/src/index.d.ts +2 -2
  7. package/dist/declarations/src/types.d.ts +0 -7
  8. package/dist/declarations/src/utils/assert.d.ts +1 -0
  9. package/dist/declarations/src/vendor/react.d.ts +2 -2
  10. package/dist/react-resizable-panels.browser.cjs.js +253 -518
  11. package/dist/react-resizable-panels.browser.cjs.mjs +2 -1
  12. package/dist/react-resizable-panels.browser.development.cjs.js +279 -574
  13. package/dist/react-resizable-panels.browser.development.cjs.mjs +2 -1
  14. package/dist/react-resizable-panels.browser.development.esm.js +279 -575
  15. package/dist/react-resizable-panels.browser.esm.js +253 -519
  16. package/dist/react-resizable-panels.cjs.d.ts +88 -1
  17. package/dist/react-resizable-panels.cjs.d.ts.map +1 -1
  18. package/dist/react-resizable-panels.cjs.js +1481 -1983
  19. package/dist/react-resizable-panels.cjs.js.map +1 -1
  20. package/dist/react-resizable-panels.cjs.mjs +2 -1
  21. package/dist/react-resizable-panels.development.cjs.js +281 -576
  22. package/dist/react-resizable-panels.development.cjs.mjs +2 -1
  23. package/dist/react-resizable-panels.development.esm.js +281 -577
  24. package/dist/react-resizable-panels.development.node.cjs.js +267 -502
  25. package/dist/react-resizable-panels.development.node.cjs.mjs +2 -1
  26. package/dist/react-resizable-panels.development.node.esm.js +267 -503
  27. package/dist/react-resizable-panels.esm.js +1476 -1959
  28. package/dist/react-resizable-panels.esm.js.map +1 -1
  29. package/dist/react-resizable-panels.node.cjs.js +239 -444
  30. package/dist/react-resizable-panels.node.cjs.mjs +2 -1
  31. package/dist/react-resizable-panels.node.esm.js +239 -445
  32. package/package.json +1 -1
  33. package/src/Panel.test.tsx +74 -73
  34. package/src/Panel.ts +44 -68
  35. package/src/PanelGroup.test.tsx +43 -42
  36. package/src/PanelGroup.ts +221 -411
  37. package/src/PanelGroupContext.ts +2 -3
  38. package/src/PanelResizeHandle.test.tsx +68 -0
  39. package/src/PanelResizeHandle.ts +31 -22
  40. package/src/hooks/useWindowSplitterBehavior.ts +2 -1
  41. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +22 -33
  42. package/src/index.ts +4 -3
  43. package/src/types.ts +0 -9
  44. package/src/utils/adjustLayoutByDelta.test.ts +206 -336
  45. package/src/utils/adjustLayoutByDelta.ts +59 -51
  46. package/src/utils/assert.ts +1 -1
  47. package/src/utils/calculateAriaValues.test.ts +6 -11
  48. package/src/utils/calculateAriaValues.ts +7 -29
  49. package/src/utils/calculateDeltaPercentage.ts +8 -15
  50. package/src/utils/calculateDragOffsetPercentage.ts +11 -5
  51. package/src/utils/calculateUnsafeDefaultLayout.test.ts +4 -9
  52. package/src/utils/calculateUnsafeDefaultLayout.ts +13 -18
  53. package/src/utils/callPanelCallbacks.ts +11 -46
  54. package/src/utils/getResizeEventCursorPosition.ts +2 -0
  55. package/src/utils/resizePanel.test.ts +6 -52
  56. package/src/utils/resizePanel.ts +24 -46
  57. package/src/utils/test-utils.ts +6 -7
  58. package/src/utils/validatePanelConstraints.test.ts +12 -65
  59. package/src/utils/validatePanelConstraints.ts +26 -67
  60. package/src/utils/validatePanelGroupLayout.test.ts +27 -142
  61. package/src/utils/validatePanelGroupLayout.ts +17 -13
  62. package/src/vendor/react.ts +2 -0
  63. package/src/utils/computePercentagePanelConstraints.test.ts +0 -98
  64. package/src/utils/computePercentagePanelConstraints.ts +0 -56
  65. package/src/utils/convertPercentageToPixels.test.ts +0 -9
  66. package/src/utils/convertPercentageToPixels.ts +0 -6
  67. package/src/utils/convertPixelConstraintsToPercentages.test.ts +0 -47
  68. package/src/utils/convertPixelConstraintsToPercentages.ts +0 -72
  69. package/src/utils/convertPixelsToPercentage.test.ts +0 -9
  70. package/src/utils/convertPixelsToPercentage.ts +0 -6
  71. package/src/utils/getPercentageSizeFromMixedSizes.test.ts +0 -47
  72. package/src/utils/getPercentageSizeFromMixedSizes.ts +0 -15
  73. package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +0 -23
  74. package/src/utils/shouldMonitorPixelBasedConstraints.ts +0 -13
@@ -1,5 +1,4 @@
1
1
  import { PanelData } from "./Panel";
2
- import { MixedSizes } from "./types";
3
2
  import { CSSProperties, createContext } from "./vendor/react";
4
3
 
5
4
  export type ResizeEvent = KeyboardEvent | MouseEvent | TouchEvent;
@@ -17,14 +16,14 @@ export const PanelGroupContext = createContext<{
17
16
  direction: "horizontal" | "vertical";
18
17
  dragState: DragState | null;
19
18
  expandPanel: (panelData: PanelData) => void;
20
- getPanelSize: (panelData: PanelData) => MixedSizes;
19
+ getPanelSize: (panelData: PanelData) => number;
21
20
  getPanelStyle: (panelData: PanelData) => CSSProperties;
22
21
  groupId: string;
23
22
  isPanelCollapsed: (panelData: PanelData) => boolean;
24
23
  isPanelExpanded: (panelData: PanelData) => boolean;
25
24
  registerPanel: (panelData: PanelData) => void;
26
25
  registerResizeHandle: (dragHandleId: string) => ResizeHandler;
27
- resizePanel: (panelData: PanelData, mixedSizes: Partial<MixedSizes>) => void;
26
+ resizePanel: (panelData: PanelData, size: number) => void;
28
27
  startDragging: (dragHandleId: string, event: ResizeEvent) => void;
29
28
  stopDragging: () => void;
30
29
  unregisterPanel: (panelData: PanelData) => void;
@@ -0,0 +1,68 @@
1
+ import { Root, createRoot } from "react-dom/client";
2
+ import { act } from "react-dom/test-utils";
3
+ import { Panel, PanelGroup, PanelResizeHandle } from ".";
4
+ import { assert } from "./utils/assert";
5
+ import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
6
+
7
+ describe("PanelResizeHandle", () => {
8
+ let expectedWarnings: string[] = [];
9
+ let root: Root;
10
+
11
+ beforeEach(() => {
12
+ // @ts-expect-error
13
+ global.IS_REACT_ACT_ENVIRONMENT = true;
14
+
15
+ const container = document.createElement("div");
16
+ document.body.appendChild(container);
17
+
18
+ expectedWarnings = [];
19
+ root = createRoot(container);
20
+
21
+ jest.spyOn(console, "warn").mockImplementation((actualMessage: string) => {
22
+ const match = expectedWarnings.findIndex((expectedMessage) => {
23
+ return actualMessage.includes(expectedMessage);
24
+ });
25
+
26
+ if (match >= 0) {
27
+ expectedWarnings.splice(match, 1);
28
+ return;
29
+ }
30
+
31
+ throw Error(`Unexpected warning: ${actualMessage}`);
32
+ });
33
+ });
34
+
35
+ afterEach(() => {
36
+ jest.clearAllMocks();
37
+ jest.resetModules();
38
+
39
+ act(() => {
40
+ root.unmount();
41
+ });
42
+
43
+ expect(expectedWarnings).toHaveLength(0);
44
+ });
45
+
46
+ it("should support ...rest attributes", () => {
47
+ act(() => {
48
+ root.render(
49
+ <PanelGroup direction="horizontal">
50
+ <Panel />
51
+ <PanelResizeHandle
52
+ data-test-name="foo"
53
+ id="handle"
54
+ tabIndex={123}
55
+ title="bar"
56
+ />
57
+ <Panel />
58
+ </PanelGroup>
59
+ );
60
+ });
61
+
62
+ const element = getResizeHandleElement("handle");
63
+ assert(element);
64
+ expect(element.tabIndex).toBe(123);
65
+ expect(element.getAttribute("data-test-name")).toBe("foo");
66
+ expect(element.title).toBe("bar");
67
+ });
68
+ });
@@ -3,8 +3,9 @@ import {
3
3
  createElement,
4
4
  CSSProperties,
5
5
  ElementType,
6
+ HTMLAttributes,
7
+ PropsWithChildren,
6
8
  MouseEvent as ReactMouseEvent,
7
- ReactNode,
8
9
  TouchEvent,
9
10
  useCallback,
10
11
  useContext,
@@ -19,31 +20,32 @@ import {
19
20
  ResizeEvent,
20
21
  ResizeHandler,
21
22
  } from "./PanelGroupContext";
23
+ import { assert } from "./utils/assert";
22
24
  import { getCursorStyle } from "./utils/cursor";
23
- import { DataAttributes } from "./types";
24
25
 
25
26
  export type PanelResizeHandleOnDragging = (isDragging: boolean) => void;
26
27
 
27
- export type PanelResizeHandleProps = {
28
- children?: ReactNode;
29
- className?: string;
30
- dataAttributes?: DataAttributes;
31
- disabled?: boolean;
32
- id?: string | null;
33
- onDragging?: PanelResizeHandleOnDragging;
34
- style?: CSSProperties;
35
- tagName?: ElementType;
36
- };
28
+ export type PanelResizeHandleProps = Omit<HTMLAttributes<ElementType>, "id"> &
29
+ PropsWithChildren<{
30
+ className?: string;
31
+ disabled?: boolean;
32
+ id?: string | null;
33
+ onDragging?: PanelResizeHandleOnDragging;
34
+ style?: CSSProperties;
35
+ tabIndex?: number;
36
+ tagName?: ElementType;
37
+ }>;
37
38
 
38
39
  export function PanelResizeHandle({
39
40
  children = null,
40
41
  className: classNameFromProps = "",
41
- dataAttributes,
42
42
  disabled = false,
43
- id: idFromProps = null,
43
+ id: idFromProps,
44
44
  onDragging,
45
45
  style: styleFromProps = {},
46
+ tabIndex = 0,
46
47
  tagName: Type = "div",
48
+ ...rest
47
49
  }: PanelResizeHandleProps) {
48
50
  const divElementRef = useRef<HTMLDivElement>(null);
49
51
 
@@ -83,8 +85,9 @@ export function PanelResizeHandle({
83
85
  const stopDraggingAndBlur = useCallback(() => {
84
86
  // Clicking on the drag handle shouldn't leave it focused;
85
87
  // That would cause the PanelGroup to think it was still active.
86
- const div = divElementRef.current!;
87
- div.blur();
88
+ const divElement = divElementRef.current;
89
+ assert(divElement);
90
+ divElement.blur();
88
91
 
89
92
  stopDragging();
90
93
 
@@ -116,7 +119,9 @@ export function PanelResizeHandle({
116
119
  resizeHandler(event);
117
120
  };
118
121
 
119
- const divElement = divElementRef.current!;
122
+ const divElement = divElementRef.current;
123
+ assert(divElement);
124
+
120
125
  const targetDocument = divElement.ownerDocument;
121
126
 
122
127
  targetDocument.body.addEventListener("contextmenu", stopDraggingAndBlur);
@@ -152,6 +157,8 @@ export function PanelResizeHandle({
152
157
  };
153
158
 
154
159
  return createElement(Type, {
160
+ ...rest,
161
+
155
162
  children,
156
163
  className: classNameFromProps,
157
164
  onBlur: () => setIsFocused(false),
@@ -159,7 +166,9 @@ export function PanelResizeHandle({
159
166
  onMouseDown: (event: ReactMouseEvent) => {
160
167
  startDragging(resizeHandleId, event.nativeEvent);
161
168
 
162
- const { onDragging } = callbacksRef.current!;
169
+ const callbacks = callbacksRef.current;
170
+ assert(callbacks);
171
+ const { onDragging } = callbacks;
163
172
  if (onDragging) {
164
173
  onDragging(true);
165
174
  }
@@ -170,7 +179,9 @@ export function PanelResizeHandle({
170
179
  onTouchStart: (event: TouchEvent) => {
171
180
  startDragging(resizeHandleId, event.nativeEvent);
172
181
 
173
- const { onDragging } = callbacksRef.current!;
182
+ const callbacks = callbacksRef.current;
183
+ assert(callbacks);
184
+ const { onDragging } = callbacks;
174
185
  if (onDragging) {
175
186
  onDragging(true);
176
187
  }
@@ -181,9 +192,7 @@ export function PanelResizeHandle({
181
192
  ...style,
182
193
  ...styleFromProps,
183
194
  },
184
- tabIndex: 0,
185
-
186
- ...dataAttributes,
195
+ tabIndex,
187
196
 
188
197
  // CSS selectors
189
198
  "data-panel-group-direction": direction,
@@ -46,7 +46,8 @@ export function useWindowSplitterResizeHandlerBehavior({
46
46
  case "F6": {
47
47
  event.preventDefault();
48
48
 
49
- const groupId = handleElement.getAttribute("data-panel-group-id")!;
49
+ const groupId = handleElement.getAttribute("data-panel-group-id");
50
+ assert(groupId);
50
51
 
51
52
  const handles = getResizeHandleElementsForGroup(groupId);
52
53
  const index = getResizeHandleElementIndex(groupId, handleId);
@@ -5,12 +5,9 @@ import { adjustLayoutByDelta } from "../utils/adjustLayoutByDelta";
5
5
  import { assert } from "../utils/assert";
6
6
  import { calculateAriaValues } from "../utils/calculateAriaValues";
7
7
  import { determinePivotIndices } from "../utils/determinePivotIndices";
8
- import { calculateAvailablePanelSizeInPixels } from "../utils/dom/calculateAvailablePanelSizeInPixels";
9
- import { getAvailableGroupSizePixels } from "../utils/dom/getAvailableGroupSizePixels";
10
8
  import { getPanelGroupElement } from "../utils/dom/getPanelGroupElement";
11
9
  import { getResizeHandleElementsForGroup } from "../utils/dom/getResizeHandleElementsForGroup";
12
10
  import { getResizeHandlePanelIds } from "../utils/dom/getResizeHandlePanelIds";
13
- import { getPercentageSizeFromMixedSizes } from "../utils/getPercentageSizeFromMixedSizes";
14
11
  import { fuzzyNumbersEqual } from "../utils/numbers/fuzzyNumbersEqual";
15
12
  import { RefObject, useEffect, useRef } from "../vendor/react";
16
13
  import useIsomorphicLayoutEffect from "./useIsomorphicEffect";
@@ -43,12 +40,10 @@ export function useWindowSplitterPanelGroupBehavior({
43
40
  });
44
41
 
45
42
  useIsomorphicLayoutEffect(() => {
46
- const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
47
43
  const resizeHandleElements = getResizeHandleElementsForGroup(groupId);
48
44
 
49
45
  for (let index = 0; index < panelDataArray.length - 1; index++) {
50
46
  const { valueMax, valueMin, valueNow } = calculateAriaValues({
51
- groupSizePixels,
52
47
  layout,
53
48
  panelsArray: panelDataArray,
54
49
  pivotIndices: [index, index + 1],
@@ -68,10 +63,10 @@ export function useWindowSplitterPanelGroupBehavior({
68
63
  }
69
64
  }
70
65
  } else {
71
- resizeHandleElement.setAttribute(
72
- "aria-controls",
73
- panelDataArray[index].id
74
- );
66
+ const panelData = panelDataArray[index];
67
+ assert(panelData);
68
+
69
+ resizeHandleElement.setAttribute("aria-controls", panelData.id);
75
70
  resizeHandleElement.setAttribute(
76
71
  "aria-valuemax",
77
72
  "" + Math.round(valueMax)
@@ -82,7 +77,7 @@ export function useWindowSplitterPanelGroupBehavior({
82
77
  );
83
78
  resizeHandleElement.setAttribute(
84
79
  "aria-valuenow",
85
- "" + Math.round(valueNow)
80
+ valueNow != null ? "" + Math.round(valueNow) : ""
86
81
  );
87
82
  }
88
83
  }
@@ -98,14 +93,20 @@ export function useWindowSplitterPanelGroupBehavior({
98
93
  }, [groupId, layout, panelDataArray]);
99
94
 
100
95
  useEffect(() => {
101
- const { panelDataArray } = eagerValuesRef.current!;
96
+ const eagerValues = eagerValuesRef.current;
97
+ assert(eagerValues);
98
+
99
+ const { panelDataArray } = eagerValues;
102
100
 
103
101
  const groupElement = getPanelGroupElement(groupId);
104
102
  assert(groupElement != null, `No group found for id "${groupId}"`);
105
103
 
106
104
  const handles = getResizeHandleElementsForGroup(groupId);
105
+ assert(handles);
106
+
107
107
  const cleanupFunctions = handles.map((handle) => {
108
- const handleId = handle.getAttribute("data-panel-resize-handle-id")!;
108
+ const handleId = handle.getAttribute("data-panel-resize-handle-id");
109
+ assert(handleId);
109
110
 
110
111
  const [idBefore, idAfter] = getResizeHandlePanelIds(
111
112
  groupId,
@@ -130,33 +131,21 @@ export function useWindowSplitterPanelGroupBehavior({
130
131
  );
131
132
  if (index >= 0) {
132
133
  const panelData = panelDataArray[index];
134
+ assert(panelData);
135
+
133
136
  const size = layout[index];
134
- if (size != null && panelData.constraints.collapsible) {
135
- const groupSizePixels = getAvailableGroupSizePixels(groupId);
136
-
137
- const collapsedSize =
138
- getPercentageSizeFromMixedSizes(
139
- {
140
- sizePercentage:
141
- panelData.constraints.collapsedSizePercentage,
142
- sizePixels: panelData.constraints.collapsedSizePixels,
143
- },
144
- groupSizePixels
145
- ) ?? 0;
146
- const minSize =
147
- getPercentageSizeFromMixedSizes(
148
- {
149
- sizePercentage: panelData.constraints.minSizePercentage,
150
- sizePixels: panelData.constraints.minSizePixels,
151
- },
152
- groupSizePixels
153
- ) ?? 0;
154
137
 
138
+ const {
139
+ collapsedSize = 0,
140
+ collapsible,
141
+ minSize = 0,
142
+ } = panelData.constraints;
143
+
144
+ if (size != null && collapsible) {
155
145
  const nextLayout = adjustLayoutByDelta({
156
146
  delta: fuzzyNumbersEqual(size, collapsedSize)
157
147
  ? minSize - collapsedSize
158
148
  : collapsedSize - size,
159
- groupSizePixels,
160
149
  layout,
161
150
  panelConstraints: panelDataArray.map(
162
151
  (panelData) => panelData.constraints
package/src/index.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { Panel } from "./Panel";
2
2
  import { PanelGroup } from "./PanelGroup";
3
3
  import { PanelResizeHandle } from "./PanelResizeHandle";
4
-
5
- import type { MixedSizes } from "./types";
4
+ import { assert } from "./utils/assert";
6
5
 
7
6
  import type {
8
7
  ImperativePanelHandle,
@@ -26,7 +25,6 @@ export {
26
25
  // TypeScript types
27
26
  ImperativePanelGroupHandle,
28
27
  ImperativePanelHandle,
29
- MixedSizes,
30
28
  PanelGroupOnLayout,
31
29
  PanelGroupProps,
32
30
  PanelGroupStorage,
@@ -37,6 +35,9 @@ export {
37
35
  PanelResizeHandleOnDragging,
38
36
  PanelResizeHandleProps,
39
37
 
38
+ // Utiltiy methods
39
+ assert,
40
+
40
41
  // React components
41
42
  Panel,
42
43
  PanelGroup,
package/src/types.ts CHANGED
@@ -1,13 +1,4 @@
1
1
  export type Direction = "horizontal" | "vertical";
2
2
 
3
- export type MixedSizes = {
4
- sizePercentage: number;
5
- sizePixels: number;
6
- };
7
-
8
3
  export type ResizeEvent = KeyboardEvent | MouseEvent | TouchEvent;
9
4
  export type ResizeHandler = (event: ResizeEvent) => void;
10
-
11
- export type DataAttributes = {
12
- [attribute: string]: string | number | boolean | undefined;
13
- };