react-resizable-panels 0.0.55 → 0.0.56
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/.eslintrc.cjs +26 -0
- package/CHANGELOG.md +234 -90
- package/README.md +55 -49
- package/dist/declarations/src/Panel.d.ts +75 -20
- package/dist/declarations/src/PanelGroup.d.ts +29 -25
- package/dist/declarations/src/PanelResizeHandle.d.ts +1 -1
- package/dist/declarations/src/index.d.ts +5 -6
- package/dist/declarations/src/types.d.ts +3 -26
- package/dist/declarations/src/vendor/react.d.ts +4 -4
- package/dist/react-resizable-panels.browser.cjs.js +1241 -1035
- package/dist/react-resizable-panels.browser.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.cjs.js +1367 -1081
- package/dist/react-resizable-panels.browser.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.browser.development.esm.js +1368 -1081
- package/dist/react-resizable-panels.browser.esm.js +1242 -1035
- package/dist/react-resizable-panels.cjs.js +1241 -1035
- package/dist/react-resizable-panels.cjs.js.map +1 -1
- package/dist/react-resizable-panels.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.cjs.js +1370 -1084
- package/dist/react-resizable-panels.development.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.esm.js +1371 -1084
- package/dist/react-resizable-panels.development.node.cjs.js +1151 -940
- package/dist/react-resizable-panels.development.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.development.node.esm.js +1152 -940
- package/dist/react-resizable-panels.esm.js +1242 -1035
- package/dist/react-resizable-panels.esm.js.map +1 -1
- package/dist/react-resizable-panels.node.cjs.js +1049 -912
- package/dist/react-resizable-panels.node.cjs.mjs +1 -2
- package/dist/react-resizable-panels.node.esm.js +1050 -912
- package/jest.config.js +10 -0
- package/package.json +3 -1
- package/src/Panel.test.tsx +308 -0
- package/src/Panel.ts +175 -123
- package/src/PanelGroup.test.tsx +210 -0
- package/src/PanelGroup.ts +730 -669
- package/src/PanelGroupContext.ts +33 -0
- package/src/PanelResizeHandle.ts +13 -8
- package/src/hooks/useUniqueId.ts +1 -1
- package/src/hooks/useWindowSplitterBehavior.ts +9 -164
- package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +185 -0
- package/src/index.ts +19 -14
- package/src/types.ts +3 -30
- package/src/utils/adjustLayoutByDelta.test.ts +1808 -0
- package/src/utils/adjustLayoutByDelta.ts +211 -0
- package/src/utils/calculateAriaValues.test.ts +111 -0
- package/src/utils/calculateAriaValues.ts +67 -0
- package/src/utils/calculateDeltaPercentage.ts +68 -0
- package/src/utils/calculateDragOffsetPercentage.ts +30 -0
- package/src/utils/calculateUnsafeDefaultLayout.test.ts +92 -0
- package/src/utils/calculateUnsafeDefaultLayout.ts +55 -0
- package/src/utils/callPanelCallbacks.ts +81 -0
- package/src/utils/compareLayouts.test.ts +9 -0
- package/src/utils/compareLayouts.ts +12 -0
- package/src/utils/computePanelFlexBoxStyle.ts +44 -0
- package/src/utils/computePercentagePanelConstraints.test.ts +71 -0
- package/src/utils/computePercentagePanelConstraints.ts +56 -0
- package/src/utils/convertPercentageToPixels.test.ts +9 -0
- package/src/utils/convertPercentageToPixels.ts +6 -0
- package/src/utils/convertPixelConstraintsToPercentages.ts +55 -0
- package/src/utils/convertPixelsToPercentage.test.ts +9 -0
- package/src/utils/convertPixelsToPercentage.ts +6 -0
- package/src/utils/determinePivotIndices.ts +10 -0
- package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +29 -0
- package/src/utils/dom/getAvailableGroupSizePixels.ts +29 -0
- package/src/utils/dom/getPanelElement.ts +7 -0
- package/src/utils/dom/getPanelGroupElement.ts +7 -0
- package/src/utils/dom/getResizeHandleElement.ts +9 -0
- package/src/utils/dom/getResizeHandleElementIndex.ts +12 -0
- package/src/utils/dom/getResizeHandleElementsForGroup.ts +9 -0
- package/src/utils/dom/getResizeHandlePanelIds.ts +18 -0
- package/src/utils/events.ts +13 -0
- package/src/utils/getPercentageSizeFromMixedSizes.test.ts +47 -0
- package/src/utils/getPercentageSizeFromMixedSizes.ts +15 -0
- package/src/utils/getResizeEventCursorPosition.ts +19 -0
- package/src/utils/initializeDefaultStorage.ts +26 -0
- package/src/utils/numbers/fuzzyCompareNumbers.test.ts +16 -0
- package/src/utils/numbers/fuzzyCompareNumbers.ts +17 -0
- package/src/utils/numbers/fuzzyNumbersEqual.ts +9 -0
- package/src/utils/resizePanel.ts +41 -0
- package/src/utils/serialization.ts +9 -4
- package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +23 -0
- package/src/utils/shouldMonitorPixelBasedConstraints.ts +13 -0
- package/src/utils/test-utils.ts +136 -0
- package/src/utils/validatePanelConstraints.test.ts +151 -0
- package/src/utils/validatePanelConstraints.ts +103 -0
- package/src/utils/validatePanelGroupLayout.test.ts +233 -0
- package/src/utils/validatePanelGroupLayout.ts +88 -0
- package/src/vendor/react.ts +4 -0
- package/.eslintrc.json +0 -22
- package/dist/declarations/src/utils/group.d.ts +0 -29
- package/src/PanelContexts.ts +0 -22
- package/src/utils/coordinates.ts +0 -149
- 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,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,71 @@
|
|
|
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
|
+
});
|
|
@@ -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,55 @@
|
|
|
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
|
+
if (collapsedSizePixels != null) {
|
|
25
|
+
collapsedSizePercentage = convertPixelsToPercentage(
|
|
26
|
+
collapsedSizePixels,
|
|
27
|
+
groupSizePixels
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (defaultSizePixels != null) {
|
|
31
|
+
defaultSizePercentage = convertPixelsToPercentage(
|
|
32
|
+
defaultSizePixels,
|
|
33
|
+
groupSizePixels
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if (minSizePixels != null) {
|
|
37
|
+
minSizePercentage = convertPixelsToPercentage(
|
|
38
|
+
minSizePixels,
|
|
39
|
+
groupSizePixels
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (maxSizePixels != null) {
|
|
43
|
+
maxSizePercentage = convertPixelsToPercentage(
|
|
44
|
+
maxSizePixels,
|
|
45
|
+
groupSizePixels
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
collapsedSizePercentage,
|
|
51
|
+
defaultSizePercentage,
|
|
52
|
+
maxSizePercentage,
|
|
53
|
+
minSizePercentage,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -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,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,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,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
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ResizeEvent } from "../PanelGroupContext";
|
|
2
|
+
import { Direction } from "../types";
|
|
3
|
+
import { isMouseEvent, isTouchEvent } from "./events";
|
|
4
|
+
|
|
5
|
+
export function getResizeEventCursorPosition(
|
|
6
|
+
direction: Direction,
|
|
7
|
+
event: ResizeEvent
|
|
8
|
+
): number {
|
|
9
|
+
const isHorizontal = direction === "horizontal";
|
|
10
|
+
|
|
11
|
+
if (isMouseEvent(event)) {
|
|
12
|
+
return isHorizontal ? event.clientX : event.clientY;
|
|
13
|
+
} else if (isTouchEvent(event)) {
|
|
14
|
+
const firstTouch = event.touches[0];
|
|
15
|
+
return isHorizontal ? firstTouch.screenX : firstTouch.screenY;
|
|
16
|
+
} else {
|
|
17
|
+
throw Error(`Unsupported event type "${event.type}"`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PanelGroupStorage } from "../PanelGroup";
|
|
2
|
+
|
|
3
|
+
// PanelGroup might be rendering in a server-side environment where localStorage is not available
|
|
4
|
+
// or on a browser with cookies/storage disabled.
|
|
5
|
+
// In either case, this function avoids accessing localStorage until needed,
|
|
6
|
+
// and avoids throwing user-visible errors.
|
|
7
|
+
export function initializeDefaultStorage(storageObject: PanelGroupStorage) {
|
|
8
|
+
try {
|
|
9
|
+
if (typeof localStorage !== "undefined") {
|
|
10
|
+
// Bypass this check for future calls
|
|
11
|
+
storageObject.getItem = (name: string) => {
|
|
12
|
+
return localStorage.getItem(name);
|
|
13
|
+
};
|
|
14
|
+
storageObject.setItem = (name: string, value: string) => {
|
|
15
|
+
localStorage.setItem(name, value);
|
|
16
|
+
};
|
|
17
|
+
} else {
|
|
18
|
+
throw new Error("localStorage not supported in this environment");
|
|
19
|
+
}
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(error);
|
|
22
|
+
|
|
23
|
+
storageObject.getItem = () => null;
|
|
24
|
+
storageObject.setItem = () => {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { fuzzyCompareNumbers } from "./fuzzyCompareNumbers";
|
|
2
|
+
|
|
3
|
+
describe("fuzzyCompareNumbers", () => {
|
|
4
|
+
it("should return 0 when numbers are equal", () => {
|
|
5
|
+
expect(fuzzyCompareNumbers(10.123, 10.123, 5)).toBe(0);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("should return 0 when numbers are fuzzy equal", () => {
|
|
9
|
+
expect(fuzzyCompareNumbers(0.000001, 0.000002, 5)).toBe(0);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("should return a delta when numbers are not unequal", () => {
|
|
13
|
+
expect(fuzzyCompareNumbers(0.000001, 0.000002, 6)).toBe(-1);
|
|
14
|
+
expect(fuzzyCompareNumbers(0.000005, 0.000002, 6)).toBe(1);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PRECISION } from "../../constants";
|
|
2
|
+
|
|
3
|
+
export function fuzzyCompareNumbers(
|
|
4
|
+
actual: number,
|
|
5
|
+
expected: number,
|
|
6
|
+
fractionDigits: number = PRECISION
|
|
7
|
+
): number {
|
|
8
|
+
actual = parseFloat(actual.toFixed(fractionDigits));
|
|
9
|
+
expected = parseFloat(expected.toFixed(fractionDigits));
|
|
10
|
+
|
|
11
|
+
const delta = actual - expected;
|
|
12
|
+
if (delta === 0) {
|
|
13
|
+
return 0;
|
|
14
|
+
} else {
|
|
15
|
+
return delta > 0 ? 1 : -1;
|
|
16
|
+
}
|
|
17
|
+
}
|