react-resizable-panels 0.0.22 → 0.0.24

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.
@@ -1,4 +1,5 @@
1
1
  import {
2
+ createElement,
2
3
  CSSProperties,
3
4
  ElementType,
4
5
  MouseEvent,
@@ -16,6 +17,15 @@ import { useWindowSplitterResizeHandlerBehavior } from "./hooks/useWindowSplitte
16
17
  import { PanelGroupContext } from "./PanelContexts";
17
18
  import type { ResizeHandler, ResizeEvent } from "./types";
18
19
 
20
+ export type PanelResizeHandleProps = {
21
+ children?: ReactNode;
22
+ className?: string;
23
+ disabled?: boolean;
24
+ id?: string | null;
25
+ style?: CSSProperties;
26
+ tagName?: ElementType;
27
+ };
28
+
19
29
  export default function PanelResizeHandle({
20
30
  children = null,
21
31
  className: classNameFromProps = "",
@@ -23,14 +33,7 @@ export default function PanelResizeHandle({
23
33
  id: idFromProps = null,
24
34
  style: styleFromProps = {},
25
35
  tagName: Type = "div",
26
- }: {
27
- children?: ReactNode;
28
- className?: string;
29
- disabled?: boolean;
30
- id?: string | null;
31
- style?: CSSProperties;
32
- tagName?: ElementType;
33
- }) {
36
+ }: PanelResizeHandleProps) {
34
37
  const divElementRef = useRef<HTMLDivElement>(null);
35
38
 
36
39
  const panelGroupContext = useContext(PanelGroupContext);
@@ -89,19 +92,19 @@ export default function PanelResizeHandle({
89
92
  };
90
93
 
91
94
  document.body.addEventListener("contextmenu", stopDraggingAndBlur);
92
- document.body.addEventListener("mouseleave", stopDraggingAndBlur);
93
95
  document.body.addEventListener("mousemove", onMove);
94
96
  document.body.addEventListener("touchmove", onMove);
95
- document.body.addEventListener("mouseup", stopDraggingAndBlur);
97
+ window.addEventListener("mouseup", stopDraggingAndBlur);
98
+ window.addEventListener("touchend", stopDraggingAndBlur);
96
99
 
97
100
  return () => {
98
101
  document.body.style.cursor = "";
99
102
 
100
103
  document.body.removeEventListener("contextmenu", stopDraggingAndBlur);
101
- document.body.removeEventListener("mouseleave", stopDraggingAndBlur);
102
104
  document.body.removeEventListener("mousemove", onMove);
103
105
  document.body.removeEventListener("touchmove", onMove);
104
- document.body.removeEventListener("mouseup", stopDraggingAndBlur);
106
+ window.removeEventListener("mouseup", stopDraggingAndBlur);
107
+ window.removeEventListener("touchend", stopDraggingAndBlur);
105
108
  };
106
109
  }, [direction, disabled, isDragging, resizeHandler, stopDraggingAndBlur]);
107
110
 
@@ -117,38 +120,35 @@ export default function PanelResizeHandle({
117
120
  userSelect: "none",
118
121
  };
119
122
 
120
- return (
121
- <Type
122
- className={classNameFromProps}
123
- data-resize-handle-active={
124
- isDragging ? "pointer" : isFocused ? "keyboard" : undefined
125
- }
126
- data-panel-group-direction={direction}
127
- data-panel-group-id={groupId}
128
- data-panel-resize-handle-enabled={!disabled}
129
- data-panel-resize-handle-id={resizeHandleId}
130
- onBlur={() => setIsFocused(false)}
131
- onFocus={() => setIsFocused(true)}
132
- onMouseDown={(event: MouseEvent) =>
133
- startDragging(resizeHandleId, event.nativeEvent)
134
- }
135
- onMouseUp={stopDraggingAndBlur}
136
- onTouchCancel={stopDraggingAndBlur}
137
- onTouchEnd={stopDraggingAndBlur}
138
- onTouchStart={(event: TouchEvent) =>
139
- startDragging(resizeHandleId, event.nativeEvent)
140
- }
141
- ref={divElementRef}
142
- role="separator"
143
- style={{
144
- ...style,
145
- ...styleFromProps,
146
- }}
147
- tabIndex={0}
148
- >
149
- {children}
150
- </Type>
151
- );
123
+ return createElement(Type, {
124
+ children,
125
+ className: classNameFromProps,
126
+ "data-resize-handle-active": isDragging
127
+ ? "pointer"
128
+ : isFocused
129
+ ? "keyboard"
130
+ : undefined,
131
+ "data-panel-group-direction": direction,
132
+ "data-panel-group-id": groupId,
133
+ "data-panel-resize-handle-enabled": !disabled,
134
+ "data-panel-resize-handle-id": resizeHandleId,
135
+ onBlur: () => setIsFocused(false),
136
+ onFocus: () => setIsFocused(true),
137
+ onMouseDown: (event: MouseEvent) =>
138
+ startDragging(resizeHandleId, event.nativeEvent),
139
+ onMouseUp: stopDraggingAndBlur,
140
+ onTouchCancel: stopDraggingAndBlur,
141
+ onTouchEnd: stopDraggingAndBlur,
142
+ onTouchStart: (event: TouchEvent) =>
143
+ startDragging(resizeHandleId, event.nativeEvent),
144
+ ref: divElementRef,
145
+ role: "separator",
146
+ style: {
147
+ ...style,
148
+ ...styleFromProps,
149
+ },
150
+ tabIndex: 0,
151
+ });
152
152
  }
153
153
 
154
154
  // Workaround for Parcel scope hoisting (which renames objects/functions).
@@ -51,15 +51,27 @@ export function useWindowSplitterPanelGroupBehavior({
51
51
  return () => {};
52
52
  }
53
53
 
54
- const ariaValueMax = panelsArray.reduce((difference, panel) => {
55
- if (panel.id !== idBefore) {
56
- return difference - panel.minSize;
54
+ let minSize = 0;
55
+ let maxSize = 100;
56
+ let totalMinSize = 0;
57
+ let totalMaxSize = 0;
58
+
59
+ // A panel's effective min/max sizes also need to account for other panel's sizes.
60
+ panelsArray.forEach((panelData) => {
61
+ if (panelData.id === idBefore) {
62
+ maxSize = panelData.maxSize;
63
+ minSize = panelData.minSize;
64
+ } else {
65
+ totalMinSize += panelData.minSize;
66
+ totalMaxSize += panelData.maxSize;
57
67
  }
58
- return difference;
59
- }, 100);
68
+ });
60
69
 
61
- const ariaValueMin =
62
- panelsArray.find((panel) => panel.id == idBefore)?.minSize ?? 0;
70
+ const ariaValueMax = Math.min(maxSize, 100 - totalMinSize);
71
+ const ariaValueMin = Math.max(
72
+ minSize,
73
+ (panelsArray.length - 1) * 100 - totalMaxSize
74
+ );
63
75
 
64
76
  const flexGrow = getFlexGrow(panels, idBefore, sizes);
65
77
 
package/src/types.ts CHANGED
@@ -1,9 +1,15 @@
1
+ import { RefObject } from "react";
2
+
1
3
  export type Direction = "horizontal" | "vertical";
2
4
 
5
+ export type PanelOnResize = (size: number) => void;
6
+
3
7
  export type PanelData = {
4
8
  defaultSize: number;
5
9
  id: string;
10
+ maxSize: number;
6
11
  minSize: number;
12
+ onResizeRef: RefObject<PanelOnResize | null>;
7
13
  order: number | null;
8
14
  };
9
15
 
@@ -64,17 +64,9 @@ export function getMovement(
64
64
  case "ArrowUp":
65
65
  return isHorizontal ? 0 : -delta;
66
66
  case "End":
67
- if (isHorizontal) {
68
- return size;
69
- } else {
70
- return size;
71
- }
67
+ return size;
72
68
  case "Home":
73
- if (isHorizontal) {
74
- return -size;
75
- } else {
76
- return -size;
77
- }
69
+ return -size;
78
70
  }
79
71
  } else {
80
72
  return getDragOffset(event, handleId, direction, initialOffset);
@@ -25,12 +25,29 @@ export function adjustByDelta(
25
25
  //
26
26
  // A positive delta means the panel immediately before the resizer should "expand".
27
27
  // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resizer.
28
+
29
+ // Max-bounds check the panel being expanded first.
30
+ {
31
+ const pivotId = delta < 0 ? idAfter : idBefore;
32
+ const index = panelsArray.findIndex((panel) => panel.id === pivotId);
33
+ const panel = panelsArray[index];
34
+ const prevSize = prevSizes[index];
35
+
36
+ const nextSize = safeResizePanel(panel, Math.abs(delta), prevSize);
37
+ if (prevSize === nextSize) {
38
+ return prevSizes;
39
+ } else {
40
+ delta = delta < 0 ? prevSize - nextSize : nextSize - prevSize;
41
+ }
42
+ }
43
+
28
44
  let pivotId = delta < 0 ? idBefore : idAfter;
29
45
  let index = panelsArray.findIndex((panel) => panel.id === pivotId);
30
46
  while (true) {
31
47
  const panel = panelsArray[index];
32
48
  const prevSize = prevSizes[index];
33
- const nextSize = Math.max(prevSize - Math.abs(delta), panel.minSize);
49
+
50
+ const nextSize = safeResizePanel(panel, 0 - Math.abs(delta), prevSize);
34
51
  if (prevSize !== nextSize) {
35
52
  deltaApplied += prevSize - nextSize;
36
53
 
@@ -154,3 +171,16 @@ export function panelsMapToSortedArray(
154
171
  ): PanelData[] {
155
172
  return Array.from(panels.values()).sort((a, b) => a.order - b.order);
156
173
  }
174
+
175
+ function safeResizePanel(
176
+ panel: PanelData,
177
+ delta: number,
178
+ prevSize: number
179
+ ): number {
180
+ const nextSizeUnsafe = prevSize + delta;
181
+ const nextSize = Math.min(
182
+ panel.maxSize,
183
+ Math.max(panel.minSize, nextSizeUnsafe)
184
+ );
185
+ return nextSize;
186
+ }