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,319 @@
1
+ /**
2
+ * @file Hook for detecting swipe gestures to dismiss a dialog.
3
+ *
4
+ * This hook provides free 2D movement during swipe:
5
+ * - User can drag in any direction freely
6
+ * - Close intent is detected on release based on displacement direction
7
+ * - If movement matches close direction and exceeds threshold, dismiss
8
+ * - Otherwise, snap back to original position
9
+ */
10
+ import * as React from "react";
11
+ import { usePointerTracking } from "../../hooks/gesture/usePointerTracking.js";
12
+ import {
13
+ type ContinuousOperationState,
14
+ type Vector2,
15
+ IDLE_CONTINUOUS_OPERATION_STATE,
16
+ } from "../../hooks/gesture/types.js";
17
+ import type { DialogOpenDirection } from "./dialogAnimationUtils.js";
18
+ import { getAnimationAxis, getDirectionSign } from "./dialogAnimationUtils.js";
19
+
20
+ /**
21
+ * Default dismiss threshold (30% of container size).
22
+ */
23
+ const DEFAULT_DISMISS_THRESHOLD = 0.3;
24
+
25
+ /**
26
+ * Velocity threshold for quick flick dismissal (px/ms).
27
+ */
28
+ const VELOCITY_THRESHOLD = 0.5;
29
+
30
+ /**
31
+ * Options for useDialogSwipeInput hook.
32
+ */
33
+ export type UseDialogSwipeInputOptions = {
34
+ /** Ref to the dialog container element */
35
+ containerRef: React.RefObject<HTMLElement | null>;
36
+ /** Direction the dialog opened from (swipe closes in same direction) */
37
+ openDirection: DialogOpenDirection;
38
+ /** Whether swipe dismiss is enabled */
39
+ enabled: boolean;
40
+ /** Callback when swipe exceeds threshold and dialog should dismiss */
41
+ onSwipeDismiss: () => void;
42
+ /** Threshold ratio (0-1) of container size to trigger dismiss. @default 0.3 */
43
+ dismissThreshold?: number;
44
+ };
45
+
46
+ /**
47
+ * Result from useDialogSwipeInput hook.
48
+ */
49
+ export type UseDialogSwipeInputResult = {
50
+ /** Current operation state (idle, operating, or ended) */
51
+ state: ContinuousOperationState;
52
+ /** Props to spread on the container element */
53
+ containerProps: React.HTMLAttributes<HTMLElement> & {
54
+ style: React.CSSProperties;
55
+ };
56
+ /** Swipe progress (0-1) towards dismiss threshold */
57
+ progress: number;
58
+ /** Whether user is currently swiping */
59
+ isOperating: boolean;
60
+ /** Current displacement in pixels (primary axis based on openDirection) */
61
+ displacement: number;
62
+ /** Full 2D displacement for free movement transform */
63
+ displacement2D: Vector2;
64
+ };
65
+
66
+ /**
67
+ * Check if an element or its ancestors are scrollable in any direction.
68
+ */
69
+ function isScrollableElement(element: HTMLElement): boolean {
70
+ const style = getComputedStyle(element);
71
+
72
+ const isScrollableX =
73
+ (style.overflowX === "scroll" || style.overflowX === "auto") &&
74
+ element.scrollWidth > element.clientWidth;
75
+
76
+ const isScrollableY =
77
+ (style.overflowY === "scroll" || style.overflowY === "auto") &&
78
+ element.scrollHeight > element.clientHeight;
79
+
80
+ return isScrollableX || isScrollableY;
81
+ }
82
+
83
+ /**
84
+ * Check if we should start tracking based on scroll state.
85
+ * Returns false if the target is inside a scrollable element that can scroll in the drag direction.
86
+ */
87
+ function shouldStartDrag(event: React.PointerEvent, container: HTMLElement): boolean {
88
+ // eslint-disable-next-line no-restricted-syntax -- loop variable requires let
89
+ let current = event.target as HTMLElement | null;
90
+
91
+ while (current !== null && current !== container) {
92
+ if (isScrollableElement(current)) {
93
+ return false;
94
+ }
95
+ current = current.parentElement;
96
+ }
97
+
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * Hook for detecting swipe gestures to dismiss a dialog.
103
+ *
104
+ * Allows free 2D movement - user can drag in any direction.
105
+ * On release, detects if the movement matches the close direction:
106
+ * - "center" or "bottom": close if moved down significantly
107
+ * - "top": close if moved up significantly
108
+ * - "left": close if moved left significantly
109
+ * - "right": close if moved right significantly
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * const { state, containerProps, displacement2D } = useDialogSwipeInput({
114
+ * containerRef,
115
+ * openDirection: "bottom",
116
+ * enabled: true,
117
+ * onSwipeDismiss: () => setVisible(false),
118
+ * });
119
+ *
120
+ * // Apply 2D transform for free movement
121
+ * style={{ transform: `translate(${displacement2D.x}px, ${displacement2D.y}px)` }}
122
+ * ```
123
+ */
124
+ export function useDialogSwipeInput(
125
+ options: UseDialogSwipeInputOptions,
126
+ ): UseDialogSwipeInputResult {
127
+ const {
128
+ containerRef,
129
+ openDirection,
130
+ enabled,
131
+ onSwipeDismiss,
132
+ dismissThreshold = DEFAULT_DISMISS_THRESHOLD,
133
+ } = options;
134
+
135
+ const axis = getAnimationAxis(openDirection);
136
+ const expectedSign = getDirectionSign(openDirection);
137
+
138
+ // Track container size for progress calculation
139
+ const containerSizeRef = React.useRef<{ width: number; height: number }>({ width: 0, height: 0 });
140
+
141
+ React.useLayoutEffect(() => {
142
+ const container = containerRef.current;
143
+ if (!container) {
144
+ return;
145
+ }
146
+
147
+ const updateSize = () => {
148
+ containerSizeRef.current = {
149
+ width: container.clientWidth,
150
+ height: container.clientHeight,
151
+ };
152
+ };
153
+
154
+ updateSize();
155
+
156
+ const observer = new ResizeObserver(updateSize);
157
+ observer.observe(container);
158
+
159
+ return () => observer.disconnect();
160
+ }, [containerRef]);
161
+
162
+ // Use pointer tracking for free 2D movement
163
+ const { state: tracking, onPointerDown: baseOnPointerDown } = usePointerTracking({
164
+ enabled,
165
+ });
166
+
167
+ // Track displacement for snapback and release detection
168
+ const lastDisplacementRef = React.useRef<Vector2>({ x: 0, y: 0 });
169
+
170
+ // Wrap pointer down with scrollable check
171
+ const onPointerDown = React.useCallback(
172
+ (event: React.PointerEvent) => {
173
+ if (!enabled) {
174
+ return;
175
+ }
176
+ const container = containerRef.current;
177
+ if (!container) {
178
+ return;
179
+ }
180
+ if (!shouldStartDrag(event, container)) {
181
+ return;
182
+ }
183
+ baseOnPointerDown(event);
184
+ },
185
+ [enabled, containerRef, baseOnPointerDown],
186
+ );
187
+
188
+ // Calculate 2D displacement - preserve last value on release for snapback
189
+ const displacement2D = React.useMemo<Vector2>(() => {
190
+ if (!tracking.isDown || !tracking.start || !tracking.current) {
191
+ // Return last known displacement for snapback animation
192
+ return lastDisplacementRef.current;
193
+ }
194
+ return {
195
+ x: tracking.current.x - tracking.start.x,
196
+ y: tracking.current.y - tracking.start.y,
197
+ };
198
+ }, [tracking.isDown, tracking.start, tracking.current]);
199
+
200
+ // Calculate primary axis displacement for progress
201
+ const primaryDisplacement = axis === "x" ? displacement2D.x : displacement2D.y;
202
+
203
+ // Calculate progress towards dismiss threshold (only for correct direction)
204
+ const progress = React.useMemo(() => {
205
+ const containerSize = axis === "x" ? containerSizeRef.current.width : containerSizeRef.current.height;
206
+ if (containerSize <= 0) {
207
+ return 0;
208
+ }
209
+ const sign = primaryDisplacement > 0 ? 1 : primaryDisplacement < 0 ? -1 : 0;
210
+ if (sign !== expectedSign) {
211
+ return 0; // Wrong direction
212
+ }
213
+ return Math.min(Math.abs(primaryDisplacement) / containerSize, 1);
214
+ }, [axis, primaryDisplacement, expectedSign]);
215
+
216
+ // State machine for operation phase
217
+ const [operationPhase, setOperationPhase] = React.useState<"idle" | "operating" | "ended">("idle");
218
+
219
+ // Store displacement while dragging
220
+ React.useEffect(() => {
221
+ if (tracking.isDown && tracking.current) {
222
+ lastDisplacementRef.current = displacement2D;
223
+ }
224
+ }, [tracking.isDown, tracking.current, displacement2D]);
225
+
226
+ // Handle drag start
227
+ React.useEffect(() => {
228
+ if (tracking.isDown && operationPhase === "idle") {
229
+ setOperationPhase("operating");
230
+ }
231
+ }, [tracking.isDown, operationPhase]);
232
+
233
+ // Handle release - transition to "ended" then check dismiss
234
+ React.useEffect(() => {
235
+ if (!tracking.isDown && operationPhase === "operating") {
236
+ const hasMovement = Math.abs(lastDisplacementRef.current.x) > 1 || Math.abs(lastDisplacementRef.current.y) > 1;
237
+
238
+ if (hasMovement) {
239
+ // Transition to ended phase for snapback detection
240
+ setOperationPhase("ended");
241
+
242
+ // Check if should dismiss
243
+ const containerSize = axis === "x" ? containerSizeRef.current.width : containerSizeRef.current.height;
244
+ if (containerSize > 0) {
245
+ const finalDisplacement = lastDisplacementRef.current;
246
+ const primaryValue = axis === "x" ? finalDisplacement.x : finalDisplacement.y;
247
+ const sign = primaryValue > 0 ? 1 : primaryValue < 0 ? -1 : 0;
248
+
249
+ if (sign === expectedSign) {
250
+ const ratio = Math.abs(primaryValue) / containerSize;
251
+ const velocity = tracking.start && tracking.current
252
+ ? Math.abs(primaryValue) / Math.max(1, tracking.current.timestamp - tracking.start.timestamp)
253
+ : 0;
254
+
255
+ if (ratio >= dismissThreshold || velocity >= VELOCITY_THRESHOLD) {
256
+ onSwipeDismiss();
257
+ }
258
+ }
259
+ }
260
+ } else {
261
+ // No significant movement, go directly to idle
262
+ setOperationPhase("idle");
263
+ lastDisplacementRef.current = { x: 0, y: 0 };
264
+ }
265
+ }
266
+ }, [tracking.isDown, operationPhase, axis, expectedSign, dismissThreshold, onSwipeDismiss, tracking.start, tracking.current]);
267
+
268
+ // Transition from ended to idle after one render (to allow snapback detection)
269
+ React.useEffect(() => {
270
+ if (operationPhase === "ended") {
271
+ // Use microtask to ensure the "ended" state is seen by consumers
272
+ queueMicrotask(() => {
273
+ setOperationPhase("idle");
274
+ lastDisplacementRef.current = { x: 0, y: 0 };
275
+ });
276
+ }
277
+ }, [operationPhase]);
278
+
279
+ // Build continuous operation state
280
+ const state = React.useMemo<ContinuousOperationState>(() => {
281
+ if (operationPhase === "idle") {
282
+ return IDLE_CONTINUOUS_OPERATION_STATE;
283
+ }
284
+ if (operationPhase === "ended") {
285
+ return {
286
+ phase: "ended",
287
+ displacement: lastDisplacementRef.current,
288
+ velocity: { x: 0, y: 0 },
289
+ };
290
+ }
291
+ return {
292
+ phase: "operating",
293
+ displacement: displacement2D,
294
+ velocity: { x: 0, y: 0 },
295
+ };
296
+ }, [operationPhase, displacement2D]);
297
+
298
+ const containerProps = React.useMemo(() => {
299
+ return {
300
+ onPointerDown,
301
+ style: {
302
+ touchAction: "none" as const, // Allow free 2D movement
303
+ userSelect: "none" as const,
304
+ WebkitUserSelect: "none" as const,
305
+ },
306
+ };
307
+ }, [onPointerDown]);
308
+
309
+ const isOperating = state.phase === "operating";
310
+
311
+ return {
312
+ state,
313
+ containerProps,
314
+ progress,
315
+ isOperating,
316
+ displacement: primaryDisplacement,
317
+ displacement2D,
318
+ };
319
+ }
@@ -0,0 +1,370 @@
1
+ /**
2
+ * @file Tests for useDialogTransform hook
3
+ */
4
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
5
+ import { renderHook, act, waitFor } from "@testing-library/react";
6
+ import { useDialogTransform } from "./useDialogTransform.js";
7
+ import type { ContinuousOperationState } from "../../hooks/gesture/types.js";
8
+ import { IDLE_CONTINUOUS_OPERATION_STATE } from "../../hooks/gesture/types.js";
9
+
10
+ // Mock ResizeObserver
11
+ const mockResizeObserver = vi.fn();
12
+ vi.stubGlobal("ResizeObserver", class {
13
+ constructor(callback: ResizeObserverCallback) {
14
+ mockResizeObserver(callback);
15
+ }
16
+ observe = vi.fn();
17
+ unobserve = vi.fn();
18
+ disconnect = vi.fn();
19
+ });
20
+
21
+ describe("useDialogTransform", () => {
22
+ let rafCallbacks: FrameRequestCallback[] = [];
23
+ let rafId = 0;
24
+
25
+ beforeEach(() => {
26
+ rafCallbacks = [];
27
+ rafId = 0;
28
+
29
+ vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) => {
30
+ rafCallbacks.push(cb);
31
+ return ++rafId;
32
+ });
33
+ vi.stubGlobal("cancelAnimationFrame", (id: number) => {
34
+ // Mock cancel
35
+ });
36
+ });
37
+
38
+ afterEach(() => {
39
+ vi.unstubAllGlobals();
40
+ vi.clearAllMocks();
41
+ });
42
+
43
+ const runAnimationFrames = (count = 1, deltaMs = 16) => {
44
+ const now = performance.now();
45
+ for (let i = 0; i < count; i++) {
46
+ const callbacks = [...rafCallbacks];
47
+ rafCallbacks = [];
48
+ callbacks.forEach(cb => cb(now + deltaMs * (i + 1)));
49
+ }
50
+ };
51
+
52
+ const createMockElement = (dimensions: { width: number; height: number }) => {
53
+ return {
54
+ clientWidth: dimensions.width,
55
+ clientHeight: dimensions.height,
56
+ style: {
57
+ transform: "",
58
+ setProperty: vi.fn(),
59
+ viewTransitionName: "",
60
+ },
61
+ } as unknown as HTMLDivElement;
62
+ };
63
+
64
+ const createMockBackdrop = () => {
65
+ return {
66
+ style: {
67
+ opacity: "",
68
+ },
69
+ } as unknown as HTMLDivElement;
70
+ };
71
+
72
+ const idleSwipeState: ContinuousOperationState = IDLE_CONTINUOUS_OPERATION_STATE;
73
+ const zeroDisplacement2D = { x: 0, y: 0 };
74
+
75
+ describe("opening animation", () => {
76
+ it("should start animation when visible changes from false to true", async () => {
77
+ const element = createMockElement({ width: 400, height: 300 });
78
+ const backdrop = createMockBackdrop();
79
+ const elementRef = { current: element };
80
+ const backdropRef = { current: backdrop };
81
+
82
+ const { result, rerender } = renderHook(
83
+ ({ visible }) =>
84
+ useDialogTransform({
85
+ elementRef,
86
+ backdropRef,
87
+ visible,
88
+ openDirection: "bottom",
89
+ swipeState: idleSwipeState,
90
+ displacement: 0,
91
+ displacement2D: zeroDisplacement2D,
92
+ }),
93
+ { initialProps: { visible: false } },
94
+ );
95
+
96
+ expect(result.current.phase).toBe("closed");
97
+
98
+ // Trigger visible change
99
+ rerender({ visible: true });
100
+
101
+ // Should transition to opening
102
+ expect(result.current.phase).toBe("opening");
103
+ expect(result.current.isAnimating).toBe(true);
104
+ });
105
+
106
+ it("should apply transform during opening animation", async () => {
107
+ const element = createMockElement({ width: 400, height: 300 });
108
+ const backdrop = createMockBackdrop();
109
+ const elementRef = { current: element };
110
+ const backdropRef = { current: backdrop };
111
+
112
+ const { rerender } = renderHook(
113
+ ({ visible }) =>
114
+ useDialogTransform({
115
+ elementRef,
116
+ backdropRef,
117
+ visible,
118
+ openDirection: "bottom",
119
+ swipeState: idleSwipeState,
120
+ displacement: 0,
121
+ displacement2D: zeroDisplacement2D,
122
+ }),
123
+ { initialProps: { visible: false } },
124
+ );
125
+
126
+ // Trigger visible change
127
+ rerender({ visible: true });
128
+
129
+ // Run some animation frames
130
+ act(() => {
131
+ runAnimationFrames(5, 50);
132
+ });
133
+
134
+ // Transform should be applied
135
+ expect(element.style.transform).not.toBe("");
136
+ });
137
+
138
+ it("should call onOpenComplete when animation finishes", async () => {
139
+ const element = createMockElement({ width: 400, height: 300 });
140
+ const backdrop = createMockBackdrop();
141
+ const elementRef = { current: element };
142
+ const backdropRef = { current: backdrop };
143
+ const onOpenComplete = vi.fn();
144
+
145
+ const { result, rerender } = renderHook(
146
+ ({ visible }) =>
147
+ useDialogTransform({
148
+ elementRef,
149
+ backdropRef,
150
+ visible,
151
+ openDirection: "bottom",
152
+ swipeState: idleSwipeState,
153
+ displacement: 0,
154
+ displacement2D: zeroDisplacement2D,
155
+ animationDuration: 100,
156
+ onOpenComplete,
157
+ }),
158
+ { initialProps: { visible: false } },
159
+ );
160
+
161
+ // Trigger visible change
162
+ rerender({ visible: true });
163
+
164
+ // Run enough frames to complete animation
165
+ act(() => {
166
+ runAnimationFrames(10, 20);
167
+ });
168
+
169
+ await waitFor(() => {
170
+ expect(result.current.phase).toBe("open");
171
+ });
172
+
173
+ expect(onOpenComplete).toHaveBeenCalled();
174
+ });
175
+ });
176
+
177
+ describe("closing animation", () => {
178
+ it("should animate close when triggerClose is called", async () => {
179
+ const element = createMockElement({ width: 400, height: 300 });
180
+ const backdrop = createMockBackdrop();
181
+ const elementRef = { current: element };
182
+ const backdropRef = { current: backdrop };
183
+
184
+ const { result, rerender } = renderHook(
185
+ ({ visible }) =>
186
+ useDialogTransform({
187
+ elementRef,
188
+ backdropRef,
189
+ visible,
190
+ openDirection: "bottom",
191
+ swipeState: idleSwipeState,
192
+ displacement: 0,
193
+ displacement2D: zeroDisplacement2D,
194
+ }),
195
+ { initialProps: { visible: false } },
196
+ );
197
+
198
+ // Open first
199
+ rerender({ visible: true });
200
+ act(() => {
201
+ runAnimationFrames(30, 20);
202
+ });
203
+
204
+ await waitFor(() => {
205
+ expect(result.current.phase).toBe("open");
206
+ });
207
+
208
+ // Now trigger close
209
+ act(() => {
210
+ result.current.triggerClose();
211
+ });
212
+
213
+ expect(result.current.phase).toBe("closing");
214
+ });
215
+
216
+ it("should call onCloseComplete when close animation finishes", async () => {
217
+ const element = createMockElement({ width: 400, height: 300 });
218
+ const backdrop = createMockBackdrop();
219
+ const elementRef = { current: element };
220
+ const backdropRef = { current: backdrop };
221
+ const onCloseComplete = vi.fn();
222
+
223
+ const { result, rerender } = renderHook(
224
+ ({ visible }) =>
225
+ useDialogTransform({
226
+ elementRef,
227
+ backdropRef,
228
+ visible,
229
+ openDirection: "bottom",
230
+ swipeState: idleSwipeState,
231
+ displacement: 0,
232
+ displacement2D: zeroDisplacement2D,
233
+ animationDuration: 100,
234
+ onCloseComplete,
235
+ }),
236
+ { initialProps: { visible: false } },
237
+ );
238
+
239
+ // Open first
240
+ rerender({ visible: true });
241
+ act(() => {
242
+ runAnimationFrames(30, 20);
243
+ });
244
+
245
+ await waitFor(() => {
246
+ expect(result.current.phase).toBe("open");
247
+ });
248
+
249
+ // Trigger close
250
+ act(() => {
251
+ result.current.triggerClose();
252
+ });
253
+
254
+ // Run animation
255
+ act(() => {
256
+ runAnimationFrames(30, 20);
257
+ });
258
+
259
+ await waitFor(() => {
260
+ expect(result.current.phase).toBe("closed");
261
+ });
262
+
263
+ expect(onCloseComplete).toHaveBeenCalled();
264
+ });
265
+ });
266
+
267
+ describe("swipe interaction", () => {
268
+ it("should apply 2D transform during swipe", () => {
269
+ const element = createMockElement({ width: 400, height: 300 });
270
+ const backdrop = createMockBackdrop();
271
+ const elementRef = { current: element };
272
+ const backdropRef = { current: backdrop };
273
+
274
+ const operatingSwipeState: ContinuousOperationState = {
275
+ phase: "operating",
276
+ displacement: { x: 30, y: 100 },
277
+ velocity: { x: 0, y: 0 },
278
+ };
279
+
280
+ renderHook(() =>
281
+ useDialogTransform({
282
+ elementRef,
283
+ backdropRef,
284
+ visible: true,
285
+ openDirection: "bottom",
286
+ swipeState: operatingSwipeState,
287
+ displacement: 100,
288
+ displacement2D: { x: 30, y: 100 },
289
+ }),
290
+ );
291
+
292
+ // Transform should reflect 2D displacement
293
+ expect(element.style.transform).toContain("translate(30px, 100px)");
294
+ });
295
+
296
+ it("should animate snapback when swipe ends below threshold", async () => {
297
+ const element = createMockElement({ width: 400, height: 300 });
298
+ const backdrop = createMockBackdrop();
299
+ const elementRef = { current: element };
300
+ const backdropRef = { current: backdrop };
301
+
302
+ const operatingState: ContinuousOperationState = {
303
+ phase: "operating",
304
+ displacement: { x: 20, y: 50 },
305
+ velocity: { x: 0, y: 0 },
306
+ };
307
+
308
+ const displacement2D = { x: 20, y: 50 };
309
+
310
+ const { rerender, result } = renderHook(
311
+ ({ swipeState, displacement, d2d }) =>
312
+ useDialogTransform({
313
+ elementRef,
314
+ backdropRef,
315
+ visible: true,
316
+ openDirection: "bottom",
317
+ swipeState,
318
+ displacement,
319
+ displacement2D: d2d,
320
+ }),
321
+ { initialProps: { swipeState: operatingState, displacement: 50, d2d: displacement2D } },
322
+ );
323
+
324
+ // End swipe
325
+ const endedState: ContinuousOperationState = {
326
+ phase: "ended",
327
+ displacement: { x: 20, y: 50 },
328
+ velocity: { x: 0, y: 0 },
329
+ };
330
+ rerender({ swipeState: endedState, displacement: 50, d2d: displacement2D });
331
+
332
+ // Should start snapback animation
333
+ expect(result.current.isAnimating).toBe(true);
334
+ });
335
+ });
336
+
337
+ describe("container size handling", () => {
338
+ it("should not apply transform when container size is 0", () => {
339
+ const element = createMockElement({ width: 0, height: 0 });
340
+ const backdrop = createMockBackdrop();
341
+ const elementRef = { current: element };
342
+ const backdropRef = { current: backdrop };
343
+
344
+ const { rerender } = renderHook(
345
+ ({ visible }) =>
346
+ useDialogTransform({
347
+ elementRef,
348
+ backdropRef,
349
+ visible,
350
+ openDirection: "bottom",
351
+ swipeState: idleSwipeState,
352
+ displacement: 0,
353
+ displacement2D: zeroDisplacement2D,
354
+ }),
355
+ { initialProps: { visible: false } },
356
+ );
357
+
358
+ rerender({ visible: true });
359
+
360
+ act(() => {
361
+ runAnimationFrames(5, 50);
362
+ });
363
+
364
+ // This test documents the current BROKEN behavior
365
+ // Transform should NOT be empty - this is the bug!
366
+ // When we fix it, this expectation should change
367
+ expect(element.style.transform).toBe("");
368
+ });
369
+ });
370
+ });