react-panel-layout 0.6.0 → 0.6.1
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/dist/{FloatingPanelFrame-SgYLc6Ud.js → FloatingPanelFrame-3eU9AwPo.js} +2 -2
- package/dist/{FloatingPanelFrame-SgYLc6Ud.js.map → FloatingPanelFrame-3eU9AwPo.js.map} +1 -1
- package/dist/FloatingWindow-CUXnEtrb.js +827 -0
- package/dist/FloatingWindow-CUXnEtrb.js.map +1 -0
- package/dist/FloatingWindow-DMwyK0eK.cjs +2 -0
- package/dist/FloatingWindow-DMwyK0eK.cjs.map +1 -0
- package/dist/GridLayout-DKTg_N61.cjs +2 -0
- package/dist/{GridLayout-B4VRsC0r.cjs.map → GridLayout-DKTg_N61.cjs.map} +1 -1
- package/dist/{GridLayout-BltqeCPK.js → GridLayout-UWNxXw77.js} +34 -35
- package/dist/{GridLayout-BltqeCPK.js.map → GridLayout-UWNxXw77.js.map} +1 -1
- package/dist/{HorizontalDivider-WF1k_qND.js → HorizontalDivider-DdxzfV0l.js} +3 -3
- package/dist/{HorizontalDivider-WF1k_qND.js.map → HorizontalDivider-DdxzfV0l.js.map} +1 -1
- package/dist/{HorizontalDivider-B5Z-KZLk.cjs → HorizontalDivider-_pgV4Mcv.cjs} +2 -2
- package/dist/{HorizontalDivider-B5Z-KZLk.cjs.map → HorizontalDivider-_pgV4Mcv.cjs.map} +1 -1
- package/dist/{PanelSystem-Dr1TBhxM.js → PanelSystem-BqUzNtf2.js} +5 -5
- package/dist/{PanelSystem-Dr1TBhxM.js.map → PanelSystem-BqUzNtf2.js.map} +1 -1
- package/dist/{PanelSystem-Bs8bQwQF.cjs → PanelSystem-D603LKKv.cjs} +2 -2
- package/dist/{PanelSystem-Bs8bQwQF.cjs.map → PanelSystem-D603LKKv.cjs.map} +1 -1
- package/dist/ResizeHandle-CBcAS918.cjs +2 -0
- package/dist/{ResizeHandle-CScipO5l.cjs.map → ResizeHandle-CBcAS918.cjs.map} +1 -1
- package/dist/{ResizeHandle-CdA_JYfN.js → ResizeHandle-CXjc1meV.js} +28 -29
- package/dist/{ResizeHandle-CdA_JYfN.js.map → ResizeHandle-CXjc1meV.js.map} +1 -1
- package/dist/SwipePivotTabBar-DWrCuwEI.js +411 -0
- package/dist/SwipePivotTabBar-DWrCuwEI.js.map +1 -0
- package/dist/SwipePivotTabBar-fjjXkpj7.cjs +2 -0
- package/dist/SwipePivotTabBar-fjjXkpj7.cjs.map +1 -0
- package/dist/components/gesture/SwipeSafeZone.d.ts +40 -0
- package/dist/components/window/Drawer.d.ts +3 -1
- package/dist/components/window/DrawerLayers.d.ts +1 -1
- package/dist/components/window/drawerStyles.d.ts +69 -0
- package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
- package/dist/components/window/useDrawerSwipeTransform.d.ts +23 -0
- package/dist/config.cjs +1 -1
- package/dist/config.js +3 -3
- package/dist/constants/styles.d.ts +17 -0
- package/dist/dialog/index.d.ts +69 -0
- package/dist/floating.js +1 -1
- package/dist/grid.cjs +1 -1
- package/dist/grid.js +2 -2
- package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +7 -0
- package/dist/hooks/gesture/types.d.ts +48 -5
- package/dist/hooks/gesture/utils.d.ts +19 -0
- package/dist/hooks/useAnimationFrame.d.ts +2 -0
- package/dist/hooks/useOperationContinuity.d.ts +64 -0
- package/dist/hooks/useResizeObserver.d.ts +33 -1
- package/dist/hooks/useSharedElementTransition.d.ts +112 -0
- package/dist/hooks/useSwipeContentTransform.d.ts +9 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +7 -7
- package/dist/modules/dialog/AlertDialog.d.ts +9 -0
- package/dist/modules/dialog/DialogContainer.d.ts +37 -0
- package/dist/modules/dialog/Modal.d.ts +26 -0
- package/dist/modules/dialog/SwipeDialogContainer.d.ts +16 -0
- package/dist/modules/dialog/dialogAnimationUtils.d.ts +113 -0
- package/dist/modules/dialog/types.d.ts +183 -0
- package/dist/modules/dialog/useDialog.d.ts +39 -0
- package/dist/modules/dialog/useDialogContainer.d.ts +47 -0
- package/dist/modules/dialog/useDialogSwipeInput.d.ts +70 -0
- package/dist/modules/dialog/useDialogTransform.d.ts +82 -0
- package/dist/modules/drawer/types.d.ts +74 -0
- package/dist/modules/drawer/useDrawerSwipeInput.d.ts +24 -0
- package/dist/modules/pivot/SwipePivotTabBar.d.ts +3 -0
- package/dist/modules/stack/SwipeStackContent.d.ts +6 -3
- package/dist/modules/stack/SwipeStackOutlet.d.ts +4 -4
- package/dist/modules/stack/computeSwipeStackTransform.d.ts +1 -1
- package/dist/panels.cjs +1 -1
- package/dist/panels.js +1 -1
- package/dist/pivot.cjs +1 -1
- package/dist/pivot.js +1 -1
- package/dist/resizer.cjs +1 -1
- package/dist/resizer.js +2 -2
- package/dist/stack.cjs +1 -1
- package/dist/stack.cjs.map +1 -1
- package/dist/stack.js +503 -762
- package/dist/stack.js.map +1 -1
- package/dist/sticky-header/calculateStickyMetrics.d.ts +28 -0
- package/dist/sticky-header.cjs +1 -1
- package/dist/sticky-header.cjs.map +1 -1
- package/dist/sticky-header.js +59 -51
- package/dist/sticky-header.js.map +1 -1
- package/dist/{styles-DPPuJ0sf.js → styles-NkjuMOVS.js} +13 -13
- package/dist/{styles-DPPuJ0sf.js.map → styles-NkjuMOVS.js.map} +1 -1
- package/dist/styles-qf6ptVLD.cjs.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/useDocumentPointerEvents-DXxw3qWj.js +54 -0
- package/dist/useDocumentPointerEvents-DXxw3qWj.js.map +1 -0
- package/dist/useDocumentPointerEvents-DxDSOtip.cjs +2 -0
- package/dist/useDocumentPointerEvents-DxDSOtip.cjs.map +1 -0
- package/dist/useNativeGestureGuard-C7TSqEkr.cjs +2 -0
- package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +1 -0
- package/dist/useNativeGestureGuard-CGYo6O0r.js +347 -0
- package/dist/useNativeGestureGuard-CGYo6O0r.js.map +1 -0
- package/dist/window/index.d.ts +2 -0
- package/dist/window.cjs +1 -1
- package/dist/window.cjs.map +1 -1
- package/dist/window.js +114 -103
- package/dist/window.js.map +1 -1
- package/package.json +6 -1
- package/src/components/gesture/SwipeSafeZone.tsx +69 -0
- package/src/components/window/Drawer.tsx +249 -162
- package/src/components/window/DrawerLayers.tsx +13 -3
- package/src/components/window/drawerStyles.spec.ts +263 -0
- package/src/components/window/drawerStyles.ts +228 -0
- package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
- package/src/components/window/drawerSwipeConfig.ts +112 -0
- package/src/components/window/useDrawerSwipeTransform.spec.ts +234 -0
- package/src/components/window/useDrawerSwipeTransform.ts +129 -0
- package/src/constants/styles.ts +19 -0
- package/src/demo/pages/Dialog/alerts/index.tsx +22 -0
- package/src/demo/pages/Dialog/card/index.tsx +22 -0
- package/src/demo/pages/Dialog/components/AlertDialogDemo.tsx +124 -0
- package/src/demo/pages/Dialog/components/CardExpandDemo.module.css +243 -0
- package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +204 -0
- package/src/demo/pages/Dialog/components/CustomAlertDialogDemo.tsx +219 -0
- package/src/demo/pages/Dialog/components/DialogDemos.module.css +77 -0
- package/src/demo/pages/Dialog/components/ModalBasics.tsx +45 -0
- package/src/demo/pages/Dialog/components/SwipeDialogDemo.module.css +77 -0
- package/src/demo/pages/Dialog/components/SwipeDialogDemo.tsx +181 -0
- package/src/demo/pages/Dialog/custom-alert/index.tsx +22 -0
- package/src/demo/pages/Dialog/modal/index.tsx +17 -0
- package/src/demo/pages/Dialog/swipe/index.tsx +22 -0
- package/src/demo/pages/Drawer/components/DrawerSwipe.module.css +316 -0
- package/src/demo/pages/Drawer/components/DrawerSwipe.tsx +178 -0
- package/src/demo/pages/Drawer/swipe/index.tsx +17 -0
- package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +54 -23
- package/src/demo/pages/Pivot/swipe-debug/index.tsx +1 -1
- package/src/demo/pages/Stack/components/StackBasics.spec.tsx +152 -0
- package/src/demo/pages/Stack/components/StackBasics.tsx +179 -95
- package/src/demo/pages/Stack/components/StackTablet.spec.tsx +120 -0
- package/src/demo/pages/Stack/components/StackTablet.tsx +42 -21
- package/src/demo/routes.tsx +22 -1
- package/src/dialog/index.ts +85 -0
- package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +68 -64
- package/src/hooks/gesture/testing/createGestureSimulator.ts +112 -37
- package/src/hooks/gesture/types.ts +83 -6
- package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +22 -14
- package/src/hooks/gesture/useNativeGestureGuard.spec.ts +91 -31
- package/src/hooks/gesture/useNativeGestureGuard.ts +3 -1
- package/src/hooks/gesture/utils.ts +91 -0
- package/src/hooks/useAnimatedVisibility.spec.ts +44 -24
- package/src/hooks/useAnimatedVisibility.ts +28 -2
- package/src/hooks/useAnimationFrame.ts +8 -0
- package/src/hooks/useOperationContinuity.spec.ts +387 -0
- package/src/hooks/useOperationContinuity.ts +135 -0
- package/src/hooks/useResizeObserver.spec.tsx +277 -0
- package/src/hooks/useResizeObserver.tsx +108 -39
- package/src/hooks/useScrollContainer.ts +4 -10
- package/src/hooks/useSharedElementTransition.ts +333 -0
- package/src/hooks/useSwipeContentTransform.spec.ts +18 -18
- package/src/hooks/useSwipeContentTransform.ts +166 -28
- package/src/modules/dialog/AlertDialog.spec.tsx +387 -0
- package/src/modules/dialog/AlertDialog.tsx +221 -0
- package/src/modules/dialog/DialogContainer.spec.tsx +228 -0
- package/src/modules/dialog/DialogContainer.tsx +188 -0
- package/src/modules/dialog/Modal.spec.tsx +220 -0
- package/src/modules/dialog/Modal.tsx +182 -0
- package/src/modules/dialog/SwipeDialogContainer.tsx +208 -0
- package/src/modules/dialog/dialogAnimationUtils.spec.ts +253 -0
- package/src/modules/dialog/dialogAnimationUtils.ts +297 -0
- package/src/modules/dialog/types.ts +186 -0
- package/src/modules/dialog/useDialog.spec.tsx +447 -0
- package/src/modules/dialog/useDialog.ts +214 -0
- package/src/modules/dialog/useDialogContainer.spec.ts +331 -0
- package/src/modules/dialog/useDialogContainer.ts +150 -0
- package/src/modules/dialog/useDialogSwipeInput.spec.ts +157 -0
- package/src/modules/dialog/useDialogSwipeInput.ts +319 -0
- package/src/modules/dialog/useDialogTransform.spec.ts +370 -0
- package/src/modules/dialog/useDialogTransform.ts +407 -0
- package/src/modules/drawer/types.ts +102 -0
- package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
- package/src/modules/drawer/useDrawerSwipeInput.ts +399 -0
- package/src/modules/panels/rendering/ContentRegistry.spec.tsx +21 -14
- package/src/modules/pivot/SwipePivotContent.position.spec.tsx +12 -8
- package/src/modules/pivot/SwipePivotContent.spec.tsx +55 -25
- package/src/modules/pivot/SwipePivotContent.tsx +2 -2
- package/src/modules/pivot/SwipePivotTabBar.spec.tsx +85 -68
- package/src/modules/pivot/SwipePivotTabBar.tsx +75 -15
- package/src/modules/pivot/scaleInputState.spec.ts +11 -2
- package/src/modules/pivot/usePivot.spec.ts +17 -3
- package/src/modules/pivot/usePivotSwipeInput.spec.ts +182 -123
- package/src/modules/stack/SwipeStackContent.spec.tsx +387 -100
- package/src/modules/stack/SwipeStackContent.tsx +43 -33
- package/src/modules/stack/SwipeStackOutlet.spec.tsx +14 -16
- package/src/modules/stack/SwipeStackOutlet.tsx +6 -6
- package/src/modules/stack/computeSwipeStackTransform.spec.ts +5 -5
- package/src/modules/stack/computeSwipeStackTransform.ts +3 -3
- package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1133 -0
- package/src/modules/stack/useStackAnimationState.spec.ts +3 -1
- package/src/modules/stack/useStackAnimationState.ts +18 -13
- package/src/modules/stack/useStackNavigation.spec.ts +198 -3
- package/src/modules/stack/useStackNavigation.tsx +113 -56
- package/src/modules/stack/useStackSwipeInput.spec.ts +65 -32
- package/src/modules/stack/useStackSwipeInput.ts +1 -1
- package/src/sticky-header/StickyArea.tsx +29 -57
- package/src/sticky-header/calculateStickyMetrics.spec.ts +105 -0
- package/src/sticky-header/calculateStickyMetrics.ts +50 -0
- package/src/types.ts +18 -0
- package/src/window/index.ts +2 -0
- package/dist/FloatingWindow-BpdOpg_L.js +0 -400
- package/dist/FloatingWindow-BpdOpg_L.js.map +0 -1
- package/dist/FloatingWindow-TCDNY5gE.cjs +0 -2
- package/dist/FloatingWindow-TCDNY5gE.cjs.map +0 -1
- package/dist/GridLayout-B4VRsC0r.cjs +0 -2
- package/dist/ResizeHandle-CScipO5l.cjs +0 -2
- package/dist/SwipePivotTabBar-BGO9X94m.js +0 -407
- package/dist/SwipePivotTabBar-BGO9X94m.js.map +0 -1
- package/dist/SwipePivotTabBar-BrQismcZ.cjs +0 -2
- package/dist/SwipePivotTabBar-BrQismcZ.cjs.map +0 -1
- package/dist/useDocumentPointerEvents-CKdhGXd0.js +0 -46
- package/dist/useDocumentPointerEvents-CKdhGXd0.js.map +0 -1
- package/dist/useDocumentPointerEvents-ChqrKXDk.cjs +0 -2
- package/dist/useDocumentPointerEvents-ChqrKXDk.cjs.map +0 -1
- package/dist/useEffectEvent-Dp7HLCf0.js +0 -13
- package/dist/useEffectEvent-Dp7HLCf0.js.map +0 -1
- package/dist/useEffectEvent-huSsGUnl.cjs +0 -2
- package/dist/useEffectEvent-huSsGUnl.cjs.map +0 -1
|
@@ -4,16 +4,31 @@
|
|
|
4
4
|
import { renderHook, act } from "@testing-library/react";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import { useStackSwipeInput } from "./useStackSwipeInput.js";
|
|
7
|
-
import type { UseStackNavigationResult
|
|
7
|
+
import type { UseStackNavigationResult } from "./types.js";
|
|
8
8
|
|
|
9
9
|
describe("useStackSwipeInput", () => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
type CallTracker = {
|
|
11
|
+
calls: ReadonlyArray<ReadonlyArray<unknown>>;
|
|
12
|
+
fn: (...args: ReadonlyArray<unknown>) => void;
|
|
13
|
+
};
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const createCallTracker = (): CallTracker => {
|
|
16
|
+
const calls: Array<ReadonlyArray<unknown>> = [];
|
|
17
|
+
const fn = (...args: ReadonlyArray<unknown>): void => {
|
|
18
|
+
calls.push(args);
|
|
19
|
+
};
|
|
20
|
+
return { calls, fn };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type MockNavigationState = {
|
|
24
|
+
navigation: Pick<UseStackNavigationResult, "go" | "canGo" | "revealParent" | "dismissReveal" | "state">;
|
|
25
|
+
calls: {
|
|
26
|
+
go: CallTracker;
|
|
27
|
+
canGo: CallTracker;
|
|
28
|
+
revealParent: CallTracker;
|
|
29
|
+
dismissReveal: CallTracker;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
17
32
|
|
|
18
33
|
const createRef = (width = 300): React.RefObject<HTMLDivElement> => {
|
|
19
34
|
const element = document.createElement("div");
|
|
@@ -29,25 +44,42 @@ describe("useStackSwipeInput", () => {
|
|
|
29
44
|
y: 0,
|
|
30
45
|
toJSON: () => ({}),
|
|
31
46
|
};
|
|
32
|
-
|
|
47
|
+
Object.defineProperty(element, "getBoundingClientRect", {
|
|
48
|
+
value: () => defaultRect,
|
|
49
|
+
});
|
|
33
50
|
return { current: element };
|
|
34
51
|
};
|
|
35
52
|
|
|
36
|
-
const createMockNavigation = (canGoBack = true):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
const createMockNavigation = (canGoBack = true): MockNavigationState => {
|
|
54
|
+
const go = createCallTracker();
|
|
55
|
+
const canGo = createCallTracker();
|
|
56
|
+
const revealParent = createCallTracker();
|
|
57
|
+
const dismissReveal = createCallTracker();
|
|
58
|
+
const canGoFn = (direction: number): boolean => {
|
|
59
|
+
canGo.fn(direction);
|
|
60
|
+
return canGoBack;
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
navigation: {
|
|
64
|
+
go: go.fn,
|
|
65
|
+
canGo: canGoFn,
|
|
66
|
+
revealParent: revealParent.fn,
|
|
67
|
+
dismissReveal: dismissReveal.fn,
|
|
68
|
+
state: {
|
|
69
|
+
stack: ["root", "detail"],
|
|
70
|
+
depth: 1,
|
|
71
|
+
isRevealing: false,
|
|
72
|
+
revealDepth: null,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
calls: {
|
|
76
|
+
go,
|
|
77
|
+
canGo,
|
|
78
|
+
revealParent,
|
|
79
|
+
dismissReveal,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
};
|
|
51
83
|
|
|
52
84
|
/** Create a mock pointer event with preventDefault */
|
|
53
85
|
const createMockPointerEvent = (props: {
|
|
@@ -70,7 +102,7 @@ describe("useStackSwipeInput", () => {
|
|
|
70
102
|
describe("initialization", () => {
|
|
71
103
|
it("starts with isEdgeSwiping false and progress 0", () => {
|
|
72
104
|
const containerRef = createRef();
|
|
73
|
-
const navigation = createMockNavigation();
|
|
105
|
+
const { navigation } = createMockNavigation();
|
|
74
106
|
|
|
75
107
|
const { result } = renderHook(() =>
|
|
76
108
|
useStackSwipeInput({ containerRef, navigation }),
|
|
@@ -82,7 +114,7 @@ describe("useStackSwipeInput", () => {
|
|
|
82
114
|
|
|
83
115
|
it("provides container props with style", () => {
|
|
84
116
|
const containerRef = createRef();
|
|
85
|
-
const navigation = createMockNavigation();
|
|
117
|
+
const { navigation } = createMockNavigation();
|
|
86
118
|
|
|
87
119
|
const { result } = renderHook(() =>
|
|
88
120
|
useStackSwipeInput({ containerRef, navigation }),
|
|
@@ -95,7 +127,7 @@ describe("useStackSwipeInput", () => {
|
|
|
95
127
|
describe("edge swipe to go back", () => {
|
|
96
128
|
it("calls go(-1) when swiping from left edge", () => {
|
|
97
129
|
const containerRef = createRef();
|
|
98
|
-
const navigation = createMockNavigation();
|
|
130
|
+
const { navigation, calls } = createMockNavigation();
|
|
99
131
|
|
|
100
132
|
const { result } = renderHook(() =>
|
|
101
133
|
useStackSwipeInput({
|
|
@@ -132,12 +164,13 @@ describe("useStackSwipeInput", () => {
|
|
|
132
164
|
document.dispatchEvent(upEvent);
|
|
133
165
|
});
|
|
134
166
|
|
|
135
|
-
expect(
|
|
167
|
+
expect(calls.go.calls).toHaveLength(1);
|
|
168
|
+
expect(calls.go.calls[0]?.[0]).toBe(-1);
|
|
136
169
|
});
|
|
137
170
|
|
|
138
171
|
it("does not activate when canGo returns false", () => {
|
|
139
172
|
const containerRef = createRef();
|
|
140
|
-
const navigation = createMockNavigation(false);
|
|
173
|
+
const { navigation } = createMockNavigation(false);
|
|
141
174
|
|
|
142
175
|
const { result } = renderHook(() =>
|
|
143
176
|
useStackSwipeInput({
|
|
@@ -162,7 +195,7 @@ describe("useStackSwipeInput", () => {
|
|
|
162
195
|
describe("progress tracking", () => {
|
|
163
196
|
it("calculates progress based on displacement", () => {
|
|
164
197
|
const containerRef = createRef(300);
|
|
165
|
-
const navigation = createMockNavigation();
|
|
198
|
+
const { navigation } = createMockNavigation();
|
|
166
199
|
|
|
167
200
|
const { result } = renderHook(() =>
|
|
168
201
|
useStackSwipeInput({
|
|
@@ -195,7 +228,7 @@ describe("useStackSwipeInput", () => {
|
|
|
195
228
|
|
|
196
229
|
it("caps progress at 1.0", () => {
|
|
197
230
|
const containerRef = createRef(300);
|
|
198
|
-
const navigation = createMockNavigation();
|
|
231
|
+
const { navigation } = createMockNavigation();
|
|
199
232
|
|
|
200
233
|
const { result } = renderHook(() =>
|
|
201
234
|
useStackSwipeInput({
|
|
@@ -230,7 +263,7 @@ describe("useStackSwipeInput", () => {
|
|
|
230
263
|
describe("disabled state", () => {
|
|
231
264
|
it("does not track when disabled", () => {
|
|
232
265
|
const containerRef = createRef();
|
|
233
|
-
const navigation = createMockNavigation();
|
|
266
|
+
const { navigation } = createMockNavigation();
|
|
234
267
|
|
|
235
268
|
const { result } = renderHook(() =>
|
|
236
269
|
useStackSwipeInput({
|
|
@@ -253,7 +286,7 @@ describe("useStackSwipeInput", () => {
|
|
|
253
286
|
describe("edge configuration", () => {
|
|
254
287
|
it("respects custom edge width", () => {
|
|
255
288
|
const containerRef = createRef();
|
|
256
|
-
const navigation = createMockNavigation();
|
|
289
|
+
const { navigation } = createMockNavigation();
|
|
257
290
|
|
|
258
291
|
const { result } = renderHook(() =>
|
|
259
292
|
useStackSwipeInput({
|
|
@@ -71,7 +71,7 @@ export function useStackSwipeInput(options: UseStackSwipeInputOptions): UseStack
|
|
|
71
71
|
containerRef,
|
|
72
72
|
edge,
|
|
73
73
|
edgeWidth,
|
|
74
|
-
enabled: enabled
|
|
74
|
+
enabled: enabled ? navigation.canGo(-1) : false, // Only enable if can go back
|
|
75
75
|
onSwipeEnd: handleSwipeEnd,
|
|
76
76
|
});
|
|
77
77
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import * as React from "react";
|
|
11
11
|
import { useIsomorphicLayoutEffect } from "../hooks/useIsomorphicLayoutEffect";
|
|
12
|
+
import { calculateStickyMetrics } from "./calculateStickyMetrics";
|
|
12
13
|
import type { StickyAreaProps, StickyAreaState } from "./types";
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -127,63 +128,34 @@ export const StickyArea: React.FC<StickyAreaProps> = ({
|
|
|
127
128
|
const liveRect = area.getBoundingClientRect();
|
|
128
129
|
const viewportHeight = window.innerHeight;
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
// BOTTOM: Cover expands downward during pull-up overscroll
|
|
160
|
-
// liveRect.bottom < viewportHeight means element moved up (overscroll at bottom)
|
|
161
|
-
// liveRect.bottom > viewportHeight means element is below viewport
|
|
162
|
-
const distanceFromBottom = viewportHeight - liveRect.bottom;
|
|
163
|
-
const coverAreaHeight = Math.max(0, liveRect.height + distanceFromBottom);
|
|
164
|
-
|
|
165
|
-
if (coverAreaHeight !== prevHeight) {
|
|
166
|
-
coverArea.style.opacity = coverAreaHeight > 0 ? "1" : "0";
|
|
167
|
-
coverArea.style.height = `${coverAreaHeight}px`;
|
|
168
|
-
prevHeight = coverAreaHeight;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (liveRect.left !== prevLeft || liveRect.width !== prevWidth) {
|
|
172
|
-
coverArea.style.left = `${liveRect.left}px`;
|
|
173
|
-
coverArea.style.width = `${liveRect.width}px`;
|
|
174
|
-
prevLeft = liveRect.left;
|
|
175
|
-
prevWidth = liveRect.width;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const isStuck = liveRect.bottom > viewportHeight;
|
|
179
|
-
const scrollOffset = Math.max(0, liveRect.bottom - viewportHeight);
|
|
180
|
-
|
|
181
|
-
const shouldUpdateState = isFirstRun ? true : isStuck !== prevIsStuck;
|
|
182
|
-
if (shouldUpdateState) {
|
|
183
|
-
isFirstRun = false;
|
|
184
|
-
prevIsStuck = isStuck;
|
|
185
|
-
updateState({ isStuck, scrollOffset });
|
|
186
|
-
}
|
|
131
|
+
// Calculate metrics using pure function
|
|
132
|
+
const { coverAreaHeight, isStuck, scrollOffset } = calculateStickyMetrics(
|
|
133
|
+
position,
|
|
134
|
+
liveRect,
|
|
135
|
+
viewportHeight
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Update height/opacity
|
|
139
|
+
if (coverAreaHeight !== prevHeight) {
|
|
140
|
+
coverArea.style.opacity = coverAreaHeight > 0 ? "1" : "0";
|
|
141
|
+
coverArea.style.height = `${coverAreaHeight}px`;
|
|
142
|
+
prevHeight = coverAreaHeight;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Update left/width
|
|
146
|
+
if (liveRect.left !== prevLeft || liveRect.width !== prevWidth) {
|
|
147
|
+
coverArea.style.left = `${liveRect.left}px`;
|
|
148
|
+
coverArea.style.width = `${liveRect.width}px`;
|
|
149
|
+
prevLeft = liveRect.left;
|
|
150
|
+
prevWidth = liveRect.width;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Update state
|
|
154
|
+
const shouldUpdateState = isFirstRun ? true : isStuck !== prevIsStuck;
|
|
155
|
+
if (shouldUpdateState) {
|
|
156
|
+
isFirstRun = false;
|
|
157
|
+
prevIsStuck = isStuck;
|
|
158
|
+
updateState({ isStuck, scrollOffset });
|
|
187
159
|
}
|
|
188
160
|
};
|
|
189
161
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tests for calculateStickyMetrics pure function.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the calculation of coverAreaHeight, isStuck, and scrollOffset
|
|
5
|
+
* for both top and bottom positions under various scroll scenarios.
|
|
6
|
+
*/
|
|
7
|
+
import { calculateStickyMetrics, type ElementRect } from "./calculateStickyMetrics.js";
|
|
8
|
+
|
|
9
|
+
describe("calculateStickyMetrics", () => {
|
|
10
|
+
describe("position: top", () => {
|
|
11
|
+
it("returns expanded coverAreaHeight during overscroll (pull-down)", () => {
|
|
12
|
+
const rect: ElementRect = { top: 50, bottom: 150, height: 100 };
|
|
13
|
+
const result = calculateStickyMetrics("top", rect, 800);
|
|
14
|
+
|
|
15
|
+
expect(result).toEqual({
|
|
16
|
+
coverAreaHeight: 150, // height + top = 100 + 50
|
|
17
|
+
isStuck: false,
|
|
18
|
+
scrollOffset: 0,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("returns reduced coverAreaHeight during normal scroll", () => {
|
|
23
|
+
const rect: ElementRect = { top: -30, bottom: 70, height: 100 };
|
|
24
|
+
const result = calculateStickyMetrics("top", rect, 800);
|
|
25
|
+
|
|
26
|
+
expect(result).toEqual({
|
|
27
|
+
coverAreaHeight: 70, // height + top = 100 + (-30)
|
|
28
|
+
isStuck: true,
|
|
29
|
+
scrollOffset: 30,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("returns exact height at boundary (top === 0)", () => {
|
|
34
|
+
const rect: ElementRect = { top: 0, bottom: 100, height: 100 };
|
|
35
|
+
const result = calculateStickyMetrics("top", rect, 800);
|
|
36
|
+
|
|
37
|
+
expect(result).toEqual({
|
|
38
|
+
coverAreaHeight: 100,
|
|
39
|
+
isStuck: false,
|
|
40
|
+
scrollOffset: 0,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("clamps coverAreaHeight to 0 when fully scrolled past", () => {
|
|
45
|
+
const rect: ElementRect = { top: -150, bottom: -50, height: 100 };
|
|
46
|
+
const result = calculateStickyMetrics("top", rect, 800);
|
|
47
|
+
|
|
48
|
+
expect(result.coverAreaHeight).toBe(0);
|
|
49
|
+
expect(result.isStuck).toBe(true);
|
|
50
|
+
expect(result.scrollOffset).toBe(150);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("position: bottom", () => {
|
|
55
|
+
it("returns expanded coverAreaHeight during overscroll (pull-up)", () => {
|
|
56
|
+
const rect: ElementRect = { top: 600, bottom: 700, height: 100 };
|
|
57
|
+
const viewportHeight = 800;
|
|
58
|
+
const result = calculateStickyMetrics("bottom", rect, viewportHeight);
|
|
59
|
+
|
|
60
|
+
// distanceFromBottom = 800 - 700 = 100
|
|
61
|
+
// coverAreaHeight = 100 + 100 = 200
|
|
62
|
+
expect(result).toEqual({
|
|
63
|
+
coverAreaHeight: 200,
|
|
64
|
+
isStuck: false,
|
|
65
|
+
scrollOffset: 0,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("returns reduced coverAreaHeight during normal scroll", () => {
|
|
70
|
+
const rect: ElementRect = { top: 750, bottom: 850, height: 100 };
|
|
71
|
+
const viewportHeight = 800;
|
|
72
|
+
const result = calculateStickyMetrics("bottom", rect, viewportHeight);
|
|
73
|
+
|
|
74
|
+
// distanceFromBottom = 800 - 850 = -50
|
|
75
|
+
// coverAreaHeight = 100 + (-50) = 50
|
|
76
|
+
expect(result).toEqual({
|
|
77
|
+
coverAreaHeight: 50,
|
|
78
|
+
isStuck: true,
|
|
79
|
+
scrollOffset: 50,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("returns exact height at boundary (bottom === viewportHeight)", () => {
|
|
84
|
+
const rect: ElementRect = { top: 700, bottom: 800, height: 100 };
|
|
85
|
+
const viewportHeight = 800;
|
|
86
|
+
const result = calculateStickyMetrics("bottom", rect, viewportHeight);
|
|
87
|
+
|
|
88
|
+
expect(result).toEqual({
|
|
89
|
+
coverAreaHeight: 100,
|
|
90
|
+
isStuck: false,
|
|
91
|
+
scrollOffset: 0,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("clamps coverAreaHeight to 0 when fully scrolled past", () => {
|
|
96
|
+
const rect: ElementRect = { top: 900, bottom: 1000, height: 100 };
|
|
97
|
+
const viewportHeight = 800;
|
|
98
|
+
const result = calculateStickyMetrics("bottom", rect, viewportHeight);
|
|
99
|
+
|
|
100
|
+
expect(result.coverAreaHeight).toBe(0);
|
|
101
|
+
expect(result.isStuck).toBe(true);
|
|
102
|
+
expect(result.scrollOffset).toBe(200);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Pure function for calculating sticky area metrics.
|
|
3
|
+
*
|
|
4
|
+
* Extracts the position-dependent calculation logic from StickyArea component
|
|
5
|
+
* to enable unit testing and reduce code duplication.
|
|
6
|
+
*/
|
|
7
|
+
import type { StickyAreaPosition } from "./types";
|
|
8
|
+
|
|
9
|
+
/** Output metrics for sticky area layout. */
|
|
10
|
+
export type StickyMetrics = {
|
|
11
|
+
coverAreaHeight: number;
|
|
12
|
+
isStuck: boolean;
|
|
13
|
+
scrollOffset: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Lightweight element bounding rect used for calculations. */
|
|
17
|
+
export type ElementRect = {
|
|
18
|
+
top: number;
|
|
19
|
+
bottom: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Calculate sticky area metrics based on element position and viewport.
|
|
25
|
+
*
|
|
26
|
+
* @param position - Whether the sticky area is at "top" or "bottom"
|
|
27
|
+
* @param rect - Element bounding rect (top, bottom, height)
|
|
28
|
+
* @param viewportHeight - Current viewport height
|
|
29
|
+
* @returns Calculated metrics for cover area height, stuck state, and scroll offset
|
|
30
|
+
*/
|
|
31
|
+
export function calculateStickyMetrics(
|
|
32
|
+
position: StickyAreaPosition,
|
|
33
|
+
rect: ElementRect,
|
|
34
|
+
viewportHeight: number
|
|
35
|
+
): StickyMetrics {
|
|
36
|
+
if (position === "top") {
|
|
37
|
+
return {
|
|
38
|
+
coverAreaHeight: Math.max(0, rect.height + rect.top),
|
|
39
|
+
isStuck: rect.top < 0,
|
|
40
|
+
scrollOffset: Math.max(0, -rect.top),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// bottom
|
|
44
|
+
const distanceFromBottom = viewportHeight - rect.bottom;
|
|
45
|
+
return {
|
|
46
|
+
coverAreaHeight: Math.max(0, rect.height + distanceFromBottom),
|
|
47
|
+
isStuck: rect.bottom > viewportHeight,
|
|
48
|
+
scrollOffset: Math.max(0, rect.bottom - viewportHeight),
|
|
49
|
+
};
|
|
50
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -225,6 +225,24 @@ export type DrawerBehavior = {
|
|
|
225
225
|
title?: string;
|
|
226
226
|
showCloseButton?: boolean;
|
|
227
227
|
};
|
|
228
|
+
/**
|
|
229
|
+
* Enable swipe gestures for opening/closing the drawer.
|
|
230
|
+
* - true: Enable both edge-swipe-to-open and swipe-to-close
|
|
231
|
+
* - false: Disable swipe gestures (default)
|
|
232
|
+
* - Object: Fine-grained control over swipe behavior
|
|
233
|
+
*/
|
|
234
|
+
swipeGestures?:
|
|
235
|
+
| boolean
|
|
236
|
+
| {
|
|
237
|
+
/** Enable edge swipe from container to open. @default true */
|
|
238
|
+
edgeSwipeOpen?: boolean;
|
|
239
|
+
/** Enable swipe within drawer to close. @default true */
|
|
240
|
+
swipeClose?: boolean;
|
|
241
|
+
/** Width of edge detection zone in pixels. @default 20 */
|
|
242
|
+
edgeWidth?: number;
|
|
243
|
+
/** Threshold ratio (0-1) to trigger close. @default 0.3 */
|
|
244
|
+
dismissThreshold?: number;
|
|
245
|
+
};
|
|
228
246
|
};
|
|
229
247
|
|
|
230
248
|
// ============================================================================
|
package/src/window/index.ts
CHANGED
|
@@ -57,6 +57,7 @@ export { Drawer } from "../components/window/Drawer.js";
|
|
|
57
57
|
export { DrawerLayers } from "../components/window/DrawerLayers.js";
|
|
58
58
|
export { DialogOverlay } from "../components/window/DialogOverlay.js";
|
|
59
59
|
export { PopupLayerPortal } from "../components/window/PopupLayerPortal.js";
|
|
60
|
+
export { SwipeSafeZone } from "../components/gesture/SwipeSafeZone.js";
|
|
60
61
|
|
|
61
62
|
// Hooks
|
|
62
63
|
export { useFloatingState } from "../modules/window/useFloatingState.js";
|
|
@@ -65,3 +66,4 @@ export { useDrawerState } from "../modules/window/useDrawerState.js";
|
|
|
65
66
|
// Types
|
|
66
67
|
export type { FloatingWindowProps } from "../components/window/FloatingWindow.js";
|
|
67
68
|
export type { DrawerProps } from "../components/window/Drawer.js";
|
|
69
|
+
export type { SwipeSafeZoneProps } from "../components/gesture/SwipeSafeZone.js";
|