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.
Files changed (216) hide show
  1. package/dist/{FloatingPanelFrame-SgYLc6Ud.js → FloatingPanelFrame-3eU9AwPo.js} +2 -2
  2. package/dist/{FloatingPanelFrame-SgYLc6Ud.js.map → FloatingPanelFrame-3eU9AwPo.js.map} +1 -1
  3. package/dist/FloatingWindow-CUXnEtrb.js +827 -0
  4. package/dist/FloatingWindow-CUXnEtrb.js.map +1 -0
  5. package/dist/FloatingWindow-DMwyK0eK.cjs +2 -0
  6. package/dist/FloatingWindow-DMwyK0eK.cjs.map +1 -0
  7. package/dist/GridLayout-DKTg_N61.cjs +2 -0
  8. package/dist/{GridLayout-B4VRsC0r.cjs.map → GridLayout-DKTg_N61.cjs.map} +1 -1
  9. package/dist/{GridLayout-BltqeCPK.js → GridLayout-UWNxXw77.js} +34 -35
  10. package/dist/{GridLayout-BltqeCPK.js.map → GridLayout-UWNxXw77.js.map} +1 -1
  11. package/dist/{HorizontalDivider-WF1k_qND.js → HorizontalDivider-DdxzfV0l.js} +3 -3
  12. package/dist/{HorizontalDivider-WF1k_qND.js.map → HorizontalDivider-DdxzfV0l.js.map} +1 -1
  13. package/dist/{HorizontalDivider-B5Z-KZLk.cjs → HorizontalDivider-_pgV4Mcv.cjs} +2 -2
  14. package/dist/{HorizontalDivider-B5Z-KZLk.cjs.map → HorizontalDivider-_pgV4Mcv.cjs.map} +1 -1
  15. package/dist/{PanelSystem-Dr1TBhxM.js → PanelSystem-BqUzNtf2.js} +5 -5
  16. package/dist/{PanelSystem-Dr1TBhxM.js.map → PanelSystem-BqUzNtf2.js.map} +1 -1
  17. package/dist/{PanelSystem-Bs8bQwQF.cjs → PanelSystem-D603LKKv.cjs} +2 -2
  18. package/dist/{PanelSystem-Bs8bQwQF.cjs.map → PanelSystem-D603LKKv.cjs.map} +1 -1
  19. package/dist/ResizeHandle-CBcAS918.cjs +2 -0
  20. package/dist/{ResizeHandle-CScipO5l.cjs.map → ResizeHandle-CBcAS918.cjs.map} +1 -1
  21. package/dist/{ResizeHandle-CdA_JYfN.js → ResizeHandle-CXjc1meV.js} +28 -29
  22. package/dist/{ResizeHandle-CdA_JYfN.js.map → ResizeHandle-CXjc1meV.js.map} +1 -1
  23. package/dist/SwipePivotTabBar-DWrCuwEI.js +411 -0
  24. package/dist/SwipePivotTabBar-DWrCuwEI.js.map +1 -0
  25. package/dist/SwipePivotTabBar-fjjXkpj7.cjs +2 -0
  26. package/dist/SwipePivotTabBar-fjjXkpj7.cjs.map +1 -0
  27. package/dist/components/gesture/SwipeSafeZone.d.ts +40 -0
  28. package/dist/components/window/Drawer.d.ts +3 -1
  29. package/dist/components/window/DrawerLayers.d.ts +1 -1
  30. package/dist/components/window/drawerStyles.d.ts +69 -0
  31. package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
  32. package/dist/components/window/useDrawerSwipeTransform.d.ts +23 -0
  33. package/dist/config.cjs +1 -1
  34. package/dist/config.js +3 -3
  35. package/dist/constants/styles.d.ts +17 -0
  36. package/dist/dialog/index.d.ts +69 -0
  37. package/dist/floating.js +1 -1
  38. package/dist/grid.cjs +1 -1
  39. package/dist/grid.js +2 -2
  40. package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +7 -0
  41. package/dist/hooks/gesture/types.d.ts +48 -5
  42. package/dist/hooks/gesture/utils.d.ts +19 -0
  43. package/dist/hooks/useAnimationFrame.d.ts +2 -0
  44. package/dist/hooks/useOperationContinuity.d.ts +64 -0
  45. package/dist/hooks/useResizeObserver.d.ts +33 -1
  46. package/dist/hooks/useSharedElementTransition.d.ts +112 -0
  47. package/dist/hooks/useSwipeContentTransform.d.ts +9 -2
  48. package/dist/index.cjs +1 -1
  49. package/dist/index.js +7 -7
  50. package/dist/modules/dialog/AlertDialog.d.ts +9 -0
  51. package/dist/modules/dialog/DialogContainer.d.ts +37 -0
  52. package/dist/modules/dialog/Modal.d.ts +26 -0
  53. package/dist/modules/dialog/SwipeDialogContainer.d.ts +16 -0
  54. package/dist/modules/dialog/dialogAnimationUtils.d.ts +113 -0
  55. package/dist/modules/dialog/types.d.ts +183 -0
  56. package/dist/modules/dialog/useDialog.d.ts +39 -0
  57. package/dist/modules/dialog/useDialogContainer.d.ts +47 -0
  58. package/dist/modules/dialog/useDialogSwipeInput.d.ts +70 -0
  59. package/dist/modules/dialog/useDialogTransform.d.ts +82 -0
  60. package/dist/modules/drawer/types.d.ts +74 -0
  61. package/dist/modules/drawer/useDrawerSwipeInput.d.ts +24 -0
  62. package/dist/modules/pivot/SwipePivotTabBar.d.ts +3 -0
  63. package/dist/modules/stack/SwipeStackContent.d.ts +6 -3
  64. package/dist/modules/stack/SwipeStackOutlet.d.ts +4 -4
  65. package/dist/modules/stack/computeSwipeStackTransform.d.ts +1 -1
  66. package/dist/panels.cjs +1 -1
  67. package/dist/panels.js +1 -1
  68. package/dist/pivot.cjs +1 -1
  69. package/dist/pivot.js +1 -1
  70. package/dist/resizer.cjs +1 -1
  71. package/dist/resizer.js +2 -2
  72. package/dist/stack.cjs +1 -1
  73. package/dist/stack.cjs.map +1 -1
  74. package/dist/stack.js +503 -762
  75. package/dist/stack.js.map +1 -1
  76. package/dist/sticky-header/calculateStickyMetrics.d.ts +28 -0
  77. package/dist/sticky-header.cjs +1 -1
  78. package/dist/sticky-header.cjs.map +1 -1
  79. package/dist/sticky-header.js +59 -51
  80. package/dist/sticky-header.js.map +1 -1
  81. package/dist/{styles-DPPuJ0sf.js → styles-NkjuMOVS.js} +13 -13
  82. package/dist/{styles-DPPuJ0sf.js.map → styles-NkjuMOVS.js.map} +1 -1
  83. package/dist/styles-qf6ptVLD.cjs.map +1 -1
  84. package/dist/types.d.ts +16 -0
  85. package/dist/useDocumentPointerEvents-DXxw3qWj.js +54 -0
  86. package/dist/useDocumentPointerEvents-DXxw3qWj.js.map +1 -0
  87. package/dist/useDocumentPointerEvents-DxDSOtip.cjs +2 -0
  88. package/dist/useDocumentPointerEvents-DxDSOtip.cjs.map +1 -0
  89. package/dist/useNativeGestureGuard-C7TSqEkr.cjs +2 -0
  90. package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +1 -0
  91. package/dist/useNativeGestureGuard-CGYo6O0r.js +347 -0
  92. package/dist/useNativeGestureGuard-CGYo6O0r.js.map +1 -0
  93. package/dist/window/index.d.ts +2 -0
  94. package/dist/window.cjs +1 -1
  95. package/dist/window.cjs.map +1 -1
  96. package/dist/window.js +114 -103
  97. package/dist/window.js.map +1 -1
  98. package/package.json +6 -1
  99. package/src/components/gesture/SwipeSafeZone.tsx +69 -0
  100. package/src/components/window/Drawer.tsx +249 -162
  101. package/src/components/window/DrawerLayers.tsx +13 -3
  102. package/src/components/window/drawerStyles.spec.ts +263 -0
  103. package/src/components/window/drawerStyles.ts +228 -0
  104. package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
  105. package/src/components/window/drawerSwipeConfig.ts +112 -0
  106. package/src/components/window/useDrawerSwipeTransform.spec.ts +234 -0
  107. package/src/components/window/useDrawerSwipeTransform.ts +129 -0
  108. package/src/constants/styles.ts +19 -0
  109. package/src/demo/pages/Dialog/alerts/index.tsx +22 -0
  110. package/src/demo/pages/Dialog/card/index.tsx +22 -0
  111. package/src/demo/pages/Dialog/components/AlertDialogDemo.tsx +124 -0
  112. package/src/demo/pages/Dialog/components/CardExpandDemo.module.css +243 -0
  113. package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +204 -0
  114. package/src/demo/pages/Dialog/components/CustomAlertDialogDemo.tsx +219 -0
  115. package/src/demo/pages/Dialog/components/DialogDemos.module.css +77 -0
  116. package/src/demo/pages/Dialog/components/ModalBasics.tsx +45 -0
  117. package/src/demo/pages/Dialog/components/SwipeDialogDemo.module.css +77 -0
  118. package/src/demo/pages/Dialog/components/SwipeDialogDemo.tsx +181 -0
  119. package/src/demo/pages/Dialog/custom-alert/index.tsx +22 -0
  120. package/src/demo/pages/Dialog/modal/index.tsx +17 -0
  121. package/src/demo/pages/Dialog/swipe/index.tsx +22 -0
  122. package/src/demo/pages/Drawer/components/DrawerSwipe.module.css +316 -0
  123. package/src/demo/pages/Drawer/components/DrawerSwipe.tsx +178 -0
  124. package/src/demo/pages/Drawer/swipe/index.tsx +17 -0
  125. package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +54 -23
  126. package/src/demo/pages/Pivot/swipe-debug/index.tsx +1 -1
  127. package/src/demo/pages/Stack/components/StackBasics.spec.tsx +152 -0
  128. package/src/demo/pages/Stack/components/StackBasics.tsx +179 -95
  129. package/src/demo/pages/Stack/components/StackTablet.spec.tsx +120 -0
  130. package/src/demo/pages/Stack/components/StackTablet.tsx +42 -21
  131. package/src/demo/routes.tsx +22 -1
  132. package/src/dialog/index.ts +85 -0
  133. package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +68 -64
  134. package/src/hooks/gesture/testing/createGestureSimulator.ts +112 -37
  135. package/src/hooks/gesture/types.ts +83 -6
  136. package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +22 -14
  137. package/src/hooks/gesture/useNativeGestureGuard.spec.ts +91 -31
  138. package/src/hooks/gesture/useNativeGestureGuard.ts +3 -1
  139. package/src/hooks/gesture/utils.ts +91 -0
  140. package/src/hooks/useAnimatedVisibility.spec.ts +44 -24
  141. package/src/hooks/useAnimatedVisibility.ts +28 -2
  142. package/src/hooks/useAnimationFrame.ts +8 -0
  143. package/src/hooks/useOperationContinuity.spec.ts +387 -0
  144. package/src/hooks/useOperationContinuity.ts +135 -0
  145. package/src/hooks/useResizeObserver.spec.tsx +277 -0
  146. package/src/hooks/useResizeObserver.tsx +108 -39
  147. package/src/hooks/useScrollContainer.ts +4 -10
  148. package/src/hooks/useSharedElementTransition.ts +333 -0
  149. package/src/hooks/useSwipeContentTransform.spec.ts +18 -18
  150. package/src/hooks/useSwipeContentTransform.ts +166 -28
  151. package/src/modules/dialog/AlertDialog.spec.tsx +387 -0
  152. package/src/modules/dialog/AlertDialog.tsx +221 -0
  153. package/src/modules/dialog/DialogContainer.spec.tsx +228 -0
  154. package/src/modules/dialog/DialogContainer.tsx +188 -0
  155. package/src/modules/dialog/Modal.spec.tsx +220 -0
  156. package/src/modules/dialog/Modal.tsx +182 -0
  157. package/src/modules/dialog/SwipeDialogContainer.tsx +208 -0
  158. package/src/modules/dialog/dialogAnimationUtils.spec.ts +253 -0
  159. package/src/modules/dialog/dialogAnimationUtils.ts +297 -0
  160. package/src/modules/dialog/types.ts +186 -0
  161. package/src/modules/dialog/useDialog.spec.tsx +447 -0
  162. package/src/modules/dialog/useDialog.ts +214 -0
  163. package/src/modules/dialog/useDialogContainer.spec.ts +331 -0
  164. package/src/modules/dialog/useDialogContainer.ts +150 -0
  165. package/src/modules/dialog/useDialogSwipeInput.spec.ts +157 -0
  166. package/src/modules/dialog/useDialogSwipeInput.ts +319 -0
  167. package/src/modules/dialog/useDialogTransform.spec.ts +370 -0
  168. package/src/modules/dialog/useDialogTransform.ts +407 -0
  169. package/src/modules/drawer/types.ts +102 -0
  170. package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
  171. package/src/modules/drawer/useDrawerSwipeInput.ts +399 -0
  172. package/src/modules/panels/rendering/ContentRegistry.spec.tsx +21 -14
  173. package/src/modules/pivot/SwipePivotContent.position.spec.tsx +12 -8
  174. package/src/modules/pivot/SwipePivotContent.spec.tsx +55 -25
  175. package/src/modules/pivot/SwipePivotContent.tsx +2 -2
  176. package/src/modules/pivot/SwipePivotTabBar.spec.tsx +85 -68
  177. package/src/modules/pivot/SwipePivotTabBar.tsx +75 -15
  178. package/src/modules/pivot/scaleInputState.spec.ts +11 -2
  179. package/src/modules/pivot/usePivot.spec.ts +17 -3
  180. package/src/modules/pivot/usePivotSwipeInput.spec.ts +182 -123
  181. package/src/modules/stack/SwipeStackContent.spec.tsx +387 -100
  182. package/src/modules/stack/SwipeStackContent.tsx +43 -33
  183. package/src/modules/stack/SwipeStackOutlet.spec.tsx +14 -16
  184. package/src/modules/stack/SwipeStackOutlet.tsx +6 -6
  185. package/src/modules/stack/computeSwipeStackTransform.spec.ts +5 -5
  186. package/src/modules/stack/computeSwipeStackTransform.ts +3 -3
  187. package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1133 -0
  188. package/src/modules/stack/useStackAnimationState.spec.ts +3 -1
  189. package/src/modules/stack/useStackAnimationState.ts +18 -13
  190. package/src/modules/stack/useStackNavigation.spec.ts +198 -3
  191. package/src/modules/stack/useStackNavigation.tsx +113 -56
  192. package/src/modules/stack/useStackSwipeInput.spec.ts +65 -32
  193. package/src/modules/stack/useStackSwipeInput.ts +1 -1
  194. package/src/sticky-header/StickyArea.tsx +29 -57
  195. package/src/sticky-header/calculateStickyMetrics.spec.ts +105 -0
  196. package/src/sticky-header/calculateStickyMetrics.ts +50 -0
  197. package/src/types.ts +18 -0
  198. package/src/window/index.ts +2 -0
  199. package/dist/FloatingWindow-BpdOpg_L.js +0 -400
  200. package/dist/FloatingWindow-BpdOpg_L.js.map +0 -1
  201. package/dist/FloatingWindow-TCDNY5gE.cjs +0 -2
  202. package/dist/FloatingWindow-TCDNY5gE.cjs.map +0 -1
  203. package/dist/GridLayout-B4VRsC0r.cjs +0 -2
  204. package/dist/ResizeHandle-CScipO5l.cjs +0 -2
  205. package/dist/SwipePivotTabBar-BGO9X94m.js +0 -407
  206. package/dist/SwipePivotTabBar-BGO9X94m.js.map +0 -1
  207. package/dist/SwipePivotTabBar-BrQismcZ.cjs +0 -2
  208. package/dist/SwipePivotTabBar-BrQismcZ.cjs.map +0 -1
  209. package/dist/useDocumentPointerEvents-CKdhGXd0.js +0 -46
  210. package/dist/useDocumentPointerEvents-CKdhGXd0.js.map +0 -1
  211. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs +0 -2
  212. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs.map +0 -1
  213. package/dist/useEffectEvent-Dp7HLCf0.js +0 -13
  214. package/dist/useEffectEvent-Dp7HLCf0.js.map +0 -1
  215. package/dist/useEffectEvent-huSsGUnl.cjs +0 -2
  216. package/dist/useEffectEvent-huSsGUnl.cjs.map +0 -1
@@ -0,0 +1,370 @@
1
+ /**
2
+ * @file Tests for useDialogTransform hook
3
+ */
4
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
5
+ import { renderHook, act, waitFor } from "@testing-library/react";
6
+ import { useDialogTransform } from "./useDialogTransform.js";
7
+ import type { ContinuousOperationState } from "../../hooks/gesture/types.js";
8
+ import { IDLE_CONTINUOUS_OPERATION_STATE } from "../../hooks/gesture/types.js";
9
+
10
+ // Mock ResizeObserver
11
+ const mockResizeObserver = vi.fn();
12
+ vi.stubGlobal("ResizeObserver", class {
13
+ constructor(callback: ResizeObserverCallback) {
14
+ mockResizeObserver(callback);
15
+ }
16
+ observe = vi.fn();
17
+ unobserve = vi.fn();
18
+ disconnect = vi.fn();
19
+ });
20
+
21
+ describe("useDialogTransform", () => {
22
+ let rafCallbacks: FrameRequestCallback[] = [];
23
+ let rafId = 0;
24
+
25
+ beforeEach(() => {
26
+ rafCallbacks = [];
27
+ rafId = 0;
28
+
29
+ vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) => {
30
+ rafCallbacks.push(cb);
31
+ return ++rafId;
32
+ });
33
+ vi.stubGlobal("cancelAnimationFrame", (id: number) => {
34
+ // Mock cancel
35
+ });
36
+ });
37
+
38
+ afterEach(() => {
39
+ vi.unstubAllGlobals();
40
+ vi.clearAllMocks();
41
+ });
42
+
43
+ const runAnimationFrames = (count = 1, deltaMs = 16) => {
44
+ const now = performance.now();
45
+ for (let i = 0; i < count; i++) {
46
+ const callbacks = [...rafCallbacks];
47
+ rafCallbacks = [];
48
+ callbacks.forEach(cb => cb(now + deltaMs * (i + 1)));
49
+ }
50
+ };
51
+
52
+ const createMockElement = (dimensions: { width: number; height: number }) => {
53
+ return {
54
+ clientWidth: dimensions.width,
55
+ clientHeight: dimensions.height,
56
+ style: {
57
+ transform: "",
58
+ setProperty: vi.fn(),
59
+ viewTransitionName: "",
60
+ },
61
+ } as unknown as HTMLDivElement;
62
+ };
63
+
64
+ const createMockBackdrop = () => {
65
+ return {
66
+ style: {
67
+ opacity: "",
68
+ },
69
+ } as unknown as HTMLDivElement;
70
+ };
71
+
72
+ const idleSwipeState: ContinuousOperationState = IDLE_CONTINUOUS_OPERATION_STATE;
73
+ const zeroDisplacement2D = { x: 0, y: 0 };
74
+
75
+ describe("opening animation", () => {
76
+ it("should start animation when visible changes from false to true", async () => {
77
+ const element = createMockElement({ width: 400, height: 300 });
78
+ const backdrop = createMockBackdrop();
79
+ const elementRef = { current: element };
80
+ const backdropRef = { current: backdrop };
81
+
82
+ const { result, rerender } = renderHook(
83
+ ({ visible }) =>
84
+ useDialogTransform({
85
+ elementRef,
86
+ backdropRef,
87
+ visible,
88
+ openDirection: "bottom",
89
+ swipeState: idleSwipeState,
90
+ displacement: 0,
91
+ displacement2D: zeroDisplacement2D,
92
+ }),
93
+ { initialProps: { visible: false } },
94
+ );
95
+
96
+ expect(result.current.phase).toBe("closed");
97
+
98
+ // Trigger visible change
99
+ rerender({ visible: true });
100
+
101
+ // Should transition to opening
102
+ expect(result.current.phase).toBe("opening");
103
+ expect(result.current.isAnimating).toBe(true);
104
+ });
105
+
106
+ it("should apply transform during opening animation", async () => {
107
+ const element = createMockElement({ width: 400, height: 300 });
108
+ const backdrop = createMockBackdrop();
109
+ const elementRef = { current: element };
110
+ const backdropRef = { current: backdrop };
111
+
112
+ const { rerender } = renderHook(
113
+ ({ visible }) =>
114
+ useDialogTransform({
115
+ elementRef,
116
+ backdropRef,
117
+ visible,
118
+ openDirection: "bottom",
119
+ swipeState: idleSwipeState,
120
+ displacement: 0,
121
+ displacement2D: zeroDisplacement2D,
122
+ }),
123
+ { initialProps: { visible: false } },
124
+ );
125
+
126
+ // Trigger visible change
127
+ rerender({ visible: true });
128
+
129
+ // Run some animation frames
130
+ act(() => {
131
+ runAnimationFrames(5, 50);
132
+ });
133
+
134
+ // Transform should be applied
135
+ expect(element.style.transform).not.toBe("");
136
+ });
137
+
138
+ it("should call onOpenComplete when animation finishes", async () => {
139
+ const element = createMockElement({ width: 400, height: 300 });
140
+ const backdrop = createMockBackdrop();
141
+ const elementRef = { current: element };
142
+ const backdropRef = { current: backdrop };
143
+ const onOpenComplete = vi.fn();
144
+
145
+ const { result, rerender } = renderHook(
146
+ ({ visible }) =>
147
+ useDialogTransform({
148
+ elementRef,
149
+ backdropRef,
150
+ visible,
151
+ openDirection: "bottom",
152
+ swipeState: idleSwipeState,
153
+ displacement: 0,
154
+ displacement2D: zeroDisplacement2D,
155
+ animationDuration: 100,
156
+ onOpenComplete,
157
+ }),
158
+ { initialProps: { visible: false } },
159
+ );
160
+
161
+ // Trigger visible change
162
+ rerender({ visible: true });
163
+
164
+ // Run enough frames to complete animation
165
+ act(() => {
166
+ runAnimationFrames(10, 20);
167
+ });
168
+
169
+ await waitFor(() => {
170
+ expect(result.current.phase).toBe("open");
171
+ });
172
+
173
+ expect(onOpenComplete).toHaveBeenCalled();
174
+ });
175
+ });
176
+
177
+ describe("closing animation", () => {
178
+ it("should animate close when triggerClose is called", async () => {
179
+ const element = createMockElement({ width: 400, height: 300 });
180
+ const backdrop = createMockBackdrop();
181
+ const elementRef = { current: element };
182
+ const backdropRef = { current: backdrop };
183
+
184
+ const { result, rerender } = renderHook(
185
+ ({ visible }) =>
186
+ useDialogTransform({
187
+ elementRef,
188
+ backdropRef,
189
+ visible,
190
+ openDirection: "bottom",
191
+ swipeState: idleSwipeState,
192
+ displacement: 0,
193
+ displacement2D: zeroDisplacement2D,
194
+ }),
195
+ { initialProps: { visible: false } },
196
+ );
197
+
198
+ // Open first
199
+ rerender({ visible: true });
200
+ act(() => {
201
+ runAnimationFrames(30, 20);
202
+ });
203
+
204
+ await waitFor(() => {
205
+ expect(result.current.phase).toBe("open");
206
+ });
207
+
208
+ // Now trigger close
209
+ act(() => {
210
+ result.current.triggerClose();
211
+ });
212
+
213
+ expect(result.current.phase).toBe("closing");
214
+ });
215
+
216
+ it("should call onCloseComplete when close animation finishes", async () => {
217
+ const element = createMockElement({ width: 400, height: 300 });
218
+ const backdrop = createMockBackdrop();
219
+ const elementRef = { current: element };
220
+ const backdropRef = { current: backdrop };
221
+ const onCloseComplete = vi.fn();
222
+
223
+ const { result, rerender } = renderHook(
224
+ ({ visible }) =>
225
+ useDialogTransform({
226
+ elementRef,
227
+ backdropRef,
228
+ visible,
229
+ openDirection: "bottom",
230
+ swipeState: idleSwipeState,
231
+ displacement: 0,
232
+ displacement2D: zeroDisplacement2D,
233
+ animationDuration: 100,
234
+ onCloseComplete,
235
+ }),
236
+ { initialProps: { visible: false } },
237
+ );
238
+
239
+ // Open first
240
+ rerender({ visible: true });
241
+ act(() => {
242
+ runAnimationFrames(30, 20);
243
+ });
244
+
245
+ await waitFor(() => {
246
+ expect(result.current.phase).toBe("open");
247
+ });
248
+
249
+ // Trigger close
250
+ act(() => {
251
+ result.current.triggerClose();
252
+ });
253
+
254
+ // Run animation
255
+ act(() => {
256
+ runAnimationFrames(30, 20);
257
+ });
258
+
259
+ await waitFor(() => {
260
+ expect(result.current.phase).toBe("closed");
261
+ });
262
+
263
+ expect(onCloseComplete).toHaveBeenCalled();
264
+ });
265
+ });
266
+
267
+ describe("swipe interaction", () => {
268
+ it("should apply 2D transform during swipe", () => {
269
+ const element = createMockElement({ width: 400, height: 300 });
270
+ const backdrop = createMockBackdrop();
271
+ const elementRef = { current: element };
272
+ const backdropRef = { current: backdrop };
273
+
274
+ const operatingSwipeState: ContinuousOperationState = {
275
+ phase: "operating",
276
+ displacement: { x: 30, y: 100 },
277
+ velocity: { x: 0, y: 0 },
278
+ };
279
+
280
+ renderHook(() =>
281
+ useDialogTransform({
282
+ elementRef,
283
+ backdropRef,
284
+ visible: true,
285
+ openDirection: "bottom",
286
+ swipeState: operatingSwipeState,
287
+ displacement: 100,
288
+ displacement2D: { x: 30, y: 100 },
289
+ }),
290
+ );
291
+
292
+ // Transform should reflect 2D displacement
293
+ expect(element.style.transform).toContain("translate(30px, 100px)");
294
+ });
295
+
296
+ it("should animate snapback when swipe ends below threshold", async () => {
297
+ const element = createMockElement({ width: 400, height: 300 });
298
+ const backdrop = createMockBackdrop();
299
+ const elementRef = { current: element };
300
+ const backdropRef = { current: backdrop };
301
+
302
+ const operatingState: ContinuousOperationState = {
303
+ phase: "operating",
304
+ displacement: { x: 20, y: 50 },
305
+ velocity: { x: 0, y: 0 },
306
+ };
307
+
308
+ const displacement2D = { x: 20, y: 50 };
309
+
310
+ const { rerender, result } = renderHook(
311
+ ({ swipeState, displacement, d2d }) =>
312
+ useDialogTransform({
313
+ elementRef,
314
+ backdropRef,
315
+ visible: true,
316
+ openDirection: "bottom",
317
+ swipeState,
318
+ displacement,
319
+ displacement2D: d2d,
320
+ }),
321
+ { initialProps: { swipeState: operatingState, displacement: 50, d2d: displacement2D } },
322
+ );
323
+
324
+ // End swipe
325
+ const endedState: ContinuousOperationState = {
326
+ phase: "ended",
327
+ displacement: { x: 20, y: 50 },
328
+ velocity: { x: 0, y: 0 },
329
+ };
330
+ rerender({ swipeState: endedState, displacement: 50, d2d: displacement2D });
331
+
332
+ // Should start snapback animation
333
+ expect(result.current.isAnimating).toBe(true);
334
+ });
335
+ });
336
+
337
+ describe("container size handling", () => {
338
+ it("should not apply transform when container size is 0", () => {
339
+ const element = createMockElement({ width: 0, height: 0 });
340
+ const backdrop = createMockBackdrop();
341
+ const elementRef = { current: element };
342
+ const backdropRef = { current: backdrop };
343
+
344
+ const { rerender } = renderHook(
345
+ ({ visible }) =>
346
+ useDialogTransform({
347
+ elementRef,
348
+ backdropRef,
349
+ visible,
350
+ openDirection: "bottom",
351
+ swipeState: idleSwipeState,
352
+ displacement: 0,
353
+ displacement2D: zeroDisplacement2D,
354
+ }),
355
+ { initialProps: { visible: false } },
356
+ );
357
+
358
+ rerender({ visible: true });
359
+
360
+ act(() => {
361
+ runAnimationFrames(5, 50);
362
+ });
363
+
364
+ // This test documents the current BROKEN behavior
365
+ // Transform should NOT be empty - this is the bug!
366
+ // When we fix it, this expectation should change
367
+ expect(element.style.transform).toBe("");
368
+ });
369
+ });
370
+ });