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,8 +4,83 @@
|
|
|
4
4
|
* Provides a fluent API for simulating pointer events in tests,
|
|
5
5
|
* making it easier to test swipe and drag gestures.
|
|
6
6
|
*/
|
|
7
|
+
import type * as React from "react";
|
|
7
8
|
import { act } from "@testing-library/react";
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Creates a complete mock React.PointerEvent with all required properties.
|
|
12
|
+
*/
|
|
13
|
+
function createFullPointerEvent(
|
|
14
|
+
type: string,
|
|
15
|
+
x: number,
|
|
16
|
+
y: number,
|
|
17
|
+
pointerId: number,
|
|
18
|
+
pointerType: "touch" | "mouse" | "pen",
|
|
19
|
+
): React.PointerEvent {
|
|
20
|
+
const noop = (): void => {};
|
|
21
|
+
const noopBool = (): boolean => false;
|
|
22
|
+
|
|
23
|
+
const element = document.createElement("div");
|
|
24
|
+
const nativeEvent = new PointerEvent(type, {
|
|
25
|
+
clientX: x,
|
|
26
|
+
clientY: y,
|
|
27
|
+
pointerId,
|
|
28
|
+
pointerType,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
clientX: x,
|
|
33
|
+
clientY: y,
|
|
34
|
+
pointerId,
|
|
35
|
+
pointerType,
|
|
36
|
+
isPrimary: true,
|
|
37
|
+
button: 0,
|
|
38
|
+
preventDefault: noop,
|
|
39
|
+
stopPropagation: noop,
|
|
40
|
+
// Event target
|
|
41
|
+
target: element,
|
|
42
|
+
currentTarget: element,
|
|
43
|
+
// Native event
|
|
44
|
+
nativeEvent,
|
|
45
|
+
// SyntheticEvent properties
|
|
46
|
+
bubbles: true,
|
|
47
|
+
cancelable: true,
|
|
48
|
+
defaultPrevented: false,
|
|
49
|
+
eventPhase: 0,
|
|
50
|
+
isTrusted: true,
|
|
51
|
+
isDefaultPrevented: noopBool,
|
|
52
|
+
isPropagationStopped: noopBool,
|
|
53
|
+
persist: noop,
|
|
54
|
+
timeStamp: Date.now(),
|
|
55
|
+
type,
|
|
56
|
+
// MouseEvent properties
|
|
57
|
+
altKey: false,
|
|
58
|
+
buttons: 1,
|
|
59
|
+
ctrlKey: false,
|
|
60
|
+
metaKey: false,
|
|
61
|
+
shiftKey: false,
|
|
62
|
+
getModifierState: noopBool,
|
|
63
|
+
movementX: 0,
|
|
64
|
+
movementY: 0,
|
|
65
|
+
pageX: x,
|
|
66
|
+
pageY: y,
|
|
67
|
+
relatedTarget: null,
|
|
68
|
+
screenX: x,
|
|
69
|
+
screenY: y,
|
|
70
|
+
// PointerEvent properties
|
|
71
|
+
height: 1,
|
|
72
|
+
pressure: 0.5,
|
|
73
|
+
tangentialPressure: 0,
|
|
74
|
+
tiltX: 0,
|
|
75
|
+
tiltY: 0,
|
|
76
|
+
twist: 0,
|
|
77
|
+
width: 1,
|
|
78
|
+
// UIEvent properties
|
|
79
|
+
detail: 0,
|
|
80
|
+
view: window as unknown as React.AbstractView,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
9
84
|
/**
|
|
10
85
|
* 2D point for gesture coordinates.
|
|
11
86
|
*/
|
|
@@ -107,10 +182,10 @@ export function createGestureSimulator(options: GestureSimulatorOptions = {}): G
|
|
|
107
182
|
pointerId = 1,
|
|
108
183
|
} = options;
|
|
109
184
|
|
|
110
|
-
|
|
185
|
+
const pointerState = { isDown: false };
|
|
111
186
|
|
|
112
187
|
const pointerDown = (x: number, y: number): void => {
|
|
113
|
-
isDown = true;
|
|
188
|
+
pointerState.isDown = true;
|
|
114
189
|
|
|
115
190
|
const event = new PointerEvent("pointerdown", {
|
|
116
191
|
clientX: x,
|
|
@@ -129,7 +204,7 @@ export function createGestureSimulator(options: GestureSimulatorOptions = {}): G
|
|
|
129
204
|
};
|
|
130
205
|
|
|
131
206
|
const pointerMove = (x: number, y: number): void => {
|
|
132
|
-
if (!isDown) {
|
|
207
|
+
if (!pointerState.isDown) {
|
|
133
208
|
return;
|
|
134
209
|
}
|
|
135
210
|
|
|
@@ -149,11 +224,11 @@ export function createGestureSimulator(options: GestureSimulatorOptions = {}): G
|
|
|
149
224
|
};
|
|
150
225
|
|
|
151
226
|
const pointerUp = (): void => {
|
|
152
|
-
if (!isDown) {
|
|
227
|
+
if (!pointerState.isDown) {
|
|
153
228
|
return;
|
|
154
229
|
}
|
|
155
230
|
|
|
156
|
-
isDown = false;
|
|
231
|
+
pointerState.isDown = false;
|
|
157
232
|
|
|
158
233
|
const event = new PointerEvent("pointerup", {
|
|
159
234
|
pointerId,
|
|
@@ -188,28 +263,37 @@ export function createGestureSimulator(options: GestureSimulatorOptions = {}): G
|
|
|
188
263
|
containerSize = 300,
|
|
189
264
|
): void => {
|
|
190
265
|
const edgeOffset = 10; // Start 10px from edge
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
266
|
+
const getEdgeSwipePoints = (
|
|
267
|
+
direction: "left" | "right" | "top" | "bottom",
|
|
268
|
+
size: number,
|
|
269
|
+
travel: number,
|
|
270
|
+
offset: number,
|
|
271
|
+
): { from: Point; to: Point } => {
|
|
272
|
+
if (direction === "left") {
|
|
273
|
+
return {
|
|
274
|
+
from: { x: offset, y: size / 2 },
|
|
275
|
+
to: { x: offset + travel, y: size / 2 },
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (direction === "right") {
|
|
279
|
+
return {
|
|
280
|
+
from: { x: size - offset, y: size / 2 },
|
|
281
|
+
to: { x: size - offset - travel, y: size / 2 },
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (direction === "top") {
|
|
285
|
+
return {
|
|
286
|
+
from: { x: size / 2, y: offset },
|
|
287
|
+
to: { x: size / 2, y: offset + travel },
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
from: { x: size / 2, y: size - offset },
|
|
292
|
+
to: { x: size / 2, y: size - offset - travel },
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const { from, to } = getEdgeSwipePoints(edge, containerSize, distance, edgeOffset);
|
|
213
297
|
|
|
214
298
|
swipe(from, to);
|
|
215
299
|
};
|
|
@@ -219,16 +303,7 @@ export function createGestureSimulator(options: GestureSimulatorOptions = {}): G
|
|
|
219
303
|
x: number,
|
|
220
304
|
y: number,
|
|
221
305
|
): React.PointerEvent => {
|
|
222
|
-
return
|
|
223
|
-
clientX: x,
|
|
224
|
-
clientY: y,
|
|
225
|
-
pointerId,
|
|
226
|
-
pointerType,
|
|
227
|
-
isPrimary: true,
|
|
228
|
-
button: 0,
|
|
229
|
-
preventDefault: () => {},
|
|
230
|
-
stopPropagation: () => {},
|
|
231
|
-
} as unknown as React.PointerEvent;
|
|
306
|
+
return createFullPointerEvent(type, x, y, pointerId, pointerType);
|
|
232
307
|
};
|
|
233
308
|
|
|
234
309
|
return {
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* - Input: how to command (swipe, click, keyboard)
|
|
7
7
|
* - Presentation: how to show (animation, transition)
|
|
8
8
|
*
|
|
9
|
-
* This file defines types for the Input layer
|
|
9
|
+
* This file defines types for the Input layer, including the abstract
|
|
10
|
+
* ContinuousOperationState that represents any continuous state transition
|
|
11
|
+
* (whether controlled by human gesture or system animation).
|
|
10
12
|
*/
|
|
11
13
|
import type * as React from "react";
|
|
12
14
|
|
|
@@ -15,11 +17,6 @@ import type * as React from "react";
|
|
|
15
17
|
*/
|
|
16
18
|
export type GestureAxis = "horizontal" | "vertical";
|
|
17
19
|
|
|
18
|
-
/**
|
|
19
|
-
* Phase of swipe input lifecycle.
|
|
20
|
-
*/
|
|
21
|
-
export type SwipeInputPhase = "idle" | "tracking" | "swiping" | "ended";
|
|
22
|
-
|
|
23
20
|
/**
|
|
24
21
|
* 2D vector for displacement and velocity.
|
|
25
22
|
*/
|
|
@@ -28,6 +25,86 @@ export type Vector2 = {
|
|
|
28
25
|
y: number;
|
|
29
26
|
};
|
|
30
27
|
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Continuous Operation State
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// A continuous operation is any state transition that occurs over time,
|
|
32
|
+
// where progress can be observed incrementally. The controlling agent
|
|
33
|
+
// may be human (gesture) or system (animation).
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Phase of a continuous operation lifecycle.
|
|
37
|
+
* - "idle": No operation in progress
|
|
38
|
+
* - "operating": Operation is in progress (human or system controlled)
|
|
39
|
+
* - "ended": Operation has completed
|
|
40
|
+
*/
|
|
41
|
+
export type ContinuousOperationPhase = "idle" | "operating" | "ended";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* State of a continuous operation.
|
|
45
|
+
*
|
|
46
|
+
* This is the abstract representation of any operation that occurs over time,
|
|
47
|
+
* whether controlled by human gesture or system animation. Components that
|
|
48
|
+
* accept this state can respond to both input types uniformly.
|
|
49
|
+
*/
|
|
50
|
+
export type ContinuousOperationState = {
|
|
51
|
+
/** Current phase of the operation */
|
|
52
|
+
phase: ContinuousOperationPhase;
|
|
53
|
+
/** Displacement from start position in pixels */
|
|
54
|
+
displacement: Vector2;
|
|
55
|
+
/** Current velocity in pixels per millisecond */
|
|
56
|
+
velocity: Vector2;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Initial idle state for ContinuousOperationState.
|
|
61
|
+
*/
|
|
62
|
+
export const IDLE_CONTINUOUS_OPERATION_STATE: ContinuousOperationState = {
|
|
63
|
+
phase: "idle",
|
|
64
|
+
displacement: { x: 0, y: 0 },
|
|
65
|
+
velocity: { x: 0, y: 0 },
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Convert SwipeInputPhase to ContinuousOperationPhase.
|
|
70
|
+
* - "idle" → "idle"
|
|
71
|
+
* - "tracking" | "swiping" → "operating"
|
|
72
|
+
* - "ended" → "ended"
|
|
73
|
+
*/
|
|
74
|
+
export function toContinuousPhase(phase: SwipeInputPhase): ContinuousOperationPhase {
|
|
75
|
+
if (phase === "idle") {
|
|
76
|
+
return "idle";
|
|
77
|
+
}
|
|
78
|
+
if (phase === "ended") {
|
|
79
|
+
return "ended";
|
|
80
|
+
}
|
|
81
|
+
return "operating";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Convert SwipeInputState to ContinuousOperationState.
|
|
86
|
+
*/
|
|
87
|
+
export function toContinuousOperationState(state: SwipeInputState): ContinuousOperationState {
|
|
88
|
+
return {
|
|
89
|
+
phase: toContinuousPhase(state.phase),
|
|
90
|
+
displacement: state.displacement,
|
|
91
|
+
velocity: state.velocity,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// Swipe Input (concrete implementation of continuous operation)
|
|
97
|
+
// ============================================================================
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Phase of swipe input lifecycle.
|
|
101
|
+
* - "idle": No swipe in progress
|
|
102
|
+
* - "tracking": Pointer down, tracking movement (direction not yet locked)
|
|
103
|
+
* - "swiping": Direction locked, actively swiping
|
|
104
|
+
* - "ended": Swipe gesture completed
|
|
105
|
+
*/
|
|
106
|
+
export type SwipeInputPhase = "idle" | "tracking" | "swiping" | "ended";
|
|
107
|
+
|
|
31
108
|
/**
|
|
32
109
|
* Point with timestamp for velocity calculation.
|
|
33
110
|
*/
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file Tests for useEdgeSwipeInput hook.
|
|
3
3
|
*/
|
|
4
|
-
/* eslint-disable no-restricted-globals, no-restricted-properties -- test requires vi for timing control */
|
|
5
4
|
import { renderHook, act } from "@testing-library/react";
|
|
6
5
|
import * as React from "react";
|
|
7
6
|
import { useEdgeSwipeInput } from "./useEdgeSwipeInput.js";
|
|
8
7
|
|
|
9
8
|
describe("useEdgeSwipeInput", () => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
type CallTracker = {
|
|
10
|
+
calls: ReadonlyArray<ReadonlyArray<unknown>>;
|
|
11
|
+
fn: (...args: ReadonlyArray<unknown>) => void;
|
|
12
|
+
};
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const createCallTracker = (): CallTracker => {
|
|
15
|
+
const calls: Array<ReadonlyArray<unknown>> = [];
|
|
16
|
+
const fn = (...args: ReadonlyArray<unknown>): void => {
|
|
17
|
+
calls.push(args);
|
|
18
|
+
};
|
|
19
|
+
return { calls, fn };
|
|
20
|
+
};
|
|
17
21
|
|
|
18
22
|
const createRef = (rect: Partial<DOMRect> = {}): React.RefObject<HTMLDivElement> => {
|
|
19
23
|
const element = document.createElement("div");
|
|
@@ -28,7 +32,10 @@ describe("useEdgeSwipeInput", () => {
|
|
|
28
32
|
y: 0,
|
|
29
33
|
toJSON: () => ({}),
|
|
30
34
|
};
|
|
31
|
-
|
|
35
|
+
const mergedRect = Object.assign({}, defaultRect, rect);
|
|
36
|
+
Object.defineProperty(element, "getBoundingClientRect", {
|
|
37
|
+
value: () => mergedRect,
|
|
38
|
+
});
|
|
32
39
|
return { current: element };
|
|
33
40
|
};
|
|
34
41
|
|
|
@@ -283,7 +290,7 @@ describe("useEdgeSwipeInput", () => {
|
|
|
283
290
|
describe("swipe completion callback", () => {
|
|
284
291
|
it("calls onSwipeEnd when edge swipe threshold is met", () => {
|
|
285
292
|
const containerRef = createRef({ left: 0, right: 300 });
|
|
286
|
-
const onSwipeEnd =
|
|
293
|
+
const onSwipeEnd = createCallTracker();
|
|
287
294
|
|
|
288
295
|
const { result } = renderHook(() =>
|
|
289
296
|
useEdgeSwipeInput({
|
|
@@ -291,7 +298,7 @@ describe("useEdgeSwipeInput", () => {
|
|
|
291
298
|
edge: "left",
|
|
292
299
|
edgeWidth: 20,
|
|
293
300
|
thresholds: { distanceThreshold: 50, velocityThreshold: 0.3, lockThreshold: 10 },
|
|
294
|
-
onSwipeEnd,
|
|
301
|
+
onSwipeEnd: onSwipeEnd.fn,
|
|
295
302
|
}),
|
|
296
303
|
);
|
|
297
304
|
|
|
@@ -327,7 +334,8 @@ describe("useEdgeSwipeInput", () => {
|
|
|
327
334
|
document.dispatchEvent(upEvent);
|
|
328
335
|
});
|
|
329
336
|
|
|
330
|
-
expect(onSwipeEnd).
|
|
337
|
+
expect(onSwipeEnd.calls).toHaveLength(1);
|
|
338
|
+
expect(onSwipeEnd.calls[0]?.[0]).toEqual(
|
|
331
339
|
expect.objectContaining({
|
|
332
340
|
phase: "ended",
|
|
333
341
|
direction: 1,
|
|
@@ -337,7 +345,7 @@ describe("useEdgeSwipeInput", () => {
|
|
|
337
345
|
|
|
338
346
|
it("does not call onSwipeEnd when gesture starts outside edge", () => {
|
|
339
347
|
const containerRef = createRef({ left: 0, right: 300 });
|
|
340
|
-
const onSwipeEnd =
|
|
348
|
+
const onSwipeEnd = createCallTracker();
|
|
341
349
|
|
|
342
350
|
const { result } = renderHook(() =>
|
|
343
351
|
useEdgeSwipeInput({
|
|
@@ -345,7 +353,7 @@ describe("useEdgeSwipeInput", () => {
|
|
|
345
353
|
edge: "left",
|
|
346
354
|
edgeWidth: 20,
|
|
347
355
|
thresholds: { distanceThreshold: 50, velocityThreshold: 0.3, lockThreshold: 10 },
|
|
348
|
-
onSwipeEnd,
|
|
356
|
+
onSwipeEnd: onSwipeEnd.fn,
|
|
349
357
|
}),
|
|
350
358
|
);
|
|
351
359
|
|
|
@@ -365,7 +373,7 @@ describe("useEdgeSwipeInput", () => {
|
|
|
365
373
|
|
|
366
374
|
// The gesture should not be tracked, so no callback should be fired
|
|
367
375
|
expect(result.current.isEdgeGesture).toBe(false);
|
|
368
|
-
expect(onSwipeEnd).
|
|
376
|
+
expect(onSwipeEnd.calls).toHaveLength(0);
|
|
369
377
|
});
|
|
370
378
|
});
|
|
371
379
|
|
|
@@ -1,11 +1,91 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file Tests for useNativeGestureGuard hook.
|
|
3
3
|
*/
|
|
4
|
-
/* eslint-disable custom/no-as-outside-guard -- test requires overrides */
|
|
5
4
|
import { renderHook, act } from "@testing-library/react";
|
|
6
5
|
import * as React from "react";
|
|
7
6
|
import { useNativeGestureGuard } from "./useNativeGestureGuard.js";
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Extended PointerEvent type with test utility method.
|
|
10
|
+
*/
|
|
11
|
+
type TestPointerEvent = React.PointerEvent<HTMLElement> & {
|
|
12
|
+
wasDefaultPrevented: () => boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a mock PointerEvent that satisfies the React.PointerEvent interface.
|
|
17
|
+
*/
|
|
18
|
+
function createMockPointerEvent(props: {
|
|
19
|
+
clientX: number;
|
|
20
|
+
clientY: number;
|
|
21
|
+
pointerType: string;
|
|
22
|
+
}): TestPointerEvent {
|
|
23
|
+
const noop = (): void => {};
|
|
24
|
+
const noopBool = (): boolean => false;
|
|
25
|
+
const state = { preventDefaultCalled: false };
|
|
26
|
+
|
|
27
|
+
const element = document.createElement("div");
|
|
28
|
+
const nativeEvent = new PointerEvent("pointerdown", {
|
|
29
|
+
clientX: props.clientX,
|
|
30
|
+
clientY: props.clientY,
|
|
31
|
+
pointerType: props.pointerType,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...props,
|
|
36
|
+
// Test utility
|
|
37
|
+
wasDefaultPrevented: () => state.preventDefaultCalled,
|
|
38
|
+
// Event target
|
|
39
|
+
target: element,
|
|
40
|
+
currentTarget: element,
|
|
41
|
+
// Native event
|
|
42
|
+
nativeEvent,
|
|
43
|
+
// SyntheticEvent properties
|
|
44
|
+
bubbles: true,
|
|
45
|
+
cancelable: true,
|
|
46
|
+
defaultPrevented: false,
|
|
47
|
+
eventPhase: 0,
|
|
48
|
+
isTrusted: true,
|
|
49
|
+
preventDefault: () => {
|
|
50
|
+
state.preventDefaultCalled = true;
|
|
51
|
+
},
|
|
52
|
+
isDefaultPrevented: () => state.preventDefaultCalled,
|
|
53
|
+
stopPropagation: noop,
|
|
54
|
+
isPropagationStopped: noopBool,
|
|
55
|
+
persist: noop,
|
|
56
|
+
timeStamp: Date.now(),
|
|
57
|
+
type: "pointerdown",
|
|
58
|
+
// MouseEvent properties
|
|
59
|
+
altKey: false,
|
|
60
|
+
button: 0,
|
|
61
|
+
buttons: 1,
|
|
62
|
+
ctrlKey: false,
|
|
63
|
+
metaKey: false,
|
|
64
|
+
shiftKey: false,
|
|
65
|
+
getModifierState: noopBool,
|
|
66
|
+
movementX: 0,
|
|
67
|
+
movementY: 0,
|
|
68
|
+
pageX: props.clientX,
|
|
69
|
+
pageY: props.clientY,
|
|
70
|
+
relatedTarget: null,
|
|
71
|
+
screenX: props.clientX,
|
|
72
|
+
screenY: props.clientY,
|
|
73
|
+
// PointerEvent properties
|
|
74
|
+
height: 1,
|
|
75
|
+
isPrimary: true,
|
|
76
|
+
pointerId: 1,
|
|
77
|
+
pressure: 0.5,
|
|
78
|
+
tangentialPressure: 0,
|
|
79
|
+
tiltX: 0,
|
|
80
|
+
tiltY: 0,
|
|
81
|
+
twist: 0,
|
|
82
|
+
width: 1,
|
|
83
|
+
// UIEvent properties
|
|
84
|
+
detail: 0,
|
|
85
|
+
view: window,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
9
89
|
describe("useNativeGestureGuard", () => {
|
|
10
90
|
const createRef = (rect: Partial<DOMRect> = {}): React.RefObject<HTMLDivElement> => {
|
|
11
91
|
const element = document.createElement("div");
|
|
@@ -25,26 +105,6 @@ describe("useNativeGestureGuard", () => {
|
|
|
25
105
|
return { current: element };
|
|
26
106
|
};
|
|
27
107
|
|
|
28
|
-
/**
|
|
29
|
-
* Creates a fake pointer event with preventDefault tracking.
|
|
30
|
-
* Uses an object to track state instead of let variable.
|
|
31
|
-
*/
|
|
32
|
-
const createFakePointerEvent = (props: {
|
|
33
|
-
clientX: number;
|
|
34
|
-
clientY: number;
|
|
35
|
-
pointerType: string;
|
|
36
|
-
}) => {
|
|
37
|
-
const state = { preventDefaultCalled: false };
|
|
38
|
-
const event = {
|
|
39
|
-
...props,
|
|
40
|
-
preventDefault: () => {
|
|
41
|
-
state.preventDefaultCalled = true;
|
|
42
|
-
},
|
|
43
|
-
wasDefaultPrevented: () => state.preventDefaultCalled,
|
|
44
|
-
};
|
|
45
|
-
return event as unknown as React.PointerEvent<HTMLElement> & { wasDefaultPrevented: () => boolean };
|
|
46
|
-
};
|
|
47
|
-
|
|
48
108
|
describe("overscroll behavior", () => {
|
|
49
109
|
it("applies overscroll-behavior: contain when active and preventOverscroll is true", () => {
|
|
50
110
|
const containerRef = createRef();
|
|
@@ -99,7 +159,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
99
159
|
}),
|
|
100
160
|
);
|
|
101
161
|
|
|
102
|
-
const mockEvent =
|
|
162
|
+
const mockEvent = createMockPointerEvent({
|
|
103
163
|
clientX: 10, // Within 20px edge zone
|
|
104
164
|
clientY: 100,
|
|
105
165
|
pointerType: "touch",
|
|
@@ -123,7 +183,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
123
183
|
}),
|
|
124
184
|
);
|
|
125
185
|
|
|
126
|
-
const mockEvent =
|
|
186
|
+
const mockEvent = createMockPointerEvent({
|
|
127
187
|
clientX: 50, // Outside 20px edge zone
|
|
128
188
|
clientY: 100,
|
|
129
189
|
pointerType: "touch",
|
|
@@ -147,7 +207,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
147
207
|
}),
|
|
148
208
|
);
|
|
149
209
|
|
|
150
|
-
const mockEvent =
|
|
210
|
+
const mockEvent = createMockPointerEvent({
|
|
151
211
|
clientX: 10, // Within edge zone
|
|
152
212
|
clientY: 100,
|
|
153
213
|
pointerType: "mouse", // Not touch
|
|
@@ -173,7 +233,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
173
233
|
}),
|
|
174
234
|
);
|
|
175
235
|
|
|
176
|
-
const mockEvent =
|
|
236
|
+
const mockEvent = createMockPointerEvent({
|
|
177
237
|
clientX: 10,
|
|
178
238
|
clientY: 100,
|
|
179
239
|
pointerType: "touch",
|
|
@@ -227,7 +287,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
227
287
|
}),
|
|
228
288
|
);
|
|
229
289
|
|
|
230
|
-
const mockEventInCustomEdge =
|
|
290
|
+
const mockEventInCustomEdge = createMockPointerEvent({
|
|
231
291
|
clientX: 40, // Within 50px edge, but outside default 20px
|
|
232
292
|
clientY: 100,
|
|
233
293
|
pointerType: "touch",
|
|
@@ -266,7 +326,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
266
326
|
}),
|
|
267
327
|
);
|
|
268
328
|
|
|
269
|
-
const mockEvent =
|
|
329
|
+
const mockEvent = createMockPointerEvent({
|
|
270
330
|
clientX: 10,
|
|
271
331
|
clientY: 100,
|
|
272
332
|
pointerType: "touch",
|
|
@@ -306,7 +366,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
306
366
|
|
|
307
367
|
expect(document.documentElement.style.overscrollBehavior).toBe("");
|
|
308
368
|
|
|
309
|
-
const mockEvent =
|
|
369
|
+
const mockEvent = createMockPointerEvent({
|
|
310
370
|
clientX: 10,
|
|
311
371
|
clientY: 100,
|
|
312
372
|
pointerType: "touch",
|
|
@@ -335,7 +395,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
335
395
|
);
|
|
336
396
|
|
|
337
397
|
// Simulate pointerdown to apply style
|
|
338
|
-
const mockEvent =
|
|
398
|
+
const mockEvent = createMockPointerEvent({
|
|
339
399
|
clientX: 10,
|
|
340
400
|
clientY: 100,
|
|
341
401
|
pointerType: "touch",
|
|
@@ -369,7 +429,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
369
429
|
);
|
|
370
430
|
|
|
371
431
|
// Simulate pointerdown to apply style
|
|
372
|
-
const mockEvent =
|
|
432
|
+
const mockEvent = createMockPointerEvent({
|
|
373
433
|
clientX: 10,
|
|
374
434
|
clientY: 100,
|
|
375
435
|
pointerType: "touch",
|
|
@@ -397,7 +457,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
397
457
|
}),
|
|
398
458
|
);
|
|
399
459
|
|
|
400
|
-
const mockEvent =
|
|
460
|
+
const mockEvent = createMockPointerEvent({
|
|
401
461
|
clientX: 10,
|
|
402
462
|
clientY: 100,
|
|
403
463
|
pointerType: "touch",
|
|
@@ -57,7 +57,9 @@ export function useNativeGestureGuard(options: UseNativeGestureGuardOptions): Us
|
|
|
57
57
|
|
|
58
58
|
// Apply overscroll-behavior to html synchronously (called from onPointerDown)
|
|
59
59
|
const applyHtmlOverscroll = React.useCallback(() => {
|
|
60
|
-
if (!preventOverscroll)
|
|
60
|
+
if (!preventOverscroll) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
61
63
|
|
|
62
64
|
const html = document.documentElement;
|
|
63
65
|
if (previousHtmlOverscrollRef.current === null) {
|