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
@@ -1 +1 @@
1
- {"version":3,"file":"stack.cjs","sources":["../src/modules/stack/computeStackContentState.ts","../src/modules/stack/StackContent.tsx","../src/modules/stack/useStackNavigation.tsx","../src/hooks/gesture/usePointerTracking.ts","../src/hooks/gesture/useDirectionalLock.ts","../src/hooks/gesture/utils.ts","../src/hooks/gesture/types.ts","../src/hooks/gesture/useSwipeInput.ts","../src/hooks/gesture/useEdgeSwipeInput.ts","../src/hooks/gesture/useNativeGestureGuard.ts","../src/modules/stack/useStackSwipeInput.ts","../src/hooks/useAnimationFrame.ts","../src/hooks/useSwipeContentTransform.ts","../src/modules/stack/computeSwipeStackTransform.ts","../src/modules/stack/SwipeStackContent.tsx","../src/modules/stack/SwipeStackOutlet.tsx"],"sourcesContent":["/**\n * @file Pure functions for computing StackContent state.\n *\n * Separates state computation logic from React/CSS concerns for testability.\n * All functions are pure and deterministic.\n */\nimport {\n STACK_ANIMATION_PUSH,\n STACK_ANIMATION_POP,\n STACK_TRANSITION_DURATION,\n STACK_TRANSITION_EASING,\n} from \"../../constants/styles.js\";\n\n/**\n * Animation type for stack panel transitions.\n */\nexport type StackAnimationType = \"push\" | \"pop\" | null;\n\n/**\n * Display mode for stack panels.\n */\nexport type StackDisplayMode = \"overlay\" | \"slide\" | \"stack\";\n\n/**\n * Transition mode for animations.\n */\nexport type StackTransitionMode = \"css\" | \"none\";\n\n/**\n * Input for computing animation type.\n */\nexport type ComputeAnimationTypeInput = {\n wasActive: boolean;\n isActive: boolean;\n transitionMode: StackTransitionMode;\n};\n\n/**\n * Compute the animation type based on active state change.\n *\n * @returns The animation type to apply, or null if no animation needed.\n */\nexport function computeAnimationType(input: ComputeAnimationTypeInput): StackAnimationType {\n const { wasActive, isActive, transitionMode } = input;\n\n if (transitionMode !== \"css\") {\n return null;\n }\n\n if (wasActive === isActive) {\n return null;\n }\n\n return isActive ? \"push\" : \"pop\";\n}\n\n/**\n * Input for computing visibility.\n */\nexport type ComputeVisibilityInput = {\n displayMode: StackDisplayMode;\n depth: number;\n navigationDepth: number;\n isActive: boolean;\n isAnimatingOut: boolean;\n isRevealing: boolean;\n revealDepth: number | null;\n};\n\n/**\n * Compute panel visibility based on display mode and state.\n *\n * @returns \"visible\" or \"hidden\"\n */\nexport function computeVisibility(input: ComputeVisibilityInput): \"visible\" | \"hidden\" {\n const {\n displayMode,\n depth,\n navigationDepth,\n isActive,\n isAnimatingOut,\n isRevealing,\n revealDepth,\n } = input;\n\n if (displayMode === \"overlay\") {\n // In overlay mode, only show active, animating out, or revealing panel\n if (isActive) {\n return \"visible\";\n }\n if (isAnimatingOut) {\n return \"visible\";\n }\n if (isRevealing && depth === revealDepth) {\n return \"visible\";\n }\n return \"hidden\";\n }\n\n // In slide/stack mode, show all panels in stack or animating out\n if (depth <= navigationDepth) {\n return \"visible\";\n }\n if (isAnimatingOut) {\n return \"visible\";\n }\n return \"hidden\";\n}\n\n/**\n * Input for computing transform.\n */\nexport type ComputeTransformInput = {\n depth: number;\n activeDepth: number;\n displayMode: StackDisplayMode;\n isRevealing: boolean;\n revealDepth: number | null;\n};\n\n/**\n * Compute the transform value for a stack panel.\n *\n * @returns CSS transform string\n */\nexport function computeTransform(input: ComputeTransformInput): string {\n const { depth, activeDepth, displayMode, isRevealing, revealDepth } = input;\n\n const isActive = depth === activeDepth;\n const isPrevious = depth < activeDepth;\n\n // During reveal, shift active panel to show parent\n if (isRevealing && isActive) {\n if (revealDepth !== null) {\n const revealProgress = 0.3;\n return `translateX(${revealProgress * 100}%)`;\n }\n }\n\n if (isActive) {\n return \"translateX(0)\";\n }\n\n if (isPrevious) {\n switch (displayMode) {\n case \"overlay\":\n return \"translateX(0)\";\n case \"slide\":\n return \"translateX(-30%)\";\n case \"stack\": {\n const offset = (activeDepth - depth) * -5;\n const scale = 1 - (activeDepth - depth) * 0.05;\n return `translateX(${offset}%) scale(${scale})`;\n }\n }\n }\n\n // Future panels stay off-screen\n return \"translateX(100%)\";\n}\n\n/**\n * Compute the transform value considering swipe progress.\n */\nfunction computeSwipeTransform(\n baseTransform: string,\n swipeProgress: number | undefined,\n isActive: boolean,\n): string {\n if (swipeProgress === undefined) {\n return baseTransform;\n }\n if (swipeProgress <= 0) {\n return baseTransform;\n }\n if (!isActive) {\n return baseTransform;\n }\n return `translateX(${swipeProgress * 100}%)`;\n}\n\n/**\n * Compute the transition CSS value.\n */\nfunction computeTransitionCss(transitionMode: StackTransitionMode): string | undefined {\n if (transitionMode !== \"css\") {\n return undefined;\n }\n return `transform ${STACK_TRANSITION_DURATION} ${STACK_TRANSITION_EASING}`;\n}\n\n/**\n * Full input for computing stack content state.\n */\nexport type StackContentStateInput = {\n depth: number;\n isActive: boolean;\n wasActive: boolean;\n currentAnimationType: StackAnimationType;\n displayMode: StackDisplayMode;\n transitionMode: StackTransitionMode;\n navigationState: {\n depth: number;\n isRevealing: boolean;\n revealDepth: number | null;\n };\n swipeProgress: number | undefined;\n};\n\n/**\n * Computed state output for stack content.\n */\nexport type StackContentStateOutput = {\n nextAnimationType: StackAnimationType;\n visibility: \"visible\" | \"hidden\";\n transform: string;\n animation: string | undefined;\n transition: string | undefined;\n zIndex: number;\n pointerEvents: \"auto\" | \"none\";\n};\n\n/**\n * Compute the complete state for a stack content panel.\n *\n * This is the main entry point that combines all state computation.\n * Pure function with no side effects.\n *\n * @param input - All inputs needed to compute state\n * @returns Computed state for rendering\n */\nexport function computeStackContentState(input: StackContentStateInput): StackContentStateOutput {\n const {\n depth,\n isActive,\n wasActive,\n currentAnimationType,\n displayMode,\n transitionMode,\n navigationState,\n swipeProgress,\n } = input;\n\n // 1. Compute animation type\n const stateChangeAnimationType = computeAnimationType({\n wasActive,\n isActive,\n transitionMode,\n });\n\n // Use new animation type if state changed, otherwise preserve current\n const nextAnimationType = stateChangeAnimationType ?? currentAnimationType;\n\n // 2. Compute visibility\n const isAnimatingOut = nextAnimationType === \"pop\";\n const visibility = computeVisibility({\n displayMode,\n depth,\n navigationDepth: navigationState.depth,\n isActive,\n isAnimatingOut,\n isRevealing: navigationState.isRevealing,\n revealDepth: navigationState.revealDepth,\n });\n\n // 3. Compute transform\n const baseTransform = computeTransform({\n depth,\n activeDepth: navigationState.depth,\n displayMode,\n isRevealing: navigationState.isRevealing,\n revealDepth: navigationState.revealDepth,\n });\n\n // Apply swipe progress transform if swiping on active panel\n const transform = computeSwipeTransform(baseTransform, swipeProgress, isActive);\n\n // 4. Compute animation CSS\n const animation = (() => {\n if (transitionMode !== \"css\") {\n return undefined;\n }\n if (nextAnimationType === \"push\") {\n return STACK_ANIMATION_PUSH;\n }\n if (nextAnimationType === \"pop\") {\n return STACK_ANIMATION_POP;\n }\n return undefined;\n })();\n\n // 5. Compute transition CSS\n const transition = computeTransitionCss(transitionMode);\n\n return {\n nextAnimationType,\n visibility,\n transform,\n animation,\n transition,\n zIndex: depth,\n pointerEvents: isActive ? \"auto\" : \"none\",\n };\n}\n","/**\n * @file StackContent component for rendering stack panels with animations.\n *\n * Override via CSS custom properties:\n * - --rpl-stack-animation-push: Animation when panel is pushed\n * - --rpl-stack-animation-pop: Animation when panel is popped\n * - --rpl-stack-transition-duration: Duration of transitions\n * - --rpl-stack-transition-easing: Easing for transitions\n */\nimport * as React from \"react\";\nimport type { StackContentProps } from \"./types.js\";\nimport { computeStackContentState } from \"./computeStackContentState.js\";\nimport type { StackAnimationType } from \"./computeStackContentState.js\";\nimport { useIsomorphicLayoutEffect } from \"../../hooks/useIsomorphicLayoutEffect.js\";\n\nconst baseStyle: React.CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/**\n * Renders a stack panel with appropriate animation based on display mode.\n */\nexport const StackContent: React.FC<StackContentProps> = React.memo(\n ({ id, depth, isActive, displayMode, transitionMode, navigationState, swipeProgress, children }) => {\n const ref = React.useRef<HTMLDivElement>(null);\n const prevActiveRef = React.useRef(isActive);\n\n // Track current animation type\n const [animationType, setAnimationType] = React.useState<StackAnimationType>(null);\n\n // Compute state using pure function\n const computedState = computeStackContentState({\n depth,\n isActive,\n wasActive: prevActiveRef.current,\n currentAnimationType: animationType,\n displayMode,\n transitionMode,\n navigationState,\n swipeProgress,\n });\n\n // Update animation type synchronously before paint\n useIsomorphicLayoutEffect(() => {\n const wasActive = prevActiveRef.current;\n prevActiveRef.current = isActive;\n\n if (wasActive !== isActive) {\n setAnimationType(computedState.nextAnimationType);\n }\n }, [isActive, computedState.nextAnimationType]);\n\n // Clear animation type after animation ends\n // Only handle animation end for this element (not bubbled from children)\n const handleAnimationEnd = React.useCallback((e: React.AnimationEvent) => {\n if (e.target === e.currentTarget) {\n setAnimationType(null);\n }\n }, []);\n\n // Build style from computed state\n const style = React.useMemo<React.CSSProperties>(() => {\n const s: React.CSSProperties = {\n ...baseStyle,\n transform: computedState.transform,\n pointerEvents: computedState.pointerEvents,\n zIndex: computedState.zIndex,\n visibility: computedState.visibility,\n };\n\n if (computedState.animation !== undefined) {\n s.animation = computedState.animation;\n }\n\n if (computedState.transition !== undefined) {\n s.transition = computedState.transition;\n }\n\n return s;\n }, [\n computedState.transform,\n computedState.pointerEvents,\n computedState.zIndex,\n computedState.visibility,\n computedState.animation,\n computedState.transition,\n ]);\n\n const content = (\n <div\n ref={ref}\n data-stack-content={id}\n data-depth={depth}\n data-active={isActive ? \"true\" : \"false\"}\n style={style}\n onAnimationEnd={handleAnimationEnd}\n >\n {children}\n </div>\n );\n\n if (transitionMode === \"none\") {\n return <React.Activity mode={isActive ? \"visible\" : \"hidden\"}>{content}</React.Activity>;\n }\n\n return content;\n },\n);\n","/**\n * @file Headless hook for managing Stack (hierarchical) navigation.\n *\n * Provides navigation operations for a stack-based UI where panels\n * are pushed and popped as the user drills down into content.\n */\nimport * as React from \"react\";\nimport type {\n UseStackNavigationOptions,\n UseStackNavigationResult,\n StackNavigationState,\n StackPanelProps,\n StackBackButtonProps,\n StackPanel,\n} from \"./types.js\";\nimport { StackContent } from \"./StackContent.js\";\nimport { useContentCache } from \"../../hooks/useContentCache.js\";\n\n/**\n * Context for sharing stack state with Outlet component.\n */\ntype StackOutletContextValue = {\n getState: () => {\n panels: ReadonlyArray<StackPanel>;\n navigationState: StackNavigationState;\n displayMode: UseStackNavigationOptions[\"displayMode\"];\n transitionMode: NonNullable<UseStackNavigationOptions[\"transitionMode\"]>;\n };\n subscribe: (callback: () => void) => () => void;\n getCachedContent: (panelId: string) => React.ReactNode | null;\n};\n\nconst StackOutletContext = React.createContext<StackOutletContextValue | null>(null);\n\n/**\n * Outlet component that renders the stack panels.\n */\nconst StackOutletInner: React.FC = React.memo(() => {\n const ctx = React.useContext(StackOutletContext);\n if (!ctx) {\n throw new Error(\"StackOutlet must be used within useStackNavigation\");\n }\n\n const [, forceUpdate] = React.useReducer((x) => x + 1, 0);\n\n React.useEffect(() => {\n return ctx.subscribe(forceUpdate);\n }, [ctx]);\n\n const { panels, navigationState, displayMode, transitionMode } = ctx.getState();\n\n // Get panels that should be rendered (only those in the current stack)\n const visiblePanels = React.useMemo(() => {\n return navigationState.stack.map((id, index) => {\n const panel = panels.find((p) => p.id === id);\n return panel ? { panel, depth: index } : null;\n }).filter((p): p is { panel: StackPanel; depth: number } => p !== null);\n }, [navigationState.stack, panels]);\n\n return (\n <>\n {visiblePanels.map(({ panel, depth }) => (\n <StackContent\n key={panel.id}\n id={panel.id}\n depth={depth}\n isActive={depth === navigationState.depth}\n displayMode={displayMode}\n transitionMode={transitionMode}\n navigationState={navigationState}\n >\n {panel.cache ? ctx.getCachedContent(panel.id) : panel.content}\n </StackContent>\n ))}\n </>\n );\n});\n\n/**\n * Headless hook for managing hierarchical stack navigation.\n *\n * @example\n * ```tsx\n * const { state, push, go, Outlet } = useStackNavigation({\n * panels: [\n * { id: 'list', title: 'Items', content: <ListPage /> },\n * { id: 'detail', title: 'Detail', content: <DetailPage /> },\n * ],\n * displayMode: 'overlay',\n * });\n *\n * return (\n * <div>\n * <button onClick={() => push('detail')}>View Detail</button>\n * <Outlet />\n * </div>\n * );\n * ```\n */\nexport function useStackNavigation<TId extends string = string>(\n options: UseStackNavigationOptions<TId>,\n): UseStackNavigationResult<TId> {\n const {\n panels,\n initialPanelId,\n displayMode,\n transitionMode = \"css\",\n onPanelChange,\n } = options;\n\n // Initialize stack with initial panel\n const [stack, setStack] = React.useState<ReadonlyArray<TId>>(() => {\n const initialId = initialPanelId ?? (panels[0]?.id as TId);\n if (!initialId) {\n throw new Error(\"useStackNavigation: No panels provided\");\n }\n return [initialId];\n });\n\n // Reveal state for parent peeking\n const [revealState, setRevealState] = React.useState<{\n isRevealing: boolean;\n revealDepth: number | null;\n }>({ isRevealing: false, revealDepth: null });\n\n // Current depth (0-indexed)\n const depth = stack.length - 1;\n\n // Navigation state\n const state: StackNavigationState<TId> = React.useMemo(() => ({\n stack,\n depth,\n isRevealing: revealState.isRevealing,\n revealDepth: revealState.revealDepth,\n }), [stack, depth, revealState.isRevealing, revealState.revealDepth]);\n\n // Current and previous panel IDs\n const currentPanelId = stack[depth] as TId;\n const previousPanelId = depth > 0 ? stack[depth - 1] as TId : null;\n\n // Push a new panel onto the stack\n const push = React.useCallback((id: TId) => {\n const panel = panels.find((p) => p.id === id);\n if (!panel) {\n return;\n }\n setStack((prev) => [...prev, id]);\n onPanelChange?.(id, depth + 1);\n }, [panels, depth, onPanelChange]);\n\n // Navigate in a direction\n const go = React.useCallback((direction: number) => {\n if (direction >= 0) {\n return; // go is only for going back in stack navigation\n }\n const targetDepth = depth + direction;\n if (targetDepth < 0) {\n return;\n }\n setStack((prev) => prev.slice(0, targetDepth + 1));\n const targetId = stack[targetDepth] as TId;\n onPanelChange?.(targetId, targetDepth);\n }, [depth, stack, onPanelChange]);\n\n // Move to absolute depth\n const move = React.useCallback((targetDepth: number) => {\n if (targetDepth < 0 || targetDepth >= stack.length) {\n return;\n }\n setStack((prev) => prev.slice(0, targetDepth + 1));\n const targetId = stack[targetDepth] as TId;\n onPanelChange?.(targetId, targetDepth);\n }, [stack, onPanelChange]);\n\n // Replace current panel\n const replace = React.useCallback((id: TId) => {\n const panel = panels.find((p) => p.id === id);\n if (!panel) {\n return;\n }\n setStack((prev) => [...prev.slice(0, -1), id]);\n onPanelChange?.(id, depth);\n }, [panels, depth, onPanelChange]);\n\n // Check if navigation is possible\n const canGo = React.useCallback((direction: number): boolean => {\n if (direction >= 0) {\n return false; // canGo only checks backward navigation for stacks\n }\n const targetDepth = depth + direction;\n return targetDepth >= 0;\n }, [depth]);\n\n // Reveal parent panel\n const revealParent = React.useCallback((targetDepth?: number) => {\n const revealTo = targetDepth ?? depth - 1;\n if (revealTo < 0 || revealTo >= depth) {\n return;\n }\n setRevealState({ isRevealing: true, revealDepth: revealTo });\n }, [depth]);\n\n // Reveal root panel\n const revealRoot = React.useCallback(() => {\n if (depth === 0) {\n return;\n }\n setRevealState({ isRevealing: true, revealDepth: 0 });\n }, [depth]);\n\n // Dismiss reveal\n const dismissReveal = React.useCallback(() => {\n setRevealState({ isRevealing: false, revealDepth: null });\n }, []);\n\n // Get props for a panel element\n const getPanelProps = React.useCallback((id: TId): StackPanelProps => {\n const panelIndex = stack.indexOf(id);\n const isActive = panelIndex === depth;\n\n return {\n \"data-stack-panel\": id,\n \"data-depth\": panelIndex,\n \"data-active\": isActive ? \"true\" : \"false\",\n \"aria-hidden\": !isActive,\n };\n }, [stack, depth]);\n\n // Get props for back button\n const getBackButtonProps = React.useCallback((): StackBackButtonProps => {\n const canGoBack = depth > 0;\n const prevPanel = previousPanelId ? panels.find((p) => p.id === previousPanelId) : null;\n const label = prevPanel?.title ? `Back to ${prevPanel.title}` : \"Go back\";\n\n return {\n onClick: () => go(-1),\n disabled: !canGoBack,\n \"aria-label\": label,\n };\n }, [depth, previousPanelId, panels, go]);\n\n // Container style\n const containerStyle: React.CSSProperties = React.useMemo(\n () => ({\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }),\n [],\n );\n\n // State ref for stable getState function\n const stateRef = React.useRef({\n panels,\n navigationState: state,\n displayMode,\n transitionMode,\n });\n\n stateRef.current = {\n panels,\n navigationState: state,\n displayMode,\n transitionMode,\n };\n\n // Subscribers for state changes\n const subscribersRef = React.useRef(new Set<() => void>());\n\n // Notify subscribers when state changes\n React.useEffect(() => {\n subscribersRef.current.forEach((callback) => callback());\n }, [state, displayMode, transitionMode]);\n\n // Content resolver for useContentCache\n const resolveContent = React.useCallback(\n (panelId: string): React.ReactNode | null => {\n const panel = stateRef.current.panels.find((p) => p.id === panelId);\n return panel?.content ?? null;\n },\n [],\n );\n\n // Valid IDs for cache cleanup\n const validIds = React.useMemo((): readonly string[] => panels.map((p) => p.id), [panels]);\n\n // Use shared content cache hook\n const { getCachedContent } = useContentCache({\n resolveContent,\n validIds,\n });\n\n // Stable context value\n const contextValue = React.useMemo<StackOutletContextValue>(\n () => ({\n getState: () => stateRef.current,\n subscribe: (callback) => {\n subscribersRef.current.add(callback);\n return () => subscribersRef.current.delete(callback);\n },\n getCachedContent,\n }),\n [getCachedContent],\n );\n\n // Stable Outlet component\n const Outlet = React.useMemo(() => {\n const OutletComponent: React.FC = () => (\n <StackOutletContext.Provider value={contextValue}>\n <div style={containerStyle} data-stack-container>\n <StackOutletInner />\n </div>\n </StackOutletContext.Provider>\n );\n OutletComponent.displayName = \"StackOutlet\";\n return OutletComponent;\n }, [contextValue, containerStyle]);\n\n return {\n state,\n push,\n go,\n move,\n replace,\n revealParent,\n revealRoot,\n dismissReveal,\n getPanelProps,\n getBackButtonProps,\n canGo,\n currentPanelId,\n previousPanelId,\n Outlet,\n };\n}\n","/**\n * @file Hook for tracking pointer state during gestures.\n *\n * Provides low-level pointer tracking with position, velocity, and timing.\n * This hook serves as the foundation for higher-level gesture hooks.\n */\nimport * as React from \"react\";\nimport { useDocumentPointerEvents } from \"../useDocumentPointerEvents.js\";\nimport { useEffectEvent } from \"../useEffectEvent.js\";\nimport type {\n PointerTrackingState,\n TimestampedPoint,\n UsePointerTrackingOptions,\n UsePointerTrackingResult,\n} from \"./types.js\";\n\n/**\n * Initial state for pointer tracking.\n */\nconst INITIAL_STATE: PointerTrackingState = {\n isDown: false,\n start: null,\n current: null,\n pointerId: null,\n wasCanceled: false,\n};\n\n/**\n * Creates a timestamped point from pointer event coordinates.\n */\nconst createTimestampedPoint = (clientX: number, clientY: number): TimestampedPoint => ({\n x: clientX,\n y: clientY,\n timestamp: performance.now(),\n});\n\n/**\n * Hook for tracking pointer state during gestures.\n *\n * Tracks pointer down/move/up events and provides position and timing data\n * that can be used by higher-level gesture detection hooks.\n *\n * @example\n * ```tsx\n * const { state, onPointerDown, reset } = usePointerTracking({\n * enabled: true,\n * primaryOnly: true,\n * });\n *\n * return (\n * <div onPointerDown={onPointerDown}>\n * {state.isDown && <span>Tracking...</span>}\n * </div>\n * );\n * ```\n */\nexport function usePointerTracking(options: UsePointerTrackingOptions): UsePointerTrackingResult {\n const { enabled, primaryOnly = true } = options;\n\n const [state, setState] = React.useState<PointerTrackingState>(INITIAL_STATE);\n\n const reset = React.useCallback(() => {\n setState(INITIAL_STATE);\n }, []);\n\n const handlePointerDown = useEffectEvent((event: React.PointerEvent) => {\n if (!enabled) {\n return;\n }\n\n // Filter non-primary pointers if primaryOnly is set\n if (primaryOnly && !event.isPrimary) {\n return;\n }\n\n // Only track left mouse button or touch/pen\n if (event.pointerType === \"mouse\" && event.button !== 0) {\n return;\n }\n\n const point = createTimestampedPoint(event.clientX, event.clientY);\n\n setState({\n isDown: true,\n start: point,\n current: point,\n pointerId: event.pointerId,\n wasCanceled: false,\n });\n });\n\n const handlePointerMove = useEffectEvent((event: PointerEvent) => {\n // Verify this is the tracked pointer\n if (state.pointerId !== event.pointerId) {\n return;\n }\n\n const point = createTimestampedPoint(event.clientX, event.clientY);\n\n setState((prev) => ({\n ...prev,\n current: point,\n }));\n });\n\n const handlePointerUp = useEffectEvent(() => {\n setState(INITIAL_STATE);\n });\n\n const handlePointerCancel = useEffectEvent(() => {\n setState({ ...INITIAL_STATE, wasCanceled: true });\n });\n\n // Use document-level pointer events for tracking after pointer down\n const shouldTrackDocument = state.isDown ? enabled : false;\n useDocumentPointerEvents(shouldTrackDocument, {\n onMove: handlePointerMove,\n onUp: handlePointerUp,\n onCancel: handlePointerCancel,\n });\n\n // Reset state when disabled\n React.useEffect(() => {\n if (!enabled && state.isDown) {\n reset();\n }\n }, [enabled, state.isDown, reset]);\n\n return {\n state,\n onPointerDown: handlePointerDown,\n reset,\n };\n}\n","/**\n * @file Hook for locking gesture direction after threshold is exceeded.\n *\n * Once the user moves beyond the lock threshold, the direction is locked\n * to either horizontal or vertical, preventing diagonal gestures from\n * triggering both scroll and swipe.\n */\nimport * as React from \"react\";\nimport type {\n GestureAxis,\n UseDirectionalLockOptions,\n UseDirectionalLockResult,\n} from \"./types.js\";\n\nconst DEFAULT_LOCK_THRESHOLD = 10;\n\n/**\n * Determines which axis the gesture is primarily moving along.\n *\n * @param deltaX - Horizontal displacement from start\n * @param deltaY - Vertical displacement from start\n * @returns The dominant axis, or null if movement is insufficient\n */\nconst determineAxis = (deltaX: number, deltaY: number): GestureAxis | null => {\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n // Require at least some movement in one direction\n if (absX === 0 && absY === 0) {\n return null;\n }\n\n // Use a 1.5x ratio to ensure clear direction before locking\n // This prevents near-diagonal gestures from locking prematurely\n if (absX > absY * 1.5) {\n return \"horizontal\";\n }\n\n if (absY > absX * 1.5) {\n return \"vertical\";\n }\n\n // Still ambiguous\n return null;\n};\n\n/**\n * Hook for locking gesture direction after threshold is exceeded.\n *\n * This hook tracks pointer movement and locks to horizontal or vertical\n * direction once the movement exceeds the configured threshold. This\n * prevents diagonal gestures from triggering both scroll and swipe behaviors.\n *\n * @example\n * ```tsx\n * const { state: tracking, onPointerDown } = usePointerTracking({ enabled: true });\n * const { lockedAxis, isLocked } = useDirectionalLock({\n * tracking,\n * lockThreshold: 10,\n * });\n *\n * // lockedAxis will be \"horizontal\" or \"vertical\" once determined\n * ```\n */\nexport function useDirectionalLock(options: UseDirectionalLockOptions): UseDirectionalLockResult {\n const { tracking, lockThreshold = DEFAULT_LOCK_THRESHOLD } = options;\n\n const [lockedAxis, setLockedAxis] = React.useState<GestureAxis | null>(null);\n\n const reset = React.useCallback(() => {\n setLockedAxis(null);\n }, []);\n\n // Determine direction when tracking is active\n React.useEffect(() => {\n // Reset lock when pointer is released\n if (!tracking.isDown) {\n if (lockedAxis !== null) {\n reset();\n }\n return;\n }\n\n // Already locked, no need to recalculate\n if (lockedAxis !== null) {\n return;\n }\n\n // Need start and current positions\n if (!tracking.start || !tracking.current) {\n return;\n }\n\n const deltaX = tracking.current.x - tracking.start.x;\n const deltaY = tracking.current.y - tracking.start.y;\n\n // Check if we've exceeded the lock threshold\n const distance = Math.max(Math.abs(deltaX), Math.abs(deltaY));\n if (distance < lockThreshold) {\n return;\n }\n\n // Try to determine axis\n const axis = determineAxis(deltaX, deltaY);\n if (axis !== null) {\n setLockedAxis(axis);\n }\n }, [tracking.isDown, tracking.start, tracking.current, lockedAxis, lockThreshold, reset]);\n\n return {\n lockedAxis,\n isLocked: lockedAxis !== null,\n reset,\n };\n}\n","/**\n * @file Utility functions for gesture detection hooks.\n *\n * Contains shared calculations and helper functions used across\n * gesture-related hooks to avoid code duplication.\n */\nimport type * as React from \"react\";\n\n/**\n * Calculate velocity from displacement and time elapsed.\n *\n * @param displacement - Distance traveled in pixels\n * @param startTime - Start timestamp in milliseconds\n * @param currentTime - Current timestamp in milliseconds\n * @returns Velocity in pixels per millisecond\n */\nexport const calculateVelocity = (\n displacement: number,\n startTime: number,\n currentTime: number,\n): number => {\n const elapsed = currentTime - startTime;\n if (elapsed <= 0) {\n return 0;\n }\n return displacement / elapsed;\n};\n\n/**\n * Determine direction from displacement.\n *\n * @param displacement - Distance from start position\n * @returns -1 for backward (left/up), 0 for no movement, 1 for forward (right/down)\n */\nexport const determineDirection = (displacement: number): -1 | 0 | 1 => {\n if (displacement > 0) {\n return 1;\n }\n if (displacement < 0) {\n return -1;\n }\n return 0;\n};\n\n/**\n * Container props type for gesture handling.\n */\nexport type GestureContainerProps = React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n};\n\n/**\n * Merge multiple container props objects for gesture handling.\n *\n * Combines style objects and chains onPointerDown handlers.\n * Useful when combining multiple gesture hooks that each provide\n * their own container props (e.g., swipe input + native gesture guard).\n *\n * @param propsArray - Array of container props to merge\n * @returns Merged container props with combined styles and handlers\n */\nexport const mergeGestureContainerProps = (\n ...propsArray: GestureContainerProps[]\n): GestureContainerProps => {\n const mergedStyle: React.CSSProperties = {};\n const pointerDownHandlers: Array<\n ((event: React.PointerEvent<HTMLElement>) => void) | undefined\n > = [];\n\n for (const props of propsArray) {\n Object.assign(mergedStyle, props.style);\n if (props.onPointerDown) {\n pointerDownHandlers.push(props.onPointerDown);\n }\n }\n\n const handlePointerDown = (event: React.PointerEvent<HTMLElement>) => {\n for (const handler of pointerDownHandlers) {\n handler?.(event);\n }\n };\n\n return {\n onPointerDown: handlePointerDown,\n style: mergedStyle,\n };\n};\n","/**\n * @file Type definitions for gesture input detection hooks.\n *\n * These types support the separation of concerns:\n * - Operation: what to do (navigate, push, pop)\n * - Input: how to command (swipe, click, keyboard)\n * - Presentation: how to show (animation, transition)\n *\n * This file defines types for the Input layer.\n */\nimport type * as React from \"react\";\n\n/**\n * Axis for gesture detection.\n */\nexport type GestureAxis = \"horizontal\" | \"vertical\";\n\n/**\n * Phase of swipe input lifecycle.\n */\nexport type SwipeInputPhase = \"idle\" | \"tracking\" | \"swiping\" | \"ended\";\n\n/**\n * 2D vector for displacement and velocity.\n */\nexport type Vector2 = {\n x: number;\n y: number;\n};\n\n/**\n * Point with timestamp for velocity calculation.\n */\nexport type TimestampedPoint = {\n x: number;\n y: number;\n timestamp: number;\n};\n\n/**\n * Swipe input state during gesture.\n */\nexport type SwipeInputState = {\n /** Current phase of the swipe input */\n phase: SwipeInputPhase;\n /** Displacement from start position in pixels */\n displacement: Vector2;\n /** Current velocity in pixels per millisecond */\n velocity: Vector2;\n /**\n * Direction of movement as a number.\n * -1 = backward (left/up), 0 = no movement, 1 = forward (right/down)\n */\n direction: -1 | 0 | 1;\n};\n\n/**\n * Thresholds for swipe input recognition.\n */\nexport type SwipeInputThresholds = {\n /** Minimum distance in pixels to trigger swipe. @default 50 */\n distanceThreshold: number;\n /** Minimum velocity in px/ms to trigger swipe. @default 0.3 */\n velocityThreshold: number;\n /** Distance threshold before direction is locked. @default 10 */\n lockThreshold: number;\n};\n\n/**\n * Options for usePointerTracking hook.\n */\nexport type UsePointerTrackingOptions = {\n /** Whether tracking is enabled */\n enabled: boolean;\n /** Restrict to primary pointer only (ignore multitouch). @default true */\n primaryOnly?: boolean;\n};\n\n/**\n * Result from usePointerTracking hook.\n */\nexport type UsePointerTrackingResult = {\n /** Current tracking state */\n state: PointerTrackingState;\n /** Handler to attach to onPointerDown */\n onPointerDown: (event: React.PointerEvent) => void;\n /** Reset tracking state */\n reset: () => void;\n};\n\n/**\n * Internal pointer tracking state.\n */\nexport type PointerTrackingState = {\n /** Whether pointer is currently down */\n isDown: boolean;\n /** Start position and timestamp */\n start: TimestampedPoint | null;\n /** Current position and timestamp */\n current: TimestampedPoint | null;\n /** Active pointer ID */\n pointerId: number | null;\n /** Whether tracking ended via pointercancel (not pointerup) */\n wasCanceled: boolean;\n};\n\n/**\n * Options for useDirectionalLock hook.\n */\nexport type UseDirectionalLockOptions = {\n /** Pointer tracking state from usePointerTracking */\n tracking: PointerTrackingState;\n /** Lock threshold in pixels. @default 10 */\n lockThreshold?: number;\n};\n\n/**\n * Result from useDirectionalLock hook.\n */\nexport type UseDirectionalLockResult = {\n /** Locked axis, or null if not yet locked */\n lockedAxis: GestureAxis | null;\n /** Whether lock has been determined */\n isLocked: boolean;\n /** Reset the lock state */\n reset: () => void;\n};\n\n/**\n * Options for useScrollBoundary hook.\n */\nexport type UseScrollBoundaryOptions = {\n /** Scroll container ref. If null, checks document scroll. */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Axis to monitor */\n axis: GestureAxis;\n /** Tolerance in pixels for \"at boundary\" detection. @default 1 */\n tolerance?: number;\n};\n\n/**\n * Result from useScrollBoundary hook.\n */\nexport type UseScrollBoundaryResult = {\n /** Whether at the start boundary (top/left) */\n atStart: boolean;\n /** Whether at the end boundary (bottom/right) */\n atEnd: boolean;\n /** Current scroll position */\n scrollPosition: number;\n /** Maximum scroll position */\n maxScrollPosition: number;\n};\n\n/**\n * Filter function to determine if a pointer event should start tracking.\n * Receives the pointer event and container element.\n * Return true to allow tracking, false to ignore the event.\n */\nexport type PointerStartFilter = (\n event: React.PointerEvent,\n container: HTMLElement,\n) => boolean;\n\n/**\n * Options for useSwipeInput hook.\n */\nexport type UseSwipeInputOptions = {\n /** Ref to the container element */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Axis to detect swipes on */\n axis: GestureAxis;\n /** Whether swipe detection is enabled. @default true */\n enabled?: boolean;\n /** Swipe thresholds configuration */\n thresholds?: Partial<SwipeInputThresholds>;\n /** Callback when swipe is completed */\n onSwipeEnd?: (state: SwipeInputState) => void;\n /** Whether to enable trackpad two-finger swipe (wheel events). @default true */\n enableWheel?: boolean;\n /**\n * Optional filter to determine if a pointer event should start tracking.\n * If provided, only events that pass this filter will be tracked.\n * Useful for edge-based swipe detection.\n */\n pointerStartFilter?: PointerStartFilter;\n};\n\n/**\n * Result from useSwipeInput hook.\n */\nexport type UseSwipeInputResult = {\n /** Current swipe input state */\n state: SwipeInputState;\n /** Props to spread on the container element */\n containerProps: React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n };\n};\n\n/**\n * Edge for edge-originated gestures.\n */\nexport type GestureEdge = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * Options for useEdgeSwipeInput hook.\n */\nexport type UseEdgeSwipeInputOptions = {\n /** Ref to the container element */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Which edge to detect swipes from */\n edge: GestureEdge;\n /** Width of the edge detection zone in pixels. @default 20 */\n edgeWidth?: number;\n /** Whether edge swipe detection is enabled. @default true */\n enabled?: boolean;\n /** Swipe thresholds configuration */\n thresholds?: Partial<SwipeInputThresholds>;\n /** Callback when edge swipe is completed */\n onSwipeEnd?: (state: SwipeInputState) => void;\n};\n\n/**\n * Result from useEdgeSwipeInput hook.\n */\nexport type UseEdgeSwipeInputResult = {\n /** Whether the current gesture started from the edge */\n isEdgeGesture: boolean;\n /** Current swipe input state */\n state: SwipeInputState;\n /** Props to spread on the container element */\n containerProps: React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n };\n};\n\n/**\n * Options for useNativeGestureGuard hook.\n */\nexport type UseNativeGestureGuardOptions = {\n /** Ref to the container element */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Whether the guard is active */\n active: boolean;\n /** Prevent iOS/macOS edge back gesture. @default true */\n preventEdgeBack?: boolean;\n /** Prevent overscroll bounce effect. @default true */\n preventOverscroll?: boolean;\n /** Width of edge zone where back gesture is prevented. @default 20 */\n edgeWidth?: number;\n};\n\n/**\n * Result from useNativeGestureGuard hook.\n */\nexport type UseNativeGestureGuardResult = {\n /** Props to spread on the container element */\n containerProps: React.HTMLAttributes<HTMLElement> & {\n style: React.CSSProperties;\n };\n};\n\n/**\n * Default swipe input thresholds.\n *\n * - distanceThreshold: 100px is ~27% of a 375px mobile screen\n * - velocityThreshold: 0.5px/ms = 500px/s, a moderate flick speed\n * - lockThreshold: 10px before direction is locked\n */\nexport const DEFAULT_SWIPE_THRESHOLDS: SwipeInputThresholds = {\n distanceThreshold: 100,\n velocityThreshold: 0.5,\n lockThreshold: 10,\n};\n\n/**\n * Default edge width for edge gesture detection.\n */\nexport const DEFAULT_EDGE_WIDTH = 20;\n\n/**\n * Initial idle state for SwipeInputState.\n */\nexport const IDLE_SWIPE_INPUT_STATE: SwipeInputState = {\n phase: \"idle\",\n displacement: { x: 0, y: 0 },\n velocity: { x: 0, y: 0 },\n direction: 0,\n};\n","/**\n * @file Hook for detecting swipe gestures on a container element.\n *\n * Combines pointer tracking and directional locking to detect swipe gestures.\n * Also supports trackpad two-finger swipe via wheel events.\n * Returns swipe state and container props for gesture handling.\n */\nimport * as React from \"react\";\nimport { usePointerTracking } from \"./usePointerTracking.js\";\nimport { useDirectionalLock } from \"./useDirectionalLock.js\";\nimport { useEffectEvent } from \"../useEffectEvent.js\";\nimport { calculateVelocity, determineDirection } from \"./utils.js\";\nimport type {\n GestureAxis,\n SwipeInputState,\n SwipeInputThresholds,\n UseSwipeInputOptions,\n UseSwipeInputResult,\n Vector2,\n} from \"./types.js\";\nimport { DEFAULT_SWIPE_THRESHOLDS, IDLE_SWIPE_INPUT_STATE } from \"./types.js\";\n\n/** Idle timeout to reset wheel state after swipe stops */\nconst WHEEL_RESET_TIMEOUT = 150;\n\n/**\n * Evaluate swipe end and call callback if threshold is met.\n */\nconst evaluateSwipeEnd = (\n displacement: Vector2,\n velocity: Vector2,\n axis: GestureAxis,\n thresholds: SwipeInputThresholds,\n onSwipeEnd: ((state: SwipeInputState) => void) | undefined,\n): void => {\n const axisDisplacement = axis === \"horizontal\" ? displacement.x : displacement.y;\n const axisVelocity = axis === \"horizontal\" ? velocity.x : velocity.y;\n\n const absDisplacement = Math.abs(axisDisplacement);\n const absVelocity = Math.abs(axisVelocity);\n\n const triggered = absDisplacement >= thresholds.distanceThreshold || absVelocity >= thresholds.velocityThreshold;\n\n if (triggered) {\n const direction = determineDirection(axisDisplacement);\n const endState: SwipeInputState = {\n phase: \"ended\",\n displacement,\n velocity,\n direction,\n };\n onSwipeEnd?.(endState);\n }\n};\n\n/**\n * Hook for detecting swipe gestures on a container element.\n */\nexport function useSwipeInput(options: UseSwipeInputOptions): UseSwipeInputResult {\n const {\n containerRef,\n axis,\n enabled = true,\n thresholds: customThresholds,\n onSwipeEnd,\n enableWheel = true,\n pointerStartFilter,\n } = options;\n\n const thresholds: SwipeInputThresholds = {\n ...DEFAULT_SWIPE_THRESHOLDS,\n ...customThresholds,\n };\n\n // Stable callback for swipe end\n const handleSwipeEnd = useEffectEvent(onSwipeEnd);\n\n // ===== Pointer-based swipe tracking =====\n const { state: tracking, onPointerDown: baseOnPointerDown } = usePointerTracking({\n enabled,\n });\n\n // Wrap pointer down handler with optional filter\n const onPointerDown = React.useCallback(\n (event: React.PointerEvent) => {\n if (!enabled) {\n return;\n }\n if (pointerStartFilter) {\n const container = containerRef.current;\n if (!container) {\n return;\n }\n if (!pointerStartFilter(event, container)) {\n return;\n }\n }\n baseOnPointerDown(event);\n },\n [enabled, pointerStartFilter, containerRef, baseOnPointerDown],\n );\n\n const { lockedAxis, isLocked } = useDirectionalLock({\n tracking,\n lockThreshold: thresholds.lockThreshold,\n });\n\n const lastActiveStateRef = React.useRef<SwipeInputState | null>(null);\n\n // Prevent native scroll when swiping on iOS\n const isLockedToSwipeAxisRef = React.useRef(false);\n\n React.useEffect(() => {\n isLockedToSwipeAxisRef.current = isLocked ? lockedAxis === axis : false;\n }, [isLocked, lockedAxis, axis]);\n\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled) {\n return;\n }\n const disableTouchMove = (event: TouchEvent) => {\n event.preventDefault();\n };\n const handleTouchStart = (event: TouchEvent) => {\n if (isLockedToSwipeAxisRef.current) {\n event.preventDefault();\n }\n document.addEventListener(\"touchmove\", disableTouchMove, { passive: false });\n };\n const handleTouchEnd = () => {\n document.removeEventListener(\"touchmove\", disableTouchMove);\n };\n document.addEventListener(\"touchend\", handleTouchEnd);\n document.addEventListener(\"touchcancel\", handleTouchEnd);\n container.addEventListener(\"touchstart\", handleTouchStart, { passive: false });\n\n return () => {\n container.removeEventListener(\"touchstart\", handleTouchStart);\n document.removeEventListener(\"touchend\", handleTouchEnd);\n document.removeEventListener(\"touchcancel\", handleTouchEnd);\n };\n }, [containerRef, enabled]);\n\n // ===== Wheel-based swipe tracking =====\n const [wheelState, setWheelState] = React.useState<SwipeInputState>(IDLE_SWIPE_INPUT_STATE);\n const wheelAccumulatedRef = React.useRef({ x: 0, y: 0 });\n const wheelIdleTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const wheelLockedRef = React.useRef(false);\n const wheelLockedAxisRef = React.useRef<GestureAxis | null>(null);\n\n const resetWheelState = React.useCallback(() => {\n wheelAccumulatedRef.current = { x: 0, y: 0 };\n wheelLockedRef.current = false;\n wheelLockedAxisRef.current = null;\n setWheelState(IDLE_SWIPE_INPUT_STATE);\n }, []);\n\n const endWheelSwipe = React.useCallback(() => {\n const displacement = { ...wheelAccumulatedRef.current };\n evaluateSwipeEnd(displacement, { x: 0, y: 0 }, axis, thresholds, handleSwipeEnd);\n resetWheelState();\n }, [axis, thresholds, handleSwipeEnd, resetWheelState]);\n\n const handleWheel = useEffectEvent((event: WheelEvent) => {\n if (!enabled || !enableWheel || tracking.isDown) {\n return;\n }\n\n const { deltaX, deltaY } = event;\n\n // Direction lock\n if (!wheelLockedRef.current) {\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n if (absX >= thresholds.lockThreshold || absY >= thresholds.lockThreshold) {\n wheelLockedRef.current = true;\n wheelLockedAxisRef.current = absX > absY ? \"horizontal\" : \"vertical\";\n }\n }\n\n // If locked to wrong axis, ignore\n if (wheelLockedRef.current && wheelLockedAxisRef.current !== axis) {\n return;\n }\n\n // Accumulate displacement (negate: wheel delta is scroll direction)\n wheelAccumulatedRef.current.x -= deltaX;\n wheelAccumulatedRef.current.y -= deltaY;\n\n const accumulated = wheelAccumulatedRef.current;\n const axisDisplacement = axis === \"horizontal\" ? accumulated.x : accumulated.y;\n\n setWheelState({\n phase: \"swiping\",\n displacement: { ...accumulated },\n velocity: { x: 0, y: 0 },\n direction: determineDirection(axisDisplacement),\n });\n\n // When wheel stops, treat as \"release\"\n if (wheelIdleTimerRef.current !== null) {\n clearTimeout(wheelIdleTimerRef.current);\n }\n wheelIdleTimerRef.current = setTimeout(endWheelSwipe, WHEEL_RESET_TIMEOUT);\n });\n\n // Set up wheel event listener\n React.useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled || !enableWheel) {\n return;\n }\n\n const listener = (event: WheelEvent) => {\n event.preventDefault();\n handleWheel(event);\n };\n\n container.addEventListener(\"wheel\", listener, { passive: false });\n\n return () => {\n container.removeEventListener(\"wheel\", listener);\n if (wheelIdleTimerRef.current !== null) {\n clearTimeout(wheelIdleTimerRef.current);\n }\n };\n }, [containerRef, enabled, enableWheel, handleWheel]);\n\n React.useEffect(() => {\n return () => {\n if (wheelIdleTimerRef.current !== null) {\n clearTimeout(wheelIdleTimerRef.current);\n }\n };\n }, []);\n\n // ===== Pointer swipe state =====\n const pointerState = React.useMemo<SwipeInputState>(() => {\n if (!tracking.isDown || !tracking.start || !tracking.current) {\n return IDLE_SWIPE_INPUT_STATE;\n }\n\n const deltaX = tracking.current.x - tracking.start.x;\n const deltaY = tracking.current.y - tracking.start.y;\n const displacement = { x: deltaX, y: deltaY };\n\n const velocity = {\n x: calculateVelocity(deltaX, tracking.start.timestamp, tracking.current.timestamp),\n y: calculateVelocity(deltaY, tracking.start.timestamp, tracking.current.timestamp),\n };\n\n if (!isLocked || lockedAxis !== axis) {\n return { phase: \"tracking\", displacement, velocity, direction: 0 };\n }\n\n const axisDisplacement = axis === \"horizontal\" ? deltaX : deltaY;\n return {\n phase: \"swiping\",\n displacement,\n velocity,\n direction: determineDirection(axisDisplacement),\n };\n }, [tracking.isDown, tracking.start, tracking.current, isLocked, lockedAxis, axis]);\n\n React.useEffect(() => {\n if (pointerState.phase !== \"idle\") {\n lastActiveStateRef.current = pointerState;\n }\n }, [pointerState]);\n\n // Handle pointer up (but not cancel)\n React.useEffect(() => {\n if (tracking.isDown) {\n return;\n }\n\n const lastState = lastActiveStateRef.current;\n if (!lastState || (lastState.phase !== \"swiping\" && lastState.phase !== \"tracking\")) {\n return;\n }\n\n lastActiveStateRef.current = null;\n\n // Skip navigation if the gesture was canceled (e.g., browser took over for native scroll)\n if (tracking.wasCanceled) {\n return;\n }\n\n evaluateSwipeEnd(lastState.displacement, lastState.velocity, axis, thresholds, handleSwipeEnd);\n }, [tracking.isDown, tracking.wasCanceled, axis, thresholds, handleSwipeEnd]);\n\n // Merge states\n const state = pointerState.phase !== \"idle\" ? pointerState : wheelState;\n\n const containerProps = React.useMemo(() => {\n const touchAction = axis === \"horizontal\" ? \"pan-y pinch-zoom\" : \"pan-x pinch-zoom\";\n return {\n onPointerDown,\n style: {\n touchAction,\n userSelect: \"none\" as const,\n WebkitUserSelect: \"none\" as const,\n },\n };\n }, [axis, onPointerDown]);\n\n return { state, containerProps };\n}\n","/**\n * @file Hook for detecting swipe gestures that originate from the edge of a container.\n *\n * Edge swipes are commonly used for \"swipe back\" navigation in mobile apps.\n * This hook detects swipes that start within a configurable edge zone.\n *\n * Built on top of useSwipeInput with edge zone filtering.\n */\nimport * as React from \"react\";\nimport { useSwipeInput } from \"./useSwipeInput.js\";\nimport type {\n GestureAxis,\n GestureEdge,\n SwipeInputState,\n UseEdgeSwipeInputOptions,\n UseEdgeSwipeInputResult,\n} from \"./types.js\";\nimport { DEFAULT_EDGE_WIDTH, DEFAULT_SWIPE_THRESHOLDS, IDLE_SWIPE_INPUT_STATE } from \"./types.js\";\n\n/**\n * Get the axis associated with an edge.\n */\nconst getAxisForEdge = (edge: GestureEdge): GestureAxis => {\n if (edge === \"left\" || edge === \"right\") {\n return \"horizontal\";\n }\n return \"vertical\";\n};\n\n/**\n * Check if a point is within the edge zone of a container.\n */\nconst isInEdgeZone = (\n clientX: number,\n clientY: number,\n container: HTMLElement,\n edge: GestureEdge,\n edgeWidth: number,\n): boolean => {\n const rect = container.getBoundingClientRect();\n\n switch (edge) {\n case \"left\":\n return clientX >= rect.left && clientX <= rect.left + edgeWidth;\n case \"right\":\n return clientX >= rect.right - edgeWidth && clientX <= rect.right;\n case \"top\":\n return clientY >= rect.top && clientY <= rect.top + edgeWidth;\n case \"bottom\":\n return clientY >= rect.bottom - edgeWidth && clientY <= rect.bottom;\n }\n};\n\n/**\n * Hook for detecting swipe gestures that originate from the edge of a container.\n *\n * This is useful for implementing \"swipe back\" navigation patterns where\n * the user must start their swipe from the edge of the screen.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { isEdgeGesture, state, containerProps } = useEdgeSwipeInput({\n * containerRef,\n * edge: \"left\",\n * edgeWidth: 20,\n * onSwipeEnd: (state) => {\n * if (state.direction === 1) goBack();\n * },\n * });\n *\n * return <div ref={containerRef} {...containerProps}>{children}</div>;\n * ```\n */\nexport function useEdgeSwipeInput(options: UseEdgeSwipeInputOptions): UseEdgeSwipeInputResult {\n const {\n containerRef,\n edge,\n edgeWidth = DEFAULT_EDGE_WIDTH,\n enabled = true,\n thresholds: customThresholds,\n onSwipeEnd,\n } = options;\n\n const thresholds = {\n ...DEFAULT_SWIPE_THRESHOLDS,\n ...customThresholds,\n };\n\n const axis = getAxisForEdge(edge);\n\n // Track whether the current gesture started from the edge\n const [isEdgeGesture, setIsEdgeGesture] = React.useState(false);\n\n // Create edge zone filter for pointer events\n const pointerStartFilter = React.useCallback(\n (event: React.PointerEvent, container: HTMLElement): boolean => {\n const inEdge = isInEdgeZone(event.clientX, event.clientY, container, edge, edgeWidth);\n setIsEdgeGesture(inEdge);\n return inEdge;\n },\n [edge, edgeWidth],\n );\n\n // Use base swipe input with edge filtering\n const { state, containerProps } = useSwipeInput({\n containerRef,\n axis,\n enabled,\n thresholds,\n onSwipeEnd,\n enableWheel: false, // Edge swipe doesn't use wheel events\n pointerStartFilter,\n });\n\n // Reset edge gesture state when swipe ends\n React.useEffect(() => {\n if (state.phase === \"idle\") {\n setIsEdgeGesture(false);\n }\n }, [state.phase]);\n\n // If not an edge gesture, return idle state\n const effectiveState: SwipeInputState = isEdgeGesture ? state : IDLE_SWIPE_INPUT_STATE;\n\n return {\n isEdgeGesture,\n state: effectiveState,\n containerProps,\n };\n}\n","/**\n * @file Hook for preventing conflicts with native OS gestures.\n *\n * This hook helps prevent conflicts with:\n * - iOS/macOS edge swipe back navigation\n * - Overscroll bounce effects\n *\n * It applies appropriate CSS properties and event handlers to the container.\n */\nimport * as React from \"react\";\nimport type {\n UseNativeGestureGuardOptions,\n UseNativeGestureGuardResult,\n} from \"./types.js\";\nimport { DEFAULT_EDGE_WIDTH } from \"./types.js\";\n\n/**\n * Check if a pointer event is within the left edge zone.\n */\nconst isInLeftEdge = (clientX: number, container: HTMLElement, edgeWidth: number): boolean => {\n const rect = container.getBoundingClientRect();\n return clientX >= rect.left && clientX <= rect.left + edgeWidth;\n};\n\n/**\n * Hook for preventing conflicts with native OS gestures.\n *\n * When active, this hook:\n * - Prevents iOS/macOS edge back gesture by capturing pointerdown events in the edge zone\n * - Prevents overscroll bounce effect using CSS overscroll-behavior\n * - Dynamically applies overscroll-behavior: none to html element during gesture\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { containerProps } = useNativeGestureGuard({\n * containerRef,\n * active: isSwipeActive,\n * preventEdgeBack: true,\n * preventOverscroll: true,\n * });\n *\n * return <div ref={containerRef} {...containerProps}>{children}</div>;\n * ```\n */\nexport function useNativeGestureGuard(options: UseNativeGestureGuardOptions): UseNativeGestureGuardResult {\n const {\n containerRef,\n active,\n preventEdgeBack = true,\n preventOverscroll = true,\n edgeWidth = DEFAULT_EDGE_WIDTH,\n } = options;\n\n // Track previous html overscroll-behavior value for restoration\n const previousHtmlOverscrollRef = React.useRef<string | null>(null);\n\n // Apply overscroll-behavior to html synchronously (called from onPointerDown)\n const applyHtmlOverscroll = React.useCallback(() => {\n if (!preventOverscroll) return;\n\n const html = document.documentElement;\n if (previousHtmlOverscrollRef.current === null) {\n previousHtmlOverscrollRef.current = html.style.overscrollBehavior;\n }\n html.style.overscrollBehavior = \"none\";\n }, [preventOverscroll]);\n\n // Remove overscroll-behavior from html when gesture ends\n React.useEffect(() => {\n if (active || !preventOverscroll) {\n return;\n }\n\n // Cleanup: restore previous value when deactivated\n if (previousHtmlOverscrollRef.current !== null) {\n document.documentElement.style.overscrollBehavior = previousHtmlOverscrollRef.current;\n previousHtmlOverscrollRef.current = null;\n }\n }, [active, preventOverscroll]);\n\n // Cleanup on unmount\n React.useEffect(() => {\n return () => {\n if (previousHtmlOverscrollRef.current !== null) {\n document.documentElement.style.overscrollBehavior = previousHtmlOverscrollRef.current;\n previousHtmlOverscrollRef.current = null;\n }\n };\n }, []);\n\n // Pointer down handler that prevents edge back gesture\n // Note: This must run on EVERY pointerdown in edge zone, not just when active,\n // because browser gesture recognition starts immediately on first touch.\n const onPointerDown = React.useCallback((event: React.PointerEvent) => {\n if (!preventEdgeBack) {\n return;\n }\n\n const container = containerRef.current;\n if (!container) {\n return;\n }\n\n // Prevent for touch events in the left edge zone\n // This must happen immediately, before we know if it's \"our\" gesture\n if (event.pointerType === \"touch\" && isInLeftEdge(event.clientX, container, edgeWidth)) {\n // Apply html overscroll-behavior synchronously before browser can recognize gesture\n applyHtmlOverscroll();\n // Prevent the browser from handling this as a back gesture\n event.preventDefault();\n }\n }, [preventEdgeBack, containerRef, edgeWidth, applyHtmlOverscroll]);\n\n // Build container props\n // Styles are applied immediately (not waiting for active) to prevent browser gestures\n const containerProps = React.useMemo(() => {\n const style: React.CSSProperties = {\n // Always apply to prevent browser navigation gestures\n overscrollBehavior: preventOverscroll ? \"contain\" : undefined,\n WebkitOverflowScrolling: \"touch\",\n };\n\n return {\n onPointerDown: preventEdgeBack ? onPointerDown : undefined,\n style,\n };\n }, [preventOverscroll, preventEdgeBack, onPointerDown]);\n\n return {\n containerProps,\n };\n}\n","/**\n * @file Hook for binding edge swipe input to Stack navigation.\n *\n * This hook connects edge swipe gesture detection to Stack's navigation API,\n * enabling iOS-style \"swipe to go back\" navigation.\n */\nimport * as React from \"react\";\nimport { useEdgeSwipeInput } from \"../../hooks/gesture/useEdgeSwipeInput.js\";\nimport { useNativeGestureGuard } from \"../../hooks/gesture/useNativeGestureGuard.js\";\nimport { mergeGestureContainerProps } from \"../../hooks/gesture/utils.js\";\nimport { IDLE_SWIPE_INPUT_STATE } from \"../../hooks/gesture/types.js\";\nimport type { SwipeInputState } from \"../../hooks/gesture/types.js\";\nimport type { UseStackSwipeInputOptions, UseStackSwipeInputResult } from \"./types.js\";\n\n/**\n * Hook for binding edge swipe input to Stack navigation.\n *\n * Detects swipe gestures from the specified edge and triggers navigation:\n * - Left edge swipe → go(-1) (go back)\n * - Right edge swipe → reveals parent or custom action\n *\n * During a swipe, provides progress for animation.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const navigation = useStackNavigation({ panels, displayMode: 'overlay' });\n * const { isEdgeSwiping, progress, containerProps } = useStackSwipeInput({\n * containerRef,\n * navigation,\n * });\n *\n * return (\n * <div ref={containerRef} {...containerProps}>\n * <navigation.Outlet />\n * </div>\n * );\n * ```\n */\nexport function useStackSwipeInput(options: UseStackSwipeInputOptions): UseStackSwipeInputResult {\n const {\n containerRef,\n navigation,\n edge = \"left\",\n edgeWidth = 20,\n enabled = true,\n } = options;\n\n // Track swipe state for progress calculation\n const [swipeState, setSwipeState] = React.useState<SwipeInputState | null>(null);\n\n // Handle swipe completion - navigate back\n const handleSwipeEnd = React.useCallback(\n (state: SwipeInputState) => {\n setSwipeState(null);\n\n // Left edge swipe going right = go back\n if (edge === \"left\" && state.direction === 1) {\n if (navigation.canGo(-1)) {\n navigation.go(-1);\n }\n }\n // Right edge swipe going left = custom action (optional)\n // Could be used for forward navigation if the app supports it\n },\n [edge, navigation],\n );\n\n // Use edge swipe detection\n const { isEdgeGesture, state: inputState, containerProps: swipeProps } = useEdgeSwipeInput({\n containerRef,\n edge,\n edgeWidth,\n enabled: enabled && navigation.canGo(-1), // Only enable if can go back\n onSwipeEnd: handleSwipeEnd,\n });\n\n // Update swipe state for progress tracking\n React.useEffect(() => {\n if (isEdgeGesture && (inputState.phase === \"swiping\" || inputState.phase === \"tracking\")) {\n setSwipeState(inputState);\n } else if (inputState.phase === \"idle\") {\n setSwipeState(null);\n }\n }, [isEdgeGesture, inputState]);\n\n // Use native gesture guard during swipe\n const { containerProps: guardProps } = useNativeGestureGuard({\n containerRef,\n active: isEdgeGesture,\n preventEdgeBack: true,\n preventOverscroll: true,\n edgeWidth,\n });\n\n // Calculate swipe progress (0-1)\n const progress = React.useMemo(() => {\n if (!swipeState || !containerRef.current) {\n return 0;\n }\n\n const containerWidth = containerRef.current.clientWidth;\n if (containerWidth === 0) {\n return 0;\n }\n\n // Use X displacement for horizontal swipe\n const displacement = swipeState.displacement.x;\n\n // Only count rightward movement for left edge swipe\n if (edge === \"left\" && displacement <= 0) {\n return 0;\n }\n\n // Only count leftward movement for right edge swipe\n if (edge === \"right\" && displacement >= 0) {\n return 0;\n }\n\n const absDisplacement = Math.abs(displacement);\n return Math.min(absDisplacement / containerWidth, 1);\n }, [swipeState, containerRef, edge]);\n\n // Merge container props\n const containerProps = React.useMemo(\n () => mergeGestureContainerProps(swipeProps, guardProps),\n [swipeProps, guardProps],\n );\n\n // Effective input state: only return actual state if it's an edge gesture\n const effectiveInputState: SwipeInputState = isEdgeGesture ? inputState : IDLE_SWIPE_INPUT_STATE;\n\n return {\n isEdgeSwiping: isEdgeGesture,\n progress,\n inputState: effectiveInputState,\n containerProps,\n };\n}\n","/**\n * @file Generic requestAnimationFrame-based animation hook.\n *\n * Provides a reusable animation loop with easing support.\n * This is the foundation for more specific animation hooks.\n */\nimport * as React from \"react\";\n\n/**\n * Easing function type.\n * Takes a progress value (0-1) and returns an eased value (0-1).\n */\nexport type EasingFunction = (t: number) => number;\n\n/**\n * Built-in easing functions.\n */\nexport const easings = {\n /** Linear (no easing) */\n linear: (t: number): number => t,\n\n /** Ease out cubic */\n easeOutCubic: (t: number): number => 1 - Math.pow(1 - t, 3),\n\n /** Ease out expo (similar to cubic-bezier(0.22, 1, 0.36, 1)) */\n easeOutExpo: (t: number): number => {\n if (t === 1) {\n return 1;\n }\n return 1 - Math.pow(2, -10 * t);\n },\n\n /** Ease out quart */\n easeOutQuart: (t: number): number => 1 - Math.pow(1 - t, 4),\n\n /** Ease in out cubic */\n easeInOutCubic: (t: number): number => {\n if (t < 0.5) {\n return 4 * t * t * t;\n }\n return 1 - Math.pow(-2 * t + 2, 3) / 2;\n },\n} as const;\n\n/**\n * Animation state passed to callbacks.\n */\nexport type AnimationState = {\n /** Raw progress (0-1) */\n progress: number;\n /** Eased progress (0-1) */\n easedProgress: number;\n /** Elapsed time in ms */\n elapsed: number;\n /** Whether animation is complete */\n isComplete: boolean;\n};\n\n/**\n * Options for useAnimationFrame hook.\n */\nexport type UseAnimationFrameOptions = {\n /** Duration of animation in milliseconds */\n duration?: number;\n /** Easing function for the animation */\n easing?: EasingFunction;\n /** Callback called every frame with animation state */\n onFrame?: (state: AnimationState) => void;\n /** Callback when animation completes */\n onComplete?: () => void;\n};\n\n/**\n * Result from useAnimationFrame hook.\n */\nexport type UseAnimationFrameResult = {\n /** Whether animation is currently running */\n isAnimating: boolean;\n /** Start the animation */\n start: () => void;\n /** Cancel the animation */\n cancel: () => void;\n};\n\n/** Default animation duration in ms */\nconst DEFAULT_DURATION = 300;\n\n/**\n * Generic requestAnimationFrame-based animation hook.\n *\n * Provides a reusable animation loop with progress calculation and easing.\n * Use this as a building block for specific animation behaviors.\n *\n * @example\n * ```tsx\n * const { start, isAnimating } = useAnimationFrame({\n * duration: 300,\n * easing: easings.easeOutExpo,\n * onFrame: ({ easedProgress }) => {\n * const value = fromValue + (toValue - fromValue) * easedProgress;\n * element.style.transform = `translateX(${value}px)`;\n * },\n * onComplete: () => console.log('Done!'),\n * });\n *\n * // Start animation\n * start();\n * ```\n */\nexport function useAnimationFrame(options: UseAnimationFrameOptions): UseAnimationFrameResult {\n const {\n duration = DEFAULT_DURATION,\n easing = easings.easeOutExpo,\n onFrame,\n onComplete,\n } = options;\n\n const [isAnimating, setIsAnimating] = React.useState(false);\n const rafIdRef = React.useRef<number | null>(null);\n const startTimeRef = React.useRef<number | null>(null);\n\n // Use refs for callbacks to avoid stale closures\n const onFrameRef = React.useRef(onFrame);\n const onCompleteRef = React.useRef(onComplete);\n React.useEffect(() => {\n onFrameRef.current = onFrame;\n onCompleteRef.current = onComplete;\n }, [onFrame, onComplete]);\n\n const cancel = React.useCallback(() => {\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n rafIdRef.current = null;\n }\n startTimeRef.current = null;\n setIsAnimating(false);\n }, []);\n\n const start = React.useCallback(() => {\n // Cancel any existing animation\n cancel();\n\n setIsAnimating(true);\n startTimeRef.current = null;\n\n const step = (timestamp: number) => {\n if (startTimeRef.current === null) {\n startTimeRef.current = timestamp;\n }\n\n const elapsed = timestamp - startTimeRef.current;\n const progress = Math.min(elapsed / duration, 1);\n const easedProgress = easing(progress);\n const isComplete = progress >= 1;\n\n const state: AnimationState = {\n progress,\n easedProgress,\n elapsed,\n isComplete,\n };\n\n onFrameRef.current?.(state);\n\n if (!isComplete) {\n rafIdRef.current = requestAnimationFrame(step);\n } else {\n // Animation complete\n rafIdRef.current = null;\n startTimeRef.current = null;\n setIsAnimating(false);\n onCompleteRef.current?.();\n }\n };\n\n rafIdRef.current = requestAnimationFrame(step);\n }, [duration, easing, cancel]);\n\n // Cleanup on unmount\n React.useEffect(() => {\n return () => {\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n }\n };\n }, []);\n\n return {\n isAnimating,\n start,\n cancel,\n };\n}\n\n/**\n * Interpolate between two values using eased progress.\n */\nexport function interpolate(from: number, to: number, easedProgress: number): number {\n return from + (to - from) * easedProgress;\n}\n","/**\n * @file Shared hook for DOM-based swipe content transform.\n *\n * This hook provides immediate DOM manipulation for swipe gestures,\n * with smooth snap-back animation when the swipe ends.\n *\n * Used by both Pivot and Stack for consistent swipe behavior.\n */\nimport * as React from \"react\";\nimport { useAnimationFrame, interpolate, easings } from \"./useAnimationFrame.js\";\nimport type { GestureAxis } from \"./gesture/types.js\";\n\nconst DEFAULT_ANIMATION_DURATION = 300;\n\n/**\n * Options for useSwipeContentTransform hook.\n */\nexport type UseSwipeContentTransformOptions = {\n /** Ref to the element to transform */\n elementRef: React.RefObject<HTMLElement | null>;\n /** Target position in pixels (where element should be at rest) */\n targetPx: number;\n /** Current swipe displacement in pixels */\n displacement: number;\n /** Whether swipe gesture is active */\n isSwiping: boolean;\n /** Axis of transformation */\n axis?: GestureAxis;\n /** Duration of snap animation in ms */\n animationDuration?: number;\n /** Container size in pixels (used for snap on resize) */\n containerSize?: number;\n /**\n * Whether to animate when targetPx changes (without swipe).\n * Use this for tab bar animations triggered by click/button.\n * @default false\n */\n animateOnTargetChange?: boolean;\n /**\n * Initial position in pixels when first mounted.\n * If different from targetPx, will animate from initialPx to targetPx.\n * Use this for push animations where new panel comes from off-screen.\n */\n initialPx?: number;\n};\n\n/**\n * Animation direction information.\n */\nexport type AnimationDirection = {\n /** Source position in pixels */\n from: number;\n /** Target position in pixels */\n to: number;\n};\n\n/**\n * Result from useSwipeContentTransform hook.\n */\nexport type UseSwipeContentTransformResult = {\n /** Whether snap animation is currently running */\n isAnimating: boolean;\n /** Current position in pixels (for visibility calculations) */\n currentPx: number;\n /** Animation direction info, or null if not animating */\n animationDirection: AnimationDirection | null;\n};\n\n/**\n * Get CSS transform function name for axis.\n */\nconst getTransformFn = (axis: GestureAxis): \"translateX\" | \"translateY\" => {\n return axis === \"horizontal\" ? \"translateX\" : \"translateY\";\n};\n\n/**\n * Hook for DOM-based swipe content transform.\n *\n * During swipe: immediately updates element.style.transform to follow finger.\n * After swipe: animates from current position to target position.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { isAnimating, currentPx } = useSwipeContentTransform({\n * elementRef: containerRef,\n * targetPx: 0,\n * displacement: inputState.displacement.x,\n * isSwiping: inputState.phase === \"swiping\",\n * });\n * ```\n */\nexport function useSwipeContentTransform(\n options: UseSwipeContentTransformOptions,\n): UseSwipeContentTransformResult {\n const {\n elementRef,\n targetPx,\n displacement,\n isSwiping,\n axis = \"horizontal\",\n animationDuration = DEFAULT_ANIMATION_DURATION,\n containerSize,\n animateOnTargetChange = false,\n initialPx,\n } = options;\n\n // Use initialPx if provided, otherwise use targetPx\n const effectiveInitialPx = initialPx ?? targetPx;\n const currentPxRef = React.useRef<number>(effectiveInitialPx);\n const animRef = React.useRef<{ from: number; to: number } | null>(null);\n const prevTargetPxRef = React.useRef<number>(targetPx);\n const prevContainerSizeRef = React.useRef<number | undefined>(containerSize);\n const pendingAnimationRef = React.useRef<{ from: number; to: number } | null>(null);\n const isFirstMountRef = React.useRef<boolean>(true);\n\n // Schedule animation on first mount if initialPx differs from targetPx\n if (isFirstMountRef.current && initialPx !== undefined && initialPx !== targetPx) {\n pendingAnimationRef.current = { from: initialPx, to: targetPx };\n isFirstMountRef.current = false;\n } else if (isFirstMountRef.current) {\n isFirstMountRef.current = false;\n }\n\n // Handle target changes when not swiping\n if (targetPx !== prevTargetPxRef.current && !isSwiping && animRef.current === null) {\n if (animateOnTargetChange) {\n // Schedule animation from current position to new target\n const distance = Math.abs(currentPxRef.current - targetPx);\n if (distance > 1) {\n pendingAnimationRef.current = { from: currentPxRef.current, to: targetPx };\n } else {\n currentPxRef.current = targetPx;\n }\n } else {\n // Snap immediately (default behavior for resize, etc.)\n currentPxRef.current = targetPx;\n }\n prevTargetPxRef.current = targetPx;\n }\n\n // Snap when container size changes (resize)\n if (containerSize !== undefined && containerSize !== prevContainerSizeRef.current && containerSize > 0) {\n currentPxRef.current = targetPx;\n prevContainerSizeRef.current = containerSize;\n }\n\n // Animation frame handler\n const handleFrame = React.useCallback(\n ({ easedProgress }: { easedProgress: number }) => {\n const element = elementRef.current;\n const anim = animRef.current;\n if (!element || !anim) {\n return;\n }\n const value = interpolate(anim.from, anim.to, easedProgress);\n currentPxRef.current = value;\n element.style.transform = `${getTransformFn(axis)}(${value}px)`;\n },\n [axis, elementRef],\n );\n\n const handleComplete = React.useCallback(() => {\n animRef.current = null;\n currentPxRef.current = targetPx;\n prevTargetPxRef.current = targetPx;\n }, [targetPx]);\n\n const { isAnimating, start, cancel } = useAnimationFrame({\n duration: animationDuration,\n easing: easings.easeOutExpo,\n onFrame: handleFrame,\n onComplete: handleComplete,\n });\n\n // When swipe ends or target changes with animateOnTargetChange, animate to target\n React.useLayoutEffect(() => {\n if (isSwiping) {\n cancel();\n animRef.current = null;\n pendingAnimationRef.current = null;\n return;\n }\n\n // Check for pending animation (from target change or initial mount)\n if (pendingAnimationRef.current) {\n const pending = pendingAnimationRef.current;\n animRef.current = pending;\n pendingAnimationRef.current = null;\n // Set initial position before animation starts\n const element = elementRef.current;\n if (element) {\n element.style.transform = `${getTransformFn(axis)}(${pending.from}px)`;\n }\n start();\n return;\n }\n\n const currentPx = currentPxRef.current;\n const distance = Math.abs(currentPx - targetPx);\n\n if (distance > 1) {\n // Need to animate from current to target\n animRef.current = { from: currentPx, to: targetPx };\n start();\n } else {\n // Close enough, snap directly\n currentPxRef.current = targetPx;\n prevTargetPxRef.current = targetPx;\n }\n }, [isSwiping, targetPx, start, cancel]);\n\n // Direct DOM update during swipe\n React.useLayoutEffect(() => {\n const element = elementRef.current;\n if (!element) {\n return;\n }\n\n // Skip if animation is running, about to start, or pending\n if (isAnimating || animRef.current !== null || pendingAnimationRef.current !== null) {\n return;\n }\n\n const displayPx = targetPx + displacement;\n currentPxRef.current = displayPx;\n element.style.transform = `${getTransformFn(axis)}(${displayPx}px)`;\n }, [targetPx, displacement, axis, isAnimating, elementRef]);\n\n return {\n isAnimating,\n currentPx: currentPxRef.current,\n animationDirection: animRef.current,\n };\n}\n","/**\n * @file Pure functions for computing Stack panel transforms during swipe.\n *\n * These functions calculate positions for iOS-style \"swipe to go back\" behavior\n * where the active panel follows the finger and the behind panel reveals from -30%.\n */\n\n/**\n * Default offset for behind panel (-30% of container width).\n */\nexport const DEFAULT_BEHIND_OFFSET = -0.3;\n\n/**\n * Compute the target position for the active panel.\n *\n * Active panel starts at 0 and moves right as the user swipes.\n *\n * @param displacement - Current swipe displacement in pixels\n * @returns Target position in pixels\n */\nexport function computeActiveTargetPx(displacement: number): number {\n // Active panel follows the finger directly\n // Only move right (positive displacement) for left-edge swipe\n return Math.max(0, displacement);\n}\n\n/**\n * Compute the target position for the behind panel.\n *\n * Behind panel starts at -30% and moves to 0% as swipe progresses.\n * Uses parallax effect: moves slower than the active panel.\n *\n * @param displacement - Current swipe displacement in pixels\n * @param containerSize - Container width/height in pixels\n * @param behindOffset - Starting offset ratio (default -0.3 for -30%)\n * @returns Target position in pixels\n */\nexport function computeBehindTargetPx(\n displacement: number,\n containerSize: number,\n behindOffset: number = DEFAULT_BEHIND_OFFSET,\n): number {\n if (containerSize <= 0) {\n return 0;\n }\n\n // Only respond to positive displacement (swipe right)\n const clampedDisplacement = Math.max(0, displacement);\n\n // Calculate progress (0 to 1)\n const progress = Math.min(clampedDisplacement / containerSize, 1);\n\n // Behind panel starts at behindOffset * containerSize and moves to 0\n // Parallax: moves |behindOffset| * progress * containerSize\n const basePosition = behindOffset * containerSize;\n const parallaxOffset = Math.abs(behindOffset) * progress * containerSize;\n\n return basePosition + parallaxOffset;\n}\n\n/**\n * Compute swipe progress as a ratio (0 to 1).\n *\n * @param displacement - Current swipe displacement in pixels\n * @param containerSize - Container width/height in pixels\n * @returns Progress ratio from 0 to 1\n */\nexport function computeSwipeProgress(displacement: number, containerSize: number): number {\n if (containerSize <= 0) {\n return 0;\n }\n\n const clampedDisplacement = Math.max(0, displacement);\n return Math.min(clampedDisplacement / containerSize, 1);\n}\n\n/**\n * Input for computing swipe visibility.\n */\nexport type ComputeSwipeVisibilityInput = {\n /** Panel depth in the stack */\n depth: number;\n /** Current navigation depth (active panel) */\n navigationDepth: number;\n /** Whether this panel is currently active */\n isActive: boolean;\n /** Whether swipe gesture is active */\n isSwiping: boolean;\n /** Whether snap-back animation is running */\n isAnimating: boolean;\n};\n\n/**\n * Compute whether a panel should be visible during swipe.\n *\n * During swipe:\n * - Active panel is always visible\n * - Behind panel (depth = navigationDepth - 1) is visible when swiping/animating\n *\n * @returns true if panel should be visible\n */\nexport function computeSwipeVisibility(input: ComputeSwipeVisibilityInput): boolean {\n const { depth, navigationDepth, isActive, isSwiping, isAnimating } = input;\n\n // Active panel is always visible\n if (isActive) {\n return true;\n }\n\n // Behind panel (one level back) is visible during swipe or animation\n const isBehindPanel = depth === navigationDepth - 1;\n if (isBehindPanel) {\n if (isSwiping) {\n return true;\n }\n if (isAnimating) {\n return true;\n }\n }\n\n // Other panels are hidden during swipe\n return false;\n}\n\n/**\n * Determine the role of a panel during swipe gesture.\n */\nexport type SwipePanelRole = \"active\" | \"behind\" | \"hidden\";\n\n/**\n * Determine the role of a panel during swipe.\n *\n * @param depth - Panel depth in the stack\n * @param navigationDepth - Current navigation depth (active panel)\n * @returns Panel role for swipe handling\n */\nexport function determineSwipePanelRole(depth: number, navigationDepth: number): SwipePanelRole {\n if (depth === navigationDepth) {\n return \"active\";\n }\n if (depth === navigationDepth - 1) {\n return \"behind\";\n }\n return \"hidden\";\n}\n","/**\n * @file SwipeStackContent component for Stack panels with direct DOM manipulation.\n *\n * Provides iOS-style swipe-to-go-back behavior:\n * - Active panel follows the finger directly\n * - Behind panel reveals from -30% with parallax effect\n *\n * Uses useSwipeContentTransform for immediate DOM updates.\n */\nimport * as React from \"react\";\nimport { useSwipeContentTransform } from \"../../hooks/useSwipeContentTransform.js\";\nimport type { SwipeInputState, GestureAxis } from \"../../hooks/gesture/types.js\";\nimport type { StackDisplayMode } from \"./types.js\";\nimport {\n computeActiveTargetPx,\n computeBehindTargetPx,\n computeSwipeVisibility,\n determineSwipePanelRole,\n DEFAULT_BEHIND_OFFSET,\n} from \"./computeSwipeStackTransform.js\";\n\nconst DEFAULT_ANIMATION_DURATION = 300;\n\n/**\n * Scale factor per depth level for \"stack\" display mode.\n * Each level behind reduces scale by this amount.\n */\nconst STACK_SCALE_FACTOR = 0.05;\n\n/**\n * Offset percentage per depth level for \"stack\" display mode.\n */\nconst STACK_OFFSET_PERCENT = 5;\n\n/**\n * Maximum dimming opacity for behind panels in iOS-style navigation.\n */\nconst MAX_DIM_OPACITY = 0.1;\n\n/**\n * Props for SwipeStackContent component.\n */\nexport type SwipeStackContentProps = {\n /** Panel ID */\n id: string;\n /** Panel depth in the stack */\n depth: number;\n /** Current navigation depth (active panel) */\n navigationDepth: number;\n /** Whether this panel is currently active */\n isActive: boolean;\n /** Swipe input state from useStackSwipeInput */\n inputState: SwipeInputState;\n /** Container size in pixels (width for horizontal, height for vertical) */\n containerSize: number;\n /** Gesture axis. @default \"horizontal\" */\n axis?: GestureAxis;\n /** Behind panel offset ratio. @default -0.3 */\n behindOffset?: number;\n /** Animation duration in ms. @default 300 */\n animationDuration?: number;\n /**\n * Whether to animate when first mounted as active.\n * Set to true for push navigation animations.\n * @default false\n */\n animateOnMount?: boolean;\n /**\n * Whether to show iOS-style edge shadow on active panel.\n * @default true\n */\n showShadow?: boolean;\n /**\n * Display mode for visual styling.\n * - \"overlay\": panels overlay, no scale (iOS style)\n * - \"slide\": panels slide with parallax\n * - \"stack\": panels scale down and offset (stacked cards style)\n * @default \"overlay\"\n */\n displayMode?: StackDisplayMode;\n /**\n * Whether to show dimming overlay on behind panels.\n * Creates iOS-style darkening effect that fades during swipe.\n * @default true\n */\n showDimming?: boolean;\n /** Content to render */\n children: React.ReactNode;\n};\n\nconst BASE_STYLE: React.CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/**\n * Get displacement from input state for the given axis.\n */\nconst getAxisDisplacement = (inputState: SwipeInputState, axis: GestureAxis): number => {\n if (inputState.phase === \"idle\") {\n return 0;\n }\n return axis === \"horizontal\" ? inputState.displacement.x : inputState.displacement.y;\n};\n\n/**\n * SwipeStackContent renders a single stack panel with swipe gesture support.\n *\n * Key behaviors:\n * - Active panel: follows finger directly (translateX = displacement)\n * - Behind panel: reveals from -30% with parallax (slower movement)\n * - Hidden panels: not rendered during swipe\n *\n * @example\n * ```tsx\n * <SwipeStackContent\n * id=\"detail\"\n * depth={1}\n * navigationDepth={1}\n * isActive={true}\n * inputState={swipeInput.inputState}\n * containerSize={containerWidth}\n * >\n * <DetailPanel />\n * </SwipeStackContent>\n * ```\n */\n// iOS-style left edge shadow for active panels\nconst ACTIVE_PANEL_SHADOW = \"-5px 0 15px rgba(0, 0, 0, 0.1)\";\n\nexport const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(\n ({\n id,\n depth,\n navigationDepth,\n isActive,\n inputState,\n containerSize,\n axis = \"horizontal\",\n behindOffset = DEFAULT_BEHIND_OFFSET,\n animationDuration = DEFAULT_ANIMATION_DURATION,\n animateOnMount = false,\n showShadow = true,\n displayMode = \"overlay\",\n showDimming = true,\n children,\n }) => {\n const elementRef = React.useRef<HTMLDivElement>(null);\n const isFirstMountRef = React.useRef<boolean>(true);\n\n const displacement = getAxisDisplacement(inputState, axis);\n const isSwiping = inputState.phase === \"swiping\" || inputState.phase === \"tracking\";\n\n // Determine panel role\n const role = determineSwipePanelRole(depth, navigationDepth);\n\n // Track first mount for push animation\n const isFirstMount = isFirstMountRef.current;\n if (isFirstMountRef.current) {\n isFirstMountRef.current = false;\n }\n\n // Compute target position based on role\n const targetPx = React.useMemo(() => {\n switch (role) {\n case \"active\":\n // Active panel rests at 0\n return 0;\n case \"behind\":\n // Behind panel rests at offset position\n return behindOffset * containerSize;\n case \"hidden\":\n // Hidden panels are off-screen\n return containerSize;\n }\n }, [role, behindOffset, containerSize]);\n\n // Compute displacement for this panel\n const panelDisplacement = React.useMemo(() => {\n if (displacement <= 0) {\n return 0;\n }\n\n switch (role) {\n case \"active\":\n // Active panel follows finger directly\n return computeActiveTargetPx(displacement);\n case \"behind\": {\n // Behind panel uses parallax - compute offset from base position\n const currentPos = computeBehindTargetPx(displacement, containerSize, behindOffset);\n const basePos = behindOffset * containerSize;\n return currentPos - basePos;\n }\n case \"hidden\":\n return 0;\n }\n }, [role, displacement, containerSize, behindOffset]);\n\n // Compute initial position for push animation\n // When animateOnMount is true and panel is first mounted as \"active\",\n // it should animate in from off-screen\n // Root panel (depth=0) should not animate on mount\n const initialPx = React.useMemo(() => {\n if (!isFirstMount || !animateOnMount) {\n return undefined; // Only relevant on first mount with animateOnMount\n }\n if (role === \"active\" && depth > 0) {\n // New active panel (not root): start from off-screen right\n return containerSize;\n }\n // Root panel or other roles: start at their natural position\n return undefined;\n }, [isFirstMount, animateOnMount, role, depth, containerSize]);\n\n // Use shared transform hook for DOM manipulation\n const { isAnimating } = useSwipeContentTransform({\n elementRef,\n targetPx,\n displacement: panelDisplacement,\n isSwiping,\n axis,\n animationDuration,\n containerSize,\n // Animate when targetPx changes (button navigation)\n animateOnTargetChange: true,\n // For push animation: start from off-screen\n initialPx,\n });\n\n // Compute visibility\n const visible = computeSwipeVisibility({\n depth,\n navigationDepth,\n isActive,\n isSwiping,\n isAnimating,\n });\n\n // Compute swipe progress for scale and dimming interpolation\n const swipeProgress = React.useMemo(() => {\n if (containerSize <= 0 || displacement <= 0) {\n return 0;\n }\n return Math.min(displacement / containerSize, 1);\n }, [displacement, containerSize]);\n\n // Compute scale for \"stack\" display mode\n // Behind panels are scaled down, and scale interpolates during swipe\n const scale = React.useMemo(() => {\n if (displayMode !== \"stack\") {\n return 1; // No scale for overlay/slide modes\n }\n\n const depthDiff = navigationDepth - depth;\n\n if (role === \"active\") {\n return 1; // Active panel is always at full scale\n }\n\n if (role === \"behind\") {\n // Base scale for behind panel\n const baseScale = 1 - depthDiff * STACK_SCALE_FACTOR;\n // During swipe, interpolate toward 1\n return baseScale + swipeProgress * (1 - baseScale);\n }\n\n return 1;\n }, [displayMode, role, depth, navigationDepth, swipeProgress]);\n\n // Compute dimming opacity for behind panels\n // Full dimming at rest, fades to 0 during swipe\n const dimmingOpacity = React.useMemo(() => {\n if (!showDimming || role !== \"behind\") {\n return 0;\n }\n // Fade from MAX_DIM_OPACITY to 0 as swipe progresses\n return MAX_DIM_OPACITY * (1 - swipeProgress);\n }, [showDimming, role, swipeProgress]);\n\n // Update visibility via direct DOM manipulation\n React.useLayoutEffect(() => {\n const element = elementRef.current;\n if (element) {\n element.style.visibility = visible ? \"visible\" : \"hidden\";\n }\n }, [visible]);\n\n // Update scale via direct DOM manipulation for smooth animation\n React.useLayoutEffect(() => {\n const element = elementRef.current;\n if (!element || displayMode !== \"stack\") {\n return;\n }\n\n // Get current transform (translateX) and append scale\n const currentTransform = element.style.transform;\n if (currentTransform.includes(\"translateX\")) {\n // Extract translateX value and combine with scale\n const translateMatch = currentTransform.match(/translateX\\([^)]+\\)/);\n if (translateMatch) {\n element.style.transform = `${translateMatch[0]} scale(${scale})`;\n }\n } else {\n element.style.transform = `scale(${scale})`;\n }\n }, [scale, displayMode]);\n\n // Compute shadow for active panel\n // Shadow is shown on panels at depth > 0 when they're active or animating\n const shouldShowShadow = showShadow && depth > 0 && role === \"active\";\n\n // Static style - transform is handled entirely by useSwipeContentTransform\n // to ensure smooth animations\n const staticStyle = React.useMemo<React.CSSProperties>(\n () => ({\n ...BASE_STYLE,\n pointerEvents: isActive ? \"auto\" : \"none\",\n willChange: \"transform\",\n zIndex: depth,\n visibility: visible ? \"visible\" : \"hidden\",\n boxShadow: shouldShowShadow ? ACTIVE_PANEL_SHADOW : undefined,\n }),\n [isActive, depth, visible, shouldShowShadow],\n );\n\n // Dimming overlay style for behind panels\n const dimmingStyle = React.useMemo<React.CSSProperties | null>(() => {\n if (dimmingOpacity <= 0) {\n return null;\n }\n return {\n position: \"absolute\",\n inset: 0,\n backgroundColor: `rgba(0, 0, 0, ${dimmingOpacity})`,\n pointerEvents: \"none\",\n zIndex: 1,\n };\n }, [dimmingOpacity]);\n\n return (\n <div\n ref={elementRef}\n data-stack-content={id}\n data-depth={depth}\n data-active={isActive ? \"true\" : \"false\"}\n data-role={role}\n style={staticStyle}\n >\n {children}\n {dimmingStyle != null && <div style={dimmingStyle} data-dimming-overlay />}\n </div>\n );\n },\n);\n","/**\n * @file SwipeStackOutlet component for rendering stack with swipe support.\n *\n * Uses SwipeStackContent for direct DOM manipulation during swipe gestures,\n * providing iOS-style smooth swipe-to-go-back behavior.\n */\nimport * as React from \"react\";\nimport { SwipeStackContent } from \"./SwipeStackContent.js\";\nimport type { SwipeInputState } from \"../../hooks/gesture/types.js\";\nimport type { StackPanel, StackNavigationState } from \"./types.js\";\n\nconst DEFAULT_ANIMATION_DURATION = 300;\n\n/**\n * Props for SwipeStackOutlet component.\n */\nexport type SwipeStackOutletProps = {\n /** Array of panel definitions */\n panels: ReadonlyArray<StackPanel>;\n /** Current navigation state */\n navigationState: StackNavigationState;\n /** Swipe input state from useStackSwipeInput */\n inputState: SwipeInputState;\n /** Container size in pixels (width for horizontal swipe) */\n containerSize: number;\n /** Function to get cached content for a panel */\n getCachedContent?: (panelId: string) => React.ReactNode | null;\n /** Behind panel offset ratio. @default -0.3 */\n behindOffset?: number;\n /**\n * Whether to animate new panels on mount.\n * @default false\n */\n animateOnMount?: boolean;\n /**\n * Animation duration in ms.\n * @default 300\n */\n animationDuration?: number;\n /**\n * Whether to show iOS-style edge shadow on active panels.\n * @default true\n */\n showShadow?: boolean;\n /**\n * Display mode for visual styling.\n * - \"overlay\": panels overlay, no scale (iOS style)\n * - \"slide\": panels slide with parallax\n * - \"stack\": panels scale down and offset (stacked cards style)\n * @default \"overlay\"\n */\n displayMode?: \"overlay\" | \"slide\" | \"stack\";\n /**\n * Whether to show dimming overlay on behind panels.\n * @default true\n */\n showDimming?: boolean;\n};\n\n/**\n * Get visible panels for rendering during swipe.\n *\n * Only renders active panel and immediate behind panel for performance.\n * Also includes exiting panel when navigating back.\n */\nfunction getVisiblePanels(\n panels: ReadonlyArray<StackPanel>,\n navigationState: StackNavigationState,\n exitingPanelId: string | null,\n): Array<{ panel: StackPanel; depth: number; isExiting: boolean }> {\n const { stack, depth } = navigationState;\n\n // During swipe, we only need active and behind panels\n const visibleDepths = [depth];\n if (depth > 0) {\n visibleDepths.push(depth - 1);\n }\n\n const result: Array<{ panel: StackPanel; depth: number; isExiting: boolean }> = [];\n\n // Add panels at visible depths\n for (const d of visibleDepths) {\n const id = stack[d];\n const panel = panels.find((p) => p.id === id);\n if (panel) {\n result.push({ panel, depth: d, isExiting: false });\n }\n }\n\n // Add exiting panel if it's not already included\n if (exitingPanelId != null) {\n const alreadyIncluded = result.some((r) => r.panel.id === exitingPanelId);\n if (!alreadyIncluded) {\n const exitingPanel = panels.find((p) => p.id === exitingPanelId);\n if (exitingPanel) {\n // Exiting panel is at depth + 1 (was previously active)\n result.push({ panel: exitingPanel, depth: depth + 1, isExiting: true });\n }\n }\n }\n\n // Render in order: behind first, then active, then exiting\n return result.sort((a, b) => a.depth - b.depth);\n}\n\n/**\n * SwipeStackOutlet renders stack panels with swipe gesture support.\n *\n * Unlike the default StackOutlet, this component:\n * - Uses SwipeStackContent for direct DOM manipulation\n * - Only renders active and behind panels for performance\n * - Supports iOS-style parallax reveal animation\n *\n * @example\n * ```tsx\n * const navigation = useStackNavigation({ panels, displayMode: 'overlay' });\n * const swipeInput = useStackSwipeInput({ containerRef, navigation });\n *\n * return (\n * <div ref={containerRef} {...swipeInput.containerProps}>\n * <SwipeStackOutlet\n * panels={navigation.panels}\n * navigationState={navigation.state}\n * inputState={swipeInput.inputState}\n * containerSize={containerWidth}\n * />\n * </div>\n * );\n * ```\n */\nexport const SwipeStackOutlet: React.FC<SwipeStackOutletProps> = React.memo(\n ({\n panels,\n navigationState,\n inputState,\n containerSize,\n getCachedContent,\n behindOffset,\n animateOnMount = false,\n animationDuration = DEFAULT_ANIMATION_DURATION,\n showShadow,\n displayMode,\n showDimming,\n }) => {\n // Track the exiting panel ID when navigating back\n const [exitingPanelId, setExitingPanelId] = React.useState<string | null>(null);\n const prevDepthRef = React.useRef(navigationState.depth);\n const prevStackRef = React.useRef<ReadonlyArray<string>>(navigationState.stack);\n\n // Detect when we navigate back and need to animate out\n React.useLayoutEffect(() => {\n const prevDepth = prevDepthRef.current;\n const prevStack = prevStackRef.current;\n const { depth, stack } = navigationState;\n\n // Update refs\n prevDepthRef.current = depth;\n prevStackRef.current = stack;\n\n // Check if we went back (depth decreased)\n if (depth < prevDepth) {\n // The panel at prevDepth is exiting\n const exitingId = prevStack[prevDepth];\n if (exitingId != null) {\n setExitingPanelId(exitingId);\n\n // Clear exiting panel after animation completes\n const timeoutId = setTimeout(() => {\n setExitingPanelId(null);\n }, animationDuration);\n\n return () => clearTimeout(timeoutId);\n }\n }\n }, [navigationState.depth, navigationState.stack, animationDuration]);\n\n const visiblePanels = React.useMemo(\n () => getVisiblePanels(panels, navigationState, exitingPanelId),\n [panels, navigationState, exitingPanelId],\n );\n\n const containerStyle: React.CSSProperties = React.useMemo(\n () => ({\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }),\n [],\n );\n\n return (\n <div style={containerStyle} data-swipe-stack-container>\n {visiblePanels.map(({ panel, depth, isExiting }) => {\n const isActive = depth === navigationState.depth && !isExiting;\n const content = getCachedContent?.(panel.id) ?? panel.content;\n\n return (\n <SwipeStackContent\n key={panel.id}\n id={panel.id}\n depth={depth}\n navigationDepth={navigationState.depth}\n isActive={isActive}\n inputState={inputState}\n containerSize={containerSize}\n behindOffset={behindOffset}\n animateOnMount={animateOnMount}\n animationDuration={animationDuration}\n showShadow={showShadow}\n displayMode={displayMode}\n showDimming={showDimming}\n >\n {content}\n </SwipeStackContent>\n );\n })}\n </div>\n );\n },\n);\n"],"names":["computeAnimationType","input","wasActive","isActive","transitionMode","computeVisibility","displayMode","depth","navigationDepth","isAnimatingOut","isRevealing","revealDepth","computeTransform","activeDepth","isPrevious","offset","scale","computeSwipeTransform","baseTransform","swipeProgress","computeTransitionCss","STACK_TRANSITION_DURATION","STACK_TRANSITION_EASING","computeStackContentState","currentAnimationType","navigationState","nextAnimationType","visibility","transform","animation","STACK_ANIMATION_PUSH","STACK_ANIMATION_POP","transition","baseStyle","StackContent","React","id","children","ref","prevActiveRef","animationType","setAnimationType","computedState","useIsomorphicLayoutEffect","handleAnimationEnd","e","style","s","content","jsx","StackOutletContext","StackOutletInner","ctx","forceUpdate","x","panels","visiblePanels","index","panel","p","useStackNavigation","options","initialPanelId","onPanelChange","stack","setStack","initialId","revealState","setRevealState","state","currentPanelId","previousPanelId","push","prev","go","direction","targetDepth","targetId","move","replace","canGo","revealParent","revealTo","revealRoot","dismissReveal","getPanelProps","panelIndex","getBackButtonProps","canGoBack","prevPanel","label","containerStyle","stateRef","subscribersRef","callback","resolveContent","panelId","validIds","getCachedContent","useContentCache","contextValue","Outlet","OutletComponent","INITIAL_STATE","createTimestampedPoint","clientX","clientY","usePointerTracking","enabled","primaryOnly","setState","reset","handlePointerDown","useEffectEvent","event","point","handlePointerMove","handlePointerUp","handlePointerCancel","shouldTrackDocument","useDocumentPointerEvents","DEFAULT_LOCK_THRESHOLD","determineAxis","deltaX","deltaY","absX","absY","useDirectionalLock","tracking","lockThreshold","lockedAxis","setLockedAxis","axis","calculateVelocity","displacement","startTime","currentTime","elapsed","determineDirection","mergeGestureContainerProps","propsArray","mergedStyle","pointerDownHandlers","props","handler","DEFAULT_SWIPE_THRESHOLDS","DEFAULT_EDGE_WIDTH","IDLE_SWIPE_INPUT_STATE","WHEEL_RESET_TIMEOUT","evaluateSwipeEnd","velocity","thresholds","onSwipeEnd","axisDisplacement","axisVelocity","absDisplacement","absVelocity","useSwipeInput","containerRef","customThresholds","enableWheel","pointerStartFilter","handleSwipeEnd","baseOnPointerDown","onPointerDown","container","isLocked","lastActiveStateRef","isLockedToSwipeAxisRef","disableTouchMove","handleTouchStart","handleTouchEnd","wheelState","setWheelState","wheelAccumulatedRef","wheelIdleTimerRef","wheelLockedRef","wheelLockedAxisRef","resetWheelState","endWheelSwipe","handleWheel","accumulated","listener","pointerState","lastState","containerProps","getAxisForEdge","edge","isInEdgeZone","edgeWidth","rect","useEdgeSwipeInput","isEdgeGesture","setIsEdgeGesture","inEdge","isInLeftEdge","useNativeGestureGuard","active","preventEdgeBack","preventOverscroll","previousHtmlOverscrollRef","applyHtmlOverscroll","html","useStackSwipeInput","navigation","swipeState","setSwipeState","inputState","swipeProps","guardProps","progress","containerWidth","easings","t","DEFAULT_DURATION","useAnimationFrame","duration","easing","onFrame","onComplete","isAnimating","setIsAnimating","rafIdRef","startTimeRef","onFrameRef","onCompleteRef","cancel","start","step","timestamp","easedProgress","isComplete","interpolate","from","to","DEFAULT_ANIMATION_DURATION","getTransformFn","useSwipeContentTransform","elementRef","targetPx","isSwiping","animationDuration","containerSize","animateOnTargetChange","initialPx","effectiveInitialPx","currentPxRef","animRef","prevTargetPxRef","prevContainerSizeRef","pendingAnimationRef","isFirstMountRef","handleFrame","element","anim","value","handleComplete","pending","currentPx","displayPx","DEFAULT_BEHIND_OFFSET","computeActiveTargetPx","computeBehindTargetPx","behindOffset","clampedDisplacement","basePosition","parallaxOffset","computeSwipeVisibility","determineSwipePanelRole","STACK_SCALE_FACTOR","MAX_DIM_OPACITY","BASE_STYLE","getAxisDisplacement","ACTIVE_PANEL_SHADOW","SwipeStackContent","animateOnMount","showShadow","showDimming","role","isFirstMount","panelDisplacement","currentPos","basePos","visible","depthDiff","baseScale","dimmingOpacity","currentTransform","translateMatch","shouldShowShadow","staticStyle","dimmingStyle","jsxs","getVisiblePanels","exitingPanelId","visibleDepths","result","d","r","exitingPanel","a","b","SwipeStackOutlet","setExitingPanelId","prevDepthRef","prevStackRef","prevDepth","prevStack","exitingId","timeoutId","isExiting"],"mappings":"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"}
1
+ {"version":3,"file":"stack.cjs","sources":["../src/modules/stack/computeStackContentState.ts","../src/modules/stack/StackContent.tsx","../src/modules/stack/useStackNavigation.tsx","../src/modules/stack/useStackSwipeInput.ts","../src/hooks/useAnimationFrame.ts","../src/hooks/useSwipeContentTransform.ts","../src/hooks/useOperationContinuity.ts","../src/modules/stack/computeSwipeStackTransform.ts","../src/modules/stack/SwipeStackContent.tsx","../src/modules/stack/SwipeStackOutlet.tsx"],"sourcesContent":["/**\n * @file Pure functions for computing StackContent state.\n *\n * Separates state computation logic from React/CSS concerns for testability.\n * All functions are pure and deterministic.\n */\nimport {\n STACK_ANIMATION_PUSH,\n STACK_ANIMATION_POP,\n STACK_TRANSITION_DURATION,\n STACK_TRANSITION_EASING,\n} from \"../../constants/styles.js\";\n\n/**\n * Animation type for stack panel transitions.\n */\nexport type StackAnimationType = \"push\" | \"pop\" | null;\n\n/**\n * Display mode for stack panels.\n */\nexport type StackDisplayMode = \"overlay\" | \"slide\" | \"stack\";\n\n/**\n * Transition mode for animations.\n */\nexport type StackTransitionMode = \"css\" | \"none\";\n\n/**\n * Input for computing animation type.\n */\nexport type ComputeAnimationTypeInput = {\n wasActive: boolean;\n isActive: boolean;\n transitionMode: StackTransitionMode;\n};\n\n/**\n * Compute the animation type based on active state change.\n *\n * @returns The animation type to apply, or null if no animation needed.\n */\nexport function computeAnimationType(input: ComputeAnimationTypeInput): StackAnimationType {\n const { wasActive, isActive, transitionMode } = input;\n\n if (transitionMode !== \"css\") {\n return null;\n }\n\n if (wasActive === isActive) {\n return null;\n }\n\n return isActive ? \"push\" : \"pop\";\n}\n\n/**\n * Input for computing visibility.\n */\nexport type ComputeVisibilityInput = {\n displayMode: StackDisplayMode;\n depth: number;\n navigationDepth: number;\n isActive: boolean;\n isAnimatingOut: boolean;\n isRevealing: boolean;\n revealDepth: number | null;\n};\n\n/**\n * Compute panel visibility based on display mode and state.\n *\n * @returns \"visible\" or \"hidden\"\n */\nexport function computeVisibility(input: ComputeVisibilityInput): \"visible\" | \"hidden\" {\n const {\n displayMode,\n depth,\n navigationDepth,\n isActive,\n isAnimatingOut,\n isRevealing,\n revealDepth,\n } = input;\n\n if (displayMode === \"overlay\") {\n // In overlay mode, only show active, animating out, or revealing panel\n if (isActive) {\n return \"visible\";\n }\n if (isAnimatingOut) {\n return \"visible\";\n }\n if (isRevealing && depth === revealDepth) {\n return \"visible\";\n }\n return \"hidden\";\n }\n\n // In slide/stack mode, show all panels in stack or animating out\n if (depth <= navigationDepth) {\n return \"visible\";\n }\n if (isAnimatingOut) {\n return \"visible\";\n }\n return \"hidden\";\n}\n\n/**\n * Input for computing transform.\n */\nexport type ComputeTransformInput = {\n depth: number;\n activeDepth: number;\n displayMode: StackDisplayMode;\n isRevealing: boolean;\n revealDepth: number | null;\n};\n\n/**\n * Compute the transform value for a stack panel.\n *\n * @returns CSS transform string\n */\nexport function computeTransform(input: ComputeTransformInput): string {\n const { depth, activeDepth, displayMode, isRevealing, revealDepth } = input;\n\n const isActive = depth === activeDepth;\n const isPrevious = depth < activeDepth;\n\n // During reveal, shift active panel to show parent\n if (isRevealing && isActive) {\n if (revealDepth !== null) {\n const revealProgress = 0.3;\n return `translateX(${revealProgress * 100}%)`;\n }\n }\n\n if (isActive) {\n return \"translateX(0)\";\n }\n\n if (isPrevious) {\n switch (displayMode) {\n case \"overlay\":\n return \"translateX(0)\";\n case \"slide\":\n return \"translateX(-30%)\";\n case \"stack\": {\n const offset = (activeDepth - depth) * -5;\n const scale = 1 - (activeDepth - depth) * 0.05;\n return `translateX(${offset}%) scale(${scale})`;\n }\n }\n }\n\n // Future panels stay off-screen\n return \"translateX(100%)\";\n}\n\n/**\n * Compute the transform value considering swipe progress.\n */\nfunction computeSwipeTransform(\n baseTransform: string,\n swipeProgress: number | undefined,\n isActive: boolean,\n): string {\n if (swipeProgress === undefined) {\n return baseTransform;\n }\n if (swipeProgress <= 0) {\n return baseTransform;\n }\n if (!isActive) {\n return baseTransform;\n }\n return `translateX(${swipeProgress * 100}%)`;\n}\n\n/**\n * Compute the transition CSS value.\n */\nfunction computeTransitionCss(transitionMode: StackTransitionMode): string | undefined {\n if (transitionMode !== \"css\") {\n return undefined;\n }\n return `transform ${STACK_TRANSITION_DURATION} ${STACK_TRANSITION_EASING}`;\n}\n\n/**\n * Full input for computing stack content state.\n */\nexport type StackContentStateInput = {\n depth: number;\n isActive: boolean;\n wasActive: boolean;\n currentAnimationType: StackAnimationType;\n displayMode: StackDisplayMode;\n transitionMode: StackTransitionMode;\n navigationState: {\n depth: number;\n isRevealing: boolean;\n revealDepth: number | null;\n };\n swipeProgress: number | undefined;\n};\n\n/**\n * Computed state output for stack content.\n */\nexport type StackContentStateOutput = {\n nextAnimationType: StackAnimationType;\n visibility: \"visible\" | \"hidden\";\n transform: string;\n animation: string | undefined;\n transition: string | undefined;\n zIndex: number;\n pointerEvents: \"auto\" | \"none\";\n};\n\n/**\n * Compute the complete state for a stack content panel.\n *\n * This is the main entry point that combines all state computation.\n * Pure function with no side effects.\n *\n * @param input - All inputs needed to compute state\n * @returns Computed state for rendering\n */\nexport function computeStackContentState(input: StackContentStateInput): StackContentStateOutput {\n const {\n depth,\n isActive,\n wasActive,\n currentAnimationType,\n displayMode,\n transitionMode,\n navigationState,\n swipeProgress,\n } = input;\n\n // 1. Compute animation type\n const stateChangeAnimationType = computeAnimationType({\n wasActive,\n isActive,\n transitionMode,\n });\n\n // Use new animation type if state changed, otherwise preserve current\n const nextAnimationType = stateChangeAnimationType ?? currentAnimationType;\n\n // 2. Compute visibility\n const isAnimatingOut = nextAnimationType === \"pop\";\n const visibility = computeVisibility({\n displayMode,\n depth,\n navigationDepth: navigationState.depth,\n isActive,\n isAnimatingOut,\n isRevealing: navigationState.isRevealing,\n revealDepth: navigationState.revealDepth,\n });\n\n // 3. Compute transform\n const baseTransform = computeTransform({\n depth,\n activeDepth: navigationState.depth,\n displayMode,\n isRevealing: navigationState.isRevealing,\n revealDepth: navigationState.revealDepth,\n });\n\n // Apply swipe progress transform if swiping on active panel\n const transform = computeSwipeTransform(baseTransform, swipeProgress, isActive);\n\n // 4. Compute animation CSS\n const animation = (() => {\n if (transitionMode !== \"css\") {\n return undefined;\n }\n if (nextAnimationType === \"push\") {\n return STACK_ANIMATION_PUSH;\n }\n if (nextAnimationType === \"pop\") {\n return STACK_ANIMATION_POP;\n }\n return undefined;\n })();\n\n // 5. Compute transition CSS\n const transition = computeTransitionCss(transitionMode);\n\n return {\n nextAnimationType,\n visibility,\n transform,\n animation,\n transition,\n zIndex: depth,\n pointerEvents: isActive ? \"auto\" : \"none\",\n };\n}\n","/**\n * @file StackContent component for rendering stack panels with animations.\n *\n * Override via CSS custom properties:\n * - --rpl-stack-animation-push: Animation when panel is pushed\n * - --rpl-stack-animation-pop: Animation when panel is popped\n * - --rpl-stack-transition-duration: Duration of transitions\n * - --rpl-stack-transition-easing: Easing for transitions\n */\nimport * as React from \"react\";\nimport type { StackContentProps } from \"./types.js\";\nimport { computeStackContentState } from \"./computeStackContentState.js\";\nimport type { StackAnimationType } from \"./computeStackContentState.js\";\nimport { useIsomorphicLayoutEffect } from \"../../hooks/useIsomorphicLayoutEffect.js\";\n\nconst baseStyle: React.CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/**\n * Renders a stack panel with appropriate animation based on display mode.\n */\nexport const StackContent: React.FC<StackContentProps> = React.memo(\n ({ id, depth, isActive, displayMode, transitionMode, navigationState, swipeProgress, children }) => {\n const ref = React.useRef<HTMLDivElement>(null);\n const prevActiveRef = React.useRef(isActive);\n\n // Track current animation type\n const [animationType, setAnimationType] = React.useState<StackAnimationType>(null);\n\n // Compute state using pure function\n const computedState = computeStackContentState({\n depth,\n isActive,\n wasActive: prevActiveRef.current,\n currentAnimationType: animationType,\n displayMode,\n transitionMode,\n navigationState,\n swipeProgress,\n });\n\n // Update animation type synchronously before paint\n useIsomorphicLayoutEffect(() => {\n const wasActive = prevActiveRef.current;\n prevActiveRef.current = isActive;\n\n if (wasActive !== isActive) {\n setAnimationType(computedState.nextAnimationType);\n }\n }, [isActive, computedState.nextAnimationType]);\n\n // Clear animation type after animation ends\n // Only handle animation end for this element (not bubbled from children)\n const handleAnimationEnd = React.useCallback((e: React.AnimationEvent) => {\n if (e.target === e.currentTarget) {\n setAnimationType(null);\n }\n }, []);\n\n // Build style from computed state\n const style = React.useMemo<React.CSSProperties>(() => {\n const s: React.CSSProperties = {\n ...baseStyle,\n transform: computedState.transform,\n pointerEvents: computedState.pointerEvents,\n zIndex: computedState.zIndex,\n visibility: computedState.visibility,\n };\n\n if (computedState.animation !== undefined) {\n s.animation = computedState.animation;\n }\n\n if (computedState.transition !== undefined) {\n s.transition = computedState.transition;\n }\n\n return s;\n }, [\n computedState.transform,\n computedState.pointerEvents,\n computedState.zIndex,\n computedState.visibility,\n computedState.animation,\n computedState.transition,\n ]);\n\n const content = (\n <div\n ref={ref}\n data-stack-content={id}\n data-depth={depth}\n data-active={isActive ? \"true\" : \"false\"}\n style={style}\n onAnimationEnd={handleAnimationEnd}\n >\n {children}\n </div>\n );\n\n if (transitionMode === \"none\") {\n return <React.Activity mode={isActive ? \"visible\" : \"hidden\"}>{content}</React.Activity>;\n }\n\n return content;\n },\n);\n","/**\n * @file Headless hook for managing Stack (hierarchical) navigation.\n *\n * Provides navigation operations for a stack-based UI where panels\n * are pushed and popped as the user drills down into content.\n */\nimport * as React from \"react\";\nimport type {\n UseStackNavigationOptions,\n UseStackNavigationResult,\n StackNavigationState,\n StackPanelProps,\n StackBackButtonProps,\n StackPanel,\n} from \"./types.js\";\nimport { StackContent } from \"./StackContent.js\";\nimport { useContentCache } from \"../../hooks/useContentCache.js\";\n\n/**\n * Navigation action types for centralized state management.\n * All navigation operations go through the reducer to avoid stale closure issues.\n */\ntype StackAction<TId extends string> =\n | { type: \"push\"; id: TId }\n | { type: \"go\"; direction: number }\n | { type: \"move\"; targetDepth: number }\n | { type: \"replace\"; id: TId };\n\n/**\n * Reducer for stack navigation state.\n * Centralizes all state transitions to ensure consistent behavior during rapid navigation.\n */\nfunction stackReducer<TId extends string>(\n state: ReadonlyArray<TId>,\n action: StackAction<TId>,\n): ReadonlyArray<TId> {\n switch (action.type) {\n case \"push\":\n return [...state, action.id];\n\n case \"go\": {\n if (action.direction >= 0) {\n return state;\n }\n const currentDepth = state.length - 1;\n const targetDepth = currentDepth + action.direction;\n if (targetDepth < 0) {\n return state;\n }\n return state.slice(0, targetDepth + 1);\n }\n\n case \"move\": {\n if (action.targetDepth < 0 || action.targetDepth >= state.length) {\n return state;\n }\n return state.slice(0, action.targetDepth + 1);\n }\n\n case \"replace\": {\n if (state.length === 0) {\n return [action.id];\n }\n return [...state.slice(0, -1), action.id];\n }\n }\n}\n\n/**\n * Context for sharing stack state with Outlet component.\n */\ntype StackOutletContextValue = {\n getState: () => {\n panels: ReadonlyArray<StackPanel>;\n navigationState: StackNavigationState;\n displayMode: UseStackNavigationOptions[\"displayMode\"];\n transitionMode: NonNullable<UseStackNavigationOptions[\"transitionMode\"]>;\n };\n subscribe: (callback: () => void) => () => void;\n getCachedContent: (panelId: string) => React.ReactNode | null;\n};\n\nconst StackOutletContext = React.createContext<StackOutletContextValue | null>(null);\n\n/**\n * Outlet component that renders the stack panels.\n */\nconst StackOutletInner: React.FC = React.memo(() => {\n const ctx = React.useContext(StackOutletContext);\n if (!ctx) {\n throw new Error(\"StackOutlet must be used within useStackNavigation\");\n }\n\n const [, forceUpdate] = React.useReducer((x) => x + 1, 0);\n\n React.useEffect(() => {\n return ctx.subscribe(forceUpdate);\n }, [ctx]);\n\n const { panels, navigationState, displayMode, transitionMode } = ctx.getState();\n\n // Get panels that should be rendered (only those in the current stack)\n const visiblePanels = React.useMemo(() => {\n return navigationState.stack.map((id, index) => {\n const panel = panels.find((p) => p.id === id);\n return panel ? { panel, depth: index } : null;\n }).filter((p): p is { panel: StackPanel; depth: number } => p !== null);\n }, [navigationState.stack, panels]);\n\n return (\n <>\n {visiblePanels.map(({ panel, depth }) => (\n <StackContent\n key={panel.id}\n id={panel.id}\n depth={depth}\n isActive={depth === navigationState.depth}\n displayMode={displayMode}\n transitionMode={transitionMode}\n navigationState={navigationState}\n >\n {panel.cache ? ctx.getCachedContent(panel.id) : panel.content}\n </StackContent>\n ))}\n </>\n );\n});\n\n/**\n * Headless hook for managing hierarchical stack navigation.\n *\n * @example\n * ```tsx\n * const { state, push, go, Outlet } = useStackNavigation({\n * panels: [\n * { id: 'list', title: 'Items', content: <ListPage /> },\n * { id: 'detail', title: 'Detail', content: <DetailPage /> },\n * ],\n * displayMode: 'overlay',\n * });\n *\n * return (\n * <div>\n * <button onClick={() => push('detail')}>View Detail</button>\n * <Outlet />\n * </div>\n * );\n * ```\n */\nexport function useStackNavigation<TId extends string = string>(\n options: UseStackNavigationOptions<TId>,\n): UseStackNavigationResult<TId> {\n const {\n panels,\n initialPanelId,\n displayMode,\n transitionMode = \"css\",\n onPanelChange,\n } = options;\n\n // Initialize stack with reducer for centralized state management\n const initialId = initialPanelId ?? (panels[0]?.id as TId);\n if (!initialId) {\n throw new Error(\"useStackNavigation: No panels provided\");\n }\n\n const [stack, dispatch] = React.useReducer(\n stackReducer<TId>,\n [initialId] as ReadonlyArray<TId>,\n );\n\n // Ref for accessing current stack in callbacks without stale closure\n const stackRef = React.useRef(stack);\n stackRef.current = stack;\n\n // Track previous stack for onPanelChange callback\n const prevStackRef = React.useRef(stack);\n React.useEffect(() => {\n const prevStack = prevStackRef.current;\n prevStackRef.current = stack;\n\n if (onPanelChange && stack !== prevStack) {\n const newDepth = stack.length - 1;\n const newPanelId = stack[newDepth];\n if (newPanelId !== undefined) {\n onPanelChange(newPanelId, newDepth);\n }\n }\n }, [stack, onPanelChange]);\n\n // Reveal state for parent peeking\n const [revealState, setRevealState] = React.useState<{\n isRevealing: boolean;\n revealDepth: number | null;\n }>({ isRevealing: false, revealDepth: null });\n\n // Current depth (0-indexed)\n const depth = stack.length - 1;\n\n // Navigation state\n const state: StackNavigationState<TId> = React.useMemo(() => ({\n stack,\n depth,\n isRevealing: revealState.isRevealing,\n revealDepth: revealState.revealDepth,\n }), [stack, depth, revealState.isRevealing, revealState.revealDepth]);\n\n // Current and previous panel IDs\n const currentPanelId = stack[depth] as TId;\n const previousPanelId = depth > 0 ? stack[depth - 1] as TId : null;\n\n // All navigation functions dispatch to reducer - no stale closure issues\n const push = React.useCallback((id: TId) => {\n const panel = panels.find((p) => p.id === id);\n if (!panel) {\n return;\n }\n dispatch({ type: \"push\", id });\n }, [panels]);\n\n const go = React.useCallback((direction: number) => {\n dispatch({ type: \"go\", direction });\n }, []);\n\n const move = React.useCallback((targetDepth: number) => {\n dispatch({ type: \"move\", targetDepth });\n }, []);\n\n const replace = React.useCallback((id: TId) => {\n const panel = panels.find((p) => p.id === id);\n if (!panel) {\n return;\n }\n dispatch({ type: \"replace\", id });\n }, [panels]);\n\n // canGo uses stackRef for current state\n const canGo = React.useCallback((direction: number): boolean => {\n if (direction >= 0) {\n return false;\n }\n const currentDepth = stackRef.current.length - 1;\n return currentDepth + direction >= 0;\n }, []);\n\n // Reveal functions use stackRef for current depth\n const revealParent = React.useCallback((targetDepth?: number) => {\n const currentDepth = stackRef.current.length - 1;\n const revealTo = targetDepth ?? currentDepth - 1;\n if (revealTo < 0 || revealTo >= currentDepth) {\n return;\n }\n setRevealState({ isRevealing: true, revealDepth: revealTo });\n }, []);\n\n const revealRoot = React.useCallback(() => {\n const currentDepth = stackRef.current.length - 1;\n if (currentDepth === 0) {\n return;\n }\n setRevealState({ isRevealing: true, revealDepth: 0 });\n }, []);\n\n const dismissReveal = React.useCallback(() => {\n setRevealState({ isRevealing: false, revealDepth: null });\n }, []);\n\n // getPanelProps uses stackRef for current state\n const getPanelProps = React.useCallback((id: TId): StackPanelProps => {\n const currentStack = stackRef.current;\n const panelIndex = currentStack.indexOf(id);\n const currentDepth = currentStack.length - 1;\n const isActive = panelIndex === currentDepth;\n\n return {\n \"data-stack-panel\": id,\n \"data-depth\": panelIndex,\n \"data-active\": isActive ? \"true\" : \"false\",\n \"aria-hidden\": !isActive,\n };\n }, []);\n\n // getBackButtonProps uses stackRef for current state\n const getBackButtonProps = React.useCallback((): StackBackButtonProps => {\n const currentStack = stackRef.current;\n const currentDepth = currentStack.length - 1;\n const canGoBack = currentDepth > 0;\n const prevPanelId = currentDepth > 0 ? currentStack[currentDepth - 1] : null;\n const prevPanel = prevPanelId ? panels.find((p) => p.id === prevPanelId) : null;\n const label = prevPanel?.title ? `Back to ${prevPanel.title}` : \"Go back\";\n\n return {\n onClick: () => go(-1),\n disabled: !canGoBack,\n \"aria-label\": label,\n };\n }, [panels, go]);\n\n // Container style\n const containerStyle: React.CSSProperties = React.useMemo(\n () => ({\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }),\n [],\n );\n\n // State ref for stable getState function\n const stateRef = React.useRef({\n panels,\n navigationState: state,\n displayMode,\n transitionMode,\n });\n\n stateRef.current = {\n panels,\n navigationState: state,\n displayMode,\n transitionMode,\n };\n\n // Subscribers for state changes\n const subscribersRef = React.useRef(new Set<() => void>());\n\n // Notify subscribers when state changes\n React.useEffect(() => {\n subscribersRef.current.forEach((callback) => callback());\n }, [state, displayMode, transitionMode]);\n\n // Content resolver for useContentCache\n const resolveContent = React.useCallback(\n (panelId: string): React.ReactNode | null => {\n const panel = stateRef.current.panels.find((p) => p.id === panelId);\n return panel?.content ?? null;\n },\n [],\n );\n\n // Valid IDs for cache cleanup\n const validIds = React.useMemo((): readonly string[] => panels.map((p) => p.id), [panels]);\n\n // Use shared content cache hook\n const { getCachedContent } = useContentCache({\n resolveContent,\n validIds,\n });\n\n // Stable context value\n const contextValue = React.useMemo<StackOutletContextValue>(\n () => ({\n getState: () => stateRef.current,\n subscribe: (callback) => {\n subscribersRef.current.add(callback);\n return () => subscribersRef.current.delete(callback);\n },\n getCachedContent,\n }),\n [getCachedContent],\n );\n\n // Stable Outlet component\n const Outlet = React.useMemo(() => {\n const OutletComponent: React.FC = () => (\n <StackOutletContext.Provider value={contextValue}>\n <div style={containerStyle} data-stack-container>\n <StackOutletInner />\n </div>\n </StackOutletContext.Provider>\n );\n OutletComponent.displayName = \"StackOutlet\";\n return OutletComponent;\n }, [contextValue, containerStyle]);\n\n return {\n state,\n push,\n go,\n move,\n replace,\n revealParent,\n revealRoot,\n dismissReveal,\n getPanelProps,\n getBackButtonProps,\n canGo,\n currentPanelId,\n previousPanelId,\n Outlet,\n };\n}\n","/**\n * @file Hook for binding edge swipe input to Stack navigation.\n *\n * This hook connects edge swipe gesture detection to Stack's navigation API,\n * enabling iOS-style \"swipe to go back\" navigation.\n */\nimport * as React from \"react\";\nimport { useEdgeSwipeInput } from \"../../hooks/gesture/useEdgeSwipeInput.js\";\nimport { useNativeGestureGuard } from \"../../hooks/gesture/useNativeGestureGuard.js\";\nimport { mergeGestureContainerProps } from \"../../hooks/gesture/utils.js\";\nimport { IDLE_SWIPE_INPUT_STATE } from \"../../hooks/gesture/types.js\";\nimport type { SwipeInputState } from \"../../hooks/gesture/types.js\";\nimport type { UseStackSwipeInputOptions, UseStackSwipeInputResult } from \"./types.js\";\n\n/**\n * Hook for binding edge swipe input to Stack navigation.\n *\n * Detects swipe gestures from the specified edge and triggers navigation:\n * - Left edge swipe → go(-1) (go back)\n * - Right edge swipe → reveals parent or custom action\n *\n * During a swipe, provides progress for animation.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const navigation = useStackNavigation({ panels, displayMode: 'overlay' });\n * const { isEdgeSwiping, progress, containerProps } = useStackSwipeInput({\n * containerRef,\n * navigation,\n * });\n *\n * return (\n * <div ref={containerRef} {...containerProps}>\n * <navigation.Outlet />\n * </div>\n * );\n * ```\n */\nexport function useStackSwipeInput(options: UseStackSwipeInputOptions): UseStackSwipeInputResult {\n const {\n containerRef,\n navigation,\n edge = \"left\",\n edgeWidth = 20,\n enabled = true,\n } = options;\n\n // Track swipe state for progress calculation\n const [swipeState, setSwipeState] = React.useState<SwipeInputState | null>(null);\n\n // Handle swipe completion - navigate back\n const handleSwipeEnd = React.useCallback(\n (state: SwipeInputState) => {\n setSwipeState(null);\n\n // Left edge swipe going right = go back\n if (edge === \"left\" && state.direction === 1) {\n if (navigation.canGo(-1)) {\n navigation.go(-1);\n }\n }\n // Right edge swipe going left = custom action (optional)\n // Could be used for forward navigation if the app supports it\n },\n [edge, navigation],\n );\n\n // Use edge swipe detection\n const { isEdgeGesture, state: inputState, containerProps: swipeProps } = useEdgeSwipeInput({\n containerRef,\n edge,\n edgeWidth,\n enabled: enabled ? navigation.canGo(-1) : false, // Only enable if can go back\n onSwipeEnd: handleSwipeEnd,\n });\n\n // Update swipe state for progress tracking\n React.useEffect(() => {\n if (isEdgeGesture && (inputState.phase === \"swiping\" || inputState.phase === \"tracking\")) {\n setSwipeState(inputState);\n } else if (inputState.phase === \"idle\") {\n setSwipeState(null);\n }\n }, [isEdgeGesture, inputState]);\n\n // Use native gesture guard during swipe\n const { containerProps: guardProps } = useNativeGestureGuard({\n containerRef,\n active: isEdgeGesture,\n preventEdgeBack: true,\n preventOverscroll: true,\n edgeWidth,\n });\n\n // Calculate swipe progress (0-1)\n const progress = React.useMemo(() => {\n if (!swipeState || !containerRef.current) {\n return 0;\n }\n\n const containerWidth = containerRef.current.clientWidth;\n if (containerWidth === 0) {\n return 0;\n }\n\n // Use X displacement for horizontal swipe\n const displacement = swipeState.displacement.x;\n\n // Only count rightward movement for left edge swipe\n if (edge === \"left\" && displacement <= 0) {\n return 0;\n }\n\n // Only count leftward movement for right edge swipe\n if (edge === \"right\" && displacement >= 0) {\n return 0;\n }\n\n const absDisplacement = Math.abs(displacement);\n return Math.min(absDisplacement / containerWidth, 1);\n }, [swipeState, containerRef, edge]);\n\n // Merge container props\n const containerProps = React.useMemo(\n () => mergeGestureContainerProps(swipeProps, guardProps),\n [swipeProps, guardProps],\n );\n\n // Effective input state: only return actual state if it's an edge gesture\n const effectiveInputState: SwipeInputState = isEdgeGesture ? inputState : IDLE_SWIPE_INPUT_STATE;\n\n return {\n isEdgeSwiping: isEdgeGesture,\n progress,\n inputState: effectiveInputState,\n containerProps,\n };\n}\n","/**\n * @file Generic requestAnimationFrame-based animation hook.\n *\n * Provides a reusable animation loop with easing support.\n * This is the foundation for more specific animation hooks.\n */\nimport * as React from \"react\";\n\n/**\n * Easing function type.\n * Takes a progress value (0-1) and returns an eased value (0-1).\n */\nexport type EasingFunction = (t: number) => number;\n\n/**\n * Built-in easing functions.\n */\nexport const easings = {\n /** Linear (no easing) */\n linear: (t: number): number => t,\n\n /** Ease out cubic */\n easeOutCubic: (t: number): number => 1 - Math.pow(1 - t, 3),\n\n /** Ease out expo (similar to cubic-bezier(0.22, 1, 0.36, 1)) */\n easeOutExpo: (t: number): number => {\n if (t === 1) {\n return 1;\n }\n return 1 - Math.pow(2, -10 * t);\n },\n\n /** Ease out quart */\n easeOutQuart: (t: number): number => 1 - Math.pow(1 - t, 4),\n\n /** Ease in out cubic */\n easeInOutCubic: (t: number): number => {\n if (t < 0.5) {\n return 4 * t * t * t;\n }\n return 1 - Math.pow(-2 * t + 2, 3) / 2;\n },\n\n /** Ease in expo (accelerating, for \"suck in\" effect) */\n easeInExpo: (t: number): number => {\n if (t === 0) {\n return 0;\n }\n return Math.pow(2, 10 * t - 10);\n },\n} as const;\n\n/**\n * Animation state passed to callbacks.\n */\nexport type AnimationState = {\n /** Raw progress (0-1) */\n progress: number;\n /** Eased progress (0-1) */\n easedProgress: number;\n /** Elapsed time in ms */\n elapsed: number;\n /** Whether animation is complete */\n isComplete: boolean;\n};\n\n/**\n * Options for useAnimationFrame hook.\n */\nexport type UseAnimationFrameOptions = {\n /** Duration of animation in milliseconds */\n duration?: number;\n /** Easing function for the animation */\n easing?: EasingFunction;\n /** Callback called every frame with animation state */\n onFrame?: (state: AnimationState) => void;\n /** Callback when animation completes */\n onComplete?: () => void;\n};\n\n/**\n * Result from useAnimationFrame hook.\n */\nexport type UseAnimationFrameResult = {\n /** Whether animation is currently running */\n isAnimating: boolean;\n /** Start the animation */\n start: () => void;\n /** Cancel the animation */\n cancel: () => void;\n};\n\n/** Default animation duration in ms */\nconst DEFAULT_DURATION = 300;\n\n/**\n * Generic requestAnimationFrame-based animation hook.\n *\n * Provides a reusable animation loop with progress calculation and easing.\n * Use this as a building block for specific animation behaviors.\n *\n * @example\n * ```tsx\n * const { start, isAnimating } = useAnimationFrame({\n * duration: 300,\n * easing: easings.easeOutExpo,\n * onFrame: ({ easedProgress }) => {\n * const value = fromValue + (toValue - fromValue) * easedProgress;\n * element.style.transform = `translateX(${value}px)`;\n * },\n * onComplete: () => console.log('Done!'),\n * });\n *\n * // Start animation\n * start();\n * ```\n */\nexport function useAnimationFrame(options: UseAnimationFrameOptions): UseAnimationFrameResult {\n const {\n duration = DEFAULT_DURATION,\n easing = easings.easeOutExpo,\n onFrame,\n onComplete,\n } = options;\n\n const [isAnimating, setIsAnimating] = React.useState(false);\n const rafIdRef = React.useRef<number | null>(null);\n const startTimeRef = React.useRef<number | null>(null);\n\n // Use refs for callbacks to avoid stale closures\n const onFrameRef = React.useRef(onFrame);\n const onCompleteRef = React.useRef(onComplete);\n React.useEffect(() => {\n onFrameRef.current = onFrame;\n onCompleteRef.current = onComplete;\n }, [onFrame, onComplete]);\n\n const cancel = React.useCallback(() => {\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n rafIdRef.current = null;\n }\n startTimeRef.current = null;\n setIsAnimating(false);\n }, []);\n\n const start = React.useCallback(() => {\n // Cancel any existing animation\n cancel();\n\n setIsAnimating(true);\n startTimeRef.current = null;\n\n const step = (timestamp: number) => {\n if (startTimeRef.current === null) {\n startTimeRef.current = timestamp;\n }\n\n const elapsed = timestamp - startTimeRef.current;\n const progress = Math.min(elapsed / duration, 1);\n const easedProgress = easing(progress);\n const isComplete = progress >= 1;\n\n const state: AnimationState = {\n progress,\n easedProgress,\n elapsed,\n isComplete,\n };\n\n onFrameRef.current?.(state);\n\n if (!isComplete) {\n rafIdRef.current = requestAnimationFrame(step);\n } else {\n // Animation complete\n rafIdRef.current = null;\n startTimeRef.current = null;\n setIsAnimating(false);\n onCompleteRef.current?.();\n }\n };\n\n rafIdRef.current = requestAnimationFrame(step);\n }, [duration, easing, cancel]);\n\n // Cleanup on unmount\n React.useEffect(() => {\n return () => {\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n }\n };\n }, []);\n\n return {\n isAnimating,\n start,\n cancel,\n };\n}\n\n/**\n * Interpolate between two values using eased progress.\n */\nexport function interpolate(from: number, to: number, easedProgress: number): number {\n return from + (to - from) * easedProgress;\n}\n","/**\n * @file Shared hook for DOM-based swipe content transform.\n *\n * This hook provides immediate DOM manipulation for swipe gestures,\n * with smooth snap-back animation when the swipe ends.\n *\n * Used by both Pivot and Stack for consistent swipe behavior.\n */\nimport * as React from \"react\";\nimport { useAnimationFrame, interpolate, easings } from \"./useAnimationFrame.js\";\nimport type { GestureAxis } from \"./gesture/types.js\";\n\nconst DEFAULT_ANIMATION_DURATION = 300;\n\n/**\n * Options for useSwipeContentTransform hook.\n */\nexport type UseSwipeContentTransformOptions = {\n /** Ref to the element to transform */\n elementRef: React.RefObject<HTMLElement | null>;\n /** Target position in pixels (where element should be at rest) */\n targetPx: number;\n /** Current swipe displacement in pixels */\n displacement: number;\n /** Whether swipe gesture is active */\n isOperating: boolean;\n /** Axis of transformation */\n axis?: GestureAxis;\n /** Duration of snap animation in ms */\n animationDuration?: number;\n /** Container size in pixels (used for snap on resize) */\n containerSize?: number;\n /**\n * Whether to animate when targetPx changes (without swipe).\n * Use this for tab bar animations triggered by click/button.\n * @default false\n */\n animateOnTargetChange?: boolean;\n /**\n * Initial position in pixels when first mounted.\n * If different from targetPx, will animate from initialPx to targetPx.\n * Use this for push animations where new panel comes from off-screen.\n */\n initialPx?: number;\n /**\n * Skip animation when targetPx changes.\n * Use this when the target changed during an operation (from useOperationContinuity).\n * When true, target changes will snap instead of animate.\n * @default false\n */\n skipTargetChangeAnimation?: boolean;\n};\n\n/**\n * Animation direction information.\n */\nexport type AnimationDirection = {\n /** Source position in pixels */\n from: number;\n /** Target position in pixels */\n to: number;\n};\n\n/**\n * Result from useSwipeContentTransform hook.\n */\nexport type UseSwipeContentTransformResult = {\n /** Whether snap animation is currently running */\n isAnimating: boolean;\n /** Current position in pixels (for visibility calculations) */\n currentPx: number;\n /** Animation direction info, or null if not animating */\n animationDirection: AnimationDirection | null;\n};\n\n/**\n * Get CSS transform function name for axis.\n */\nconst getTransformFn = (axis: GestureAxis): \"translateX\" | \"translateY\" => {\n return axis === \"horizontal\" ? \"translateX\" : \"translateY\";\n};\n\n/**\n * Check if initial mount animation should be scheduled.\n * Returns animation info if conditions are met, null otherwise.\n *\n * Conditions for scheduling:\n * 1. Animation not already consumed\n * 2. containerSize is valid (> 0) - handles React effect execution order\n * 3. initialPx is provided\n * 4. initialPx differs from targetPx\n */\nconst computeInitialMountAnimation = (\n hasConsumed: boolean,\n containerSize: number | undefined,\n initialPx: number | undefined,\n targetPx: number,\n): { from: number; to: number } | null => {\n if (hasConsumed) {\n return null;\n }\n if (containerSize === undefined) {\n return null;\n }\n if (containerSize <= 0) {\n return null;\n }\n if (initialPx === undefined) {\n return null;\n }\n if (initialPx === targetPx) {\n return null;\n }\n return { from: initialPx, to: targetPx };\n};\n\n/**\n * Result type for target change handling.\n */\ntype TargetChangeResult =\n | { type: \"animate\"; animation: { from: number; to: number } }\n | { type: \"snap\"; position: number }\n | { type: \"none\" };\n\n/**\n * Compute action for target position change when not swiping.\n * Returns the appropriate action: animate, snap, or none.\n *\n * @param skipAnimation - If true, skip animation and snap directly.\n * Use this when the target changed during an operation (from useOperationContinuity).\n */\nconst computeTargetChangeAction = (\n targetPx: number,\n prevTargetPx: number,\n currentPx: number,\n isOperating: boolean,\n isAnimating: boolean,\n animateOnTargetChange: boolean,\n skipAnimation: boolean,\n): TargetChangeResult => {\n if (targetPx === prevTargetPx) {\n return { type: \"none\" };\n }\n if (isOperating) {\n return { type: \"none\" };\n }\n if (isAnimating) {\n return { type: \"none\" };\n }\n if (!animateOnTargetChange) {\n return { type: \"snap\", position: targetPx };\n }\n\n const distance = Math.abs(currentPx - targetPx);\n if (distance <= 1) {\n return { type: \"snap\", position: targetPx };\n }\n\n // Skip animation if requested (e.g., role changed during operation)\n // This prevents unwanted animations after an operation ends.\n // However, allow forward animations (currentPx < targetPx) for normal swipe-to-complete.\n // Only skip backward animations (currentPx > targetPx) which occur during over-swipe.\n if (skipAnimation) {\n // Backward direction (over-swipe): snap, don't animate backward\n if (currentPx > targetPx) {\n return { type: \"snap\", position: targetPx };\n }\n // Forward direction (normal swipe-to-complete): animate from current position\n return { type: \"animate\", animation: { from: currentPx, to: targetPx } };\n }\n\n return { type: \"animate\", animation: { from: currentPx, to: targetPx } };\n};\n\n/**\n * Check if container size change requires position snap.\n * Returns the new position to snap to, or null if no snap needed.\n */\nconst computeContainerResizeSnap = (\n containerSize: number | undefined,\n prevContainerSize: number | undefined,\n targetPx: number,\n): number | null => {\n if (containerSize === undefined) {\n return null;\n }\n if (containerSize === prevContainerSize) {\n return null;\n }\n if (containerSize <= 0) {\n return null;\n }\n return targetPx;\n};\n\n/**\n * Hook for DOM-based swipe content transform.\n *\n * During swipe: immediately updates element.style.transform to follow finger.\n * After swipe: animates from current position to target position.\n *\n * @example\n * ```tsx\n * const containerRef = useRef<HTMLDivElement>(null);\n * const { isAnimating, currentPx } = useSwipeContentTransform({\n * elementRef: containerRef,\n * targetPx: 0,\n * displacement: inputState.displacement.x,\n * isOperating: inputState.phase === \"swiping\",\n * });\n * ```\n */\nexport function useSwipeContentTransform(\n options: UseSwipeContentTransformOptions,\n): UseSwipeContentTransformResult {\n const {\n elementRef,\n targetPx,\n displacement,\n isOperating,\n axis = \"horizontal\",\n animationDuration = DEFAULT_ANIMATION_DURATION,\n containerSize,\n animateOnTargetChange = false,\n initialPx,\n skipTargetChangeAnimation = false,\n } = options;\n\n // Use initialPx if provided, otherwise use targetPx\n const effectiveInitialPx = initialPx ?? targetPx;\n const currentPxRef = React.useRef<number>(effectiveInitialPx);\n const animRef = React.useRef<{ from: number; to: number } | null>(null);\n const prevTargetPxRef = React.useRef<number>(targetPx);\n const prevContainerSizeRef = React.useRef<number | undefined>(containerSize);\n const pendingAnimationRef = React.useRef<{ from: number; to: number } | null>(null);\n // Track if initial mount animation has been consumed\n const hasConsumedInitialMountRef = React.useRef<boolean>(false);\n\n // Schedule animation on first mount if initialPx differs from targetPx.\n const initialMountAnimation = computeInitialMountAnimation(\n hasConsumedInitialMountRef.current,\n containerSize,\n initialPx,\n targetPx,\n );\n if (initialMountAnimation !== null) {\n pendingAnimationRef.current = initialMountAnimation;\n hasConsumedInitialMountRef.current = true;\n }\n\n // Handle target changes when not swiping\n const targetChangeAction = computeTargetChangeAction(\n targetPx,\n prevTargetPxRef.current,\n currentPxRef.current,\n isOperating,\n animRef.current !== null,\n animateOnTargetChange,\n skipTargetChangeAnimation,\n );\n if (targetChangeAction.type === \"animate\") {\n pendingAnimationRef.current = targetChangeAction.animation;\n prevTargetPxRef.current = targetPx;\n } else if (targetChangeAction.type === \"snap\") {\n currentPxRef.current = targetChangeAction.position;\n prevTargetPxRef.current = targetPx;\n }\n\n // Snap when container size changes (resize)\n const resizeSnapPosition = computeContainerResizeSnap(\n containerSize,\n prevContainerSizeRef.current,\n targetPx,\n );\n if (resizeSnapPosition !== null) {\n currentPxRef.current = resizeSnapPosition;\n prevContainerSizeRef.current = containerSize;\n }\n\n // Animation frame handler\n const handleFrame = React.useCallback(\n ({ easedProgress }: { easedProgress: number }) => {\n const element = elementRef.current;\n const anim = animRef.current;\n if (!element || !anim) {\n return;\n }\n const value = interpolate(anim.from, anim.to, easedProgress);\n currentPxRef.current = value;\n element.style.transform = `${getTransformFn(axis)}(${value}px)`;\n },\n [axis, elementRef],\n );\n\n const handleComplete = React.useCallback(() => {\n animRef.current = null;\n currentPxRef.current = targetPx;\n prevTargetPxRef.current = targetPx;\n }, [targetPx]);\n\n const { isAnimating, start, cancel } = useAnimationFrame({\n duration: animationDuration,\n easing: easings.easeOutExpo,\n onFrame: handleFrame,\n onComplete: handleComplete,\n });\n\n // When swipe ends or target changes with animateOnTargetChange, animate to target\n React.useLayoutEffect(() => {\n if (isOperating) {\n cancel();\n animRef.current = null;\n pendingAnimationRef.current = null;\n return;\n }\n\n // Check for pending animation (from target change or initial mount)\n if (pendingAnimationRef.current) {\n const pending = pendingAnimationRef.current;\n animRef.current = pending;\n pendingAnimationRef.current = null;\n // Set initial position before animation starts\n const element = elementRef.current;\n if (element) {\n element.style.transform = `${getTransformFn(axis)}(${pending.from}px)`;\n }\n start();\n return;\n }\n\n const currentPx = currentPxRef.current;\n const distance = Math.abs(currentPx - targetPx);\n\n if (distance > 1) {\n // Need to animate from current to target\n animRef.current = { from: currentPx, to: targetPx };\n start();\n } else {\n // Close enough, snap directly\n currentPxRef.current = targetPx;\n prevTargetPxRef.current = targetPx;\n }\n }, [isOperating, targetPx, start, cancel]);\n\n // Direct DOM update during swipe\n React.useLayoutEffect(() => {\n const element = elementRef.current;\n if (!element) {\n return;\n }\n\n // Skip if animation is running, about to start, or pending\n if (isAnimating) {\n return;\n }\n if (animRef.current !== null) {\n return;\n }\n if (pendingAnimationRef.current !== null) {\n return;\n }\n\n const displayPx = targetPx + displacement;\n currentPxRef.current = displayPx;\n element.style.transform = `${getTransformFn(axis)}(${displayPx}px)`;\n }, [targetPx, displacement, axis, isAnimating, elementRef]);\n\n return {\n isAnimating,\n currentPx: currentPxRef.current,\n animationDirection: animRef.current,\n };\n}\n","/**\n * @file Hook for maintaining value continuity during continuous operations.\n *\n * During operations like swipe gestures, external state (navigation depth, panel roles)\n * may change before the gesture ends. This hook provides a pattern to:\n * - Retain the previous value during the operation for visual continuity\n * - Accept the new value when the operation ends\n * - Track whether the value changed during the operation\n *\n * This is a core primitive for the \"operation continuity\" pattern used throughout\n * the swipe gesture system.\n */\nimport * as React from \"react\";\n\n/**\n * Result from useOperationContinuity hook.\n */\nexport type UseOperationContinuityResult<T> = {\n /** The effective value (retained during operation, current after) */\n value: T;\n /**\n * True if the value changed during the operation.\n *\n * This is useful for determining how to handle the transition when the\n * operation ends. For example, if the role changed during a swipe,\n * the target position change at operation end should snap rather than animate.\n *\n * This flag is true on the render where shouldRetainPrevious becomes false\n * (operation end), allowing consumers to handle the transition appropriately.\n * It resets to false on subsequent renders.\n */\n changedDuringOperation: boolean;\n /**\n * True on the render where the operation just ended.\n *\n * This is true when shouldRetainPrevious transitions from true to false,\n * regardless of whether the value changed. Use this to detect the moment\n * when an operation completes and delay any immediate animations.\n *\n * In the over-swipe case, this helps prevent unwanted snap-back animation\n * in the intermediate render before navigation changes.\n */\n operationJustEnded: boolean;\n};\n\n/**\n * Hook for maintaining value continuity during continuous operations.\n *\n * When an operation is in progress, this hook retains the previous value\n * to prevent sudden visual changes from state updates. Once the operation\n * ends (shouldRetainPrevious becomes false), the new value is accepted.\n *\n * Additionally, this hook tracks whether the value changed during the operation,\n * which is useful for determining animation behavior at operation end.\n *\n * IMPORTANT: This hook is designed to be idempotent during render to work\n * correctly with React StrictMode, which calls the render function twice.\n * All ref mutations happen in useLayoutEffect, not during render.\n *\n * @param value - The current value from external state\n * @param shouldRetainPrevious - Whether to retain the previous value (true during operation)\n * @returns Object with effective value and whether it changed during operation\n *\n * @example\n * ```tsx\n * // Maintain role continuity during swipe\n * const { value: effectiveRole, changedDuringOperation } = useOperationContinuity(\n * role,\n * displacement > 0,\n * );\n *\n * // Use changedDuringOperation to skip animation on operation end\n * useSwipeContentTransform({\n * // ...\n * skipTargetChangeAnimation: changedDuringOperation,\n * });\n * ```\n */\nexport function useOperationContinuity<T>(\n value: T,\n shouldRetainPrevious: boolean,\n): UseOperationContinuityResult<T> {\n // Store previous shouldRetainPrevious to detect transitions\n const prevShouldRetainRef = React.useRef(shouldRetainPrevious);\n // Store retained value (the value at the start of retention)\n const retainedValueRef = React.useRef(value);\n // Track if value changed during retention\n const changedDuringRetentionRef = React.useRef(false);\n\n // Derive operationJustEnded from transition: true → false\n // This is idempotent - safe for StrictMode double-render\n const wasRetaining = prevShouldRetainRef.current;\n const operationJustEnded = wasRetaining && !shouldRetainPrevious;\n\n // Check if value diverged from retained value\n // This includes both current-render divergence and previously-tracked divergence\n const valueDiverged = value !== retainedValueRef.current;\n const currentlyDiverged = shouldRetainPrevious && valueDiverged;\n\n // Derive changedDuringOperation\n // True if:\n // 1. Value diverged during retention (tracked from previous renders via ref)\n // 2. Value diverges right now during retention (immediate comparison)\n // 3. Value diverged at the moment retention ends\n const changedDuringRetention = changedDuringRetentionRef.current || currentlyDiverged;\n const changedAtExit = operationJustEnded && valueDiverged;\n const changedDuringOperation = changedDuringRetention || changedAtExit;\n\n // Determine effective value\n // During retention: use retained value\n // After retention ends: use current value\n const effectiveValue = shouldRetainPrevious ? retainedValueRef.current : value;\n\n // Update refs in useLayoutEffect to ensure idempotency during render.\n // This runs once per commit, not per render in StrictMode.\n React.useLayoutEffect(() => {\n if (!shouldRetainPrevious) {\n // Retention ended or never started - reset state\n changedDuringRetentionRef.current = false;\n retainedValueRef.current = value;\n } else {\n // During retention - track if value diverged\n if (currentlyDiverged) {\n changedDuringRetentionRef.current = true;\n }\n }\n prevShouldRetainRef.current = shouldRetainPrevious;\n });\n\n return {\n value: effectiveValue,\n changedDuringOperation,\n operationJustEnded,\n };\n}\n","/**\n * @file Pure functions for computing Stack panel transforms during swipe.\n *\n * These functions calculate positions for iOS-style \"swipe to go back\" behavior\n * where the active panel follows the finger and the behind panel reveals from -30%.\n */\n\n/**\n * Default offset for behind panel (-30% of container width).\n */\nexport const DEFAULT_BEHIND_OFFSET = -0.3;\n\n/**\n * Compute the target position for the active panel.\n *\n * Active panel starts at 0 and moves right as the user swipes.\n *\n * @param displacement - Current swipe displacement in pixels\n * @returns Target position in pixels\n */\nexport function computeActiveTargetPx(displacement: number): number {\n // Active panel follows the finger directly\n // Only move right (positive displacement) for left-edge swipe\n return Math.max(0, displacement);\n}\n\n/**\n * Compute the target position for the behind panel.\n *\n * Behind panel starts at -30% and moves to 0% as swipe progresses.\n * Uses parallax effect: moves slower than the active panel.\n *\n * @param displacement - Current swipe displacement in pixels\n * @param containerSize - Container width/height in pixels\n * @param behindOffset - Starting offset ratio (default -0.3 for -30%)\n * @returns Target position in pixels\n */\nexport function computeBehindTargetPx(\n displacement: number,\n containerSize: number,\n behindOffset: number = DEFAULT_BEHIND_OFFSET,\n): number {\n if (containerSize <= 0) {\n return 0;\n }\n\n // Only respond to positive displacement (swipe right)\n const clampedDisplacement = Math.max(0, displacement);\n\n // Calculate progress (0 to 1)\n const progress = Math.min(clampedDisplacement / containerSize, 1);\n\n // Behind panel starts at behindOffset * containerSize and moves to 0\n // Parallax: moves |behindOffset| * progress * containerSize\n const basePosition = behindOffset * containerSize;\n const parallaxOffset = Math.abs(behindOffset) * progress * containerSize;\n\n return basePosition + parallaxOffset;\n}\n\n/**\n * Compute swipe progress as a ratio (0 to 1).\n *\n * @param displacement - Current swipe displacement in pixels\n * @param containerSize - Container width/height in pixels\n * @returns Progress ratio from 0 to 1\n */\nexport function computeSwipeProgress(displacement: number, containerSize: number): number {\n if (containerSize <= 0) {\n return 0;\n }\n\n const clampedDisplacement = Math.max(0, displacement);\n return Math.min(clampedDisplacement / containerSize, 1);\n}\n\n/**\n * Input for computing swipe visibility.\n */\nexport type ComputeSwipeVisibilityInput = {\n /** Panel depth in the stack */\n depth: number;\n /** Current navigation depth (active panel) */\n navigationDepth: number;\n /** Whether this panel is currently active */\n isActive: boolean;\n /** Whether swipe gesture is active */\n isOperating: boolean;\n /** Whether snap-back animation is running */\n isAnimating: boolean;\n};\n\n/**\n * Compute whether a panel should be visible during swipe.\n *\n * During swipe:\n * - Active panel is always visible\n * - Behind panel (depth = navigationDepth - 1) is visible when swiping/animating\n *\n * @returns true if panel should be visible\n */\nexport function computeSwipeVisibility(input: ComputeSwipeVisibilityInput): boolean {\n const { depth, navigationDepth, isActive, isOperating, isAnimating } = input;\n\n // Active panel is always visible\n if (isActive) {\n return true;\n }\n\n // Behind panel (one level back) is visible during swipe or animation\n const isBehindPanel = depth === navigationDepth - 1;\n if (isBehindPanel) {\n if (isOperating) {\n return true;\n }\n if (isAnimating) {\n return true;\n }\n }\n\n // Other panels are hidden during swipe\n return false;\n}\n\n/**\n * Determine the role of a panel during swipe gesture.\n */\nexport type SwipePanelRole = \"active\" | \"behind\" | \"hidden\";\n\n/**\n * Determine the role of a panel during swipe.\n *\n * @param depth - Panel depth in the stack\n * @param navigationDepth - Current navigation depth (active panel)\n * @returns Panel role for swipe handling\n */\nexport function determineSwipePanelRole(depth: number, navigationDepth: number): SwipePanelRole {\n if (depth === navigationDepth) {\n return \"active\";\n }\n if (depth === navigationDepth - 1) {\n return \"behind\";\n }\n return \"hidden\";\n}\n","/**\n * @file SwipeStackContent component for Stack panels with direct DOM manipulation.\n *\n * Provides iOS-style swipe-to-go-back behavior:\n * - Active panel follows the finger directly\n * - Behind panel reveals from -30% with parallax effect\n *\n * Uses useSwipeContentTransform for immediate DOM updates.\n */\nimport * as React from \"react\";\nimport { useSwipeContentTransform } from \"../../hooks/useSwipeContentTransform.js\";\nimport { useOperationContinuity } from \"../../hooks/useOperationContinuity.js\";\nimport type { ContinuousOperationState, GestureAxis } from \"../../hooks/gesture/types.js\";\nimport type { StackDisplayMode } from \"./types.js\";\nimport {\n computeActiveTargetPx,\n computeBehindTargetPx,\n computeSwipeVisibility,\n determineSwipePanelRole,\n DEFAULT_BEHIND_OFFSET,\n} from \"./computeSwipeStackTransform.js\";\n\nconst DEFAULT_ANIMATION_DURATION = 300;\n\n/**\n * Scale factor per depth level for \"stack\" display mode.\n * Each level behind reduces scale by this amount.\n */\nconst STACK_SCALE_FACTOR = 0.05;\n\n/**\n * Maximum dimming opacity for behind panels in iOS-style navigation.\n */\nconst MAX_DIM_OPACITY = 0.1;\n\n/**\n * Props for SwipeStackContent component.\n *\n * This component accepts ContinuousOperationState, meaning it responds uniformly\n * to any continuous operation (whether human gesture or system animation).\n */\nexport type SwipeStackContentProps = {\n /** Panel ID */\n id: string;\n /** Panel depth in the stack */\n depth: number;\n /** Current navigation depth (active panel) */\n navigationDepth: number;\n /** Whether this panel is currently active */\n isActive: boolean;\n /** Continuous operation state (from gesture input or animation system) */\n operationState: ContinuousOperationState;\n /** Container size in pixels (width for horizontal, height for vertical) */\n containerSize: number;\n /** Gesture axis. @default \"horizontal\" */\n axis?: GestureAxis;\n /** Behind panel offset ratio. @default -0.3 */\n behindOffset?: number;\n /** Animation duration in ms. @default 300 */\n animationDuration?: number;\n /**\n * Whether to animate when first mounted as active.\n * Set to true for push navigation animations.\n * @default false\n */\n animateOnMount?: boolean;\n /**\n * Whether to show iOS-style edge shadow on active panel.\n * @default true\n */\n showShadow?: boolean;\n /**\n * Display mode for visual styling.\n * - \"overlay\": panels overlay, no scale (iOS style)\n * - \"slide\": panels slide with parallax\n * - \"stack\": panels scale down and offset (stacked cards style)\n * @default \"overlay\"\n */\n displayMode?: StackDisplayMode;\n /**\n * Whether to show dimming overlay on behind panels.\n * Creates iOS-style darkening effect that fades during swipe.\n * @default true\n */\n showDimming?: boolean;\n /** Content to render */\n children: React.ReactNode;\n};\n\nconst BASE_STYLE: React.CSSProperties = {\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/**\n * Get displacement from operation state for the given axis.\n */\nconst getAxisDisplacement = (state: ContinuousOperationState, axis: GestureAxis): number => {\n if (state.phase === \"idle\") {\n return 0;\n }\n return axis === \"horizontal\" ? state.displacement.x : state.displacement.y;\n};\n\n/**\n * SwipeStackContent renders a single stack panel with swipe gesture support.\n *\n * Key behaviors:\n * - Active panel: follows finger directly (translateX = displacement)\n * - Behind panel: reveals from -30% with parallax (slower movement)\n * - Hidden panels: not rendered during swipe\n *\n * @example\n * ```tsx\n * <SwipeStackContent\n * id=\"detail\"\n * depth={1}\n * navigationDepth={1}\n * isActive={true}\n * inputState={swipeInput.inputState}\n * containerSize={containerWidth}\n * >\n * <DetailPanel />\n * </SwipeStackContent>\n * ```\n */\n// iOS-style left edge shadow for active panels\nconst ACTIVE_PANEL_SHADOW = \"-5px 0 15px rgba(0, 0, 0, 0.1)\";\n\nexport const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(\n ({\n id,\n depth,\n navigationDepth,\n isActive,\n operationState,\n containerSize,\n axis = \"horizontal\",\n behindOffset = DEFAULT_BEHIND_OFFSET,\n animationDuration = DEFAULT_ANIMATION_DURATION,\n animateOnMount = false,\n showShadow = true,\n displayMode = \"overlay\",\n showDimming = true,\n children,\n }) => {\n const elementRef = React.useRef<HTMLDivElement>(null);\n\n const displacement = getAxisDisplacement(operationState, axis);\n const isOperating = operationState.phase === \"operating\";\n\n // Determine panel role\n const role = determineSwipePanelRole(depth, navigationDepth);\n\n // Maintain role continuity during swipe operations.\n // When navigation changes before the gesture ends (e.g., role changes from\n // \"behind\" to \"active\"), we keep using the previous role for position\n // calculations to prevent visual jumps.\n // changedDuringOperation tells us if the role changed during the operation,\n // which we use to skip backward target change animation (over-swipe case).\n const { value: effectiveRole, changedDuringOperation } = useOperationContinuity(\n role,\n displacement > 0,\n );\n\n // Compute target position based on effective role\n const targetPx = React.useMemo(() => {\n switch (effectiveRole) {\n case \"active\":\n // Active panel rests at 0\n return 0;\n case \"behind\":\n // Behind panel rests at offset position\n return behindOffset * containerSize;\n case \"hidden\":\n // Hidden panels are off-screen\n return containerSize;\n }\n }, [effectiveRole, behindOffset, containerSize]);\n\n // Compute displacement for this panel\n const panelDisplacement = React.useMemo(() => {\n if (displacement <= 0) {\n return 0;\n }\n\n switch (effectiveRole) {\n case \"active\":\n // Active panel follows finger directly\n return computeActiveTargetPx(displacement);\n case \"behind\": {\n // Behind panel uses parallax - compute offset from base position\n const currentPos = computeBehindTargetPx(displacement, containerSize, behindOffset);\n const basePos = behindOffset * containerSize;\n return currentPos - basePos;\n }\n case \"hidden\":\n return 0;\n }\n }, [effectiveRole, displacement, containerSize, behindOffset]);\n\n // Compute initial position for push animation\n // When animateOnMount is true and panel is first mounted as \"active\",\n // it should animate in from off-screen\n // Root panel (depth=0) should not animate on mount\n // Note: useSwipeContentTransform handles first-mount tracking internally,\n // so we just declare the initial position; the hook consumes it only once.\n const initialPx = React.useMemo(() => {\n if (!animateOnMount) {\n return undefined;\n }\n if (role === \"active\" && depth > 0) {\n // New active panel (not root): start from off-screen right\n return containerSize;\n }\n // Root panel or other roles: start at their natural position\n return undefined;\n }, [animateOnMount, role, depth, containerSize]);\n\n // Use shared transform hook for DOM manipulation\n const { isAnimating } = useSwipeContentTransform({\n elementRef,\n targetPx,\n displacement: panelDisplacement,\n isOperating,\n axis,\n animationDuration,\n containerSize,\n // Animate when targetPx changes (button navigation)\n animateOnTargetChange: true,\n // For push animation: start from off-screen\n initialPx,\n // Skip backward animation if role changed during the operation.\n // This handles over-swipe where panel moves beyond 100% and needs to snap back.\n // useSwipeContentTransform allows forward animations (normal swipe-to-complete)\n // but skips backward animations (over-swipe snap).\n skipTargetChangeAnimation: changedDuringOperation,\n });\n\n // Compute visibility\n const visible = computeSwipeVisibility({\n depth,\n navigationDepth,\n isActive,\n isOperating,\n isAnimating,\n });\n\n // Compute swipe progress for scale and dimming interpolation\n const swipeProgress = React.useMemo(() => {\n if (containerSize <= 0 || displacement <= 0) {\n return 0;\n }\n return Math.min(displacement / containerSize, 1);\n }, [displacement, containerSize]);\n\n // Compute scale for \"stack\" display mode\n // Behind panels are scaled down, and scale interpolates during swipe\n const scale = React.useMemo(() => {\n if (displayMode !== \"stack\") {\n return 1; // No scale for overlay/slide modes\n }\n\n const depthDiff = navigationDepth - depth;\n\n if (role === \"active\") {\n return 1; // Active panel is always at full scale\n }\n\n if (role === \"behind\") {\n // Base scale for behind panel\n const baseScale = 1 - depthDiff * STACK_SCALE_FACTOR;\n // During swipe, interpolate toward 1\n return baseScale + swipeProgress * (1 - baseScale);\n }\n\n return 1;\n }, [displayMode, role, depth, navigationDepth, swipeProgress]);\n\n // Compute dimming opacity for behind panels\n // Full dimming at rest, fades to 0 during swipe\n const dimmingOpacity = React.useMemo(() => {\n if (!showDimming || role !== \"behind\") {\n return 0;\n }\n // Fade from MAX_DIM_OPACITY to 0 as swipe progresses\n return MAX_DIM_OPACITY * (1 - swipeProgress);\n }, [showDimming, role, swipeProgress]);\n\n // Update visibility via direct DOM manipulation\n React.useLayoutEffect(() => {\n const element = elementRef.current;\n if (element) {\n element.style.visibility = visible ? \"visible\" : \"hidden\";\n }\n }, [visible]);\n\n // Update scale via direct DOM manipulation for smooth animation\n React.useLayoutEffect(() => {\n const element = elementRef.current;\n if (!element || displayMode !== \"stack\") {\n return;\n }\n\n // Get current transform (translateX) and append scale\n const currentTransform = element.style.transform;\n if (currentTransform.includes(\"translateX\")) {\n // Extract translateX value and combine with scale\n const translateMatch = currentTransform.match(/translateX\\([^)]+\\)/);\n if (translateMatch) {\n element.style.transform = `${translateMatch[0]} scale(${scale})`;\n }\n } else {\n element.style.transform = `scale(${scale})`;\n }\n }, [scale, displayMode]);\n\n // Compute shadow for active panel\n // Shadow is shown on panels at depth > 0 when they're active or animating\n const shouldShowShadow = showShadow ? depth > 0 && role === \"active\" : false;\n\n // Static style - transform is handled entirely by useSwipeContentTransform\n // to ensure smooth animations\n const staticStyle = React.useMemo<React.CSSProperties>(\n () => ({\n ...BASE_STYLE,\n pointerEvents: isActive ? \"auto\" : \"none\",\n willChange: \"transform\",\n zIndex: depth,\n visibility: visible ? \"visible\" : \"hidden\",\n boxShadow: shouldShowShadow ? ACTIVE_PANEL_SHADOW : undefined,\n }),\n [isActive, depth, visible, shouldShowShadow],\n );\n\n // Dimming overlay style for behind panels\n const dimmingStyle = React.useMemo<React.CSSProperties | null>(() => {\n if (dimmingOpacity <= 0) {\n return null;\n }\n return {\n position: \"absolute\",\n inset: 0,\n backgroundColor: `rgba(0, 0, 0, ${dimmingOpacity})`,\n pointerEvents: \"none\",\n zIndex: 1,\n };\n }, [dimmingOpacity]);\n\n return (\n <div\n ref={elementRef}\n data-stack-content={id}\n data-depth={depth}\n data-active={isActive ? \"true\" : \"false\"}\n data-role={role}\n style={staticStyle}\n >\n {children}\n {dimmingStyle != null ? <div style={dimmingStyle} data-dimming-overlay /> : null}\n </div>\n );\n },\n);\n","/**\n * @file SwipeStackOutlet component for rendering stack with swipe support.\n *\n * Uses SwipeStackContent for direct DOM manipulation during swipe gestures,\n * providing iOS-style smooth swipe-to-go-back behavior.\n */\nimport * as React from \"react\";\nimport { SwipeStackContent } from \"./SwipeStackContent.js\";\nimport type { ContinuousOperationState } from \"../../hooks/gesture/types.js\";\nimport type { StackPanel, StackNavigationState } from \"./types.js\";\n\nconst DEFAULT_ANIMATION_DURATION = 300;\n\n/**\n * Props for SwipeStackOutlet component.\n */\nexport type SwipeStackOutletProps = {\n /** Array of panel definitions */\n panels: ReadonlyArray<StackPanel>;\n /** Current navigation state */\n navigationState: StackNavigationState;\n /** Continuous operation state (from gesture input or animation system) */\n operationState: ContinuousOperationState;\n /** Container size in pixels (width for horizontal swipe) */\n containerSize: number;\n /** Function to get cached content for a panel */\n getCachedContent?: (panelId: string) => React.ReactNode | null;\n /** Behind panel offset ratio. @default -0.3 */\n behindOffset?: number;\n /**\n * Whether to animate new panels on mount.\n * @default false\n */\n animateOnMount?: boolean;\n /**\n * Animation duration in ms.\n * @default 300\n */\n animationDuration?: number;\n /**\n * Whether to show iOS-style edge shadow on active panels.\n * @default true\n */\n showShadow?: boolean;\n /**\n * Display mode for visual styling.\n * - \"overlay\": panels overlay, no scale (iOS style)\n * - \"slide\": panels slide with parallax\n * - \"stack\": panels scale down and offset (stacked cards style)\n * @default \"overlay\"\n */\n displayMode?: \"overlay\" | \"slide\" | \"stack\";\n /**\n * Whether to show dimming overlay on behind panels.\n * @default true\n */\n showDimming?: boolean;\n};\n\n/**\n * Get visible panels for rendering during swipe.\n *\n * Only renders active panel and immediate behind panel for performance.\n * Also includes exiting panel when navigating back.\n */\nfunction getVisiblePanels(\n panels: ReadonlyArray<StackPanel>,\n navigationState: StackNavigationState,\n exitingPanelId: string | null,\n): Array<{ panel: StackPanel; depth: number; isExiting: boolean }> {\n const { stack, depth } = navigationState;\n\n // During swipe, we only need active and behind panels\n const visibleDepths = [depth];\n if (depth > 0) {\n visibleDepths.push(depth - 1);\n }\n\n const result: Array<{ panel: StackPanel; depth: number; isExiting: boolean }> = [];\n\n // Add panels at visible depths\n for (const d of visibleDepths) {\n const id = stack[d];\n const panel = panels.find((p) => p.id === id);\n if (panel) {\n result.push({ panel, depth: d, isExiting: false });\n }\n }\n\n // Add exiting panel if it's not already included\n if (exitingPanelId != null) {\n const alreadyIncluded = result.some((r) => r.panel.id === exitingPanelId);\n if (!alreadyIncluded) {\n const exitingPanel = panels.find((p) => p.id === exitingPanelId);\n if (exitingPanel) {\n // Exiting panel is at depth + 1 (was previously active)\n result.push({ panel: exitingPanel, depth: depth + 1, isExiting: true });\n }\n }\n }\n\n // Render in order: behind first, then active, then exiting\n return result.sort((a, b) => a.depth - b.depth);\n}\n\n/**\n * SwipeStackOutlet renders stack panels with swipe gesture support.\n *\n * Unlike the default StackOutlet, this component:\n * - Uses SwipeStackContent for direct DOM manipulation\n * - Only renders active and behind panels for performance\n * - Supports iOS-style parallax reveal animation\n *\n * @example\n * ```tsx\n * const navigation = useStackNavigation({ panels, displayMode: 'overlay' });\n * const swipeInput = useStackSwipeInput({ containerRef, navigation });\n *\n * return (\n * <div ref={containerRef} {...swipeInput.containerProps}>\n * <SwipeStackOutlet\n * panels={navigation.panels}\n * navigationState={navigation.state}\n * operationState={toContinuousOperationState(swipeInput.inputState)}\n * containerSize={containerWidth}\n * />\n * </div>\n * );\n * ```\n */\nexport const SwipeStackOutlet: React.FC<SwipeStackOutletProps> = React.memo(\n ({\n panels,\n navigationState,\n operationState,\n containerSize,\n getCachedContent,\n behindOffset,\n animateOnMount = false,\n animationDuration = DEFAULT_ANIMATION_DURATION,\n showShadow,\n displayMode,\n showDimming,\n }) => {\n // Track the exiting panel ID when navigating back\n const [exitingPanelId, setExitingPanelId] = React.useState<string | null>(null);\n const prevDepthRef = React.useRef(navigationState.depth);\n const prevStackRef = React.useRef<ReadonlyArray<string>>(navigationState.stack);\n\n // Detect when we navigate back and need to animate out\n React.useLayoutEffect(() => {\n const prevDepth = prevDepthRef.current;\n const prevStack = prevStackRef.current;\n const { depth, stack } = navigationState;\n\n // Update refs\n prevDepthRef.current = depth;\n prevStackRef.current = stack;\n\n // Check if we went back (depth decreased)\n if (depth < prevDepth) {\n // The panel at prevDepth is exiting\n const exitingId = prevStack[prevDepth];\n if (exitingId != null) {\n setExitingPanelId(exitingId);\n\n // Clear exiting panel after animation completes\n const timeoutId = setTimeout(() => {\n setExitingPanelId(null);\n }, animationDuration);\n\n return () => clearTimeout(timeoutId);\n }\n }\n }, [navigationState.depth, navigationState.stack, animationDuration]);\n\n const visiblePanels = React.useMemo(\n () => getVisiblePanels(panels, navigationState, exitingPanelId),\n [panels, navigationState, exitingPanelId],\n );\n\n const containerStyle: React.CSSProperties = React.useMemo(\n () => ({\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n }),\n [],\n );\n\n return (\n <div style={containerStyle} data-swipe-stack-container>\n {visiblePanels.map(({ panel, depth, isExiting }) => {\n const isActive = depth === navigationState.depth && !isExiting;\n const content = getCachedContent?.(panel.id) ?? panel.content;\n\n return (\n <SwipeStackContent\n key={panel.id}\n id={panel.id}\n depth={depth}\n navigationDepth={navigationState.depth}\n isActive={isActive}\n operationState={operationState}\n containerSize={containerSize}\n behindOffset={behindOffset}\n animateOnMount={animateOnMount}\n animationDuration={animationDuration}\n showShadow={showShadow}\n displayMode={displayMode}\n showDimming={showDimming}\n >\n {content}\n </SwipeStackContent>\n );\n })}\n </div>\n );\n },\n);\n"],"names":["computeAnimationType","input","wasActive","isActive","transitionMode","computeVisibility","displayMode","depth","navigationDepth","isAnimatingOut","isRevealing","revealDepth","computeTransform","activeDepth","isPrevious","offset","scale","computeSwipeTransform","baseTransform","swipeProgress","computeTransitionCss","STACK_TRANSITION_DURATION","STACK_TRANSITION_EASING","computeStackContentState","currentAnimationType","navigationState","nextAnimationType","visibility","transform","animation","STACK_ANIMATION_PUSH","STACK_ANIMATION_POP","transition","baseStyle","StackContent","React","id","children","ref","prevActiveRef","animationType","setAnimationType","computedState","useIsomorphicLayoutEffect","handleAnimationEnd","e","style","s","content","jsx","stackReducer","state","action","targetDepth","StackOutletContext","StackOutletInner","ctx","forceUpdate","x","panels","visiblePanels","index","panel","p","useStackNavigation","options","initialPanelId","onPanelChange","initialId","stack","dispatch","stackRef","prevStackRef","prevStack","newDepth","newPanelId","revealState","setRevealState","currentPanelId","previousPanelId","push","go","direction","move","replace","canGo","revealParent","currentDepth","revealTo","revealRoot","dismissReveal","getPanelProps","currentStack","panelIndex","getBackButtonProps","canGoBack","prevPanelId","prevPanel","label","containerStyle","stateRef","subscribersRef","callback","resolveContent","panelId","validIds","getCachedContent","useContentCache","contextValue","Outlet","OutletComponent","useStackSwipeInput","containerRef","navigation","edge","edgeWidth","enabled","swipeState","setSwipeState","handleSwipeEnd","isEdgeGesture","inputState","swipeProps","useEdgeSwipeInput","guardProps","useNativeGestureGuard","progress","containerWidth","displacement","absDisplacement","containerProps","mergeGestureContainerProps","effectiveInputState","IDLE_SWIPE_INPUT_STATE","easings","DEFAULT_DURATION","useAnimationFrame","duration","easing","onFrame","onComplete","isAnimating","setIsAnimating","rafIdRef","startTimeRef","onFrameRef","onCompleteRef","cancel","start","step","timestamp","elapsed","easedProgress","isComplete","interpolate","from","to","DEFAULT_ANIMATION_DURATION","getTransformFn","axis","computeInitialMountAnimation","hasConsumed","containerSize","initialPx","targetPx","computeTargetChangeAction","prevTargetPx","currentPx","isOperating","animateOnTargetChange","skipAnimation","computeContainerResizeSnap","prevContainerSize","useSwipeContentTransform","elementRef","animationDuration","skipTargetChangeAnimation","effectiveInitialPx","currentPxRef","animRef","prevTargetPxRef","prevContainerSizeRef","pendingAnimationRef","hasConsumedInitialMountRef","initialMountAnimation","targetChangeAction","resizeSnapPosition","handleFrame","element","anim","value","handleComplete","pending","displayPx","useOperationContinuity","shouldRetainPrevious","prevShouldRetainRef","retainedValueRef","changedDuringRetentionRef","operationJustEnded","valueDiverged","currentlyDiverged","changedDuringOperation","effectiveValue","DEFAULT_BEHIND_OFFSET","computeActiveTargetPx","computeBehindTargetPx","behindOffset","clampedDisplacement","basePosition","parallaxOffset","computeSwipeVisibility","determineSwipePanelRole","STACK_SCALE_FACTOR","MAX_DIM_OPACITY","BASE_STYLE","getAxisDisplacement","ACTIVE_PANEL_SHADOW","SwipeStackContent","operationState","animateOnMount","showShadow","showDimming","role","effectiveRole","panelDisplacement","currentPos","basePos","visible","depthDiff","baseScale","dimmingOpacity","currentTransform","translateMatch","shouldShowShadow","staticStyle","dimmingStyle","jsxs","getVisiblePanels","exitingPanelId","visibleDepths","result","d","r","exitingPanel","a","b","SwipeStackOutlet","setExitingPanelId","prevDepthRef","prevDepth","exitingId","timeoutId","isExiting"],"mappings":"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"}