react-panel-layout 0.5.2 → 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 (541) hide show
  1. package/dist/{FloatingPanelFrame-lLg-Lpg7.js → FloatingPanelFrame-3eU9AwPo.js} +11 -11
  2. package/dist/{FloatingPanelFrame-lLg-Lpg7.js.map → FloatingPanelFrame-3eU9AwPo.js.map} +1 -1
  3. package/dist/{FloatingPanelFrame-D9Cp2al1.cjs → FloatingPanelFrame-CEmXDvUA.cjs} +2 -2
  4. package/dist/{FloatingPanelFrame-D9Cp2al1.cjs.map → FloatingPanelFrame-CEmXDvUA.cjs.map} +1 -1
  5. package/dist/FloatingWindow-CUXnEtrb.js +827 -0
  6. package/dist/FloatingWindow-CUXnEtrb.js.map +1 -0
  7. package/dist/FloatingWindow-DMwyK0eK.cjs +2 -0
  8. package/dist/FloatingWindow-DMwyK0eK.cjs.map +1 -0
  9. package/dist/GridLayout-DKTg_N61.cjs +2 -0
  10. package/dist/GridLayout-DKTg_N61.cjs.map +1 -0
  11. package/dist/GridLayout-UWNxXw77.js +926 -0
  12. package/dist/GridLayout-UWNxXw77.js.map +1 -0
  13. package/dist/HorizontalDivider-DdxzfV0l.js +30 -0
  14. package/dist/HorizontalDivider-DdxzfV0l.js.map +1 -0
  15. package/dist/HorizontalDivider-_pgV4Mcv.cjs +2 -0
  16. package/dist/HorizontalDivider-_pgV4Mcv.cjs.map +1 -0
  17. package/dist/PanelSystem-BqUzNtf2.js +1946 -0
  18. package/dist/PanelSystem-BqUzNtf2.js.map +1 -0
  19. package/dist/PanelSystem-D603LKKv.cjs +3 -0
  20. package/dist/PanelSystem-D603LKKv.cjs.map +1 -0
  21. package/dist/ResizeHandle-CBcAS918.cjs +2 -0
  22. package/dist/ResizeHandle-CBcAS918.cjs.map +1 -0
  23. package/dist/ResizeHandle-CXjc1meV.js +119 -0
  24. package/dist/ResizeHandle-CXjc1meV.js.map +1 -0
  25. package/dist/SwipePivotTabBar-DWrCuwEI.js +411 -0
  26. package/dist/SwipePivotTabBar-DWrCuwEI.js.map +1 -0
  27. package/dist/SwipePivotTabBar-fjjXkpj7.cjs +2 -0
  28. package/dist/SwipePivotTabBar-fjjXkpj7.cjs.map +1 -0
  29. package/dist/components/gesture/SwipeSafeZone.d.ts +40 -0
  30. package/dist/components/window/Drawer.d.ts +3 -1
  31. package/dist/components/window/DrawerLayers.d.ts +1 -1
  32. package/dist/components/window/drawerStyles.d.ts +69 -0
  33. package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
  34. package/dist/components/window/useDrawerSwipeTransform.d.ts +23 -0
  35. package/dist/config.cjs +1 -1
  36. package/dist/config.cjs.map +1 -1
  37. package/dist/config.js +11 -9
  38. package/dist/config.js.map +1 -1
  39. package/dist/constants/styles.d.ts +35 -4
  40. package/dist/dialog/index.d.ts +69 -0
  41. package/dist/floating.cjs +1 -1
  42. package/dist/floating.js +1 -1
  43. package/dist/grid/index.d.ts +58 -0
  44. package/dist/grid.cjs +2 -0
  45. package/dist/grid.cjs.map +1 -0
  46. package/dist/grid.js +13 -0
  47. package/dist/grid.js.map +1 -0
  48. package/dist/hooks/gesture/presets.d.ts +33 -0
  49. package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +117 -0
  50. package/dist/hooks/gesture/thresholdValue.d.ts +44 -0
  51. package/dist/hooks/gesture/types.d.ts +297 -0
  52. package/dist/hooks/gesture/useDirectionalLock.d.ts +20 -0
  53. package/dist/hooks/gesture/useEdgeSwipeInput.d.ts +23 -0
  54. package/dist/hooks/gesture/useNativeGestureGuard.d.ts +23 -0
  55. package/dist/hooks/gesture/usePointerTracking.d.ts +22 -0
  56. package/dist/hooks/gesture/useScrollBoundary.d.ts +23 -0
  57. package/dist/hooks/gesture/useSwipeInput.d.ts +5 -0
  58. package/dist/hooks/gesture/utils.d.ts +59 -0
  59. package/dist/hooks/useAnimatedVisibility.d.ts +58 -0
  60. package/dist/hooks/useAnimationFrame.d.ts +86 -0
  61. package/dist/hooks/useOperationContinuity.d.ts +64 -0
  62. package/dist/hooks/useResizeObserver.d.ts +33 -1
  63. package/dist/hooks/useSharedElementTransition.d.ts +112 -0
  64. package/dist/hooks/useSnapAnimation.d.ts +54 -0
  65. package/dist/hooks/useSwipeContentTransform.d.ts +86 -0
  66. package/dist/index.cjs +1 -2
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.d.ts +0 -1
  69. package/dist/index.js +25 -2006
  70. package/dist/index.js.map +1 -1
  71. package/dist/modules/dialog/AlertDialog.d.ts +9 -0
  72. package/dist/modules/dialog/DialogContainer.d.ts +37 -0
  73. package/dist/modules/dialog/Modal.d.ts +26 -0
  74. package/dist/modules/dialog/SwipeDialogContainer.d.ts +16 -0
  75. package/dist/modules/dialog/dialogAnimationUtils.d.ts +113 -0
  76. package/dist/modules/dialog/types.d.ts +183 -0
  77. package/dist/modules/dialog/useDialog.d.ts +39 -0
  78. package/dist/modules/dialog/useDialogContainer.d.ts +47 -0
  79. package/dist/modules/dialog/useDialogSwipeInput.d.ts +70 -0
  80. package/dist/modules/dialog/useDialogTransform.d.ts +82 -0
  81. package/dist/modules/drawer/types.d.ts +74 -0
  82. package/dist/modules/drawer/useDrawerSwipeInput.d.ts +24 -0
  83. package/dist/modules/pivot/PivotContent.d.ts +1 -1
  84. package/dist/modules/pivot/SwipePivotContent.d.ts +39 -0
  85. package/dist/modules/pivot/SwipePivotContent.debug.tmp.d.ts +25 -0
  86. package/dist/modules/pivot/SwipePivotContent.test.d.ts +1 -0
  87. package/dist/modules/pivot/SwipePivotTabBar.d.ts +92 -0
  88. package/dist/modules/pivot/index.d.ts +3 -0
  89. package/dist/modules/pivot/scaleInputState.d.ts +37 -0
  90. package/dist/modules/pivot/types.d.ts +67 -2
  91. package/dist/modules/pivot/usePivotSwipeInput.d.ts +68 -0
  92. package/dist/modules/stack/StackContent.d.ts +15 -0
  93. package/dist/modules/stack/SwipeStackContent.d.ts +66 -0
  94. package/dist/modules/stack/SwipeStackOutlet.d.ts +80 -0
  95. package/dist/modules/stack/computeStackContentState.d.ts +99 -0
  96. package/dist/modules/stack/computeSwipeStackTransform.d.ts +76 -0
  97. package/dist/modules/stack/types.d.ts +194 -0
  98. package/dist/modules/stack/useStackAnimationState.d.ts +32 -0
  99. package/dist/modules/stack/useStackNavigation.d.ts +23 -0
  100. package/dist/modules/stack/useStackSwipeInput.d.ts +27 -0
  101. package/dist/panels/index.d.ts +67 -0
  102. package/dist/panels.cjs +2 -0
  103. package/dist/panels.cjs.map +1 -0
  104. package/dist/panels.js +28 -0
  105. package/dist/panels.js.map +1 -0
  106. package/dist/pivot/index.d.ts +3 -0
  107. package/dist/pivot.cjs +1 -1
  108. package/dist/pivot.cjs.map +1 -1
  109. package/dist/pivot.js +20 -2
  110. package/dist/pivot.js.map +1 -1
  111. package/dist/resizer/index.d.ts +57 -0
  112. package/dist/resizer.cjs +2 -0
  113. package/dist/resizer.cjs.map +1 -0
  114. package/dist/resizer.js +8 -0
  115. package/dist/resizer.js.map +1 -0
  116. package/dist/stack/index.d.ts +72 -0
  117. package/dist/stack.cjs +2 -0
  118. package/dist/stack.cjs.map +1 -0
  119. package/dist/stack.js +721 -0
  120. package/dist/stack.js.map +1 -0
  121. package/dist/sticky-header/StickyArea.d.ts +38 -0
  122. package/dist/sticky-header/calculateStickyMetrics.d.ts +28 -0
  123. package/dist/sticky-header/index.d.ts +4 -4
  124. package/dist/sticky-header/types.d.ts +35 -22
  125. package/dist/sticky-header.cjs +1 -1
  126. package/dist/sticky-header.cjs.map +1 -1
  127. package/dist/sticky-header.js +73 -174
  128. package/dist/sticky-header.js.map +1 -1
  129. package/dist/styles-NkjuMOVS.js +57 -0
  130. package/dist/styles-NkjuMOVS.js.map +1 -0
  131. package/dist/styles-qf6ptVLD.cjs +2 -0
  132. package/dist/styles-qf6ptVLD.cjs.map +1 -0
  133. package/dist/types.d.ts +16 -0
  134. package/dist/useContentCache-CO3LYNmz.js +24 -0
  135. package/dist/useContentCache-CO3LYNmz.js.map +1 -0
  136. package/dist/useContentCache-DqXtLrLs.cjs +2 -0
  137. package/dist/useContentCache-DqXtLrLs.cjs.map +1 -0
  138. package/dist/useDocumentPointerEvents-DXxw3qWj.js +54 -0
  139. package/dist/useDocumentPointerEvents-DXxw3qWj.js.map +1 -0
  140. package/dist/useDocumentPointerEvents-DxDSOtip.cjs +2 -0
  141. package/dist/useDocumentPointerEvents-DxDSOtip.cjs.map +1 -0
  142. package/dist/useFloatingState-C4kRaW_R.cjs +2 -0
  143. package/dist/useFloatingState-C4kRaW_R.cjs.map +1 -0
  144. package/dist/useFloatingState-tEfA_wbc.js +74 -0
  145. package/dist/useFloatingState-tEfA_wbc.js.map +1 -0
  146. package/dist/useNativeGestureGuard-C7TSqEkr.cjs +2 -0
  147. package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +1 -0
  148. package/dist/useNativeGestureGuard-CGYo6O0r.js +347 -0
  149. package/dist/useNativeGestureGuard-CGYo6O0r.js.map +1 -0
  150. package/dist/window/index.d.ts +63 -0
  151. package/dist/window.cjs +2 -0
  152. package/dist/window.cjs.map +1 -0
  153. package/dist/window.js +160 -0
  154. package/dist/window.js.map +1 -0
  155. package/docs/design-tokens.md +405 -0
  156. package/package.json +34 -4
  157. package/src/PanelSystemContext.tsx +88 -0
  158. package/src/components/gesture/SwipeSafeZone.tsx +69 -0
  159. package/src/components/grid/GridLayerList.tsx +172 -0
  160. package/src/components/grid/GridLayerResizeHandles.tsx +145 -0
  161. package/src/components/grid/GridLayout.spec.tsx +743 -0
  162. package/src/components/grid/GridLayout.tsx +130 -0
  163. package/src/components/grid/GridTrackResizeHandle.tsx +87 -0
  164. package/src/components/paneling/FloatingPanelFrame.tsx +203 -0
  165. package/src/components/panels/DropSuggestOverlay.tsx +131 -0
  166. package/src/components/panels/PanelGroupView.tsx +112 -0
  167. package/src/components/pivot/PivotLayer.tsx +27 -0
  168. package/src/components/resizer/HorizontalDivider.tsx +52 -0
  169. package/src/components/resizer/ResizeHandle.tsx +118 -0
  170. package/src/components/tabs/TabBar.tsx +223 -0
  171. package/src/components/tabs/TabBarTab.tsx +133 -0
  172. package/src/components/tabs/TabDragOverlay.tsx +92 -0
  173. package/src/components/window/DialogOverlay.tsx +180 -0
  174. package/src/components/window/Drawer.tsx +369 -0
  175. package/src/components/window/DrawerLayers.tsx +68 -0
  176. package/src/components/window/FloatingWindow.tsx +95 -0
  177. package/src/components/window/PopupLayerPortal.tsx +218 -0
  178. package/src/components/window/drawerStyles.spec.ts +263 -0
  179. package/src/components/window/drawerStyles.ts +228 -0
  180. package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
  181. package/src/components/window/drawerSwipeConfig.ts +112 -0
  182. package/src/components/window/useDrawerSwipeTransform.spec.ts +234 -0
  183. package/src/components/window/useDrawerSwipeTransform.ts +129 -0
  184. package/src/config/PanelContentDeclaration.tsx +427 -0
  185. package/src/config/index.tsx +52 -0
  186. package/src/config/panelJsx.spec.tsx +54 -0
  187. package/src/config/panelJsxConfig.spec.tsx +54 -0
  188. package/src/config/panelJsxDrawer.spec.tsx +33 -0
  189. package/src/config/panelRouter.spec.ts +68 -0
  190. package/src/config/panelRouter.tsx +155 -0
  191. package/src/constants/styles.ts +280 -0
  192. package/src/demo/Layout.module.css +258 -0
  193. package/src/demo/Layout.tsx +176 -0
  194. package/src/demo/components/CodeBlock.module.css +54 -0
  195. package/src/demo/components/CodeBlock.tsx +34 -0
  196. package/src/demo/components/CodePreview.module.css +37 -0
  197. package/src/demo/components/CodePreview.tsx +31 -0
  198. package/src/demo/components/DataPreview.module.css +177 -0
  199. package/src/demo/components/DataPreview.tsx +115 -0
  200. package/src/demo/components/Story.module.css +68 -0
  201. package/src/demo/components/Story.tsx +54 -0
  202. package/src/demo/components/layout/CodePanel.module.css +183 -0
  203. package/src/demo/components/layout/CodePanel.tsx +149 -0
  204. package/src/demo/components/layout/DemoPage.module.css +60 -0
  205. package/src/demo/components/layout/DemoPage.tsx +56 -0
  206. package/src/demo/components/layout/SingleSamplePage.module.css +11 -0
  207. package/src/demo/components/layout/SingleSamplePage.tsx +35 -0
  208. package/src/demo/components/layout/SplitDemoLayout.module.css +107 -0
  209. package/src/demo/components/layout/SplitDemoLayout.tsx +218 -0
  210. package/src/demo/components/layout/index.ts +11 -0
  211. package/src/demo/components/tab-styles/ChromeTabBar.module.css +75 -0
  212. package/src/demo/components/tab-styles/ChromeTabBar.tsx +111 -0
  213. package/src/demo/components/tab-styles/GitHubTabBar.module.css +81 -0
  214. package/src/demo/components/tab-styles/GitHubTabBar.tsx +109 -0
  215. package/src/demo/components/tab-styles/VSCodeTabBar.module.css +78 -0
  216. package/src/demo/components/tab-styles/VSCodeTabBar.tsx +109 -0
  217. package/src/demo/components/tab-styles/index.ts +6 -0
  218. package/src/demo/components/ui/DemoButton.module.css +63 -0
  219. package/src/demo/components/ui/DemoButton.tsx +32 -0
  220. package/src/demo/components/ui/DemoCard.module.css +15 -0
  221. package/src/demo/components/ui/DemoCard.tsx +30 -0
  222. package/src/demo/components/ui/DemoContainer.module.css +17 -0
  223. package/src/demo/components/ui/DemoContainer.tsx +30 -0
  224. package/src/demo/components/ui/DemoPanel.module.css +23 -0
  225. package/src/demo/components/ui/DemoPanel.tsx +33 -0
  226. package/src/demo/components/ui/PanelText.module.css +18 -0
  227. package/src/demo/components/ui/PanelText.tsx +29 -0
  228. package/src/demo/components/ui/PanelTitle.module.css +18 -0
  229. package/src/demo/components/ui/PanelTitle.tsx +31 -0
  230. package/src/demo/contexts/TabbarDemoConfig.tsx +218 -0
  231. package/src/demo/demo.css +172 -0
  232. package/src/demo/hooks/useMedia.ts +41 -0
  233. package/src/demo/hooks/useShikiHighlight.ts +55 -0
  234. package/src/demo/index.tsx +293 -0
  235. package/src/demo/pages/Dialog/alerts/index.tsx +22 -0
  236. package/src/demo/pages/Dialog/card/index.tsx +22 -0
  237. package/src/demo/pages/Dialog/components/AlertDialogDemo.tsx +124 -0
  238. package/src/demo/pages/Dialog/components/CardExpandDemo.module.css +243 -0
  239. package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +204 -0
  240. package/src/demo/pages/Dialog/components/CustomAlertDialogDemo.tsx +219 -0
  241. package/src/demo/pages/Dialog/components/DialogDemos.module.css +77 -0
  242. package/src/demo/pages/Dialog/components/ModalBasics.tsx +45 -0
  243. package/src/demo/pages/Dialog/components/SwipeDialogDemo.module.css +77 -0
  244. package/src/demo/pages/Dialog/components/SwipeDialogDemo.tsx +181 -0
  245. package/src/demo/pages/Dialog/custom-alert/index.tsx +22 -0
  246. package/src/demo/pages/Dialog/modal/index.tsx +17 -0
  247. package/src/demo/pages/Dialog/swipe/index.tsx +22 -0
  248. package/src/demo/pages/Drawer/animations/index.tsx +22 -0
  249. package/src/demo/pages/Drawer/basics/index.tsx +17 -0
  250. package/src/demo/pages/Drawer/components/DrawerAnimations.module.css +125 -0
  251. package/src/demo/pages/Drawer/components/DrawerAnimations.tsx +118 -0
  252. package/src/demo/pages/Drawer/components/DrawerBasics.module.css +55 -0
  253. package/src/demo/pages/Drawer/components/DrawerBasics.tsx +76 -0
  254. package/src/demo/pages/Drawer/components/DrawerMenuLayout.module.css +332 -0
  255. package/src/demo/pages/Drawer/components/DrawerMenuLayout.tsx +199 -0
  256. package/src/demo/pages/Drawer/components/DrawerSwipe.module.css +316 -0
  257. package/src/demo/pages/Drawer/components/DrawerSwipe.tsx +178 -0
  258. package/src/demo/pages/Drawer/menu/index.tsx +17 -0
  259. package/src/demo/pages/Drawer/swipe/index.tsx +17 -0
  260. package/src/demo/pages/FloatingPanelFrame/ResizableFloatingPanelsPreview.module.css +163 -0
  261. package/src/demo/pages/FloatingPanelFrame/ResizableFloatingPanelsPreview.tsx +234 -0
  262. package/src/demo/pages/FloatingPanelFrame/basic/index.tsx +17 -0
  263. package/src/demo/pages/FloatingPanelFrame/complex/index.tsx +26 -0
  264. package/src/demo/pages/FloatingPanelFrame/components/BasicPanel.module.css +16 -0
  265. package/src/demo/pages/FloatingPanelFrame/components/BasicPanel.tsx +24 -0
  266. package/src/demo/pages/FloatingPanelFrame/components/ComplexPanel.module.css +54 -0
  267. package/src/demo/pages/FloatingPanelFrame/components/ComplexPanel.tsx +67 -0
  268. package/src/demo/pages/FloatingPanelFrame/components/PanelWithControls.module.css +21 -0
  269. package/src/demo/pages/FloatingPanelFrame/components/PanelWithControls.tsx +41 -0
  270. package/src/demo/pages/FloatingPanelFrame/components/PanelWithMeta.module.css +5 -0
  271. package/src/demo/pages/FloatingPanelFrame/components/PanelWithMeta.tsx +43 -0
  272. package/src/demo/pages/FloatingPanelFrame/components/ScrollablePanel.module.css +11 -0
  273. package/src/demo/pages/FloatingPanelFrame/components/ScrollablePanel.tsx +42 -0
  274. package/src/demo/pages/FloatingPanelFrame/index.tsx +80 -0
  275. package/src/demo/pages/FloatingPanelFrame/scrollable/index.tsx +30 -0
  276. package/src/demo/pages/FloatingPanelFrame/with-controls/index.tsx +30 -0
  277. package/src/demo/pages/FloatingPanelFrame/with-meta/index.tsx +17 -0
  278. package/src/demo/pages/HorizontalDivider/components/PanelsWithRichContent.module.css +112 -0
  279. package/src/demo/pages/HorizontalDivider/components/PanelsWithRichContent.tsx +56 -0
  280. package/src/demo/pages/HorizontalDivider/components/SimpleResizablePanels.module.css +46 -0
  281. package/src/demo/pages/HorizontalDivider/components/SimpleResizablePanels.tsx +29 -0
  282. package/src/demo/pages/HorizontalDivider/components/ThreePanelLayout.module.css +54 -0
  283. package/src/demo/pages/HorizontalDivider/components/ThreePanelLayout.tsx +30 -0
  284. package/src/demo/pages/HorizontalDivider/index.module.css +14 -0
  285. package/src/demo/pages/HorizontalDivider/index.tsx +64 -0
  286. package/src/demo/pages/HorizontalDivider/panels-with-rich-content/index.tsx +21 -0
  287. package/src/demo/pages/HorizontalDivider/simple-resizable-panels/index.tsx +21 -0
  288. package/src/demo/pages/HorizontalDivider/three-panel-layout/index.tsx +21 -0
  289. package/src/demo/pages/PanelLayout/PanelLayoutDemo.module.css +174 -0
  290. package/src/demo/pages/PanelLayout/PanelLayoutDemo.tsx +248 -0
  291. package/src/demo/pages/PanelLayout/components/DashboardLayout.module.css +115 -0
  292. package/src/demo/pages/PanelLayout/components/DashboardLayout.tsx +124 -0
  293. package/src/demo/pages/PanelLayout/components/DraggableOverlays.module.css +101 -0
  294. package/src/demo/pages/PanelLayout/components/DraggableOverlays.tsx +122 -0
  295. package/src/demo/pages/PanelLayout/components/IDELayout.module.css +104 -0
  296. package/src/demo/pages/PanelLayout/components/IDELayout.tsx +143 -0
  297. package/src/demo/pages/PanelLayout/components/SimpleGrid.module.css +19 -0
  298. package/src/demo/pages/PanelLayout/components/SimpleGrid.tsx +62 -0
  299. package/src/demo/pages/PanelLayout/dashboard/index.tsx +22 -0
  300. package/src/demo/pages/PanelLayout/draggable-overlays/index.tsx +22 -0
  301. package/src/demo/pages/PanelLayout/ide-layout/index.tsx +22 -0
  302. package/src/demo/pages/PanelLayout/index.tsx +94 -0
  303. package/src/demo/pages/PanelLayout/simple-grid/index.tsx +22 -0
  304. package/src/demo/pages/PanelSystem/PanelSystemPreview.module.css +20 -0
  305. package/src/demo/pages/PanelSystem/PanelSystemPreview.tsx +101 -0
  306. package/src/demo/pages/PanelSystem/preview/index.tsx +18 -0
  307. package/src/demo/pages/PanelSystem/tabbar/index.tsx +129 -0
  308. package/src/demo/pages/Pivot/basics/index.tsx +17 -0
  309. package/src/demo/pages/Pivot/components/Pivot.module.css +278 -0
  310. package/src/demo/pages/Pivot/components/PivotBasics.tsx +103 -0
  311. package/src/demo/pages/Pivot/components/PivotSidebar.tsx +168 -0
  312. package/src/demo/pages/Pivot/components/PivotTabs.tsx +129 -0
  313. package/src/demo/pages/Pivot/components/PivotTransitions.tsx +120 -0
  314. package/src/demo/pages/Pivot/components/SwipePivot.module.css +114 -0
  315. package/src/demo/pages/Pivot/components/SwipePivot.tsx +193 -0
  316. package/src/demo/pages/Pivot/components/SwipeTabsPivot.module.css +203 -0
  317. package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +320 -0
  318. package/src/demo/pages/Pivot/sidebar/index.tsx +17 -0
  319. package/src/demo/pages/Pivot/swipe/index.tsx +16 -0
  320. package/src/demo/pages/Pivot/swipe-debug/index.tsx +287 -0
  321. package/src/demo/pages/Pivot/swipe-tabs/index.tsx +15 -0
  322. package/src/demo/pages/Pivot/tabs/index.tsx +17 -0
  323. package/src/demo/pages/Pivot/transitions/index.tsx +17 -0
  324. package/src/demo/pages/ResizeHandle/both-directions/index.tsx +17 -0
  325. package/src/demo/pages/ResizeHandle/components/BothDirectionsDemo.module.css +72 -0
  326. package/src/demo/pages/ResizeHandle/components/BothDirectionsDemo.tsx +41 -0
  327. package/src/demo/pages/ResizeHandle/components/HorizontalResizeDemo.module.css +61 -0
  328. package/src/demo/pages/ResizeHandle/components/HorizontalResizeDemo.tsx +33 -0
  329. package/src/demo/pages/ResizeHandle/components/NestedPanelsDemo.module.css +83 -0
  330. package/src/demo/pages/ResizeHandle/components/NestedPanelsDemo.tsx +53 -0
  331. package/src/demo/pages/ResizeHandle/components/VerticalResizeDemo.module.css +68 -0
  332. package/src/demo/pages/ResizeHandle/components/VerticalResizeDemo.tsx +33 -0
  333. package/src/demo/pages/ResizeHandle/horizontal/index.tsx +17 -0
  334. package/src/demo/pages/ResizeHandle/index.module.css +11 -0
  335. package/src/demo/pages/ResizeHandle/index.tsx +71 -0
  336. package/src/demo/pages/ResizeHandle/nested-panels/index.tsx +17 -0
  337. package/src/demo/pages/ResizeHandle/vertical/index.tsx +17 -0
  338. package/src/demo/pages/ResponsiveLayout/adaptive-workspace/index.tsx +22 -0
  339. package/src/demo/pages/ResponsiveLayout/components/ResponsiveWorkspace.module.css +423 -0
  340. package/src/demo/pages/ResponsiveLayout/components/ResponsiveWorkspace.tsx +398 -0
  341. package/src/demo/pages/Stack/basics/index.tsx +22 -0
  342. package/src/demo/pages/Stack/components/Stack.module.css +234 -0
  343. package/src/demo/pages/Stack/components/StackBasics.spec.tsx +152 -0
  344. package/src/demo/pages/Stack/components/StackBasics.tsx +301 -0
  345. package/src/demo/pages/Stack/components/StackTablet.module.css +299 -0
  346. package/src/demo/pages/Stack/components/StackTablet.spec.tsx +120 -0
  347. package/src/demo/pages/Stack/components/StackTablet.tsx +422 -0
  348. package/src/demo/pages/Stack/tablet/index.tsx +22 -0
  349. package/src/demo/pages/StickyHeader/basics/index.tsx +17 -0
  350. package/src/demo/pages/StickyHeader/components/StickyHeader.module.css +219 -0
  351. package/src/demo/pages/StickyHeader/components/StickyHeaderBasics.tsx +103 -0
  352. package/src/demo/routes.tsx +214 -0
  353. package/src/demo/styles/animations.css +68 -0
  354. package/src/demo/styles/stack-themes.css +35 -0
  355. package/src/demo/utils/createPanelView.tsx +58 -0
  356. package/src/dialog/index.ts +85 -0
  357. package/src/floating/index.ts +24 -0
  358. package/src/grid/index.ts +75 -0
  359. package/src/hooks/ContentCacheContext.tsx +87 -0
  360. package/src/hooks/gesture/presets.spec.ts +86 -0
  361. package/src/hooks/gesture/presets.ts +95 -0
  362. package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +241 -0
  363. package/src/hooks/gesture/testing/createGestureSimulator.ts +385 -0
  364. package/src/hooks/gesture/thresholdValue.spec.ts +103 -0
  365. package/src/hooks/gesture/thresholdValue.ts +77 -0
  366. package/src/hooks/gesture/types.ts +367 -0
  367. package/src/hooks/gesture/useDirectionalLock.spec.ts +271 -0
  368. package/src/hooks/gesture/useDirectionalLock.ts +115 -0
  369. package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +462 -0
  370. package/src/hooks/gesture/useEdgeSwipeInput.ts +131 -0
  371. package/src/hooks/gesture/useNativeGestureGuard.spec.ts +473 -0
  372. package/src/hooks/gesture/useNativeGestureGuard.ts +135 -0
  373. package/src/hooks/gesture/usePointerTracking.spec.ts +364 -0
  374. package/src/hooks/gesture/usePointerTracking.ts +134 -0
  375. package/src/hooks/gesture/useScrollBoundary.spec.ts +249 -0
  376. package/src/hooks/gesture/useScrollBoundary.ts +113 -0
  377. package/src/hooks/gesture/useSwipeInput.spec.ts +592 -0
  378. package/src/hooks/gesture/useSwipeInput.ts +310 -0
  379. package/src/hooks/gesture/utils.spec.ts +152 -0
  380. package/src/hooks/gesture/utils.ts +178 -0
  381. package/src/hooks/useAnimatedVisibility.spec.ts +277 -0
  382. package/src/hooks/useAnimatedVisibility.ts +172 -0
  383. package/src/hooks/useAnimationFrame.ts +208 -0
  384. package/src/hooks/useCSSMatrix.spec.ts +214 -0
  385. package/src/hooks/useCSSMatrix.ts +262 -0
  386. package/src/hooks/useClonedElementPreview.ts +28 -0
  387. package/src/hooks/useContainerScroll.ts +78 -0
  388. package/src/hooks/useContentCache.spec.tsx +232 -0
  389. package/src/hooks/useContentCache.tsx +127 -0
  390. package/src/hooks/useDocumentPointerEvents.ts +137 -0
  391. package/src/hooks/useDocumentScroll.ts +41 -0
  392. package/src/hooks/useEffectEvent.ts +40 -0
  393. package/src/hooks/useElementComponentWrapper.tsx +63 -0
  394. package/src/hooks/useIntersectionObserver.tsx +125 -0
  395. package/src/hooks/useIsomorphicLayoutEffect.ts +29 -0
  396. package/src/hooks/useOperationContinuity.spec.ts +387 -0
  397. package/src/hooks/useOperationContinuity.ts +135 -0
  398. package/src/hooks/useResizeObserver.spec.tsx +277 -0
  399. package/src/hooks/useResizeObserver.tsx +150 -0
  400. package/src/hooks/useScrollContainer.ts +73 -0
  401. package/src/hooks/useSharedElementTransition.ts +333 -0
  402. package/src/hooks/useSnapAnimation.ts +128 -0
  403. package/src/hooks/useSwipeContentTransform.spec.ts +133 -0
  404. package/src/hooks/useSwipeContentTransform.ts +373 -0
  405. package/src/hooks/useTransitionState.ts +95 -0
  406. package/src/index.tsx +88 -0
  407. package/src/modules/dialog/AlertDialog.spec.tsx +387 -0
  408. package/src/modules/dialog/AlertDialog.tsx +221 -0
  409. package/src/modules/dialog/DialogContainer.spec.tsx +228 -0
  410. package/src/modules/dialog/DialogContainer.tsx +188 -0
  411. package/src/modules/dialog/Modal.spec.tsx +220 -0
  412. package/src/modules/dialog/Modal.tsx +182 -0
  413. package/src/modules/dialog/SwipeDialogContainer.tsx +208 -0
  414. package/src/modules/dialog/dialogAnimationUtils.spec.ts +253 -0
  415. package/src/modules/dialog/dialogAnimationUtils.ts +297 -0
  416. package/src/modules/dialog/types.ts +186 -0
  417. package/src/modules/dialog/useDialog.spec.tsx +447 -0
  418. package/src/modules/dialog/useDialog.ts +214 -0
  419. package/src/modules/dialog/useDialogContainer.spec.ts +331 -0
  420. package/src/modules/dialog/useDialogContainer.ts +150 -0
  421. package/src/modules/dialog/useDialogSwipeInput.spec.ts +157 -0
  422. package/src/modules/dialog/useDialogSwipeInput.ts +319 -0
  423. package/src/modules/dialog/useDialogTransform.spec.ts +370 -0
  424. package/src/modules/dialog/useDialogTransform.ts +407 -0
  425. package/src/modules/drawer/types.ts +102 -0
  426. package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
  427. package/src/modules/drawer/useDrawerSwipeInput.ts +399 -0
  428. package/src/modules/grid/GridLayoutContext.tsx +57 -0
  429. package/src/modules/grid/LayerInstanceContext.tsx +56 -0
  430. package/src/modules/grid/resizeHandles.ts +157 -0
  431. package/src/modules/grid/trackUtils.ts +146 -0
  432. package/src/modules/grid/useGridPlacements.ts +143 -0
  433. package/src/modules/grid/useGridTracks.ts +156 -0
  434. package/src/modules/grid/useLayerDragHandle.ts +16 -0
  435. package/src/modules/grid/useLayerInteractions.tsx +850 -0
  436. package/src/modules/keybindings/KeybindingsProvider.tsx +111 -0
  437. package/src/modules/panels/dom/DomRegistry.tsx +94 -0
  438. package/src/modules/panels/index.ts +45 -0
  439. package/src/modules/panels/interactions/InteractionsContext.test.tsx +330 -0
  440. package/src/modules/panels/interactions/InteractionsContext.tsx +394 -0
  441. package/src/modules/panels/interactions/dnd.ts +28 -0
  442. package/src/modules/panels/keybindings/KeybindingsInstaller.tsx +15 -0
  443. package/src/modules/panels/layout/adapter.ts +124 -0
  444. package/src/modules/panels/rendering/ContentRegistry.spec.tsx +311 -0
  445. package/src/modules/panels/rendering/ContentRegistry.tsx +205 -0
  446. package/src/modules/panels/rendering/GroupContainer.tsx +65 -0
  447. package/src/modules/panels/rendering/RenderBridge.tsx +115 -0
  448. package/src/modules/panels/rendering/RenderContext.tsx +31 -0
  449. package/src/modules/panels/state/PanelSplitHandles.tsx +147 -0
  450. package/src/modules/panels/state/PanelSystemContext.splitLimits.spec.tsx +50 -0
  451. package/src/modules/panels/state/PanelSystemContext.tsx +289 -0
  452. package/src/modules/panels/state/StateContext.tsx +12 -0
  453. package/src/modules/panels/state/cleanup.ts +37 -0
  454. package/src/modules/panels/state/commands.ts +53 -0
  455. package/src/modules/panels/state/focus/Context.tsx +25 -0
  456. package/src/modules/panels/state/focus/logic.ts +57 -0
  457. package/src/modules/panels/state/groups/Context.tsx +25 -0
  458. package/src/modules/panels/state/groups/logic.ts +105 -0
  459. package/src/modules/panels/state/splitLimits.spec.ts +46 -0
  460. package/src/modules/panels/state/splitLimits.ts +90 -0
  461. package/src/modules/panels/state/state.spec.ts +49 -0
  462. package/src/modules/panels/state/tree/Context.tsx +24 -0
  463. package/src/modules/panels/state/tree/logic.spec.ts +34 -0
  464. package/src/modules/panels/state/tree/logic.ts +138 -0
  465. package/src/modules/panels/state/types.ts +142 -0
  466. package/src/modules/panels/system/PanelSystem.empty-tabbar.spec.tsx +53 -0
  467. package/src/modules/panels/system/PanelSystem.tab-click-activates.spec.tsx +44 -0
  468. package/src/modules/panels/system/PanelSystem.tab-reorder.spec.tsx +64 -0
  469. package/src/modules/panels/system/PanelSystem.tabs-no-dup.spec.tsx +57 -0
  470. package/src/modules/panels/system/PanelSystem.tsx +206 -0
  471. package/src/modules/pivot/PivotContent.spec.tsx +179 -0
  472. package/src/modules/pivot/PivotContent.tsx +77 -0
  473. package/src/modules/pivot/SwipePivotContent.debug.tmp.tsx +237 -0
  474. package/src/modules/pivot/SwipePivotContent.position.spec.tsx +171 -0
  475. package/src/modules/pivot/SwipePivotContent.spec.tsx +494 -0
  476. package/src/modules/pivot/SwipePivotContent.test.tsx +502 -0
  477. package/src/modules/pivot/SwipePivotContent.tsx +197 -0
  478. package/src/modules/pivot/SwipePivotTabBar.spec.tsx +882 -0
  479. package/src/modules/pivot/SwipePivotTabBar.tsx +583 -0
  480. package/src/modules/pivot/index.ts +8 -0
  481. package/src/modules/pivot/scaleInputState.spec.ts +219 -0
  482. package/src/modules/pivot/scaleInputState.ts +66 -0
  483. package/src/modules/pivot/types.ts +139 -0
  484. package/src/modules/pivot/usePivot.spec.ts +635 -0
  485. package/src/modules/pivot/usePivot.spec.tsx +186 -0
  486. package/src/modules/pivot/usePivot.tsx +345 -0
  487. package/src/modules/pivot/usePivotSwipeInput.spec.ts +708 -0
  488. package/src/modules/pivot/usePivotSwipeInput.ts +136 -0
  489. package/src/modules/resizer/useResizeDrag.ts +94 -0
  490. package/src/modules/stack/StackContent.spec.tsx +264 -0
  491. package/src/modules/stack/StackContent.tsx +111 -0
  492. package/src/modules/stack/SwipeStackContent.spec.tsx +1564 -0
  493. package/src/modules/stack/SwipeStackContent.tsx +366 -0
  494. package/src/modules/stack/SwipeStackOutlet.spec.tsx +250 -0
  495. package/src/modules/stack/SwipeStackOutlet.tsx +221 -0
  496. package/src/modules/stack/computeStackContentState.spec.ts +281 -0
  497. package/src/modules/stack/computeStackContentState.ts +304 -0
  498. package/src/modules/stack/computeSwipeStackTransform.spec.ts +186 -0
  499. package/src/modules/stack/computeSwipeStackTransform.ts +145 -0
  500. package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1133 -0
  501. package/src/modules/stack/types.ts +226 -0
  502. package/src/modules/stack/useStackAnimationState.spec.ts +188 -0
  503. package/src/modules/stack/useStackAnimationState.ts +143 -0
  504. package/src/modules/stack/useStackNavigation.spec.ts +672 -0
  505. package/src/modules/stack/useStackNavigation.tsx +393 -0
  506. package/src/modules/stack/useStackSwipeInput.spec.ts +309 -0
  507. package/src/modules/stack/useStackSwipeInput.ts +139 -0
  508. package/src/modules/window/useDrawerState.ts +81 -0
  509. package/src/modules/window/useFloatingState.spec.ts +252 -0
  510. package/src/modules/window/useFloatingState.ts +141 -0
  511. package/src/panels/index.ts +119 -0
  512. package/src/pivot/index.ts +19 -0
  513. package/src/resizer/index.ts +68 -0
  514. package/src/stack/index.ts +91 -0
  515. package/src/sticky-header/StickyArea.tsx +193 -0
  516. package/src/sticky-header/calculateStickyMetrics.spec.ts +105 -0
  517. package/src/sticky-header/calculateStickyMetrics.ts +50 -0
  518. package/src/sticky-header/index.ts +18 -0
  519. package/src/sticky-header/types.ts +68 -0
  520. package/src/types.ts +341 -0
  521. package/src/utils/CSSMatrix.ts +321 -0
  522. package/src/utils/css.ts +65 -0
  523. package/src/utils/dialogUtils.ts +43 -0
  524. package/src/utils/math.ts +18 -0
  525. package/src/utils/polyfills/createDialogPolyfill.ts +18 -0
  526. package/src/utils/typedActions.ts +103 -0
  527. package/src/vite-env.d.ts +6 -0
  528. package/src/window/index.ts +69 -0
  529. package/dist/GridLayout-BQQ63eA1.cjs +0 -2
  530. package/dist/GridLayout-BQQ63eA1.cjs.map +0 -1
  531. package/dist/GridLayout-CJTKq7Mp.js +0 -1465
  532. package/dist/GridLayout-CJTKq7Mp.js.map +0 -1
  533. package/dist/sticky-header/StickyHeader.d.ts +0 -53
  534. package/dist/styles-CA2_zLZt.js +0 -52
  535. package/dist/styles-CA2_zLZt.js.map +0 -1
  536. package/dist/styles-PsqGOEJP.cjs +0 -2
  537. package/dist/styles-PsqGOEJP.cjs.map +0 -1
  538. package/dist/usePivot-7ctin_P_.cjs +0 -2
  539. package/dist/usePivot-7ctin_P_.cjs.map +0 -1
  540. package/dist/usePivot-CgQxB8rc.js +0 -124
  541. package/dist/usePivot-CgQxB8rc.js.map +0 -1
@@ -0,0 +1,310 @@
1
+ /**
2
+ * @file Hook for detecting swipe gestures on a container element.
3
+ *
4
+ * Combines pointer tracking and directional locking to detect swipe gestures.
5
+ * Also supports trackpad two-finger swipe via wheel events.
6
+ * Returns swipe state and container props for gesture handling.
7
+ */
8
+ import * as React from "react";
9
+ import { usePointerTracking } from "./usePointerTracking.js";
10
+ import { useDirectionalLock } from "./useDirectionalLock.js";
11
+ import { useEffectEvent } from "../useEffectEvent.js";
12
+ import { calculateVelocity, determineDirection } from "./utils.js";
13
+ import type {
14
+ GestureAxis,
15
+ SwipeInputState,
16
+ SwipeInputThresholds,
17
+ UseSwipeInputOptions,
18
+ UseSwipeInputResult,
19
+ Vector2,
20
+ } from "./types.js";
21
+ import { DEFAULT_SWIPE_THRESHOLDS, IDLE_SWIPE_INPUT_STATE } from "./types.js";
22
+
23
+ /** Idle timeout to reset wheel state after swipe stops */
24
+ const WHEEL_RESET_TIMEOUT = 150;
25
+
26
+ /**
27
+ * Evaluate swipe end and call callback if threshold is met.
28
+ */
29
+ const evaluateSwipeEnd = (
30
+ displacement: Vector2,
31
+ velocity: Vector2,
32
+ axis: GestureAxis,
33
+ thresholds: SwipeInputThresholds,
34
+ onSwipeEnd: ((state: SwipeInputState) => void) | undefined,
35
+ ): void => {
36
+ const axisDisplacement = axis === "horizontal" ? displacement.x : displacement.y;
37
+ const axisVelocity = axis === "horizontal" ? velocity.x : velocity.y;
38
+
39
+ const absDisplacement = Math.abs(axisDisplacement);
40
+ const absVelocity = Math.abs(axisVelocity);
41
+
42
+ const triggered = absDisplacement >= thresholds.distanceThreshold || absVelocity >= thresholds.velocityThreshold;
43
+
44
+ if (triggered) {
45
+ const direction = determineDirection(axisDisplacement);
46
+ const endState: SwipeInputState = {
47
+ phase: "ended",
48
+ displacement,
49
+ velocity,
50
+ direction,
51
+ };
52
+ onSwipeEnd?.(endState);
53
+ }
54
+ };
55
+
56
+ /**
57
+ * Hook for detecting swipe gestures on a container element.
58
+ */
59
+ export function useSwipeInput(options: UseSwipeInputOptions): UseSwipeInputResult {
60
+ const {
61
+ containerRef,
62
+ axis,
63
+ enabled = true,
64
+ thresholds: customThresholds,
65
+ onSwipeEnd,
66
+ enableWheel = true,
67
+ pointerStartFilter,
68
+ } = options;
69
+
70
+ const thresholds: SwipeInputThresholds = {
71
+ ...DEFAULT_SWIPE_THRESHOLDS,
72
+ ...customThresholds,
73
+ };
74
+
75
+ // Stable callback for swipe end
76
+ const handleSwipeEnd = useEffectEvent(onSwipeEnd);
77
+
78
+ // ===== Pointer-based swipe tracking =====
79
+ const { state: tracking, onPointerDown: baseOnPointerDown } = usePointerTracking({
80
+ enabled,
81
+ });
82
+
83
+ // Wrap pointer down handler with optional filter
84
+ const onPointerDown = React.useCallback(
85
+ (event: React.PointerEvent) => {
86
+ if (!enabled) {
87
+ return;
88
+ }
89
+ if (pointerStartFilter) {
90
+ const container = containerRef.current;
91
+ if (!container) {
92
+ return;
93
+ }
94
+ if (!pointerStartFilter(event, container)) {
95
+ return;
96
+ }
97
+ }
98
+ baseOnPointerDown(event);
99
+ },
100
+ [enabled, pointerStartFilter, containerRef, baseOnPointerDown],
101
+ );
102
+
103
+ const { lockedAxis, isLocked } = useDirectionalLock({
104
+ tracking,
105
+ lockThreshold: thresholds.lockThreshold,
106
+ });
107
+
108
+ const lastActiveStateRef = React.useRef<SwipeInputState | null>(null);
109
+
110
+ // Prevent native scroll when swiping on iOS
111
+ const isLockedToSwipeAxisRef = React.useRef(false);
112
+
113
+ React.useEffect(() => {
114
+ isLockedToSwipeAxisRef.current = isLocked ? lockedAxis === axis : false;
115
+ }, [isLocked, lockedAxis, axis]);
116
+
117
+ React.useEffect(() => {
118
+ const container = containerRef.current;
119
+ if (!container || !enabled) {
120
+ return;
121
+ }
122
+ const disableTouchMove = (event: TouchEvent) => {
123
+ event.preventDefault();
124
+ };
125
+ const handleTouchStart = (event: TouchEvent) => {
126
+ if (isLockedToSwipeAxisRef.current) {
127
+ event.preventDefault();
128
+ }
129
+ document.addEventListener("touchmove", disableTouchMove, { passive: false });
130
+ };
131
+ const handleTouchEnd = () => {
132
+ document.removeEventListener("touchmove", disableTouchMove);
133
+ };
134
+ document.addEventListener("touchend", handleTouchEnd);
135
+ document.addEventListener("touchcancel", handleTouchEnd);
136
+ container.addEventListener("touchstart", handleTouchStart, { passive: false });
137
+
138
+ return () => {
139
+ container.removeEventListener("touchstart", handleTouchStart);
140
+ document.removeEventListener("touchend", handleTouchEnd);
141
+ document.removeEventListener("touchcancel", handleTouchEnd);
142
+ };
143
+ }, [containerRef, enabled]);
144
+
145
+ // ===== Wheel-based swipe tracking =====
146
+ const [wheelState, setWheelState] = React.useState<SwipeInputState>(IDLE_SWIPE_INPUT_STATE);
147
+ const wheelAccumulatedRef = React.useRef({ x: 0, y: 0 });
148
+ const wheelIdleTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
149
+ const wheelLockedRef = React.useRef(false);
150
+ const wheelLockedAxisRef = React.useRef<GestureAxis | null>(null);
151
+
152
+ const resetWheelState = React.useCallback(() => {
153
+ wheelAccumulatedRef.current = { x: 0, y: 0 };
154
+ wheelLockedRef.current = false;
155
+ wheelLockedAxisRef.current = null;
156
+ setWheelState(IDLE_SWIPE_INPUT_STATE);
157
+ }, []);
158
+
159
+ const endWheelSwipe = React.useCallback(() => {
160
+ const displacement = { ...wheelAccumulatedRef.current };
161
+ evaluateSwipeEnd(displacement, { x: 0, y: 0 }, axis, thresholds, handleSwipeEnd);
162
+ resetWheelState();
163
+ }, [axis, thresholds, handleSwipeEnd, resetWheelState]);
164
+
165
+ const handleWheel = useEffectEvent((event: WheelEvent) => {
166
+ if (!enabled || !enableWheel || tracking.isDown) {
167
+ return;
168
+ }
169
+
170
+ const { deltaX, deltaY } = event;
171
+
172
+ // Direction lock
173
+ if (!wheelLockedRef.current) {
174
+ const absX = Math.abs(deltaX);
175
+ const absY = Math.abs(deltaY);
176
+
177
+ if (absX >= thresholds.lockThreshold || absY >= thresholds.lockThreshold) {
178
+ wheelLockedRef.current = true;
179
+ wheelLockedAxisRef.current = absX > absY ? "horizontal" : "vertical";
180
+ }
181
+ }
182
+
183
+ // If locked to wrong axis, ignore
184
+ if (wheelLockedRef.current && wheelLockedAxisRef.current !== axis) {
185
+ return;
186
+ }
187
+
188
+ // Accumulate displacement (negate: wheel delta is scroll direction)
189
+ wheelAccumulatedRef.current.x -= deltaX;
190
+ wheelAccumulatedRef.current.y -= deltaY;
191
+
192
+ const accumulated = wheelAccumulatedRef.current;
193
+ const axisDisplacement = axis === "horizontal" ? accumulated.x : accumulated.y;
194
+
195
+ setWheelState({
196
+ phase: "swiping",
197
+ displacement: { ...accumulated },
198
+ velocity: { x: 0, y: 0 },
199
+ direction: determineDirection(axisDisplacement),
200
+ });
201
+
202
+ // When wheel stops, treat as "release"
203
+ if (wheelIdleTimerRef.current !== null) {
204
+ clearTimeout(wheelIdleTimerRef.current);
205
+ }
206
+ wheelIdleTimerRef.current = setTimeout(endWheelSwipe, WHEEL_RESET_TIMEOUT);
207
+ });
208
+
209
+ // Set up wheel event listener
210
+ React.useEffect(() => {
211
+ const container = containerRef.current;
212
+ if (!container || !enabled || !enableWheel) {
213
+ return;
214
+ }
215
+
216
+ const listener = (event: WheelEvent) => {
217
+ event.preventDefault();
218
+ handleWheel(event);
219
+ };
220
+
221
+ container.addEventListener("wheel", listener, { passive: false });
222
+
223
+ return () => {
224
+ container.removeEventListener("wheel", listener);
225
+ if (wheelIdleTimerRef.current !== null) {
226
+ clearTimeout(wheelIdleTimerRef.current);
227
+ }
228
+ };
229
+ }, [containerRef, enabled, enableWheel, handleWheel]);
230
+
231
+ React.useEffect(() => {
232
+ return () => {
233
+ if (wheelIdleTimerRef.current !== null) {
234
+ clearTimeout(wheelIdleTimerRef.current);
235
+ }
236
+ };
237
+ }, []);
238
+
239
+ // ===== Pointer swipe state =====
240
+ const pointerState = React.useMemo<SwipeInputState>(() => {
241
+ if (!tracking.isDown || !tracking.start || !tracking.current) {
242
+ return IDLE_SWIPE_INPUT_STATE;
243
+ }
244
+
245
+ const deltaX = tracking.current.x - tracking.start.x;
246
+ const deltaY = tracking.current.y - tracking.start.y;
247
+ const displacement = { x: deltaX, y: deltaY };
248
+
249
+ const velocity = {
250
+ x: calculateVelocity(deltaX, tracking.start.timestamp, tracking.current.timestamp),
251
+ y: calculateVelocity(deltaY, tracking.start.timestamp, tracking.current.timestamp),
252
+ };
253
+
254
+ if (!isLocked || lockedAxis !== axis) {
255
+ return { phase: "tracking", displacement, velocity, direction: 0 };
256
+ }
257
+
258
+ const axisDisplacement = axis === "horizontal" ? deltaX : deltaY;
259
+ return {
260
+ phase: "swiping",
261
+ displacement,
262
+ velocity,
263
+ direction: determineDirection(axisDisplacement),
264
+ };
265
+ }, [tracking.isDown, tracking.start, tracking.current, isLocked, lockedAxis, axis]);
266
+
267
+ React.useEffect(() => {
268
+ if (pointerState.phase !== "idle") {
269
+ lastActiveStateRef.current = pointerState;
270
+ }
271
+ }, [pointerState]);
272
+
273
+ // Handle pointer up (but not cancel)
274
+ React.useEffect(() => {
275
+ if (tracking.isDown) {
276
+ return;
277
+ }
278
+
279
+ const lastState = lastActiveStateRef.current;
280
+ if (!lastState || (lastState.phase !== "swiping" && lastState.phase !== "tracking")) {
281
+ return;
282
+ }
283
+
284
+ lastActiveStateRef.current = null;
285
+
286
+ // Skip navigation if the gesture was canceled (e.g., browser took over for native scroll)
287
+ if (tracking.wasCanceled) {
288
+ return;
289
+ }
290
+
291
+ evaluateSwipeEnd(lastState.displacement, lastState.velocity, axis, thresholds, handleSwipeEnd);
292
+ }, [tracking.isDown, tracking.wasCanceled, axis, thresholds, handleSwipeEnd]);
293
+
294
+ // Merge states
295
+ const state = pointerState.phase !== "idle" ? pointerState : wheelState;
296
+
297
+ const containerProps = React.useMemo(() => {
298
+ const touchAction = axis === "horizontal" ? "pan-y pinch-zoom" : "pan-x pinch-zoom";
299
+ return {
300
+ onPointerDown,
301
+ style: {
302
+ touchAction,
303
+ userSelect: "none" as const,
304
+ WebkitUserSelect: "none" as const,
305
+ },
306
+ };
307
+ }, [axis, onPointerDown]);
308
+
309
+ return { state, containerProps };
310
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @file Unit tests for gesture utility functions.
3
+ */
4
+ import { calculateVelocity, determineDirection, mergeGestureContainerProps } from "./utils.js";
5
+ import type { GestureContainerProps } from "./utils.js";
6
+
7
+ describe("calculateVelocity", () => {
8
+ it("returns 0 when elapsed time is 0", () => {
9
+ const result = calculateVelocity(100, 1000, 1000);
10
+ expect(result).toBe(0);
11
+ });
12
+
13
+ it("returns 0 when elapsed time is negative", () => {
14
+ const result = calculateVelocity(100, 1000, 900);
15
+ expect(result).toBe(0);
16
+ });
17
+
18
+ it("calculates positive velocity correctly", () => {
19
+ // 100px in 50ms = 2 px/ms
20
+ const result = calculateVelocity(100, 1000, 1050);
21
+ expect(result).toBe(2);
22
+ });
23
+
24
+ it("calculates negative velocity correctly", () => {
25
+ // -100px in 50ms = -2 px/ms
26
+ const result = calculateVelocity(-100, 1000, 1050);
27
+ expect(result).toBe(-2);
28
+ });
29
+ });
30
+
31
+ describe("determineDirection", () => {
32
+ it("returns 1 for positive displacement", () => {
33
+ expect(determineDirection(10)).toBe(1);
34
+ expect(determineDirection(0.1)).toBe(1);
35
+ });
36
+
37
+ it("returns -1 for negative displacement", () => {
38
+ expect(determineDirection(-10)).toBe(-1);
39
+ expect(determineDirection(-0.1)).toBe(-1);
40
+ });
41
+
42
+ it("returns 0 for zero displacement", () => {
43
+ expect(determineDirection(0)).toBe(0);
44
+ });
45
+ });
46
+
47
+ /**
48
+ * Simple fake handler that tracks calls.
49
+ */
50
+ const createFakeHandler = () => {
51
+ const calls: Array<React.PointerEvent<HTMLElement>> = [];
52
+ const handler = (event: React.PointerEvent<HTMLElement>) => {
53
+ calls.push(event);
54
+ };
55
+ return { handler, calls };
56
+ };
57
+
58
+ describe("mergeGestureContainerProps", () => {
59
+ it("merges styles from multiple props", () => {
60
+ const props1: GestureContainerProps = {
61
+ style: { touchAction: "pan-y" },
62
+ };
63
+ const props2: GestureContainerProps = {
64
+ style: { userSelect: "none" },
65
+ };
66
+
67
+ const result = mergeGestureContainerProps(props1, props2);
68
+
69
+ expect(result.style).toEqual({
70
+ touchAction: "pan-y",
71
+ userSelect: "none",
72
+ });
73
+ });
74
+
75
+ it("later styles override earlier styles", () => {
76
+ const props1: GestureContainerProps = {
77
+ style: { touchAction: "pan-y" },
78
+ };
79
+ const props2: GestureContainerProps = {
80
+ style: { touchAction: "none" },
81
+ };
82
+
83
+ const result = mergeGestureContainerProps(props1, props2);
84
+
85
+ expect(result.style.touchAction).toBe("none");
86
+ });
87
+
88
+ it("chains onPointerDown handlers", () => {
89
+ const fake1 = createFakeHandler();
90
+ const fake2 = createFakeHandler();
91
+
92
+ const props1: GestureContainerProps = {
93
+ onPointerDown: fake1.handler,
94
+ style: {},
95
+ };
96
+ const props2: GestureContainerProps = {
97
+ onPointerDown: fake2.handler,
98
+ style: {},
99
+ };
100
+
101
+ const result = mergeGestureContainerProps(props1, props2);
102
+ const mockEvent = {} as React.PointerEvent<HTMLElement>;
103
+ result.onPointerDown?.(mockEvent);
104
+
105
+ expect(fake1.calls).toHaveLength(1);
106
+ expect(fake1.calls[0]).toBe(mockEvent);
107
+ expect(fake2.calls).toHaveLength(1);
108
+ expect(fake2.calls[0]).toBe(mockEvent);
109
+ });
110
+
111
+ it("handles props without onPointerDown", () => {
112
+ const fake = createFakeHandler();
113
+
114
+ const props1: GestureContainerProps = {
115
+ style: { touchAction: "pan-y" },
116
+ };
117
+ const props2: GestureContainerProps = {
118
+ onPointerDown: fake.handler,
119
+ style: {},
120
+ };
121
+
122
+ const result = mergeGestureContainerProps(props1, props2);
123
+ const mockEvent = {} as React.PointerEvent<HTMLElement>;
124
+ result.onPointerDown?.(mockEvent);
125
+
126
+ expect(fake.calls).toHaveLength(1);
127
+ expect(fake.calls[0]).toBe(mockEvent);
128
+ });
129
+
130
+ it("handles empty props array", () => {
131
+ const result = mergeGestureContainerProps();
132
+
133
+ expect(result.style).toEqual({});
134
+ expect(result.onPointerDown).toBeDefined();
135
+ });
136
+
137
+ it("handles single props", () => {
138
+ const fake = createFakeHandler();
139
+ const props: GestureContainerProps = {
140
+ onPointerDown: fake.handler,
141
+ style: { touchAction: "pan-y" },
142
+ };
143
+
144
+ const result = mergeGestureContainerProps(props);
145
+
146
+ expect(result.style).toEqual({ touchAction: "pan-y" });
147
+ const mockEvent = {} as React.PointerEvent<HTMLElement>;
148
+ result.onPointerDown?.(mockEvent);
149
+ expect(fake.calls).toHaveLength(1);
150
+ expect(fake.calls[0]).toBe(mockEvent);
151
+ });
152
+ });
@@ -0,0 +1,178 @@
1
+ /**
2
+ * @file Utility functions for gesture detection hooks.
3
+ *
4
+ * Contains shared calculations and helper functions used across
5
+ * gesture-related hooks to avoid code duplication.
6
+ */
7
+ import type * as React from "react";
8
+
9
+ /**
10
+ * Calculate velocity from displacement and time elapsed.
11
+ *
12
+ * @param displacement - Distance traveled in pixels
13
+ * @param startTime - Start timestamp in milliseconds
14
+ * @param currentTime - Current timestamp in milliseconds
15
+ * @returns Velocity in pixels per millisecond
16
+ */
17
+ export const calculateVelocity = (
18
+ displacement: number,
19
+ startTime: number,
20
+ currentTime: number,
21
+ ): number => {
22
+ const elapsed = currentTime - startTime;
23
+ if (elapsed <= 0) {
24
+ return 0;
25
+ }
26
+ return displacement / elapsed;
27
+ };
28
+
29
+ /**
30
+ * Determine direction from displacement.
31
+ *
32
+ * @param displacement - Distance from start position
33
+ * @returns -1 for backward (left/up), 0 for no movement, 1 for forward (right/down)
34
+ */
35
+ export const determineDirection = (displacement: number): -1 | 0 | 1 => {
36
+ if (displacement > 0) {
37
+ return 1;
38
+ }
39
+ if (displacement < 0) {
40
+ return -1;
41
+ }
42
+ return 0;
43
+ };
44
+
45
+ /**
46
+ * Container props type for gesture handling.
47
+ */
48
+ export type GestureContainerProps = React.HTMLAttributes<HTMLElement> & {
49
+ style: React.CSSProperties;
50
+ };
51
+
52
+ /**
53
+ * Merge multiple container props objects for gesture handling.
54
+ *
55
+ * Combines style objects and chains onPointerDown handlers.
56
+ * Useful when combining multiple gesture hooks that each provide
57
+ * their own container props (e.g., swipe input + native gesture guard).
58
+ *
59
+ * @param propsArray - Array of container props to merge
60
+ * @returns Merged container props with combined styles and handlers
61
+ */
62
+ export const mergeGestureContainerProps = (
63
+ ...propsArray: GestureContainerProps[]
64
+ ): GestureContainerProps => {
65
+ const mergedStyle: React.CSSProperties = {};
66
+ const pointerDownHandlers: Array<
67
+ ((event: React.PointerEvent<HTMLElement>) => void) | undefined
68
+ > = [];
69
+
70
+ for (const props of propsArray) {
71
+ Object.assign(mergedStyle, props.style);
72
+ if (props.onPointerDown) {
73
+ pointerDownHandlers.push(props.onPointerDown);
74
+ }
75
+ }
76
+
77
+ const handlePointerDown = (event: React.PointerEvent<HTMLElement>) => {
78
+ for (const handler of pointerDownHandlers) {
79
+ handler?.(event);
80
+ }
81
+ };
82
+
83
+ return {
84
+ onPointerDown: handlePointerDown,
85
+ style: mergedStyle,
86
+ };
87
+ };
88
+
89
+ // ============================================================================
90
+ // Scroll Detection Utilities
91
+ // ============================================================================
92
+
93
+ /**
94
+ * Check if an element is scrollable in any direction.
95
+ */
96
+ export function isScrollableElement(element: HTMLElement): boolean {
97
+ const style = getComputedStyle(element);
98
+
99
+ const isScrollableX =
100
+ (style.overflowX === "scroll" || style.overflowX === "auto") &&
101
+ element.scrollWidth > element.clientWidth;
102
+
103
+ const isScrollableY =
104
+ (style.overflowY === "scroll" || style.overflowY === "auto") &&
105
+ element.scrollHeight > element.clientHeight;
106
+
107
+ return isScrollableX || isScrollableY;
108
+ }
109
+
110
+ /**
111
+ * Check if we should start drag based on scroll state.
112
+ * Returns false if the target is inside a scrollable element.
113
+ */
114
+ export function shouldStartDrag(
115
+ event: React.PointerEvent,
116
+ container: HTMLElement,
117
+ ): boolean {
118
+ // eslint-disable-next-line no-restricted-syntax -- loop variable requires let
119
+ let current = event.target as HTMLElement | null;
120
+
121
+ while (current !== null && current !== container) {
122
+ if (isScrollableElement(current)) {
123
+ return false;
124
+ }
125
+ current = current.parentElement;
126
+ }
127
+
128
+ return true;
129
+ }
130
+
131
+ /**
132
+ * Check if an element or its ancestors are scrollable in the specified direction.
133
+ * Returns true if scrolling is possible and would block the swipe gesture.
134
+ *
135
+ * @param element - The target element to check
136
+ * @param container - The container boundary
137
+ * @param axis - The axis to check ("x" or "y")
138
+ * @param direction - The swipe direction (1 = right/down, -1 = left/up)
139
+ */
140
+ export function isScrollableInDirection(
141
+ element: HTMLElement,
142
+ container: HTMLElement,
143
+ axis: "x" | "y",
144
+ direction: 1 | -1,
145
+ ): boolean {
146
+ // eslint-disable-next-line no-restricted-syntax -- loop variable requires let
147
+ let current: HTMLElement | null = element;
148
+
149
+ while (current !== null && current !== container) {
150
+ const style = getComputedStyle(current);
151
+ const isHorizontal = axis === "x";
152
+
153
+ const overflow = isHorizontal ? style.overflowX : style.overflowY;
154
+ const isScrollable = overflow === "scroll" || overflow === "auto";
155
+
156
+ if (isScrollable) {
157
+ const scrollSize = isHorizontal
158
+ ? current.scrollWidth - current.clientWidth
159
+ : current.scrollHeight - current.clientHeight;
160
+
161
+ if (scrollSize > 0) {
162
+ const scrollPos = isHorizontal ? current.scrollLeft : current.scrollTop;
163
+
164
+ // If swiping in close direction and not at boundary, block swipe
165
+ if (direction === -1 && scrollPos > 1) {
166
+ return true; // Can scroll left/up, block swipe
167
+ }
168
+ if (direction === 1 && scrollPos < scrollSize - 1) {
169
+ return true; // Can scroll right/down, block swipe
170
+ }
171
+ }
172
+ }
173
+
174
+ current = current.parentElement;
175
+ }
176
+
177
+ return false;
178
+ }