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,502 @@
1
+ /**
2
+ * @file SwipePivotContent tests
3
+ *
4
+ * Tests for swipe pivot content positioning, animation, and visibility.
5
+ */
6
+ /* eslint-disable no-restricted-imports, no-restricted-properties, no-restricted-syntax -- integration test requires vitest APIs for timer/rAF mocks */
7
+ import { vi, describe, it, expect, beforeEach, afterEach } from "vitest";
8
+ import { render, screen, act } from "@testing-library/react";
9
+ import * as React from "react";
10
+ import { SwipePivotContent } from "./SwipePivotContent";
11
+ import type { SwipeInputState } from "../../hooks/gesture/types";
12
+
13
+ // Idle state for tests
14
+ const IDLE_STATE: SwipeInputState = {
15
+ phase: "idle",
16
+ displacement: { x: 0, y: 0 },
17
+ velocity: { x: 0, y: 0 },
18
+ direction: 0,
19
+ };
20
+
21
+ // Helper to create swiping state
22
+ const createSwipingState = (dx: number, direction: -1 | 0 | 1): SwipeInputState => ({
23
+ phase: "swiping",
24
+ displacement: { x: dx, y: 0 },
25
+ velocity: { x: 0, y: 0 },
26
+ direction,
27
+ });
28
+
29
+ describe("SwipePivotContent", () => {
30
+ // Mock requestAnimationFrame for animation tests
31
+ let rafCallbacks: FrameRequestCallback[] = [];
32
+ let rafId = 0;
33
+
34
+ beforeEach(() => {
35
+ rafCallbacks = [];
36
+ rafId = 0;
37
+
38
+ vi.spyOn(window, "requestAnimationFrame").mockImplementation((callback) => {
39
+ rafCallbacks.push(callback);
40
+ return ++rafId;
41
+ });
42
+
43
+ vi.spyOn(window, "cancelAnimationFrame").mockImplementation((id) => {
44
+ rafCallbacks = rafCallbacks.filter((_, i) => i + 1 !== id);
45
+ });
46
+ });
47
+
48
+ afterEach(() => {
49
+ vi.restoreAllMocks();
50
+ });
51
+
52
+ // Helper to advance animation frames
53
+ const runAnimationFrame = (time: number = 16) => {
54
+ const callbacks = [...rafCallbacks];
55
+ rafCallbacks = [];
56
+ callbacks.forEach((cb) => cb(time));
57
+ };
58
+
59
+ describe("Initial positioning", () => {
60
+ it("should position active item at center (position 0)", () => {
61
+ render(
62
+ <SwipePivotContent
63
+ id="page1"
64
+ isActive={true}
65
+ position={0}
66
+ inputState={IDLE_STATE}
67
+ containerSize={400}
68
+ >
69
+ <div>Content</div>
70
+ </SwipePivotContent>
71
+ );
72
+
73
+ const element = screen.getByText("Content").parentElement;
74
+ expect(element).toHaveStyle({ transform: "translateX(0px)" });
75
+ expect(element).toHaveStyle({ visibility: "visible" });
76
+ });
77
+
78
+ it("should position next item off-screen right (position 1)", () => {
79
+ render(
80
+ <SwipePivotContent
81
+ id="page2"
82
+ isActive={false}
83
+ position={1}
84
+ inputState={IDLE_STATE}
85
+ containerSize={400}
86
+ >
87
+ <div>Content</div>
88
+ </SwipePivotContent>
89
+ );
90
+
91
+ const element = screen.getByText("Content").parentElement;
92
+ expect(element).toHaveStyle({ transform: "translateX(400px)" });
93
+ // Non-active items in idle state should be hidden
94
+ expect(element).toHaveStyle({ visibility: "hidden" });
95
+ });
96
+
97
+ it("should position previous item off-screen left (position -1)", () => {
98
+ render(
99
+ <SwipePivotContent
100
+ id="page0"
101
+ isActive={false}
102
+ position={-1}
103
+ inputState={IDLE_STATE}
104
+ containerSize={400}
105
+ >
106
+ <div>Content</div>
107
+ </SwipePivotContent>
108
+ );
109
+
110
+ const element = screen.getByText("Content").parentElement;
111
+ expect(element).toHaveStyle({ transform: "translateX(-400px)" });
112
+ expect(element).toHaveStyle({ visibility: "hidden" });
113
+ });
114
+ });
115
+
116
+ describe("Visibility during swipe", () => {
117
+ it("should show next item when swiping left (direction -1)", () => {
118
+ render(
119
+ <SwipePivotContent
120
+ id="page2"
121
+ isActive={false}
122
+ position={1}
123
+ inputState={createSwipingState(-50, -1)}
124
+ containerSize={400}
125
+ canNavigate={true}
126
+ >
127
+ <div>Content</div>
128
+ </SwipePivotContent>
129
+ );
130
+
131
+ const element = screen.getByText("Content").parentElement;
132
+ expect(element).toHaveStyle({ visibility: "visible" });
133
+ });
134
+
135
+ it("should show previous item when swiping right (direction 1)", () => {
136
+ render(
137
+ <SwipePivotContent
138
+ id="page0"
139
+ isActive={false}
140
+ position={-1}
141
+ inputState={createSwipingState(50, 1)}
142
+ containerSize={400}
143
+ canNavigate={true}
144
+ >
145
+ <div>Content</div>
146
+ </SwipePivotContent>
147
+ );
148
+
149
+ const element = screen.getByText("Content").parentElement;
150
+ expect(element).toHaveStyle({ visibility: "visible" });
151
+ });
152
+
153
+ it("should hide item when swiping in opposite direction", () => {
154
+ // Swiping left, but this is the previous page (position -1)
155
+ render(
156
+ <SwipePivotContent
157
+ id="page0"
158
+ isActive={false}
159
+ position={-1}
160
+ inputState={createSwipingState(-50, -1)}
161
+ containerSize={400}
162
+ canNavigate={true}
163
+ >
164
+ <div>Content</div>
165
+ </SwipePivotContent>
166
+ );
167
+
168
+ const element = screen.getByText("Content").parentElement;
169
+ expect(element).toHaveStyle({ visibility: "hidden" });
170
+ });
171
+
172
+ it("should hide item when canNavigate is false", () => {
173
+ render(
174
+ <SwipePivotContent
175
+ id="page2"
176
+ isActive={false}
177
+ position={1}
178
+ inputState={createSwipingState(-50, -1)}
179
+ containerSize={400}
180
+ canNavigate={false}
181
+ >
182
+ <div>Content</div>
183
+ </SwipePivotContent>
184
+ );
185
+
186
+ const element = screen.getByText("Content").parentElement;
187
+ expect(element).toHaveStyle({ visibility: "hidden" });
188
+ });
189
+ });
190
+
191
+ describe("Displacement during swipe", () => {
192
+ it("should apply displacement offset during swipe", () => {
193
+ const { rerender } = render(
194
+ <SwipePivotContent
195
+ id="page1"
196
+ isActive={true}
197
+ position={0}
198
+ inputState={IDLE_STATE}
199
+ containerSize={400}
200
+ >
201
+ <div>Content</div>
202
+ </SwipePivotContent>
203
+ );
204
+
205
+ // Start swiping
206
+ rerender(
207
+ <SwipePivotContent
208
+ id="page1"
209
+ isActive={true}
210
+ position={0}
211
+ inputState={createSwipingState(-100, -1)}
212
+ containerSize={400}
213
+ >
214
+ <div>Content</div>
215
+ </SwipePivotContent>
216
+ );
217
+
218
+ const element = screen.getByText("Content").parentElement;
219
+ // Position 0 + displacement -100 = -100
220
+ expect(element).toHaveStyle({ transform: "translateX(-100px)" });
221
+ });
222
+ });
223
+
224
+ describe("Container size changes (resize)", () => {
225
+ it("should snap to new position when containerSize changes", () => {
226
+ const { rerender } = render(
227
+ <SwipePivotContent
228
+ id="page2"
229
+ isActive={false}
230
+ position={1}
231
+ inputState={IDLE_STATE}
232
+ containerSize={400}
233
+ >
234
+ <div>Content</div>
235
+ </SwipePivotContent>
236
+ );
237
+
238
+ const element = screen.getByText("Content").parentElement;
239
+ expect(element).toHaveStyle({ transform: "translateX(400px)" });
240
+
241
+ // Resize container
242
+ rerender(
243
+ <SwipePivotContent
244
+ id="page2"
245
+ isActive={false}
246
+ position={1}
247
+ inputState={IDLE_STATE}
248
+ containerSize={600}
249
+ >
250
+ <div>Content</div>
251
+ </SwipePivotContent>
252
+ );
253
+
254
+ // Should immediately snap to new position (no animation)
255
+ expect(element).toHaveStyle({ transform: "translateX(600px)" });
256
+ });
257
+
258
+ it("should snap all positions correctly after resize", () => {
259
+ // Test that all positions (-1, 0, 1) update correctly on resize
260
+ const { rerender } = render(
261
+ <div>
262
+ <SwipePivotContent
263
+ id="page0"
264
+ isActive={false}
265
+ position={-1}
266
+ inputState={IDLE_STATE}
267
+ containerSize={400}
268
+ >
269
+ <div data-testid="prev">Prev</div>
270
+ </SwipePivotContent>
271
+ <SwipePivotContent
272
+ id="page1"
273
+ isActive={true}
274
+ position={0}
275
+ inputState={IDLE_STATE}
276
+ containerSize={400}
277
+ >
278
+ <div data-testid="current">Current</div>
279
+ </SwipePivotContent>
280
+ <SwipePivotContent
281
+ id="page2"
282
+ isActive={false}
283
+ position={1}
284
+ inputState={IDLE_STATE}
285
+ containerSize={400}
286
+ >
287
+ <div data-testid="next">Next</div>
288
+ </SwipePivotContent>
289
+ </div>
290
+ );
291
+
292
+ // Verify initial positions
293
+ expect(screen.getByTestId("prev").parentElement).toHaveStyle({ transform: "translateX(-400px)" });
294
+ expect(screen.getByTestId("current").parentElement).toHaveStyle({ transform: "translateX(0px)" });
295
+ expect(screen.getByTestId("next").parentElement).toHaveStyle({ transform: "translateX(400px)" });
296
+
297
+ // Resize to 800px
298
+ rerender(
299
+ <div>
300
+ <SwipePivotContent
301
+ id="page0"
302
+ isActive={false}
303
+ position={-1}
304
+ inputState={IDLE_STATE}
305
+ containerSize={800}
306
+ >
307
+ <div data-testid="prev">Prev</div>
308
+ </SwipePivotContent>
309
+ <SwipePivotContent
310
+ id="page1"
311
+ isActive={true}
312
+ position={0}
313
+ inputState={IDLE_STATE}
314
+ containerSize={800}
315
+ >
316
+ <div data-testid="current">Current</div>
317
+ </SwipePivotContent>
318
+ <SwipePivotContent
319
+ id="page2"
320
+ isActive={false}
321
+ position={1}
322
+ inputState={IDLE_STATE}
323
+ containerSize={800}
324
+ >
325
+ <div data-testid="next">Next</div>
326
+ </SwipePivotContent>
327
+ </div>
328
+ );
329
+
330
+ // All should snap to new positions based on new containerSize
331
+ expect(screen.getByTestId("prev").parentElement).toHaveStyle({ transform: "translateX(-800px)" });
332
+ expect(screen.getByTestId("current").parentElement).toHaveStyle({ transform: "translateX(0px)" });
333
+ expect(screen.getByTestId("next").parentElement).toHaveStyle({ transform: "translateX(800px)" });
334
+ });
335
+
336
+ it("should handle resize after swipe interaction", () => {
337
+ const { rerender } = render(
338
+ <SwipePivotContent
339
+ id="page1"
340
+ isActive={true}
341
+ position={0}
342
+ inputState={IDLE_STATE}
343
+ containerSize={400}
344
+ >
345
+ <div>Content</div>
346
+ </SwipePivotContent>
347
+ );
348
+
349
+ const element = screen.getByText("Content").parentElement;
350
+
351
+ // Simulate swipe
352
+ rerender(
353
+ <SwipePivotContent
354
+ id="page1"
355
+ isActive={true}
356
+ position={0}
357
+ inputState={createSwipingState(-100, -1)}
358
+ containerSize={400}
359
+ >
360
+ <div>Content</div>
361
+ </SwipePivotContent>
362
+ );
363
+ expect(element).toHaveStyle({ transform: "translateX(-100px)" });
364
+
365
+ // End swipe and resize at the same time
366
+ rerender(
367
+ <SwipePivotContent
368
+ id="page1"
369
+ isActive={true}
370
+ position={0}
371
+ inputState={IDLE_STATE}
372
+ containerSize={600}
373
+ >
374
+ <div>Content</div>
375
+ </SwipePivotContent>
376
+ );
377
+
378
+ // Should snap to new position 0 (active item stays at center)
379
+ expect(element).toHaveStyle({ transform: "translateX(0px)" });
380
+ });
381
+
382
+ it("should not render content when containerSize is 0", () => {
383
+ // This test verifies that parent components should guard against containerSize=0
384
+ // SwipePivotContent itself will render, but all items will be at position 0
385
+ render(
386
+ <SwipePivotContent
387
+ id="page1"
388
+ isActive={true}
389
+ position={0}
390
+ inputState={IDLE_STATE}
391
+ containerSize={0}
392
+ >
393
+ <div>Content</div>
394
+ </SwipePivotContent>
395
+ );
396
+
397
+ const element = screen.getByText("Content").parentElement;
398
+ // With containerSize 0, position * 0 = 0
399
+ expect(element).toHaveStyle({ transform: "translateX(0px)" });
400
+ });
401
+ });
402
+
403
+ describe("Position changes (navigation)", () => {
404
+ it("should animate when position changes and swipe ends", async () => {
405
+ const { rerender } = render(
406
+ <SwipePivotContent
407
+ id="page1"
408
+ isActive={true}
409
+ position={0}
410
+ inputState={createSwipingState(-200, -1)}
411
+ containerSize={400}
412
+ >
413
+ <div>Content</div>
414
+ </SwipePivotContent>
415
+ );
416
+
417
+ const element = screen.getByText("Content").parentElement;
418
+ // During swipe: position 0 + displacement -200 = -200
419
+ expect(element).toHaveStyle({ transform: "translateX(-200px)" });
420
+
421
+ // Swipe ends, position changes to -1 (this page is now to the left)
422
+ rerender(
423
+ <SwipePivotContent
424
+ id="page1"
425
+ isActive={false}
426
+ position={-1}
427
+ inputState={IDLE_STATE}
428
+ containerSize={400}
429
+ >
430
+ <div>Content</div>
431
+ </SwipePivotContent>
432
+ );
433
+
434
+ // Animation should start - we need to advance animation frames
435
+ // The animation goes from current position (-200) to target (-400)
436
+ act(() => {
437
+ runAnimationFrame(0); // Start animation
438
+ });
439
+
440
+ // After animation starts, element should be animating towards target
441
+ // The exact value depends on animation progress, but it should not be at target yet
442
+ // or should be animating (we can't easily test intermediate values)
443
+ });
444
+ });
445
+
446
+ describe("Active item always visible", () => {
447
+ it("should always show active item regardless of input state", () => {
448
+ render(
449
+ <SwipePivotContent
450
+ id="page1"
451
+ isActive={true}
452
+ position={0}
453
+ inputState={IDLE_STATE}
454
+ containerSize={400}
455
+ >
456
+ <div>Content</div>
457
+ </SwipePivotContent>
458
+ );
459
+
460
+ const element = screen.getByText("Content").parentElement;
461
+ expect(element).toHaveStyle({ visibility: "visible" });
462
+ });
463
+
464
+ it("should show active item during swiping", () => {
465
+ render(
466
+ <SwipePivotContent
467
+ id="page1"
468
+ isActive={true}
469
+ position={0}
470
+ inputState={createSwipingState(-100, -1)}
471
+ containerSize={400}
472
+ >
473
+ <div>Content</div>
474
+ </SwipePivotContent>
475
+ );
476
+
477
+ const element = screen.getByText("Content").parentElement;
478
+ expect(element).toHaveStyle({ visibility: "visible" });
479
+ });
480
+ });
481
+
482
+ describe("Data attributes", () => {
483
+ it("should set correct data attributes", () => {
484
+ render(
485
+ <SwipePivotContent
486
+ id="test-page"
487
+ isActive={true}
488
+ position={0}
489
+ inputState={IDLE_STATE}
490
+ containerSize={400}
491
+ >
492
+ <div>Content</div>
493
+ </SwipePivotContent>
494
+ );
495
+
496
+ const element = screen.getByText("Content").parentElement;
497
+ expect(element).toHaveAttribute("data-pivot-content", "test-page");
498
+ expect(element).toHaveAttribute("data-active", "true");
499
+ expect(element).toHaveAttribute("data-position", "0");
500
+ });
501
+ });
502
+ });
@@ -0,0 +1,197 @@
1
+ /**
2
+ * @file SwipePivotContent component for pivot items with swipe animation.
3
+ *
4
+ * Positioning model:
5
+ * - targetPx = position * containerSize (where the item should be)
6
+ * - During swipe: displayPx = targetPx + displacement (follows finger)
7
+ * - After swipe: animate currentPx from finger position to targetPx
8
+ *
9
+ * Visibility modes:
10
+ * 1. Adjacent mode (default): Active item + adjacent items during swipe/animation
11
+ * 2. Viewport mode (viewportSize specified): Items visible if within viewport bounds
12
+ *
13
+ * Uses shared useSwipeContentTransform hook for DOM manipulation.
14
+ */
15
+ import * as React from "react";
16
+ import { useSwipeContentTransform } from "../../hooks/useSwipeContentTransform.js";
17
+ import type { AnimationDirection } from "../../hooks/useSwipeContentTransform.js";
18
+ import type { SwipeInputState, GestureAxis } from "../../hooks/gesture/types.js";
19
+
20
+ const DEFAULT_ANIMATION_DURATION = 300;
21
+
22
+ export type SwipePivotContentProps = {
23
+ id: string;
24
+ isActive: boolean;
25
+ /**
26
+ * Position offset in number of items from center (0).
27
+ * Positive values are to the right/bottom, negative to the left/top.
28
+ */
29
+ position: number;
30
+ inputState: SwipeInputState;
31
+ axis?: GestureAxis;
32
+ /** Size of each item (width for horizontal, height for vertical) */
33
+ containerSize: number;
34
+ children: React.ReactNode;
35
+ canNavigate?: boolean;
36
+ animationDuration?: number;
37
+ /**
38
+ * Viewport size for viewport-based visibility mode.
39
+ * When specified, items are visible if they intersect with the viewport [0, viewportSize].
40
+ * When not specified, uses adjacent-item visibility logic (default).
41
+ */
42
+ viewportSize?: number;
43
+ };
44
+
45
+ const BASE_STYLE: React.CSSProperties = {
46
+ position: "absolute",
47
+ inset: 0,
48
+ width: "100%",
49
+ height: "100%",
50
+ };
51
+
52
+ /** Get displacement value for the given axis from input state */
53
+ const getAxisDisplacement = (inputState: SwipeInputState, axis: GestureAxis): number => {
54
+ if (inputState.phase === "idle") {
55
+ return 0;
56
+ }
57
+ return axis === "horizontal" ? inputState.displacement.x : inputState.displacement.y;
58
+ };
59
+
60
+ /** Check if animating towards center (position 0) */
61
+ const isAnimatingTowardsCenter = (animationDirection: AnimationDirection | null): boolean => {
62
+ if (!animationDirection) {
63
+ return false;
64
+ }
65
+ const { from, to } = animationDirection;
66
+ return Math.abs(to) < Math.abs(from);
67
+ };
68
+
69
+ /** Check if animating away from center (snap-back) */
70
+ const isAnimatingAwayFromCenter = (animationDirection: AnimationDirection | null): boolean => {
71
+ if (!animationDirection) {
72
+ return false;
73
+ }
74
+ const { from, to } = animationDirection;
75
+ return Math.abs(to) > Math.abs(from);
76
+ };
77
+
78
+ /** Determine if an item should be visible based on current state (adjacent mode) */
79
+ const computeAdjacentVisibility = (
80
+ isActive: boolean,
81
+ position: number,
82
+ inputState: SwipeInputState,
83
+ canNavigate: boolean,
84
+ animationDirection: AnimationDirection | null,
85
+ ): boolean => {
86
+ if (isActive) {
87
+ return true;
88
+ }
89
+ if (isAnimatingTowardsCenter(animationDirection)) {
90
+ return true;
91
+ }
92
+ // Keep visible during snap-back animation (moving away from center)
93
+ if (isAnimatingAwayFromCenter(animationDirection)) {
94
+ return true;
95
+ }
96
+ if (!canNavigate) {
97
+ return false;
98
+ }
99
+ if (inputState.phase === "idle") {
100
+ return false;
101
+ }
102
+ // Show adjacent item when swiping towards it
103
+ if (position === -1 && inputState.direction === 1) {
104
+ return true;
105
+ }
106
+ if (position === 1 && inputState.direction === -1) {
107
+ return true;
108
+ }
109
+ return false;
110
+ };
111
+
112
+ /**
113
+ * Determine if an item should be visible based on viewport intersection.
114
+ * An item is visible if any part of it intersects with the viewport [0, viewportSize].
115
+ */
116
+ const computeViewportVisibility = (
117
+ itemPosition: number,
118
+ containerSize: number,
119
+ viewportSize: number,
120
+ ): boolean => {
121
+ const itemStart = itemPosition;
122
+ const itemEnd = itemPosition + containerSize;
123
+ // Item is visible if it intersects with [0, viewportSize]
124
+ return itemEnd > 0 && itemStart < viewportSize;
125
+ };
126
+
127
+ export const SwipePivotContent: React.FC<SwipePivotContentProps> = React.memo(({
128
+ id,
129
+ isActive,
130
+ position,
131
+ inputState,
132
+ axis = "horizontal",
133
+ containerSize,
134
+ children,
135
+ canNavigate = true,
136
+ animationDuration = DEFAULT_ANIMATION_DURATION,
137
+ viewportSize,
138
+ }) => {
139
+ const elementRef = React.useRef<HTMLDivElement>(null);
140
+
141
+ const targetPx = position * containerSize;
142
+ const displacement = getAxisDisplacement(inputState, axis);
143
+ const isOperating = inputState.phase === "swiping" || inputState.phase === "tracking";
144
+
145
+ // Use shared transform hook for DOM manipulation
146
+ const { animationDirection } = useSwipeContentTransform({
147
+ elementRef,
148
+ targetPx,
149
+ displacement,
150
+ isOperating,
151
+ axis,
152
+ animationDuration,
153
+ containerSize,
154
+ });
155
+
156
+ // Compute visibility based on mode
157
+ const computeVisible = (): boolean => {
158
+ if (viewportSize !== undefined) {
159
+ return computeViewportVisibility(targetPx + displacement, containerSize, viewportSize);
160
+ }
161
+ return computeAdjacentVisibility(isActive, position, inputState, canNavigate, animationDirection);
162
+ };
163
+ const visible = computeVisible();
164
+
165
+ // Update visibility via direct DOM manipulation
166
+ React.useLayoutEffect(() => {
167
+ const element = elementRef.current;
168
+ if (element) {
169
+ element.style.visibility = visible ? "visible" : "hidden";
170
+ }
171
+ }, [visible]);
172
+
173
+ // Initial transform and visibility to prevent flash of unstyled content
174
+ const transformFn = axis === "horizontal" ? "translateX" : "translateY";
175
+ const initialTransform = `${transformFn}(${targetPx}px)`;
176
+ const initialVisibility = visible ? "visible" : "hidden";
177
+
178
+ const staticStyle = React.useMemo<React.CSSProperties>(() => ({
179
+ ...BASE_STYLE,
180
+ pointerEvents: isActive ? "auto" : "none",
181
+ willChange: "transform",
182
+ transform: initialTransform,
183
+ visibility: initialVisibility,
184
+ }), [isActive, initialTransform, initialVisibility]);
185
+
186
+ return (
187
+ <div
188
+ ref={elementRef}
189
+ data-pivot-content={id}
190
+ data-active={isActive ? "true" : "false"}
191
+ data-position={position}
192
+ style={staticStyle}
193
+ >
194
+ {children}
195
+ </div>
196
+ );
197
+ });