react-resizable-panels 0.0.28 → 0.0.29

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/src/PanelGroup.ts CHANGED
@@ -16,6 +16,8 @@ import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
16
16
  import { getDragOffset, getMovement } from "./utils/coordinates";
17
17
  import {
18
18
  adjustByDelta,
19
+ callPanelCallbacks,
20
+ getBeforeAndAfterIds,
19
21
  getFlexGrow,
20
22
  getPanelGroup,
21
23
  getResizeHandlePanelIds,
@@ -48,7 +50,7 @@ export type PanelGroupProps = {
48
50
  tagName?: ElementType;
49
51
  };
50
52
 
51
- export default function PanelGroup({
53
+ export function PanelGroup({
52
54
  autoSaveId,
53
55
  children = null,
54
56
  className: classNameFromProps = "",
@@ -67,6 +69,9 @@ export default function PanelGroup({
67
69
 
68
70
  const dragOffsetRef = useRef<number>(0);
69
71
 
72
+ // Used to support imperative collapse/expand API.
73
+ const panelSizeBeforeCollapse = useRef<Map<string, number>>(new Map());
74
+
70
75
  // Store committed values to avoid unnecessarily re-running memoization/effects functions.
71
76
  const committedValuesRef = useRef<CommittedValues>({
72
77
  direction,
@@ -191,9 +196,13 @@ export default function PanelGroup({
191
196
 
192
197
  // Without this, Panel sizes may be unintentionally overridden by their content.
193
198
  overflow: "hidden",
199
+
200
+ // Disable pointer events inside of a panel during resize.
201
+ // This avoid edge cases like nested iframes.
202
+ pointerEvents: activeHandleId !== null ? "none" : undefined,
194
203
  };
195
204
  },
196
- [direction, sizes]
205
+ [activeHandleId, direction, sizes]
197
206
  );
198
207
 
199
208
  const registerPanel = useCallback((id: string, panel: PanelData) => {
@@ -275,24 +284,7 @@ export default function PanelGroup({
275
284
  setGlobalCursorStyle(isHorizontal ? "horizontal" : "vertical");
276
285
 
277
286
  // If resize change handlers have been declared, this is the time to call them.
278
- nextSizes.forEach((nextSize, index) => {
279
- const prevSize = prevSizes[index];
280
- if (prevSize !== nextSize) {
281
- const { onCollapse, onResize } =
282
- panelsArray[index].callbacksRef.current;
283
- if (onResize) {
284
- onResize(nextSize);
285
- }
286
-
287
- if (onCollapse) {
288
- if (prevSize === 0 && nextSize !== 0) {
289
- onCollapse(false);
290
- } else if (prevSize !== 0 && nextSize === 0) {
291
- onCollapse(true);
292
- }
293
- }
294
- }
295
- });
287
+ callPanelCallbacks(panelsArray, prevSizes, nextSizes);
296
288
 
297
289
  setSizes(nextSizes);
298
290
  }
@@ -316,14 +308,156 @@ export default function PanelGroup({
316
308
  });
317
309
  }, []);
318
310
 
311
+ const collapsePanel = useCallback((id: string) => {
312
+ const { panels, sizes: prevSizes } = committedValuesRef.current;
313
+
314
+ const panel = panels.get(id);
315
+ if (panel == null || !panel.collapsible) {
316
+ return;
317
+ }
318
+
319
+ const panelsArray = panelsMapToSortedArray(panels);
320
+
321
+ const index = panelsArray.indexOf(panel);
322
+ if (index < 0) {
323
+ return;
324
+ }
325
+
326
+ const currentSize = prevSizes[index];
327
+ if (currentSize === 0) {
328
+ // Panel is already collapsed.
329
+ return;
330
+ }
331
+
332
+ panelSizeBeforeCollapse.current.set(id, currentSize);
333
+
334
+ const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
335
+ if (idBefore == null || idAfter == null) {
336
+ return;
337
+ }
338
+
339
+ const isLastPanel = index === panelsArray.length - 1;
340
+ const delta = isLastPanel ? currentSize : 0 - currentSize;
341
+
342
+ const nextSizes = adjustByDelta(
343
+ panels,
344
+ idBefore,
345
+ idAfter,
346
+ delta,
347
+ prevSizes
348
+ );
349
+ if (prevSizes !== nextSizes) {
350
+ // If resize change handlers have been declared, this is the time to call them.
351
+ callPanelCallbacks(panelsArray, prevSizes, nextSizes);
352
+
353
+ setSizes(nextSizes);
354
+ }
355
+ }, []);
356
+
357
+ const expandPanel = useCallback((id: string) => {
358
+ const { panels, sizes: prevSizes } = committedValuesRef.current;
359
+
360
+ const panel = panels.get(id);
361
+ if (panel == null) {
362
+ return;
363
+ }
364
+
365
+ const sizeBeforeCollapse =
366
+ panelSizeBeforeCollapse.current.get(id) || panel.minSize;
367
+ if (!sizeBeforeCollapse) {
368
+ return;
369
+ }
370
+
371
+ const panelsArray = panelsMapToSortedArray(panels);
372
+
373
+ const index = panelsArray.indexOf(panel);
374
+ if (index < 0) {
375
+ return;
376
+ }
377
+
378
+ const currentSize = prevSizes[index];
379
+ if (currentSize !== 0) {
380
+ // Panel is already expanded.
381
+ return;
382
+ }
383
+
384
+ const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
385
+ if (idBefore == null || idAfter == null) {
386
+ return;
387
+ }
388
+
389
+ const isLastPanel = index === panelsArray.length - 1;
390
+ const delta = isLastPanel ? 0 - sizeBeforeCollapse : sizeBeforeCollapse;
391
+
392
+ const nextSizes = adjustByDelta(
393
+ panels,
394
+ idBefore,
395
+ idAfter,
396
+ delta,
397
+ prevSizes
398
+ );
399
+ if (prevSizes !== nextSizes) {
400
+ // If resize change handlers have been declared, this is the time to call them.
401
+ callPanelCallbacks(panelsArray, prevSizes, nextSizes);
402
+
403
+ setSizes(nextSizes);
404
+ }
405
+ }, []);
406
+
407
+ const resizePanel = useCallback((id: string, nextSize: number) => {
408
+ const { panels, sizes: prevSizes } = committedValuesRef.current;
409
+
410
+ const panel = panels.get(id);
411
+ if (panel == null) {
412
+ return;
413
+ }
414
+
415
+ const panelsArray = panelsMapToSortedArray(panels);
416
+
417
+ const index = panelsArray.indexOf(panel);
418
+ if (index < 0) {
419
+ return;
420
+ }
421
+
422
+ const currentSize = prevSizes[index];
423
+ if (currentSize === nextSize) {
424
+ return;
425
+ }
426
+
427
+ const [idBefore, idAfter] = getBeforeAndAfterIds(id, panelsArray);
428
+ if (idBefore == null || idAfter == null) {
429
+ return;
430
+ }
431
+
432
+ const isLastPanel = index === panelsArray.length - 1;
433
+ const delta = isLastPanel ? currentSize - nextSize : nextSize - currentSize;
434
+
435
+ const nextSizes = adjustByDelta(
436
+ panels,
437
+ idBefore,
438
+ idAfter,
439
+ delta,
440
+ prevSizes
441
+ );
442
+ if (prevSizes !== nextSizes) {
443
+ // If resize change handlers have been declared, this is the time to call them.
444
+ callPanelCallbacks(panelsArray, prevSizes, nextSizes);
445
+
446
+ setSizes(nextSizes);
447
+ }
448
+ }, []);
449
+
319
450
  const context = useMemo(
320
451
  () => ({
321
452
  activeHandleId,
453
+ collapsePanel,
322
454
  direction,
455
+ expandPanel,
323
456
  getPanelStyle,
324
457
  groupId,
325
458
  registerPanel,
326
459
  registerResizeHandle,
460
+ resizePanel,
327
461
  startDragging: (id: string, event: ResizeEvent) => {
328
462
  setActiveHandleId(id);
329
463
 
@@ -337,11 +471,14 @@ export default function PanelGroup({
337
471
  }),
338
472
  [
339
473
  activeHandleId,
474
+ collapsePanel,
340
475
  direction,
476
+ expandPanel,
341
477
  getPanelStyle,
342
478
  groupId,
343
479
  registerPanel,
344
480
  registerResizeHandle,
481
+ resizePanel,
345
482
  unregisterPanel,
346
483
  ]
347
484
  );
@@ -27,7 +27,7 @@ export type PanelResizeHandleProps = {
27
27
  tagName?: ElementType;
28
28
  };
29
29
 
30
- export default function PanelResizeHandle({
30
+ export function PanelResizeHandle({
31
31
  children = null,
32
32
  className: classNameFromProps = "",
33
33
  disabled = false,
@@ -1,6 +1,13 @@
1
1
  import { useEffect, useLayoutEffect } from "react";
2
2
 
3
- const useIsomorphicLayoutEffect =
4
- typeof window !== "undefined" ? useLayoutEffect : useEffect;
3
+ const canUseEffectHooks = !!(
4
+ typeof window !== "undefined" &&
5
+ typeof window.document !== "undefined" &&
6
+ typeof window.document.createElement !== "undefined"
7
+ );
8
+
9
+ const useIsomorphicLayoutEffect = canUseEffectHooks
10
+ ? useLayoutEffect
11
+ : () => {};
5
12
 
6
13
  export default useIsomorphicLayoutEffect;
package/src/index.ts CHANGED
@@ -1,5 +1,19 @@
1
- import Panel from "./Panel";
2
- import PanelGroup from "./PanelGroup";
3
- import PanelResizeHandle from "./PanelResizeHandle";
1
+ import { Panel } from "./Panel";
2
+ import { PanelGroup } from "./PanelGroup";
3
+ import { PanelResizeHandle } from "./PanelResizeHandle";
4
4
 
5
- export { Panel, PanelGroup, PanelResizeHandle };
5
+ import type { ImperativePanelHandle, PanelProps } from "./Panel";
6
+ import type { PanelGroupProps } from "./PanelGroup";
7
+ import type { PanelResizeHandleProps } from "./PanelResizeHandle";
8
+
9
+ export {
10
+ Panel,
11
+ PanelGroup,
12
+ PanelResizeHandle,
13
+
14
+ // TypeScript types
15
+ ImperativePanelHandle,
16
+ PanelGroupProps,
17
+ PanelProps,
18
+ PanelResizeHandleProps,
19
+ };
@@ -83,6 +83,52 @@ export function adjustByDelta(
83
83
  return nextSizes;
84
84
  }
85
85
 
86
+ export function callPanelCallbacks(
87
+ panelsArray: PanelData[],
88
+ prevSizes: number[],
89
+ nextSizes: number[]
90
+ ) {
91
+ nextSizes.forEach((nextSize, index) => {
92
+ const prevSize = prevSizes[index];
93
+ if (prevSize !== nextSize) {
94
+ const { callbacksRef } = panelsArray[index];
95
+ const { onCollapse, onResize } = callbacksRef.current;
96
+
97
+ if (onResize) {
98
+ onResize(nextSize);
99
+ }
100
+
101
+ if (onCollapse) {
102
+ if (prevSize === 0 && nextSize !== 0) {
103
+ onCollapse(false);
104
+ } else if (prevSize !== 0 && nextSize === 0) {
105
+ onCollapse(true);
106
+ }
107
+ }
108
+ }
109
+ });
110
+ }
111
+
112
+ export function getBeforeAndAfterIds(
113
+ id: string,
114
+ panelsArray: PanelData[]
115
+ ): [idBefore: string | null, idAFter: string | null] {
116
+ if (panelsArray.length < 2) {
117
+ return [null, null];
118
+ }
119
+
120
+ const index = panelsArray.findIndex((panel) => panel.id === id);
121
+ if (index < 0) {
122
+ return [null, null];
123
+ }
124
+
125
+ const isLastPanel = index === panelsArray.length - 1;
126
+ const idBefore = isLastPanel ? panelsArray[index - 1].id : id;
127
+ const idAfter = isLastPanel ? id : panelsArray[index + 1].id;
128
+
129
+ return [idBefore, idAfter];
130
+ }
131
+
86
132
  // This method returns a number between 1 and 100 representing
87
133
  // the % of the group's overall space this panel should occupy.
88
134
  export function getFlexGrow(