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,81 @@
1
+ import { PanelData } from "../Panel";
2
+ import { MixedSizes } from "../types";
3
+ import { calculateAvailablePanelSizeInPixels } from "../utils/dom/calculateAvailablePanelSizeInPixels";
4
+ import { convertPercentageToPixels } from "./convertPercentageToPixels";
5
+ import { getPercentageSizeFromMixedSizes } from "./getPercentageSizeFromMixedSizes";
6
+
7
+ // Layout should be pre-converted into percentages
8
+ export function callPanelCallbacks(
9
+ groupId: string,
10
+ panelsArray: PanelData[],
11
+ layout: number[],
12
+ panelIdToLastNotifiedMixedSizesMap: Record<string, MixedSizes>
13
+ ) {
14
+ const groupSizePixels = calculateAvailablePanelSizeInPixels(groupId);
15
+
16
+ layout.forEach((sizePercentage, index) => {
17
+ const panelData = panelsArray[index];
18
+ if (!panelData) {
19
+ // Handle initial mount (when panels are registered too late to be in the panels array)
20
+ // The subsequent render+effects will handle the resize notification
21
+ return;
22
+ }
23
+
24
+ const { callbacks, constraints, id: panelId } = panelData;
25
+ const { collapsible } = constraints;
26
+
27
+ const mixedSizes: MixedSizes = {
28
+ sizePercentage,
29
+ sizePixels: convertPercentageToPixels(sizePercentage, groupSizePixels),
30
+ };
31
+
32
+ const lastNotifiedMixedSizes = panelIdToLastNotifiedMixedSizesMap[panelId];
33
+ if (
34
+ lastNotifiedMixedSizes == null ||
35
+ mixedSizes.sizePercentage !== lastNotifiedMixedSizes.sizePercentage ||
36
+ mixedSizes.sizePixels !== lastNotifiedMixedSizes.sizePixels
37
+ ) {
38
+ panelIdToLastNotifiedMixedSizesMap[panelId] = mixedSizes;
39
+
40
+ const { onCollapse, onExpand, onResize } = callbacks;
41
+
42
+ if (onResize) {
43
+ onResize(mixedSizes, lastNotifiedMixedSizes);
44
+ }
45
+
46
+ if (collapsible && (onCollapse || onExpand)) {
47
+ const collapsedSize =
48
+ getPercentageSizeFromMixedSizes(
49
+ {
50
+ sizePercentage: constraints.collapsedSizePercentage,
51
+ sizePixels: constraints.collapsedSizePixels,
52
+ },
53
+ groupSizePixels
54
+ ) ?? 0;
55
+
56
+ const size = getPercentageSizeFromMixedSizes(
57
+ mixedSizes,
58
+ groupSizePixels
59
+ );
60
+
61
+ if (
62
+ onExpand &&
63
+ (lastNotifiedMixedSizes == null ||
64
+ lastNotifiedMixedSizes.sizePercentage === collapsedSize) &&
65
+ size !== collapsedSize
66
+ ) {
67
+ onExpand();
68
+ }
69
+
70
+ if (
71
+ onCollapse &&
72
+ (lastNotifiedMixedSizes == null ||
73
+ lastNotifiedMixedSizes.sizePercentage !== collapsedSize) &&
74
+ size === collapsedSize
75
+ ) {
76
+ onCollapse();
77
+ }
78
+ }
79
+ }
80
+ });
81
+ }
@@ -0,0 +1,9 @@
1
+ import { compareLayouts } from "./compareLayouts";
2
+
3
+ describe("compareLayouts", () => {
4
+ it("should work", () => {
5
+ expect(compareLayouts([1, 2], [1])).toBe(false);
6
+ expect(compareLayouts([1], [1, 2])).toBe(false);
7
+ expect(compareLayouts([1, 2, 3], [1, 2, 3])).toBe(true);
8
+ });
9
+ });
@@ -0,0 +1,12 @@
1
+ export function compareLayouts(a: number[], b: number[]) {
2
+ if (a.length !== b.length) {
3
+ return false;
4
+ } else {
5
+ for (let index = 0; index < a.length; index++) {
6
+ if (a[index] != b[index]) {
7
+ return false;
8
+ }
9
+ }
10
+ }
11
+ return true;
12
+ }
@@ -0,0 +1,44 @@
1
+ // This method returns a number between 1 and 100 representing
2
+
3
+ import { PanelData } from "../Panel";
4
+ import { DragState } from "../PanelGroupContext";
5
+ import { CSSProperties } from "../vendor/react";
6
+
7
+ // the % of the group's overall space this panel should occupy.
8
+ export function computePanelFlexBoxStyle({
9
+ dragState,
10
+ layout,
11
+ panelData,
12
+ panelIndex,
13
+ precision = 3,
14
+ }: {
15
+ layout: number[];
16
+ dragState: DragState | null;
17
+ panelData: PanelData[];
18
+ panelIndex: number;
19
+ precision?: number;
20
+ }): CSSProperties {
21
+ const size = layout[panelIndex];
22
+
23
+ let flexGrow;
24
+ if (panelData.length === 1) {
25
+ flexGrow = "100";
26
+ } else if (size == null) {
27
+ flexGrow = "0";
28
+ } else {
29
+ flexGrow = size.toPrecision(precision);
30
+ }
31
+
32
+ return {
33
+ flexBasis: 0,
34
+ flexGrow,
35
+ flexShrink: 1,
36
+
37
+ // Without this, Panel sizes may be unintentionally overridden by their content
38
+ overflow: "hidden",
39
+
40
+ // Disable pointer events inside of a panel during resize
41
+ // This avoid edge cases like nested iframes
42
+ pointerEvents: dragState !== null ? "none" : undefined,
43
+ };
44
+ }
@@ -0,0 +1,98 @@
1
+ import { PanelConstraints } from "../Panel";
2
+ import { computePercentagePanelConstraints } from "./computePercentagePanelConstraints";
3
+
4
+ describe("computePercentagePanelConstraints", () => {
5
+ it("should compute reasonable defaults with no constraints", () => {
6
+ expect(computePercentagePanelConstraints([{}, {}], 0, 100))
7
+ .toMatchInlineSnapshot(`
8
+ {
9
+ "collapsedSizePercentage": 0,
10
+ "defaultSizePercentage": undefined,
11
+ "maxSizePercentage": 100,
12
+ "minSizePercentage": 0,
13
+ }
14
+ `);
15
+
16
+ expect(computePercentagePanelConstraints([{}, {}], 1, 100))
17
+ .toMatchInlineSnapshot(`
18
+ {
19
+ "collapsedSizePercentage": 0,
20
+ "defaultSizePercentage": undefined,
21
+ "maxSizePercentage": 100,
22
+ "minSizePercentage": 0,
23
+ }
24
+ `);
25
+ });
26
+
27
+ it("should compute percentage based constraints based on a mix of pixels and percentages", () => {
28
+ const constraints: PanelConstraints[] = [
29
+ {
30
+ maxSizePixels: 20,
31
+ minSizePixels: 10,
32
+ },
33
+ {
34
+ minSizePixels: 10,
35
+ },
36
+ {
37
+ minSizePixels: 10,
38
+ },
39
+ ];
40
+
41
+ expect(computePercentagePanelConstraints(constraints, 0, 100))
42
+ .toMatchInlineSnapshot(`
43
+ {
44
+ "collapsedSizePercentage": 0,
45
+ "defaultSizePercentage": undefined,
46
+ "maxSizePercentage": 20,
47
+ "minSizePercentage": 10,
48
+ }
49
+ `);
50
+
51
+ expect(computePercentagePanelConstraints(constraints, 1, 100))
52
+ .toMatchInlineSnapshot(`
53
+ {
54
+ "collapsedSizePercentage": 0,
55
+ "defaultSizePercentage": undefined,
56
+ "maxSizePercentage": 80,
57
+ "minSizePercentage": 10,
58
+ }
59
+ `);
60
+
61
+ expect(computePercentagePanelConstraints(constraints, 2, 100))
62
+ .toMatchInlineSnapshot(`
63
+ {
64
+ "collapsedSizePercentage": 0,
65
+ "defaultSizePercentage": undefined,
66
+ "maxSizePercentage": 80,
67
+ "minSizePercentage": 10,
68
+ }
69
+ `);
70
+ });
71
+
72
+ it("should compute reasonable percentage based constraints from pixels if group size is negative", () => {
73
+ jest.spyOn(console, "warn").mockImplementation(() => {});
74
+
75
+ expect(
76
+ computePercentagePanelConstraints(
77
+ [
78
+ {
79
+ minSizePixels: 25,
80
+ maxSizePixels: 100,
81
+ },
82
+ ],
83
+
84
+ 0,
85
+ -100
86
+ )
87
+ ).toMatchInlineSnapshot(`
88
+ {
89
+ "collapsedSizePercentage": 0,
90
+ "defaultSizePercentage": undefined,
91
+ "maxSizePercentage": 0,
92
+ "minSizePercentage": 0,
93
+ }
94
+ `);
95
+
96
+ expect(console.warn).toHaveBeenCalledTimes(1);
97
+ });
98
+ });
@@ -0,0 +1,56 @@
1
+ import { PanelConstraints } from "../Panel";
2
+ import { convertPixelConstraintsToPercentages } from "./convertPixelConstraintsToPercentages";
3
+
4
+ export function computePercentagePanelConstraints(
5
+ panelConstraintsArray: PanelConstraints[],
6
+ panelIndex: number,
7
+ groupSizePixels: number
8
+ ): {
9
+ collapsedSizePercentage: number;
10
+ defaultSizePercentage: number | undefined;
11
+ maxSizePercentage: number;
12
+ minSizePercentage: number;
13
+ } {
14
+ // All panel constraints, excluding the current one
15
+ let totalMinConstraints = 0;
16
+ let totalMaxConstraints = 0;
17
+
18
+ for (let index = 0; index < panelConstraintsArray.length; index++) {
19
+ if (index !== panelIndex) {
20
+ const { collapsible } = panelConstraintsArray[index]!;
21
+ const { collapsedSizePercentage, maxSizePercentage, minSizePercentage } =
22
+ convertPixelConstraintsToPercentages(
23
+ panelConstraintsArray[index]!,
24
+ groupSizePixels
25
+ );
26
+
27
+ totalMaxConstraints += maxSizePercentage;
28
+ totalMinConstraints += collapsible
29
+ ? collapsedSizePercentage
30
+ : minSizePercentage;
31
+ }
32
+ }
33
+
34
+ const {
35
+ collapsedSizePercentage,
36
+ defaultSizePercentage,
37
+ maxSizePercentage,
38
+ minSizePercentage,
39
+ } = convertPixelConstraintsToPercentages(
40
+ panelConstraintsArray[panelIndex]!,
41
+ groupSizePixels
42
+ );
43
+
44
+ return {
45
+ collapsedSizePercentage,
46
+ defaultSizePercentage,
47
+ maxSizePercentage:
48
+ panelConstraintsArray.length > 1
49
+ ? Math.min(maxSizePercentage, 100 - totalMinConstraints)
50
+ : maxSizePercentage,
51
+ minSizePercentage:
52
+ panelConstraintsArray.length > 1
53
+ ? Math.max(minSizePercentage, 100 - totalMaxConstraints)
54
+ : minSizePercentage,
55
+ };
56
+ }
@@ -0,0 +1,9 @@
1
+ import { convertPercentageToPixels } from "./convertPercentageToPixels";
2
+
3
+ describe("convertPercentageToPixels", () => {
4
+ it("should convert percentages to pixels", () => {
5
+ expect(convertPercentageToPixels(0, 100_000)).toBe(0);
6
+ expect(convertPercentageToPixels(50, 100_000)).toBe(50_000);
7
+ expect(convertPercentageToPixels(100, 100_000)).toBe(100_000);
8
+ });
9
+ });
@@ -0,0 +1,6 @@
1
+ export function convertPercentageToPixels(
2
+ percentage: number,
3
+ groupSizePixels: number
4
+ ): number {
5
+ return (percentage / 100) * groupSizePixels;
6
+ }
@@ -0,0 +1,47 @@
1
+ import { convertPixelConstraintsToPercentages } from "./convertPixelConstraintsToPercentages";
2
+
3
+ describe("convertPixelConstraintsToPercentages", () => {
4
+ it("should respect percentage panel constraints if group size is negative", () => {
5
+ jest.spyOn(console, "warn").mockImplementation(() => {});
6
+
7
+ expect(
8
+ convertPixelConstraintsToPercentages(
9
+ {
10
+ minSizePercentage: 25,
11
+ defaultSizePercentage: 50,
12
+ maxSizePercentage: 75,
13
+ },
14
+ -100
15
+ )
16
+ ).toEqual({
17
+ collapsedSizePercentage: 0,
18
+ defaultSizePercentage: 50,
19
+ maxSizePercentage: 75,
20
+ minSizePercentage: 25,
21
+ });
22
+
23
+ expect(console.warn).toHaveBeenCalledTimes(0);
24
+ });
25
+
26
+ // Edge case test (issues/206)
27
+ it("should ignore pixel panel constraints if group size is negative", () => {
28
+ jest.spyOn(console, "warn").mockImplementation(() => {});
29
+
30
+ expect(
31
+ convertPixelConstraintsToPercentages(
32
+ {
33
+ minSizePixels: 25,
34
+ maxSizePixels: 75,
35
+ },
36
+ -100
37
+ )
38
+ ).toEqual({
39
+ collapsedSizePercentage: 0,
40
+ defaultSizePercentage: undefined,
41
+ maxSizePercentage: 0,
42
+ minSizePercentage: 0,
43
+ });
44
+
45
+ expect(console.warn).toHaveBeenCalledTimes(1);
46
+ });
47
+ });
@@ -0,0 +1,72 @@
1
+ import { PanelConstraints } from "../Panel";
2
+ import { convertPixelsToPercentage } from "./convertPixelsToPercentage";
3
+
4
+ export function convertPixelConstraintsToPercentages(
5
+ panelConstraints: PanelConstraints,
6
+ groupSizePixels: number
7
+ ): {
8
+ collapsedSizePercentage: number;
9
+ defaultSizePercentage: number | undefined;
10
+ maxSizePercentage: number;
11
+ minSizePercentage: number;
12
+ } {
13
+ let {
14
+ collapsedSizePercentage = 0,
15
+ collapsedSizePixels,
16
+ defaultSizePercentage,
17
+ defaultSizePixels,
18
+ maxSizePercentage = 100,
19
+ maxSizePixels,
20
+ minSizePercentage = 0,
21
+ minSizePixels,
22
+ } = panelConstraints;
23
+
24
+ const hasPixelConstraints =
25
+ collapsedSizePixels != null ||
26
+ defaultSizePixels != null ||
27
+ minSizePixels != null ||
28
+ maxSizePixels != null;
29
+
30
+ if (hasPixelConstraints && groupSizePixels <= 0) {
31
+ console.warn(`WARNING: Invalid group size: ${groupSizePixels}px`);
32
+
33
+ return {
34
+ collapsedSizePercentage: 0,
35
+ defaultSizePercentage,
36
+ maxSizePercentage: 0,
37
+ minSizePercentage: 0,
38
+ };
39
+ }
40
+
41
+ if (collapsedSizePixels != null) {
42
+ collapsedSizePercentage = convertPixelsToPercentage(
43
+ collapsedSizePixels,
44
+ groupSizePixels
45
+ );
46
+ }
47
+ if (defaultSizePixels != null) {
48
+ defaultSizePercentage = convertPixelsToPercentage(
49
+ defaultSizePixels,
50
+ groupSizePixels
51
+ );
52
+ }
53
+ if (minSizePixels != null) {
54
+ minSizePercentage = convertPixelsToPercentage(
55
+ minSizePixels,
56
+ groupSizePixels
57
+ );
58
+ }
59
+ if (maxSizePixels != null) {
60
+ maxSizePercentage = convertPixelsToPercentage(
61
+ maxSizePixels,
62
+ groupSizePixels
63
+ );
64
+ }
65
+
66
+ return {
67
+ collapsedSizePercentage,
68
+ defaultSizePercentage,
69
+ maxSizePercentage,
70
+ minSizePercentage,
71
+ };
72
+ }
@@ -0,0 +1,9 @@
1
+ import { convertPixelsToPercentage } from "./convertPixelsToPercentage";
2
+
3
+ describe("convertPixelsToPercentage", () => {
4
+ it("should convert pixels to percentages", () => {
5
+ expect(convertPixelsToPercentage(0, 100_000)).toBe(0);
6
+ expect(convertPixelsToPercentage(50_000, 100_000)).toBe(50);
7
+ expect(convertPixelsToPercentage(100_000, 100_000)).toBe(100);
8
+ });
9
+ });
@@ -0,0 +1,6 @@
1
+ export function convertPixelsToPercentage(
2
+ pixels: number,
3
+ groupSizePixels: number
4
+ ): number {
5
+ return (pixels / groupSizePixels) * 100;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { getResizeHandleElementIndex } from "../utils/dom/getResizeHandleElementIndex";
2
+
3
+ export function determinePivotIndices(
4
+ groupId: string,
5
+ dragHandleId: string
6
+ ): [indexBefore: number, indexAfter: number] {
7
+ const index = getResizeHandleElementIndex(groupId, dragHandleId);
8
+
9
+ return index != null ? [index, index + 1] : [-1, -1];
10
+ }
@@ -0,0 +1,29 @@
1
+ import { getPanelGroupElement } from "./getPanelGroupElement";
2
+ import { getResizeHandleElementsForGroup } from "./getResizeHandleElementsForGroup";
3
+
4
+ export function calculateAvailablePanelSizeInPixels(groupId: string): number {
5
+ const panelGroupElement = getPanelGroupElement(groupId);
6
+ if (panelGroupElement == null) {
7
+ return NaN;
8
+ }
9
+
10
+ const direction = panelGroupElement.getAttribute(
11
+ "data-panel-group-direction"
12
+ );
13
+ const resizeHandles = getResizeHandleElementsForGroup(groupId);
14
+ if (direction === "horizontal") {
15
+ return (
16
+ panelGroupElement.offsetWidth -
17
+ resizeHandles.reduce((accumulated, handle) => {
18
+ return accumulated + handle.offsetWidth;
19
+ }, 0)
20
+ );
21
+ } else {
22
+ return (
23
+ panelGroupElement.offsetHeight -
24
+ resizeHandles.reduce((accumulated, handle) => {
25
+ return accumulated + handle.offsetHeight;
26
+ }, 0)
27
+ );
28
+ }
29
+ }
@@ -0,0 +1,29 @@
1
+ import { getPanelGroupElement } from "./getPanelGroupElement";
2
+ import { getResizeHandleElementsForGroup } from "./getResizeHandleElementsForGroup";
3
+
4
+ export function getAvailableGroupSizePixels(groupId: string): number {
5
+ const panelGroupElement = getPanelGroupElement(groupId);
6
+ if (panelGroupElement == null) {
7
+ return NaN;
8
+ }
9
+
10
+ const direction = panelGroupElement.getAttribute(
11
+ "data-panel-group-direction"
12
+ );
13
+ const resizeHandles = getResizeHandleElementsForGroup(groupId);
14
+ if (direction === "horizontal") {
15
+ return (
16
+ panelGroupElement.offsetWidth -
17
+ resizeHandles.reduce((accumulated, handle) => {
18
+ return accumulated + handle.offsetWidth;
19
+ }, 0)
20
+ );
21
+ } else {
22
+ return (
23
+ panelGroupElement.offsetHeight -
24
+ resizeHandles.reduce((accumulated, handle) => {
25
+ return accumulated + handle.offsetHeight;
26
+ }, 0)
27
+ );
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ export function getPanelElement(id: string): HTMLDivElement | null {
2
+ const element = document.querySelector(`[data-panel-id="${id}"]`);
3
+ if (element) {
4
+ return element as HTMLDivElement;
5
+ }
6
+ return null;
7
+ }
@@ -0,0 +1,9 @@
1
+ export function getPanelGroupElement(id: string): HTMLDivElement | null {
2
+ const element = document.querySelector(
3
+ `[data-panel-group][data-panel-group-id="${id}"]`
4
+ );
5
+ if (element) {
6
+ return element as HTMLDivElement;
7
+ }
8
+ return null;
9
+ }
@@ -0,0 +1,9 @@
1
+ export function getResizeHandleElement(id: string): HTMLDivElement | null {
2
+ const element = document.querySelector(
3
+ `[data-panel-resize-handle-id="${id}"]`
4
+ );
5
+ if (element) {
6
+ return element as HTMLDivElement;
7
+ }
8
+ return null;
9
+ }
@@ -0,0 +1,12 @@
1
+ import { getResizeHandleElementsForGroup } from "./getResizeHandleElementsForGroup";
2
+
3
+ export function getResizeHandleElementIndex(
4
+ groupId: string,
5
+ id: string
6
+ ): number | null {
7
+ const handles = getResizeHandleElementsForGroup(groupId);
8
+ const index = handles.findIndex(
9
+ (handle) => handle.getAttribute("data-panel-resize-handle-id") === id
10
+ );
11
+ return index ?? null;
12
+ }
@@ -0,0 +1,9 @@
1
+ export function getResizeHandleElementsForGroup(
2
+ groupId: string
3
+ ): HTMLDivElement[] {
4
+ return Array.from(
5
+ document.querySelectorAll(
6
+ `[data-panel-resize-handle-id][data-panel-group-id="${groupId}"]`
7
+ )
8
+ );
9
+ }
@@ -0,0 +1,18 @@
1
+ import { PanelData } from "../../Panel";
2
+ import { getResizeHandleElement } from "./getResizeHandleElement";
3
+ import { getResizeHandleElementsForGroup } from "./getResizeHandleElementsForGroup";
4
+
5
+ export function getResizeHandlePanelIds(
6
+ groupId: string,
7
+ handleId: string,
8
+ panelsArray: PanelData[]
9
+ ): [idBefore: string | null, idAfter: string | null] {
10
+ const handle = getResizeHandleElement(handleId);
11
+ const handles = getResizeHandleElementsForGroup(groupId);
12
+ const index = handle ? handles.indexOf(handle) : -1;
13
+
14
+ const idBefore: string | null = panelsArray[index]?.id ?? null;
15
+ const idAfter: string | null = panelsArray[index + 1]?.id ?? null;
16
+
17
+ return [idBefore, idAfter];
18
+ }
@@ -0,0 +1,13 @@
1
+ import { ResizeEvent } from "../PanelGroupContext";
2
+
3
+ export function isKeyDown(event: ResizeEvent): event is KeyboardEvent {
4
+ return event.type === "keydown";
5
+ }
6
+
7
+ export function isMouseEvent(event: ResizeEvent): event is MouseEvent {
8
+ return event.type.startsWith("mouse");
9
+ }
10
+
11
+ export function isTouchEvent(event: ResizeEvent): event is TouchEvent {
12
+ return event.type.startsWith("touch");
13
+ }
@@ -0,0 +1,47 @@
1
+ import { getPercentageSizeFromMixedSizes } from "./getPercentageSizeFromMixedSizes";
2
+
3
+ describe("getPercentageSizeFromMixedSizes", () => {
4
+ it("should return percentage sizes as-is", () => {
5
+ expect(
6
+ getPercentageSizeFromMixedSizes(
7
+ {
8
+ sizePercentage: 50,
9
+ },
10
+ 100_000
11
+ )
12
+ ).toBe(50);
13
+ expect(
14
+ getPercentageSizeFromMixedSizes(
15
+ {
16
+ sizePercentage: 25,
17
+ sizePixels: 100,
18
+ },
19
+ 100_000
20
+ )
21
+ ).toBe(25);
22
+ });
23
+
24
+ it("should convert pixels to percentages", () => {
25
+ expect(
26
+ getPercentageSizeFromMixedSizes(
27
+ {
28
+ sizePixels: 50_000,
29
+ },
30
+ 100_000
31
+ )
32
+ ).toBe(50);
33
+ expect(
34
+ getPercentageSizeFromMixedSizes(
35
+ {
36
+ sizePercentage: 25,
37
+ sizePixels: 50_000,
38
+ },
39
+ 100_000
40
+ )
41
+ ).toBe(25);
42
+ });
43
+
44
+ it("should return undefined if neither pixel nor percentage sizes specified", () => {
45
+ expect(getPercentageSizeFromMixedSizes({}, 100_000)).toBeUndefined();
46
+ });
47
+ });
@@ -0,0 +1,15 @@
1
+ import { MixedSizes } from "../types";
2
+ import { convertPixelsToPercentage } from "./convertPixelsToPercentage";
3
+
4
+ export function getPercentageSizeFromMixedSizes(
5
+ { sizePercentage, sizePixels }: Partial<MixedSizes>,
6
+ groupSizePixels: number
7
+ ): number | undefined {
8
+ if (sizePercentage != null) {
9
+ return sizePercentage;
10
+ } else if (sizePixels != null) {
11
+ return convertPixelsToPercentage(sizePixels, groupSizePixels);
12
+ }
13
+
14
+ return undefined;
15
+ }