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
@@ -9,7 +9,8 @@
9
9
  */
10
10
  import * as React from "react";
11
11
  import { useSwipeContentTransform } from "../../hooks/useSwipeContentTransform.js";
12
- import type { SwipeInputState, GestureAxis } from "../../hooks/gesture/types.js";
12
+ import { useOperationContinuity } from "../../hooks/useOperationContinuity.js";
13
+ import type { ContinuousOperationState, GestureAxis } from "../../hooks/gesture/types.js";
13
14
  import type { StackDisplayMode } from "./types.js";
14
15
  import {
15
16
  computeActiveTargetPx,
@@ -27,11 +28,6 @@ const DEFAULT_ANIMATION_DURATION = 300;
27
28
  */
28
29
  const STACK_SCALE_FACTOR = 0.05;
29
30
 
30
- /**
31
- * Offset percentage per depth level for "stack" display mode.
32
- */
33
- const STACK_OFFSET_PERCENT = 5;
34
-
35
31
  /**
36
32
  * Maximum dimming opacity for behind panels in iOS-style navigation.
37
33
  */
@@ -39,6 +35,9 @@ const MAX_DIM_OPACITY = 0.1;
39
35
 
40
36
  /**
41
37
  * Props for SwipeStackContent component.
38
+ *
39
+ * This component accepts ContinuousOperationState, meaning it responds uniformly
40
+ * to any continuous operation (whether human gesture or system animation).
42
41
  */
43
42
  export type SwipeStackContentProps = {
44
43
  /** Panel ID */
@@ -49,8 +48,8 @@ export type SwipeStackContentProps = {
49
48
  navigationDepth: number;
50
49
  /** Whether this panel is currently active */
51
50
  isActive: boolean;
52
- /** Swipe input state from useStackSwipeInput */
53
- inputState: SwipeInputState;
51
+ /** Continuous operation state (from gesture input or animation system) */
52
+ operationState: ContinuousOperationState;
54
53
  /** Container size in pixels (width for horizontal, height for vertical) */
55
54
  containerSize: number;
56
55
  /** Gesture axis. @default "horizontal" */
@@ -96,13 +95,13 @@ const BASE_STYLE: React.CSSProperties = {
96
95
  };
97
96
 
98
97
  /**
99
- * Get displacement from input state for the given axis.
98
+ * Get displacement from operation state for the given axis.
100
99
  */
101
- const getAxisDisplacement = (inputState: SwipeInputState, axis: GestureAxis): number => {
102
- if (inputState.phase === "idle") {
100
+ const getAxisDisplacement = (state: ContinuousOperationState, axis: GestureAxis): number => {
101
+ if (state.phase === "idle") {
103
102
  return 0;
104
103
  }
105
- return axis === "horizontal" ? inputState.displacement.x : inputState.displacement.y;
104
+ return axis === "horizontal" ? state.displacement.x : state.displacement.y;
106
105
  };
107
106
 
108
107
  /**
@@ -136,7 +135,7 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
136
135
  depth,
137
136
  navigationDepth,
138
137
  isActive,
139
- inputState,
138
+ operationState,
140
139
  containerSize,
141
140
  axis = "horizontal",
142
141
  behindOffset = DEFAULT_BEHIND_OFFSET,
@@ -148,23 +147,27 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
148
147
  children,
149
148
  }) => {
150
149
  const elementRef = React.useRef<HTMLDivElement>(null);
151
- const isFirstMountRef = React.useRef<boolean>(true);
152
150
 
153
- const displacement = getAxisDisplacement(inputState, axis);
154
- const isSwiping = inputState.phase === "swiping" || inputState.phase === "tracking";
151
+ const displacement = getAxisDisplacement(operationState, axis);
152
+ const isOperating = operationState.phase === "operating";
155
153
 
156
154
  // Determine panel role
157
155
  const role = determineSwipePanelRole(depth, navigationDepth);
158
156
 
159
- // Track first mount for push animation
160
- const isFirstMount = isFirstMountRef.current;
161
- if (isFirstMountRef.current) {
162
- isFirstMountRef.current = false;
163
- }
157
+ // Maintain role continuity during swipe operations.
158
+ // When navigation changes before the gesture ends (e.g., role changes from
159
+ // "behind" to "active"), we keep using the previous role for position
160
+ // calculations to prevent visual jumps.
161
+ // changedDuringOperation tells us if the role changed during the operation,
162
+ // which we use to skip backward target change animation (over-swipe case).
163
+ const { value: effectiveRole, changedDuringOperation } = useOperationContinuity(
164
+ role,
165
+ displacement > 0,
166
+ );
164
167
 
165
- // Compute target position based on role
168
+ // Compute target position based on effective role
166
169
  const targetPx = React.useMemo(() => {
167
- switch (role) {
170
+ switch (effectiveRole) {
168
171
  case "active":
169
172
  // Active panel rests at 0
170
173
  return 0;
@@ -175,7 +178,7 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
175
178
  // Hidden panels are off-screen
176
179
  return containerSize;
177
180
  }
178
- }, [role, behindOffset, containerSize]);
181
+ }, [effectiveRole, behindOffset, containerSize]);
179
182
 
180
183
  // Compute displacement for this panel
181
184
  const panelDisplacement = React.useMemo(() => {
@@ -183,7 +186,7 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
183
186
  return 0;
184
187
  }
185
188
 
186
- switch (role) {
189
+ switch (effectiveRole) {
187
190
  case "active":
188
191
  // Active panel follows finger directly
189
192
  return computeActiveTargetPx(displacement);
@@ -196,15 +199,17 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
196
199
  case "hidden":
197
200
  return 0;
198
201
  }
199
- }, [role, displacement, containerSize, behindOffset]);
202
+ }, [effectiveRole, displacement, containerSize, behindOffset]);
200
203
 
201
204
  // Compute initial position for push animation
202
205
  // When animateOnMount is true and panel is first mounted as "active",
203
206
  // it should animate in from off-screen
204
207
  // Root panel (depth=0) should not animate on mount
208
+ // Note: useSwipeContentTransform handles first-mount tracking internally,
209
+ // so we just declare the initial position; the hook consumes it only once.
205
210
  const initialPx = React.useMemo(() => {
206
- if (!isFirstMount || !animateOnMount) {
207
- return undefined; // Only relevant on first mount with animateOnMount
211
+ if (!animateOnMount) {
212
+ return undefined;
208
213
  }
209
214
  if (role === "active" && depth > 0) {
210
215
  // New active panel (not root): start from off-screen right
@@ -212,14 +217,14 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
212
217
  }
213
218
  // Root panel or other roles: start at their natural position
214
219
  return undefined;
215
- }, [isFirstMount, animateOnMount, role, depth, containerSize]);
220
+ }, [animateOnMount, role, depth, containerSize]);
216
221
 
217
222
  // Use shared transform hook for DOM manipulation
218
223
  const { isAnimating } = useSwipeContentTransform({
219
224
  elementRef,
220
225
  targetPx,
221
226
  displacement: panelDisplacement,
222
- isSwiping,
227
+ isOperating,
223
228
  axis,
224
229
  animationDuration,
225
230
  containerSize,
@@ -227,6 +232,11 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
227
232
  animateOnTargetChange: true,
228
233
  // For push animation: start from off-screen
229
234
  initialPx,
235
+ // Skip backward animation if role changed during the operation.
236
+ // This handles over-swipe where panel moves beyond 100% and needs to snap back.
237
+ // useSwipeContentTransform allows forward animations (normal swipe-to-complete)
238
+ // but skips backward animations (over-swipe snap).
239
+ skipTargetChangeAnimation: changedDuringOperation,
230
240
  });
231
241
 
232
242
  // Compute visibility
@@ -234,7 +244,7 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
234
244
  depth,
235
245
  navigationDepth,
236
246
  isActive,
237
- isSwiping,
247
+ isOperating,
238
248
  isAnimating,
239
249
  });
240
250
 
@@ -309,7 +319,7 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
309
319
 
310
320
  // Compute shadow for active panel
311
321
  // Shadow is shown on panels at depth > 0 when they're active or animating
312
- const shouldShowShadow = showShadow && depth > 0 && role === "active";
322
+ const shouldShowShadow = showShadow ? depth > 0 && role === "active" : false;
313
323
 
314
324
  // Static style - transform is handled entirely by useSwipeContentTransform
315
325
  // to ensure smooth animations
@@ -349,7 +359,7 @@ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
349
359
  style={staticStyle}
350
360
  >
351
361
  {children}
352
- {dimmingStyle != null && <div style={dimmingStyle} data-dimming-overlay />}
362
+ {dimmingStyle != null ? <div style={dimmingStyle} data-dimming-overlay /> : null}
353
363
  </div>
354
364
  );
355
365
  },
@@ -3,21 +3,19 @@
3
3
  */
4
4
  import { render } from "@testing-library/react";
5
5
  import { SwipeStackOutlet } from "./SwipeStackOutlet.js";
6
- import type { SwipeInputState } from "../../hooks/gesture/types.js";
6
+ import type { ContinuousOperationState } from "../../hooks/gesture/types.js";
7
7
  import type { StackPanel, StackNavigationState } from "./types.js";
8
8
 
9
- const IDLE_STATE: SwipeInputState = {
9
+ const IDLE_STATE: ContinuousOperationState = {
10
10
  phase: "idle",
11
11
  displacement: { x: 0, y: 0 },
12
12
  velocity: { x: 0, y: 0 },
13
- direction: 0,
14
13
  };
15
14
 
16
- const createSwipingState = (displacementX: number): SwipeInputState => ({
17
- phase: "swiping",
15
+ const createOperatingState = (displacementX: number): ContinuousOperationState => ({
16
+ phase: "operating",
18
17
  displacement: { x: displacementX, y: 0 },
19
18
  velocity: { x: 0.5, y: 0 },
20
- direction: displacementX > 0 ? 1 : displacementX < 0 ? -1 : 0,
21
19
  });
22
20
 
23
21
  const createPanels = (): StackPanel[] => [
@@ -40,7 +38,7 @@ describe("SwipeStackOutlet", () => {
40
38
  <SwipeStackOutlet
41
39
  panels={createPanels()}
42
40
  navigationState={navigationState}
43
- inputState={IDLE_STATE}
41
+ operationState={IDLE_STATE}
44
42
  containerSize={400}
45
43
  />,
46
44
  );
@@ -61,7 +59,7 @@ describe("SwipeStackOutlet", () => {
61
59
  <SwipeStackOutlet
62
60
  panels={createPanels()}
63
61
  navigationState={navigationState}
64
- inputState={IDLE_STATE}
62
+ operationState={IDLE_STATE}
65
63
  containerSize={400}
66
64
  />,
67
65
  );
@@ -85,7 +83,7 @@ describe("SwipeStackOutlet", () => {
85
83
  <SwipeStackOutlet
86
84
  panels={createPanels()}
87
85
  navigationState={navigationState}
88
- inputState={IDLE_STATE}
86
+ operationState={IDLE_STATE}
89
87
  containerSize={400}
90
88
  />,
91
89
  );
@@ -109,7 +107,7 @@ describe("SwipeStackOutlet", () => {
109
107
  <SwipeStackOutlet
110
108
  panels={createPanels()}
111
109
  navigationState={navigationState}
112
- inputState={IDLE_STATE}
110
+ operationState={IDLE_STATE}
113
111
  containerSize={400}
114
112
  />,
115
113
  );
@@ -131,13 +129,13 @@ describe("SwipeStackOutlet", () => {
131
129
  revealDepth: null,
132
130
  };
133
131
 
134
- const swipeState = createSwipingState(200);
132
+ const swipeState = createOperatingState(200);
135
133
 
136
134
  const { container, rerender } = render(
137
135
  <SwipeStackOutlet
138
136
  panels={createPanels()}
139
137
  navigationState={navigationState}
140
- inputState={IDLE_STATE}
138
+ operationState={IDLE_STATE}
141
139
  containerSize={400}
142
140
  />,
143
141
  );
@@ -147,7 +145,7 @@ describe("SwipeStackOutlet", () => {
147
145
  <SwipeStackOutlet
148
146
  panels={createPanels()}
149
147
  navigationState={navigationState}
150
- inputState={swipeState}
148
+ operationState={swipeState}
151
149
  containerSize={400}
152
150
  />,
153
151
  );
@@ -185,7 +183,7 @@ describe("SwipeStackOutlet", () => {
185
183
  <SwipeStackOutlet
186
184
  panels={createPanels()}
187
185
  navigationState={navigationState}
188
- inputState={IDLE_STATE}
186
+ operationState={IDLE_STATE}
189
187
  containerSize={400}
190
188
  getCachedContent={getCachedContent}
191
189
  />,
@@ -210,7 +208,7 @@ describe("SwipeStackOutlet", () => {
210
208
  <SwipeStackOutlet
211
209
  panels={createPanels()}
212
210
  navigationState={navigationState}
213
- inputState={IDLE_STATE}
211
+ operationState={IDLE_STATE}
214
212
  containerSize={400}
215
213
  getCachedContent={getCachedContent}
216
214
  />,
@@ -233,7 +231,7 @@ describe("SwipeStackOutlet", () => {
233
231
  <SwipeStackOutlet
234
232
  panels={createPanels()}
235
233
  navigationState={navigationState}
236
- inputState={IDLE_STATE}
234
+ operationState={IDLE_STATE}
237
235
  containerSize={400}
238
236
  />,
239
237
  );
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import * as React from "react";
8
8
  import { SwipeStackContent } from "./SwipeStackContent.js";
9
- import type { SwipeInputState } from "../../hooks/gesture/types.js";
9
+ import type { ContinuousOperationState } from "../../hooks/gesture/types.js";
10
10
  import type { StackPanel, StackNavigationState } from "./types.js";
11
11
 
12
12
  const DEFAULT_ANIMATION_DURATION = 300;
@@ -19,8 +19,8 @@ export type SwipeStackOutletProps = {
19
19
  panels: ReadonlyArray<StackPanel>;
20
20
  /** Current navigation state */
21
21
  navigationState: StackNavigationState;
22
- /** Swipe input state from useStackSwipeInput */
23
- inputState: SwipeInputState;
22
+ /** Continuous operation state (from gesture input or animation system) */
23
+ operationState: ContinuousOperationState;
24
24
  /** Container size in pixels (width for horizontal swipe) */
25
25
  containerSize: number;
26
26
  /** Function to get cached content for a panel */
@@ -121,7 +121,7 @@ function getVisiblePanels(
121
121
  * <SwipeStackOutlet
122
122
  * panels={navigation.panels}
123
123
  * navigationState={navigation.state}
124
- * inputState={swipeInput.inputState}
124
+ * operationState={toContinuousOperationState(swipeInput.inputState)}
125
125
  * containerSize={containerWidth}
126
126
  * />
127
127
  * </div>
@@ -132,7 +132,7 @@ export const SwipeStackOutlet: React.FC<SwipeStackOutletProps> = React.memo(
132
132
  ({
133
133
  panels,
134
134
  navigationState,
135
- inputState,
135
+ operationState,
136
136
  containerSize,
137
137
  getCachedContent,
138
138
  behindOffset,
@@ -202,7 +202,7 @@ export const SwipeStackOutlet: React.FC<SwipeStackOutletProps> = React.memo(
202
202
  depth={depth}
203
203
  navigationDepth={navigationState.depth}
204
204
  isActive={isActive}
205
- inputState={inputState}
205
+ operationState={operationState}
206
206
  containerSize={containerSize}
207
207
  behindOffset={behindOffset}
208
208
  animateOnMount={animateOnMount}
@@ -106,7 +106,7 @@ describe("computeSwipeVisibility", () => {
106
106
  depth: 2,
107
107
  navigationDepth: 2,
108
108
  isActive: true,
109
- isSwiping: false,
109
+ isOperating: false,
110
110
  isAnimating: false,
111
111
  }),
112
112
  ).toBe(true);
@@ -118,7 +118,7 @@ describe("computeSwipeVisibility", () => {
118
118
  depth: 1,
119
119
  navigationDepth: 2,
120
120
  isActive: false,
121
- isSwiping: true,
121
+ isOperating: true,
122
122
  isAnimating: false,
123
123
  }),
124
124
  ).toBe(true);
@@ -130,7 +130,7 @@ describe("computeSwipeVisibility", () => {
130
130
  depth: 1,
131
131
  navigationDepth: 2,
132
132
  isActive: false,
133
- isSwiping: false,
133
+ isOperating: false,
134
134
  isAnimating: true,
135
135
  }),
136
136
  ).toBe(true);
@@ -142,7 +142,7 @@ describe("computeSwipeVisibility", () => {
142
142
  depth: 1,
143
143
  navigationDepth: 2,
144
144
  isActive: false,
145
- isSwiping: false,
145
+ isOperating: false,
146
146
  isAnimating: false,
147
147
  }),
148
148
  ).toBe(false);
@@ -154,7 +154,7 @@ describe("computeSwipeVisibility", () => {
154
154
  depth: 0,
155
155
  navigationDepth: 2,
156
156
  isActive: false,
157
- isSwiping: true,
157
+ isOperating: true,
158
158
  isAnimating: false,
159
159
  }),
160
160
  ).toBe(false);
@@ -85,7 +85,7 @@ export type ComputeSwipeVisibilityInput = {
85
85
  /** Whether this panel is currently active */
86
86
  isActive: boolean;
87
87
  /** Whether swipe gesture is active */
88
- isSwiping: boolean;
88
+ isOperating: boolean;
89
89
  /** Whether snap-back animation is running */
90
90
  isAnimating: boolean;
91
91
  };
@@ -100,7 +100,7 @@ export type ComputeSwipeVisibilityInput = {
100
100
  * @returns true if panel should be visible
101
101
  */
102
102
  export function computeSwipeVisibility(input: ComputeSwipeVisibilityInput): boolean {
103
- const { depth, navigationDepth, isActive, isSwiping, isAnimating } = input;
103
+ const { depth, navigationDepth, isActive, isOperating, isAnimating } = input;
104
104
 
105
105
  // Active panel is always visible
106
106
  if (isActive) {
@@ -110,7 +110,7 @@ export function computeSwipeVisibility(input: ComputeSwipeVisibilityInput): bool
110
110
  // Behind panel (one level back) is visible during swipe or animation
111
111
  const isBehindPanel = depth === navigationDepth - 1;
112
112
  if (isBehindPanel) {
113
- if (isSwiping) {
113
+ if (isOperating) {
114
114
  return true;
115
115
  }
116
116
  if (isAnimating) {