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

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.
@@ -1116,7 +1116,7 @@ function PanelGroupWithForwardedRef({
1116
1116
  // Store size before collapse;
1117
1117
  // This is the size that gets restored if the expand() API is used.
1118
1118
  panelSizeBeforeCollapseRef.current.set(panelData.id, panelSize);
1119
- const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
1119
+ const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
1120
1120
  const delta = isLastPanel ? panelSize - collapsedSize : collapsedSize - panelSize;
1121
1121
  const nextLayout = adjustLayoutByDelta({
1122
1122
  delta,
@@ -1158,7 +1158,7 @@ function PanelGroupWithForwardedRef({
1158
1158
  // Restore this panel to the size it was before it was collapsed, if possible.
1159
1159
  const prevPanelSize = panelSizeBeforeCollapseRef.current.get(panelData.id);
1160
1160
  const baseSize = prevPanelSize != null && prevPanelSize >= minSize ? prevPanelSize : minSize;
1161
- const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
1161
+ const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
1162
1162
  const delta = isLastPanel ? panelSize - baseSize : baseSize - panelSize;
1163
1163
  const nextLayout = adjustLayoutByDelta({
1164
1164
  delta,
@@ -1197,7 +1197,7 @@ function PanelGroupWithForwardedRef({
1197
1197
  const {
1198
1198
  panelDataArray
1199
1199
  } = eagerValuesRef.current;
1200
- const panelIndex = panelDataArray.indexOf(panelData);
1200
+ const panelIndex = findPanelDataIndex(panelDataArray, panelData);
1201
1201
  return computePanelFlexBoxStyle({
1202
1202
  dragState,
1203
1203
  layout,
@@ -1245,6 +1245,19 @@ function PanelGroupWithForwardedRef({
1245
1245
  layout: prevLayout,
1246
1246
  panelDataArray
1247
1247
  } = eagerValuesRef.current;
1248
+
1249
+ // HACK
1250
+ // This appears to be triggered by some React Suspense+Offscreen+StrictMode bug;
1251
+ // see app.replay.io/recording/17b6e11d-4500-4173-b23d-61dfd141fed1
1252
+ const index = findPanelDataIndex(panelDataArray, panelData);
1253
+ if (index >= 0) {
1254
+ if (panelData.idIsFromProps) {
1255
+ console.warn(`Panel with id "${panelData.id}" registered twice`);
1256
+ } else {
1257
+ console.warn(`Panel registered twice`);
1258
+ }
1259
+ return;
1260
+ }
1248
1261
  panelDataArray.push(panelData);
1249
1262
  panelDataArray.sort((panelA, panelB) => {
1250
1263
  const orderA = panelA.order;
@@ -1386,7 +1399,7 @@ function PanelGroupWithForwardedRef({
1386
1399
  pivotIndices
1387
1400
  } = panelDataHelper(panelDataArray, panelData, prevLayout);
1388
1401
  assert(panelSize != null);
1389
- const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
1402
+ const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
1390
1403
  const delta = isLastPanel ? panelSize - unsafePanelSize : unsafePanelSize - panelSize;
1391
1404
  const nextLayout = adjustLayoutByDelta({
1392
1405
  delta,
@@ -1437,7 +1450,7 @@ function PanelGroupWithForwardedRef({
1437
1450
  layout: prevLayout,
1438
1451
  panelDataArray
1439
1452
  } = eagerValuesRef.current;
1440
- const index = panelDataArray.indexOf(panelData);
1453
+ const index = findPanelDataIndex(panelDataArray, panelData);
1441
1454
  if (index >= 0) {
1442
1455
  panelDataArray.splice(index, 1);
1443
1456
  unregisterPanelRef.current.pendingPanelIds.add(panelData.id);
@@ -1453,7 +1466,7 @@ function PanelGroupWithForwardedRef({
1453
1466
  const {
1454
1467
  pendingPanelIds
1455
1468
  } = unregisterPanelRef.current;
1456
- const map = panelIdToLastNotifiedSizeMapRef.current;
1469
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1457
1470
 
1458
1471
  // TRICKY
1459
1472
  // Strict effects mode
@@ -1464,15 +1477,15 @@ function PanelGroupWithForwardedRef({
1464
1477
  id
1465
1478
  }) => id === panelId) != null) {
1466
1479
  unmountDueToStrictMode = true;
1467
-
1480
+ } else {
1468
1481
  // TRICKY
1469
1482
  // When a panel is removed from the group, we should delete the most recent prev-size entry for it.
1470
1483
  // If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
1471
1484
  // Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
1472
- delete map[panelData.id];
1485
+ delete panelIdToLastNotifiedSizeMap[panelId];
1473
1486
  }
1474
1487
  });
1475
- if (!unmountDueToStrictMode) {
1488
+ if (unmountDueToStrictMode) {
1476
1489
  return;
1477
1490
  }
1478
1491
  if (panelDataArray.length === 0) {
@@ -1545,9 +1558,12 @@ const PanelGroup = forwardRef((props, ref) => createElement(PanelGroupWithForwar
1545
1558
  }));
1546
1559
  PanelGroupWithForwardedRef.displayName = "PanelGroup";
1547
1560
  PanelGroup.displayName = "forwardRef(PanelGroup)";
1561
+ function findPanelDataIndex(panelDataArray, panelData) {
1562
+ return panelDataArray.findIndex(prevPanelData => prevPanelData === panelData || prevPanelData.id === panelData.id);
1563
+ }
1548
1564
  function panelDataHelper(panelDataArray, panelData, layout) {
1549
1565
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1550
- const panelIndex = panelDataArray.indexOf(panelData);
1566
+ const panelIndex = findPanelDataIndex(panelDataArray, panelData);
1551
1567
  const panelConstraints = panelConstraintsArray[panelIndex];
1552
1568
  const isLastPanel = panelIndex === panelDataArray.length - 1;
1553
1569
  const pivotIndices = isLastPanel ? [panelIndex - 1, panelIndex] : [panelIndex, panelIndex + 1];
@@ -1092,7 +1092,7 @@ function PanelGroupWithForwardedRef({
1092
1092
  // Store size before collapse;
1093
1093
  // This is the size that gets restored if the expand() API is used.
1094
1094
  panelSizeBeforeCollapseRef.current.set(panelData.id, panelSize);
1095
- const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
1095
+ const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
1096
1096
  const delta = isLastPanel ? panelSize - collapsedSize : collapsedSize - panelSize;
1097
1097
  const nextLayout = adjustLayoutByDelta({
1098
1098
  delta,
@@ -1134,7 +1134,7 @@ function PanelGroupWithForwardedRef({
1134
1134
  // Restore this panel to the size it was before it was collapsed, if possible.
1135
1135
  const prevPanelSize = panelSizeBeforeCollapseRef.current.get(panelData.id);
1136
1136
  const baseSize = prevPanelSize != null && prevPanelSize >= minSize ? prevPanelSize : minSize;
1137
- const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
1137
+ const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
1138
1138
  const delta = isLastPanel ? panelSize - baseSize : baseSize - panelSize;
1139
1139
  const nextLayout = adjustLayoutByDelta({
1140
1140
  delta,
@@ -1173,7 +1173,7 @@ function PanelGroupWithForwardedRef({
1173
1173
  const {
1174
1174
  panelDataArray
1175
1175
  } = eagerValuesRef.current;
1176
- const panelIndex = panelDataArray.indexOf(panelData);
1176
+ const panelIndex = findPanelDataIndex(panelDataArray, panelData);
1177
1177
  return computePanelFlexBoxStyle({
1178
1178
  dragState,
1179
1179
  layout,
@@ -1221,6 +1221,19 @@ function PanelGroupWithForwardedRef({
1221
1221
  layout: prevLayout,
1222
1222
  panelDataArray
1223
1223
  } = eagerValuesRef.current;
1224
+
1225
+ // HACK
1226
+ // This appears to be triggered by some React Suspense+Offscreen+StrictMode bug;
1227
+ // see app.replay.io/recording/17b6e11d-4500-4173-b23d-61dfd141fed1
1228
+ const index = findPanelDataIndex(panelDataArray, panelData);
1229
+ if (index >= 0) {
1230
+ if (panelData.idIsFromProps) {
1231
+ console.warn(`Panel with id "${panelData.id}" registered twice`);
1232
+ } else {
1233
+ console.warn(`Panel registered twice`);
1234
+ }
1235
+ return;
1236
+ }
1224
1237
  panelDataArray.push(panelData);
1225
1238
  panelDataArray.sort((panelA, panelB) => {
1226
1239
  const orderA = panelA.order;
@@ -1362,7 +1375,7 @@ function PanelGroupWithForwardedRef({
1362
1375
  pivotIndices
1363
1376
  } = panelDataHelper(panelDataArray, panelData, prevLayout);
1364
1377
  assert(panelSize != null);
1365
- const isLastPanel = panelDataArray.indexOf(panelData) === panelDataArray.length - 1;
1378
+ const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
1366
1379
  const delta = isLastPanel ? panelSize - unsafePanelSize : unsafePanelSize - panelSize;
1367
1380
  const nextLayout = adjustLayoutByDelta({
1368
1381
  delta,
@@ -1413,7 +1426,7 @@ function PanelGroupWithForwardedRef({
1413
1426
  layout: prevLayout,
1414
1427
  panelDataArray
1415
1428
  } = eagerValuesRef.current;
1416
- const index = panelDataArray.indexOf(panelData);
1429
+ const index = findPanelDataIndex(panelDataArray, panelData);
1417
1430
  if (index >= 0) {
1418
1431
  panelDataArray.splice(index, 1);
1419
1432
  unregisterPanelRef.current.pendingPanelIds.add(panelData.id);
@@ -1429,7 +1442,7 @@ function PanelGroupWithForwardedRef({
1429
1442
  const {
1430
1443
  pendingPanelIds
1431
1444
  } = unregisterPanelRef.current;
1432
- const map = panelIdToLastNotifiedSizeMapRef.current;
1445
+ const panelIdToLastNotifiedSizeMap = panelIdToLastNotifiedSizeMapRef.current;
1433
1446
 
1434
1447
  // TRICKY
1435
1448
  // Strict effects mode
@@ -1440,15 +1453,15 @@ function PanelGroupWithForwardedRef({
1440
1453
  id
1441
1454
  }) => id === panelId) != null) {
1442
1455
  unmountDueToStrictMode = true;
1443
-
1456
+ } else {
1444
1457
  // TRICKY
1445
1458
  // When a panel is removed from the group, we should delete the most recent prev-size entry for it.
1446
1459
  // If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
1447
1460
  // Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
1448
- delete map[panelData.id];
1461
+ delete panelIdToLastNotifiedSizeMap[panelId];
1449
1462
  }
1450
1463
  });
1451
- if (!unmountDueToStrictMode) {
1464
+ if (unmountDueToStrictMode) {
1452
1465
  return;
1453
1466
  }
1454
1467
  if (panelDataArray.length === 0) {
@@ -1521,9 +1534,12 @@ const PanelGroup = forwardRef((props, ref) => createElement(PanelGroupWithForwar
1521
1534
  }));
1522
1535
  PanelGroupWithForwardedRef.displayName = "PanelGroup";
1523
1536
  PanelGroup.displayName = "forwardRef(PanelGroup)";
1537
+ function findPanelDataIndex(panelDataArray, panelData) {
1538
+ return panelDataArray.findIndex(prevPanelData => prevPanelData === panelData || prevPanelData.id === panelData.id);
1539
+ }
1524
1540
  function panelDataHelper(panelDataArray, panelData, layout) {
1525
1541
  const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
1526
- const panelIndex = panelDataArray.indexOf(panelData);
1542
+ const panelIndex = findPanelDataIndex(panelDataArray, panelData);
1527
1543
  const panelConstraints = panelConstraintsArray[panelIndex];
1528
1544
  const isLastPanel = panelIndex === panelDataArray.length - 1;
1529
1545
  const pivotIndices = isLastPanel ? [panelIndex - 1, panelIndex] : [panelIndex, panelIndex + 1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0-rc.3",
4
4
  "description": "React components for resizable panel groups/layouts",
5
5
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
6
6
  "license": "MIT",
@@ -0,0 +1,149 @@
1
+ import { isDevelopment } from "#is-development";
2
+ import { PanelData } from "./Panel";
3
+ import { DragState, PanelGroupContext, ResizeEvent } from "./PanelGroupContext";
4
+ import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
5
+ import useUniqueId from "./hooks/useUniqueId";
6
+ import { useWindowSplitterPanelGroupBehavior } from "./hooks/useWindowSplitterPanelGroupBehavior";
7
+ import { Direction } from "./types";
8
+ import { adjustLayoutByDelta } from "./utils/adjustLayoutByDelta";
9
+ import { areEqual } from "./utils/arrays";
10
+ import { assert } from "./utils/assert";
11
+ import { calculateDeltaPercentage } from "./utils/calculateDeltaPercentage";
12
+ import { calculateUnsafeDefaultLayout } from "./utils/calculateUnsafeDefaultLayout";
13
+ import { callPanelCallbacks } from "./utils/callPanelCallbacks";
14
+ import { compareLayouts } from "./utils/compareLayouts";
15
+ import { computePanelFlexBoxStyle } from "./utils/computePanelFlexBoxStyle";
16
+ import { resetGlobalCursorStyle, setGlobalCursorStyle } from "./utils/cursor";
17
+ import debounce from "./utils/debounce";
18
+ import { determinePivotIndices } from "./utils/determinePivotIndices";
19
+ import { getPanelElementsForGroup } from "./utils/dom/getPanelElementsForGroup";
20
+ import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
21
+ import { isKeyDown, isMouseEvent, isTouchEvent } from "./utils/events";
22
+ import { getResizeEventCursorPosition } from "./utils/getResizeEventCursorPosition";
23
+ import { initializeDefaultStorage } from "./utils/initializeDefaultStorage";
24
+ import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
25
+ import { validatePanelConstraints } from "./utils/validatePanelConstraints";
26
+ import { validatePanelGroupLayout } from "./utils/validatePanelGroupLayout";
27
+ import {
28
+ CSSProperties,
29
+ ElementType,
30
+ ForwardedRef,
31
+ HTMLAttributes,
32
+ PropsWithChildren,
33
+ createElement,
34
+ forwardRef,
35
+ useCallback,
36
+ useEffect,
37
+ useImperativeHandle,
38
+ useMemo,
39
+ useRef,
40
+ useState,
41
+ } from "./vendor/react";
42
+
43
+ const LOCAL_STORAGE_DEBOUNCE_INTERVAL = 100;
44
+
45
+ export type ImperativePanelGroupHandle = {
46
+ getId: () => string;
47
+ getLayout: () => number[];
48
+ setLayout: (layout: number[]) => void;
49
+ };
50
+
51
+ export type PanelGroupStorage = {
52
+ getItem(name: string): string | null;
53
+ setItem(name: string, value: string): void;
54
+ };
55
+
56
+ export type PanelGroupOnLayout = (layout: number[]) => void;
57
+
58
+ const defaultStorage: PanelGroupStorage = {
59
+ getItem: (name: string) => {
60
+ initializeDefaultStorage(defaultStorage);
61
+ return defaultStorage.getItem(name);
62
+ },
63
+ setItem: (name: string, value: string) => {
64
+ initializeDefaultStorage(defaultStorage);
65
+ defaultStorage.setItem(name, value);
66
+ },
67
+ };
68
+
69
+ export type PanelGroupProps = Omit<HTMLAttributes<ElementType>, "id"> &
70
+ PropsWithChildren<{
71
+ autoSaveId?: string | null;
72
+ className?: string;
73
+ direction: Direction;
74
+ id?: string | null;
75
+ keyboardResizeBy?: number | null;
76
+ onLayout?: PanelGroupOnLayout | null;
77
+ storage?: PanelGroupStorage;
78
+ style?: CSSProperties;
79
+ tagName?: ElementType;
80
+ }>;
81
+
82
+ const debounceMap: {
83
+ [key: string]: typeof savePanelGroupLayout;
84
+ } = {};
85
+
86
+ function PanelGroupWithForwardedRef({
87
+ autoSaveId = null,
88
+ children,
89
+ className: classNameFromProps = "",
90
+ direction,
91
+ forwardedRef,
92
+ id: idFromProps = null,
93
+ onLayout = null,
94
+ keyboardResizeBy = null,
95
+ storage = defaultStorage,
96
+ style: styleFromProps,
97
+ tagName: Type = "div",
98
+ ...rest
99
+ }: PanelGroupProps & {
100
+ forwardedRef: ForwardedRef<ImperativePanelGroupHandle>;
101
+ }) {
102
+ const groupId = useUniqueId(idFromProps);
103
+
104
+ const stateRef = useRef<{
105
+ layout: number[];
106
+ }>({
107
+ layout: [],
108
+ });
109
+
110
+ const context = null as any;
111
+
112
+ const style: CSSProperties = {
113
+ display: "flex",
114
+ flexDirection: direction === "horizontal" ? "row" : "column",
115
+ height: "100%",
116
+ overflow: "hidden",
117
+ width: "100%",
118
+ };
119
+
120
+ return createElement(
121
+ PanelGroupContext.Provider,
122
+ { value: context },
123
+ createElement(Type, {
124
+ ...rest,
125
+
126
+ children,
127
+ className: classNameFromProps,
128
+ style: {
129
+ ...style,
130
+ ...styleFromProps,
131
+ },
132
+
133
+ // CSS selectors
134
+ "data-panel-group": "",
135
+ "data-panel-group-direction": direction,
136
+ "data-panel-group-id": groupId,
137
+ })
138
+ );
139
+ }
140
+
141
+ export const PanelGroup = forwardRef<
142
+ ImperativePanelGroupHandle,
143
+ PanelGroupProps
144
+ >((props: PanelGroupProps, ref: ForwardedRef<ImperativePanelGroupHandle>) =>
145
+ createElement(PanelGroupWithForwardedRef, { ...props, forwardedRef: ref })
146
+ );
147
+
148
+ PanelGroupWithForwardedRef.displayName = "PanelGroup";
149
+ PanelGroup.displayName = "forwardRef(PanelGroup)";