react-panel-layout 0.6.0 → 0.7.0
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-Bw2djgpz.js +1542 -0
- package/dist/FloatingWindow-Bw2djgpz.js.map +1 -0
- package/dist/FloatingWindow-Cvyokf0m.cjs +2 -0
- package/dist/FloatingWindow-Cvyokf0m.cjs.map +1 -0
- package/dist/GridLayout-B4aCqSyd.js +947 -0
- package/dist/{GridLayout-BltqeCPK.js.map → GridLayout-B4aCqSyd.js.map} +1 -1
- package/dist/GridLayout-DNOClFzz.cjs +2 -0
- package/dist/{GridLayout-B4VRsC0r.cjs.map → GridLayout-DNOClFzz.cjs.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-B8Igvnb2.cjs +3 -0
- package/dist/PanelSystem-B8Igvnb2.cjs.map +1 -0
- package/dist/{PanelSystem-Dr1TBhxM.js → PanelSystem-DDUSFjXD.js} +209 -248
- package/dist/PanelSystem-DDUSFjXD.js.map +1 -0
- 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 +4 -1
- package/dist/components/window/DrawerLayers.d.ts +1 -1
- package/dist/components/window/DrawerRevealContext.d.ts +61 -0
- package/dist/components/window/drawerRevealAnimationUtils.d.ts +212 -0
- package/dist/components/window/drawerStyles.d.ts +74 -0
- package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
- package/dist/components/window/useDrawerSwipeTransform.d.ts +29 -0
- package/dist/components/window/useDrawerTransform.d.ts +68 -0
- package/dist/components/window/useRevealDrawerTransform.d.ts +56 -0
- package/dist/config.cjs +1 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.js +9 -8
- package/dist/config.js.map +1 -1
- 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/drawerStateMachine.d.ts +168 -0
- package/dist/modules/drawer/revealDrawerConstants.d.ts +33 -0
- package/dist/modules/drawer/revealDrawerStateMachine.d.ts +146 -0
- package/dist/modules/drawer/strategies/index.d.ts +8 -0
- package/dist/modules/drawer/strategies/overlayStrategy.d.ts +12 -0
- package/dist/modules/drawer/strategies/revealStrategy.d.ts +12 -0
- package/dist/modules/drawer/strategies/types.d.ts +116 -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 +480 -780
- 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 +30 -0
- package/dist/useAnimationFrame-BZ6D2lMq.cjs +2 -0
- package/dist/useAnimationFrame-BZ6D2lMq.cjs.map +1 -0
- package/dist/useAnimationFrame-Bg4e-H8O.js +394 -0
- package/dist/useAnimationFrame-Bg4e-H8O.js.map +1 -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/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 +70 -0
- package/src/components/grid/GridLayout.tsx +110 -38
- package/src/components/window/Drawer.tsx +353 -162
- package/src/components/window/DrawerLayers.tsx +54 -11
- package/src/components/window/DrawerRevealContext.spec.ts +20 -0
- package/src/components/window/DrawerRevealContext.tsx +99 -0
- package/src/components/window/drawerRevealAnimationUtils.spec.ts +375 -0
- package/src/components/window/drawerRevealAnimationUtils.ts +415 -0
- package/src/components/window/drawerStyles.spec.ts +302 -0
- package/src/components/window/drawerStyles.ts +252 -0
- package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
- package/src/components/window/drawerSwipeConfig.ts +112 -0
- package/src/components/window/useDrawerSwipeTransform.ts +67 -0
- package/src/components/window/useDrawerTransform.ts +505 -0
- package/src/components/window/useRevealDrawerTransform.spec.ts +1936 -0
- package/src/components/window/useRevealDrawerTransform.ts +105 -0
- package/src/constants/styles.ts +19 -0
- package/src/demo/components/FullscreenDemoPage.tsx +47 -0
- package/src/demo/fullscreenRoutes.tsx +32 -0
- package/src/demo/index.tsx +5 -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 +219 -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/DrawerBasics.module.css +6 -1
- package/src/demo/pages/Drawer/components/DrawerBasics.tsx +14 -4
- package/src/demo/pages/Drawer/components/DrawerReveal.module.css +157 -0
- package/src/demo/pages/Drawer/components/DrawerReveal.tsx +128 -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/reveal/index.tsx +17 -0
- package/src/demo/pages/Drawer/reveal-fullscreen/index.tsx +135 -0
- package/src/demo/pages/Drawer/reveal-fullscreen/styles.module.css +233 -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 +156 -0
- package/src/demo/pages/Stack/components/StackBasics.tsx +179 -95
- package/src/demo/pages/Stack/components/StackTablet.spec.tsx +110 -0
- package/src/demo/pages/Stack/components/StackTablet.tsx +42 -21
- package/src/demo/routes.tsx +24 -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 +113 -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 +99 -31
- package/src/hooks/gesture/useNativeGestureGuard.ts +3 -1
- package/src/hooks/gesture/utils.ts +102 -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 +394 -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 +354 -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 +252 -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 +339 -0
- package/src/modules/dialog/useDialogContainer.ts +150 -0
- package/src/modules/dialog/useDialogSwipeInput.spec.ts +178 -0
- package/src/modules/dialog/useDialogSwipeInput.ts +350 -0
- package/src/modules/dialog/useDialogTransform.spec.ts +403 -0
- package/src/modules/dialog/useDialogTransform.ts +407 -0
- package/src/modules/drawer/drawerStateMachine.ts +500 -0
- package/src/modules/drawer/revealDrawerConstants.ts +38 -0
- package/src/modules/drawer/revealDrawerStateMachine.spec.ts +558 -0
- package/src/modules/drawer/revealDrawerStateMachine.ts +197 -0
- package/src/modules/drawer/strategies/index.ts +9 -0
- package/src/modules/drawer/strategies/overlayStrategy.ts +133 -0
- package/src/modules/drawer/strategies/revealStrategy.ts +111 -0
- package/src/modules/drawer/strategies/types.ts +160 -0
- package/src/modules/drawer/types.ts +102 -0
- package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
- package/src/modules/drawer/useDrawerSwipeInput.ts +402 -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 +66 -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 +33 -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/GridLayout-BltqeCPK.js +0 -927
- package/dist/PanelSystem-Bs8bQwQF.cjs +0 -3
- package/dist/PanelSystem-Bs8bQwQF.cjs.map +0 -1
- package/dist/PanelSystem-Dr1TBhxM.js.map +0 -1
- 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
|
@@ -1,11 +1,99 @@
|
|
|
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
|
+
* Create a mock view for test events.
|
|
17
|
+
*/
|
|
18
|
+
function createMockView(): React.PointerEvent<HTMLElement>["view"] {
|
|
19
|
+
// eslint-disable-next-line custom/no-as-outside-guard -- test helper for view casting
|
|
20
|
+
return window as unknown as React.PointerEvent<HTMLElement>["view"];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a mock PointerEvent that satisfies the React.PointerEvent interface.
|
|
25
|
+
*/
|
|
26
|
+
function createMockPointerEvent(props: {
|
|
27
|
+
clientX: number;
|
|
28
|
+
clientY: number;
|
|
29
|
+
pointerType: "mouse" | "touch" | "pen";
|
|
30
|
+
}): TestPointerEvent {
|
|
31
|
+
const noop = (): void => {};
|
|
32
|
+
const noopBool = (): boolean => false;
|
|
33
|
+
const state = { preventDefaultCalled: false };
|
|
34
|
+
|
|
35
|
+
const element = document.createElement("div");
|
|
36
|
+
const nativeEvent = new PointerEvent("pointerdown", {
|
|
37
|
+
clientX: props.clientX,
|
|
38
|
+
clientY: props.clientY,
|
|
39
|
+
pointerType: props.pointerType,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
...props,
|
|
44
|
+
// Test utility
|
|
45
|
+
wasDefaultPrevented: () => state.preventDefaultCalled,
|
|
46
|
+
// Event target
|
|
47
|
+
target: element,
|
|
48
|
+
currentTarget: element,
|
|
49
|
+
// Native event
|
|
50
|
+
nativeEvent,
|
|
51
|
+
// SyntheticEvent properties
|
|
52
|
+
bubbles: true,
|
|
53
|
+
cancelable: true,
|
|
54
|
+
defaultPrevented: false,
|
|
55
|
+
eventPhase: 0,
|
|
56
|
+
isTrusted: true,
|
|
57
|
+
preventDefault: () => {
|
|
58
|
+
state.preventDefaultCalled = true;
|
|
59
|
+
},
|
|
60
|
+
isDefaultPrevented: () => state.preventDefaultCalled,
|
|
61
|
+
stopPropagation: noop,
|
|
62
|
+
isPropagationStopped: noopBool,
|
|
63
|
+
persist: noop,
|
|
64
|
+
timeStamp: Date.now(),
|
|
65
|
+
type: "pointerdown",
|
|
66
|
+
// MouseEvent properties
|
|
67
|
+
altKey: false,
|
|
68
|
+
button: 0,
|
|
69
|
+
buttons: 1,
|
|
70
|
+
ctrlKey: false,
|
|
71
|
+
metaKey: false,
|
|
72
|
+
shiftKey: false,
|
|
73
|
+
getModifierState: noopBool,
|
|
74
|
+
movementX: 0,
|
|
75
|
+
movementY: 0,
|
|
76
|
+
pageX: props.clientX,
|
|
77
|
+
pageY: props.clientY,
|
|
78
|
+
relatedTarget: null,
|
|
79
|
+
screenX: props.clientX,
|
|
80
|
+
screenY: props.clientY,
|
|
81
|
+
// PointerEvent properties
|
|
82
|
+
height: 1,
|
|
83
|
+
isPrimary: true,
|
|
84
|
+
pointerId: 1,
|
|
85
|
+
pressure: 0.5,
|
|
86
|
+
tangentialPressure: 0,
|
|
87
|
+
tiltX: 0,
|
|
88
|
+
tiltY: 0,
|
|
89
|
+
twist: 0,
|
|
90
|
+
width: 1,
|
|
91
|
+
// UIEvent properties
|
|
92
|
+
detail: 0,
|
|
93
|
+
view: createMockView(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
9
97
|
describe("useNativeGestureGuard", () => {
|
|
10
98
|
const createRef = (rect: Partial<DOMRect> = {}): React.RefObject<HTMLDivElement> => {
|
|
11
99
|
const element = document.createElement("div");
|
|
@@ -25,26 +113,6 @@ describe("useNativeGestureGuard", () => {
|
|
|
25
113
|
return { current: element };
|
|
26
114
|
};
|
|
27
115
|
|
|
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
116
|
describe("overscroll behavior", () => {
|
|
49
117
|
it("applies overscroll-behavior: contain when active and preventOverscroll is true", () => {
|
|
50
118
|
const containerRef = createRef();
|
|
@@ -99,7 +167,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
99
167
|
}),
|
|
100
168
|
);
|
|
101
169
|
|
|
102
|
-
const mockEvent =
|
|
170
|
+
const mockEvent = createMockPointerEvent({
|
|
103
171
|
clientX: 10, // Within 20px edge zone
|
|
104
172
|
clientY: 100,
|
|
105
173
|
pointerType: "touch",
|
|
@@ -123,7 +191,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
123
191
|
}),
|
|
124
192
|
);
|
|
125
193
|
|
|
126
|
-
const mockEvent =
|
|
194
|
+
const mockEvent = createMockPointerEvent({
|
|
127
195
|
clientX: 50, // Outside 20px edge zone
|
|
128
196
|
clientY: 100,
|
|
129
197
|
pointerType: "touch",
|
|
@@ -147,7 +215,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
147
215
|
}),
|
|
148
216
|
);
|
|
149
217
|
|
|
150
|
-
const mockEvent =
|
|
218
|
+
const mockEvent = createMockPointerEvent({
|
|
151
219
|
clientX: 10, // Within edge zone
|
|
152
220
|
clientY: 100,
|
|
153
221
|
pointerType: "mouse", // Not touch
|
|
@@ -173,7 +241,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
173
241
|
}),
|
|
174
242
|
);
|
|
175
243
|
|
|
176
|
-
const mockEvent =
|
|
244
|
+
const mockEvent = createMockPointerEvent({
|
|
177
245
|
clientX: 10,
|
|
178
246
|
clientY: 100,
|
|
179
247
|
pointerType: "touch",
|
|
@@ -227,7 +295,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
227
295
|
}),
|
|
228
296
|
);
|
|
229
297
|
|
|
230
|
-
const mockEventInCustomEdge =
|
|
298
|
+
const mockEventInCustomEdge = createMockPointerEvent({
|
|
231
299
|
clientX: 40, // Within 50px edge, but outside default 20px
|
|
232
300
|
clientY: 100,
|
|
233
301
|
pointerType: "touch",
|
|
@@ -266,7 +334,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
266
334
|
}),
|
|
267
335
|
);
|
|
268
336
|
|
|
269
|
-
const mockEvent =
|
|
337
|
+
const mockEvent = createMockPointerEvent({
|
|
270
338
|
clientX: 10,
|
|
271
339
|
clientY: 100,
|
|
272
340
|
pointerType: "touch",
|
|
@@ -306,7 +374,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
306
374
|
|
|
307
375
|
expect(document.documentElement.style.overscrollBehavior).toBe("");
|
|
308
376
|
|
|
309
|
-
const mockEvent =
|
|
377
|
+
const mockEvent = createMockPointerEvent({
|
|
310
378
|
clientX: 10,
|
|
311
379
|
clientY: 100,
|
|
312
380
|
pointerType: "touch",
|
|
@@ -335,7 +403,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
335
403
|
);
|
|
336
404
|
|
|
337
405
|
// Simulate pointerdown to apply style
|
|
338
|
-
const mockEvent =
|
|
406
|
+
const mockEvent = createMockPointerEvent({
|
|
339
407
|
clientX: 10,
|
|
340
408
|
clientY: 100,
|
|
341
409
|
pointerType: "touch",
|
|
@@ -369,7 +437,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
369
437
|
);
|
|
370
438
|
|
|
371
439
|
// Simulate pointerdown to apply style
|
|
372
|
-
const mockEvent =
|
|
440
|
+
const mockEvent = createMockPointerEvent({
|
|
373
441
|
clientX: 10,
|
|
374
442
|
clientY: 100,
|
|
375
443
|
pointerType: "touch",
|
|
@@ -397,7 +465,7 @@ describe("useNativeGestureGuard", () => {
|
|
|
397
465
|
}),
|
|
398
466
|
);
|
|
399
467
|
|
|
400
|
-
const mockEvent =
|
|
468
|
+
const mockEvent = createMockPointerEvent({
|
|
401
469
|
clientX: 10,
|
|
402
470
|
clientY: 100,
|
|
403
471
|
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) {
|
|
@@ -85,3 +85,105 @@ export const mergeGestureContainerProps = (
|
|
|
85
85
|
style: mergedStyle,
|
|
86
86
|
};
|
|
87
87
|
};
|
|
88
|
+
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// Scroll Detection Utilities
|
|
91
|
+
// ============================================================================
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Compute scroll size for an element based on axis.
|
|
95
|
+
*/
|
|
96
|
+
function computeScrollSize(element: HTMLElement, isHorizontal: boolean): number {
|
|
97
|
+
if (isHorizontal) {
|
|
98
|
+
return element.scrollWidth - element.clientWidth;
|
|
99
|
+
}
|
|
100
|
+
return element.scrollHeight - element.clientHeight;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if an element is scrollable in any direction.
|
|
105
|
+
*/
|
|
106
|
+
export function isScrollableElement(element: HTMLElement): boolean {
|
|
107
|
+
const style = getComputedStyle(element);
|
|
108
|
+
|
|
109
|
+
const isScrollableX =
|
|
110
|
+
(style.overflowX === "scroll" || style.overflowX === "auto") &&
|
|
111
|
+
element.scrollWidth > element.clientWidth;
|
|
112
|
+
|
|
113
|
+
const isScrollableY =
|
|
114
|
+
(style.overflowY === "scroll" || style.overflowY === "auto") &&
|
|
115
|
+
element.scrollHeight > element.clientHeight;
|
|
116
|
+
|
|
117
|
+
if (isScrollableX) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return isScrollableY;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Check if we should start drag based on scroll state.
|
|
125
|
+
* Returns false if the target is inside a scrollable element.
|
|
126
|
+
*/
|
|
127
|
+
export function shouldStartDrag(
|
|
128
|
+
event: React.PointerEvent,
|
|
129
|
+
container: HTMLElement,
|
|
130
|
+
): boolean {
|
|
131
|
+
// eslint-disable-next-line no-restricted-syntax -- loop variable requires let
|
|
132
|
+
let current = event.target as HTMLElement | null;
|
|
133
|
+
|
|
134
|
+
while (current !== null && current !== container) {
|
|
135
|
+
if (isScrollableElement(current)) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
current = current.parentElement;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check if an element or its ancestors are scrollable in the specified direction.
|
|
146
|
+
* Returns true if scrolling is possible and would block the swipe gesture.
|
|
147
|
+
*
|
|
148
|
+
* @param element - The target element to check
|
|
149
|
+
* @param container - The container boundary
|
|
150
|
+
* @param axis - The axis to check ("x" or "y")
|
|
151
|
+
* @param direction - The swipe direction (1 = right/down, -1 = left/up)
|
|
152
|
+
*/
|
|
153
|
+
export function isScrollableInDirection(
|
|
154
|
+
element: HTMLElement,
|
|
155
|
+
container: HTMLElement,
|
|
156
|
+
axis: "x" | "y",
|
|
157
|
+
direction: 1 | -1,
|
|
158
|
+
): boolean {
|
|
159
|
+
// eslint-disable-next-line no-restricted-syntax -- loop variable requires let
|
|
160
|
+
let current: HTMLElement | null = element;
|
|
161
|
+
|
|
162
|
+
while (current !== null && current !== container) {
|
|
163
|
+
const style = getComputedStyle(current);
|
|
164
|
+
const isHorizontal = axis === "x";
|
|
165
|
+
|
|
166
|
+
const overflow = isHorizontal ? style.overflowX : style.overflowY;
|
|
167
|
+
const isScrollable = overflow === "scroll" || overflow === "auto";
|
|
168
|
+
|
|
169
|
+
if (isScrollable) {
|
|
170
|
+
const scrollSize = computeScrollSize(current, isHorizontal);
|
|
171
|
+
|
|
172
|
+
if (scrollSize > 0) {
|
|
173
|
+
const scrollPos = isHorizontal ? current.scrollLeft : current.scrollTop;
|
|
174
|
+
|
|
175
|
+
// If swiping in close direction and not at boundary, block swipe
|
|
176
|
+
if (direction === -1 && scrollPos > 1) {
|
|
177
|
+
return true; // Can scroll left/up, block swipe
|
|
178
|
+
}
|
|
179
|
+
if (direction === 1 && scrollPos < scrollSize - 1) {
|
|
180
|
+
return true; // Can scroll right/down, block swipe
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
current = current.parentElement;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
@@ -5,9 +5,46 @@
|
|
|
5
5
|
* 1. アニメーションなし → 即座にdisplay:none
|
|
6
6
|
* 2. アニメーションあり → 完了待ってdisplay:none
|
|
7
7
|
*/
|
|
8
|
+
import type * as React from "react";
|
|
8
9
|
import { renderHook, act } from "@testing-library/react";
|
|
9
10
|
import { useAnimatedVisibility } from "./useAnimatedVisibility.js";
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Create a mock AnimationEvent for testing.
|
|
14
|
+
*/
|
|
15
|
+
function createMockAnimationEvent(
|
|
16
|
+
target: Element,
|
|
17
|
+
currentTarget: Element,
|
|
18
|
+
): React.AnimationEvent<Element> {
|
|
19
|
+
const noop = (): void => {};
|
|
20
|
+
const noopBool = (): boolean => false;
|
|
21
|
+
const nativeEvent = {
|
|
22
|
+
animationName: "test",
|
|
23
|
+
elapsedTime: 0,
|
|
24
|
+
pseudoElement: "",
|
|
25
|
+
} as AnimationEvent;
|
|
26
|
+
return {
|
|
27
|
+
target,
|
|
28
|
+
currentTarget,
|
|
29
|
+
nativeEvent,
|
|
30
|
+
bubbles: true,
|
|
31
|
+
cancelable: false,
|
|
32
|
+
defaultPrevented: false,
|
|
33
|
+
eventPhase: 0,
|
|
34
|
+
isTrusted: true,
|
|
35
|
+
preventDefault: noop,
|
|
36
|
+
isDefaultPrevented: noopBool,
|
|
37
|
+
stopPropagation: noop,
|
|
38
|
+
isPropagationStopped: noopBool,
|
|
39
|
+
persist: noop,
|
|
40
|
+
timeStamp: Date.now(),
|
|
41
|
+
type: "animationend",
|
|
42
|
+
animationName: "test",
|
|
43
|
+
elapsedTime: 0,
|
|
44
|
+
pseudoElement: "",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
11
48
|
describe("useAnimatedVisibility", () => {
|
|
12
49
|
describe("initial state", () => {
|
|
13
50
|
it("displays when initially visible", () => {
|
|
@@ -120,10 +157,7 @@ describe("useAnimatedVisibility", () => {
|
|
|
120
157
|
|
|
121
158
|
// Simulate animationend event
|
|
122
159
|
const sharedElement = document.createElement("div");
|
|
123
|
-
const mockEvent =
|
|
124
|
-
target: sharedElement,
|
|
125
|
-
currentTarget: sharedElement,
|
|
126
|
-
} as unknown as React.AnimationEvent;
|
|
160
|
+
const mockEvent = createMockAnimationEvent(sharedElement, sharedElement);
|
|
127
161
|
|
|
128
162
|
act(() => {
|
|
129
163
|
result.current.props.onAnimationEnd(mockEvent);
|
|
@@ -149,10 +183,7 @@ describe("useAnimatedVisibility", () => {
|
|
|
149
183
|
// Simulate animationend from a child element (target !== currentTarget)
|
|
150
184
|
const parent = document.createElement("div");
|
|
151
185
|
const child = document.createElement("div");
|
|
152
|
-
const mockEvent =
|
|
153
|
-
target: child,
|
|
154
|
-
currentTarget: parent,
|
|
155
|
-
} as unknown as React.AnimationEvent;
|
|
186
|
+
const mockEvent = createMockAnimationEvent(child, parent);
|
|
156
187
|
|
|
157
188
|
act(() => {
|
|
158
189
|
result.current.props.onAnimationEnd(mockEvent);
|
|
@@ -188,14 +219,12 @@ describe("useAnimatedVisibility", () => {
|
|
|
188
219
|
|
|
189
220
|
describe("timeout fallback", () => {
|
|
190
221
|
it("hides after timeout if animationEnd never fires", async () => {
|
|
191
|
-
vi.useFakeTimers();
|
|
192
|
-
|
|
193
222
|
const { result, rerender } = renderHook(
|
|
194
223
|
({ isVisible }) =>
|
|
195
224
|
useAnimatedVisibility({
|
|
196
225
|
isVisible,
|
|
197
226
|
leaveAnimation: "fadeOut 200ms ease-out",
|
|
198
|
-
animationTimeout:
|
|
227
|
+
animationTimeout: 10,
|
|
199
228
|
}),
|
|
200
229
|
{ initialProps: { isVisible: true } },
|
|
201
230
|
);
|
|
@@ -206,25 +235,21 @@ describe("useAnimatedVisibility", () => {
|
|
|
206
235
|
|
|
207
236
|
// Advance time past timeout
|
|
208
237
|
await act(async () => {
|
|
209
|
-
|
|
238
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
210
239
|
});
|
|
211
240
|
|
|
212
241
|
// Should be hidden now (fallback triggered)
|
|
213
242
|
expect(result.current.style.display).toBe("none");
|
|
214
243
|
expect(result.current.state.isAnimatingOut).toBe(false);
|
|
215
|
-
|
|
216
|
-
vi.useRealTimers();
|
|
217
244
|
});
|
|
218
245
|
|
|
219
246
|
it("clears timeout when animationEnd fires before timeout", async () => {
|
|
220
|
-
vi.useFakeTimers();
|
|
221
|
-
|
|
222
247
|
const { result, rerender } = renderHook(
|
|
223
248
|
({ isVisible }) =>
|
|
224
249
|
useAnimatedVisibility({
|
|
225
250
|
isVisible,
|
|
226
251
|
leaveAnimation: "fadeOut 200ms ease-out",
|
|
227
|
-
animationTimeout:
|
|
252
|
+
animationTimeout: 10,
|
|
228
253
|
}),
|
|
229
254
|
{ initialProps: { isVisible: true } },
|
|
230
255
|
);
|
|
@@ -233,10 +258,7 @@ describe("useAnimatedVisibility", () => {
|
|
|
233
258
|
|
|
234
259
|
// Fire animationEnd before timeout
|
|
235
260
|
const sharedElement = document.createElement("div");
|
|
236
|
-
const mockEvent =
|
|
237
|
-
target: sharedElement,
|
|
238
|
-
currentTarget: sharedElement,
|
|
239
|
-
} as unknown as React.AnimationEvent;
|
|
261
|
+
const mockEvent = createMockAnimationEvent(sharedElement, sharedElement);
|
|
240
262
|
|
|
241
263
|
act(() => {
|
|
242
264
|
result.current.props.onAnimationEnd(mockEvent);
|
|
@@ -246,12 +268,10 @@ describe("useAnimatedVisibility", () => {
|
|
|
246
268
|
|
|
247
269
|
// Advance past timeout - should not affect state
|
|
248
270
|
await act(async () => {
|
|
249
|
-
|
|
271
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
250
272
|
});
|
|
251
273
|
|
|
252
274
|
expect(result.current.style.display).toBe("none");
|
|
253
|
-
|
|
254
|
-
vi.useRealTimers();
|
|
255
275
|
});
|
|
256
276
|
});
|
|
257
277
|
});
|
|
@@ -72,6 +72,32 @@ export function useAnimatedVisibility({
|
|
|
72
72
|
const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
73
73
|
|
|
74
74
|
// Clear timeout on unmount
|
|
75
|
+
const shouldSkipLeaveAnimation = (
|
|
76
|
+
isSkipped: boolean,
|
|
77
|
+
animation: string | undefined,
|
|
78
|
+
): boolean => {
|
|
79
|
+
if (isSkipped) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (!animation) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (animation === "none") {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const getShouldDisplay = (visible: boolean, animatingOut: boolean): boolean => {
|
|
92
|
+
if (visible) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
if (animatingOut) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
};
|
|
100
|
+
|
|
75
101
|
React.useEffect(() => {
|
|
76
102
|
return () => {
|
|
77
103
|
if (timeoutRef.current) {
|
|
@@ -92,7 +118,7 @@ export function useAnimatedVisibility({
|
|
|
92
118
|
|
|
93
119
|
if (wasVisible && !isVisible) {
|
|
94
120
|
// Transitioning from visible to hidden
|
|
95
|
-
if (skipAnimation
|
|
121
|
+
if (shouldSkipLeaveAnimation(skipAnimation, leaveAnimation)) {
|
|
96
122
|
// No animation, hide immediately
|
|
97
123
|
setIsAnimatingOut(false);
|
|
98
124
|
} else {
|
|
@@ -129,7 +155,7 @@ export function useAnimatedVisibility({
|
|
|
129
155
|
// Element should be displayed if:
|
|
130
156
|
// - It's visible, OR
|
|
131
157
|
// - It's animating out (leave animation in progress)
|
|
132
|
-
const shouldDisplay = isVisible
|
|
158
|
+
const shouldDisplay = getShouldDisplay(isVisible, isAnimatingOut);
|
|
133
159
|
|
|
134
160
|
return {
|
|
135
161
|
state: {
|
|
@@ -40,6 +40,14 @@ export const easings = {
|
|
|
40
40
|
}
|
|
41
41
|
return 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
42
42
|
},
|
|
43
|
+
|
|
44
|
+
/** Ease in expo (accelerating, for "suck in" effect) */
|
|
45
|
+
easeInExpo: (t: number): number => {
|
|
46
|
+
if (t === 0) {
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
return Math.pow(2, 10 * t - 10);
|
|
50
|
+
},
|
|
43
51
|
} as const;
|
|
44
52
|
|
|
45
53
|
/**
|