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,583 @@
1
+ /**
2
+ * @file SwipePivotTabBar - Swipeable tab bar for pivot navigation with proper looping.
3
+ *
4
+ * Infinite loop model:
5
+ * - Uses continuous scroll offset (not discrete positions)
6
+ * - Renders tab slots at fixed positions, content determined by scroll offset
7
+ * - Clones tabs at boundaries for seamless looping
8
+ * - Each slot has stable key (by position), preventing remount jumps
9
+ */
10
+ import * as React from "react";
11
+ import type { SwipeInputState, GestureAxis } from "../../hooks/gesture/types.js";
12
+
13
+ /**
14
+ * Props passed to the indicator render function.
15
+ * Use these to position a sliding indicator (iOS-style).
16
+ */
17
+ export type IndicatorRenderProps = {
18
+ /** Current offset in pixels (includes swipe displacement and animation) */
19
+ offsetPx: number;
20
+ /** Width of each tab */
21
+ tabWidth: number;
22
+ /** Center X position where active tab is located */
23
+ centerX: number;
24
+ /** Whether currently swiping */
25
+ isSwiping: boolean;
26
+ /** Whether animation is in progress */
27
+ isAnimating: boolean;
28
+ };
29
+
30
+ export type SwipePivotTabBarProps<TId extends string = string> = {
31
+ /** Tab items to render */
32
+ items: ReadonlyArray<{ id: TId; label?: string }>;
33
+ /** Currently active tab ID */
34
+ activeId: TId;
35
+ /** Index of active tab */
36
+ activeIndex: number;
37
+ /** Total number of items */
38
+ itemCount: number;
39
+ /** Current swipe input state */
40
+ inputState: SwipeInputState;
41
+ /** Width of each tab */
42
+ tabWidth: number;
43
+ /** Width of the visible viewport */
44
+ viewportWidth: number;
45
+ /** Navigation mode */
46
+ navigationMode?: "linear" | "loop";
47
+ /** Axis for swipe (horizontal or vertical) */
48
+ axis?: GestureAxis;
49
+ /** Render function for each tab */
50
+ renderTab: (item: { id: TId; label?: string }, isActive: boolean, index: number) => React.ReactNode;
51
+ /** Animation duration in ms */
52
+ animationDuration?: number;
53
+ /**
54
+ * When true, tabs stay at fixed positions and only the indicator moves.
55
+ * Use this for iOS segmented control style where the "window" slides over fixed tabs.
56
+ * @default false
57
+ */
58
+ fixedTabs?: boolean;
59
+ /**
60
+ * Optional render function for a sliding indicator (iOS-style).
61
+ * The indicator follows the active tab position during swipe and animation.
62
+ * Rendered behind the tabs.
63
+ *
64
+ * When used with fixedTabs=true, only the indicator moves while tabs stay fixed.
65
+ *
66
+ * @example
67
+ * ```tsx
68
+ * renderIndicator={({ offsetPx, tabWidth, centerX }) => (
69
+ * <div
70
+ * style={{
71
+ * position: 'absolute',
72
+ * left: centerX,
73
+ * bottom: 0,
74
+ * width: tabWidth,
75
+ * height: 3,
76
+ * backgroundColor: '#007AFF',
77
+ * transform: `translateX(${offsetPx}px)`,
78
+ * }}
79
+ * />
80
+ * )}
81
+ * ```
82
+ */
83
+ renderIndicator?: (props: IndicatorRenderProps) => React.ReactNode;
84
+ };
85
+
86
+ const DEFAULT_ANIMATION_DURATION = 300;
87
+
88
+ /** Get displacement value for the given axis from input state */
89
+ const getAxisDisplacement = (inputState: SwipeInputState, axis: GestureAxis): number => {
90
+ if (inputState.phase === "idle") {
91
+ return 0;
92
+ }
93
+ return axis === "horizontal" ? inputState.displacement.x : inputState.displacement.y;
94
+ };
95
+
96
+ /**
97
+ * Normalize index to valid range [0, count)
98
+ */
99
+ const normalizeIndex = (index: number, count: number): number => {
100
+ return ((index % count) + count) % count;
101
+ };
102
+
103
+ /**
104
+ * Calculate which item should appear at a given slot position.
105
+ * For loop mode, wraps around using modulo.
106
+ * For linear mode, returns null if out of range.
107
+ */
108
+ const getItemAtPosition = (
109
+ slotPosition: number,
110
+ activeIndex: number,
111
+ itemCount: number,
112
+ navigationMode: "linear" | "loop",
113
+ ): number | null => {
114
+ const targetIndex = activeIndex + slotPosition;
115
+
116
+ const isOutOfRange = (index: number, count: number): boolean => {
117
+ if (index < 0) {
118
+ return true;
119
+ }
120
+ if (index >= count) {
121
+ return true;
122
+ }
123
+ return false;
124
+ };
125
+
126
+ if (navigationMode === "linear") {
127
+ if (isOutOfRange(targetIndex, itemCount)) {
128
+ return null;
129
+ }
130
+ return targetIndex;
131
+ }
132
+
133
+ // Loop mode: wrap around
134
+ return normalizeIndex(targetIndex, itemCount);
135
+ };
136
+
137
+ type TabSlotProps<TId extends string> = {
138
+ slotPosition: number;
139
+ item: { id: TId; label?: string };
140
+ itemIndex: number;
141
+ isActive: boolean;
142
+ centerX: number;
143
+ tabWidth: number;
144
+ viewportWidth: number;
145
+ offsetPx: number;
146
+ axis: GestureAxis;
147
+ renderTab: (item: { id: TId; label?: string }, isActive: boolean, index: number) => React.ReactNode;
148
+ };
149
+
150
+ /**
151
+ * Tab slot component - renders a tab at a fixed slot position.
152
+ * The slot position is stable; only the content changes based on scroll offset.
153
+ */
154
+ const TabSlot = React.memo(<TId extends string>({
155
+ slotPosition,
156
+ item,
157
+ itemIndex,
158
+ isActive,
159
+ centerX,
160
+ tabWidth,
161
+ viewportWidth,
162
+ offsetPx,
163
+ axis,
164
+ renderTab,
165
+ }: TabSlotProps<TId>) => {
166
+ // Calculate visual position: centerX + slotPosition * tabWidth + offsetPx
167
+ const basePx = slotPosition * tabWidth;
168
+ const visualPx = centerX + basePx + offsetPx;
169
+
170
+ // Check if visible in viewport
171
+ const visible = visualPx + tabWidth > 0 && visualPx < viewportWidth;
172
+
173
+ const transformFn = axis === "horizontal" ? "translateX" : "translateY";
174
+
175
+ return (
176
+ <div
177
+ data-pivot-tab={item.id}
178
+ data-slot={slotPosition}
179
+ data-active={isActive ? "true" : "false"}
180
+ style={{
181
+ position: "absolute",
182
+ left: centerX,
183
+ top: 0,
184
+ width: tabWidth,
185
+ height: "100%",
186
+ visibility: visible ? "visible" : "hidden",
187
+ willChange: "transform",
188
+ transform: `${transformFn}(${basePx + offsetPx}px)`,
189
+ }}
190
+ >
191
+ {renderTab(item, isActive, itemIndex)}
192
+ </div>
193
+ );
194
+ }) as <TId extends string>(props: TabSlotProps<TId>) => React.ReactElement;
195
+
196
+ /**
197
+ * Easing function for smooth animation
198
+ */
199
+ const easeOutExpo = (t: number): number => {
200
+ return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
201
+ };
202
+
203
+ /**
204
+ * Swipeable tab bar for pivot navigation.
205
+ */
206
+ export function SwipePivotTabBar<TId extends string = string>({
207
+ items,
208
+ activeId,
209
+ activeIndex,
210
+ itemCount,
211
+ inputState,
212
+ tabWidth,
213
+ viewportWidth,
214
+ navigationMode = "linear",
215
+ axis = "horizontal",
216
+ renderTab,
217
+ animationDuration = DEFAULT_ANIMATION_DURATION,
218
+ fixedTabs = false,
219
+ renderIndicator,
220
+ }: SwipePivotTabBarProps<TId>): React.ReactElement {
221
+ const isSwipePhase = (phase: SwipeInputState["phase"]): boolean => {
222
+ if (phase === "swiping") {
223
+ return true;
224
+ }
225
+ if (phase === "tracking") {
226
+ return true;
227
+ }
228
+ return false;
229
+ };
230
+
231
+ const getIsAnimating = (
232
+ slotAnimation: typeof animationRef.current,
233
+ fixedAnimation: typeof fixedAnimationRef.current,
234
+ ): boolean => {
235
+ if (slotAnimation !== null) {
236
+ return true;
237
+ }
238
+ if (fixedAnimation !== null) {
239
+ return true;
240
+ }
241
+ return false;
242
+ };
243
+
244
+ const getDelta = (
245
+ mode: "linear" | "loop",
246
+ nextIndex: number,
247
+ previousIndex: number,
248
+ totalItems: number,
249
+ ): number => {
250
+ if (mode === "loop") {
251
+ // Use shortest path in loop mode
252
+ const forwardDist = normalizeIndex(nextIndex - previousIndex, totalItems);
253
+ const backwardDist = totalItems - forwardDist;
254
+ if (forwardDist <= backwardDist) {
255
+ return forwardDist;
256
+ }
257
+ return -backwardDist;
258
+ }
259
+ return nextIndex - previousIndex;
260
+ };
261
+
262
+ const displacement = getAxisDisplacement(inputState, axis);
263
+ const isSwiping = isSwipePhase(inputState.phase);
264
+
265
+ // ============================================================
266
+ // Animation state for SLOT-BASED mode (scrolling tabs)
267
+ // ============================================================
268
+ const [animatedOffset, setAnimatedOffset] = React.useState(0);
269
+ const animationRef = React.useRef<{
270
+ startTime: number;
271
+ startOffset: number;
272
+ targetOffset: number;
273
+ rafId: number;
274
+ } | null>(null);
275
+
276
+ // ============================================================
277
+ // Animation state for FIXED TABS mode (iOS-style indicator)
278
+ // Tracks the actual indicator position in pixels
279
+ // ============================================================
280
+ const [indicatorPosition, setIndicatorPosition] = React.useState(activeIndex * tabWidth);
281
+ const indicatorPositionRef = React.useRef(activeIndex * tabWidth); // Track current position for animation start
282
+ const fixedAnimationRef = React.useRef<{
283
+ rafId: number;
284
+ } | null>(null);
285
+ const lastSwipePositionRef = React.useRef<number | null>(null);
286
+
287
+ // Keep ref in sync with state
288
+ React.useEffect(() => {
289
+ indicatorPositionRef.current = indicatorPosition;
290
+ }, [indicatorPosition]);
291
+
292
+ const prevActiveIndexRef = React.useRef(activeIndex);
293
+
294
+ // Calculate the range of slot positions to render
295
+ const halfRange = Math.ceil(viewportWidth / tabWidth / 2) + 1;
296
+
297
+ // Center position for active tab (for slot-based mode)
298
+ const centerX = (viewportWidth - tabWidth) / 2;
299
+
300
+ // ============================================================
301
+ // Fixed tabs mode: track swipe position
302
+ // ============================================================
303
+ React.useEffect(() => {
304
+ if (!fixedTabs) {
305
+ return;
306
+ }
307
+ if (!isSwiping) {
308
+ return;
309
+ }
310
+
311
+ // During swipe, track the visual position
312
+ // Swipe direction is OPPOSITE to indicator movement
313
+ const visualPosition = activeIndex * tabWidth - displacement;
314
+ lastSwipePositionRef.current = visualPosition;
315
+ setIndicatorPosition(visualPosition);
316
+ }, [fixedTabs, isSwiping, activeIndex, tabWidth, displacement]);
317
+
318
+ // ============================================================
319
+ // Fixed tabs mode: animate when swipe ends or tab clicked
320
+ // ============================================================
321
+ React.useEffect(() => {
322
+ if (!fixedTabs) {
323
+ return;
324
+ }
325
+ if (isSwiping) {
326
+ return;
327
+ }
328
+
329
+ // When swipe ends or tab changes via click
330
+ const targetPosition = activeIndex * tabWidth;
331
+ const startPosition = lastSwipePositionRef.current ?? indicatorPositionRef.current;
332
+ lastSwipePositionRef.current = null;
333
+
334
+ // Already at target
335
+ if (Math.abs(startPosition - targetPosition) < 1) {
336
+ setIndicatorPosition(targetPosition);
337
+ return;
338
+ }
339
+
340
+ // Cancel existing animation
341
+ if (fixedAnimationRef.current) {
342
+ cancelAnimationFrame(fixedAnimationRef.current.rafId);
343
+ }
344
+
345
+ // Animate from current position to target
346
+ const startTime = performance.now();
347
+ const animationStartPosition = startPosition; // Capture for closure
348
+
349
+ const animate = (currentTime: number) => {
350
+ const elapsed = currentTime - startTime;
351
+ const progress = Math.min(elapsed / animationDuration, 1);
352
+ const easedProgress = easeOutExpo(progress);
353
+
354
+ const currentPosition = animationStartPosition + (targetPosition - animationStartPosition) * easedProgress;
355
+ setIndicatorPosition(currentPosition);
356
+
357
+ if (progress < 1) {
358
+ fixedAnimationRef.current = {
359
+ rafId: requestAnimationFrame(animate),
360
+ };
361
+ } else {
362
+ fixedAnimationRef.current = null;
363
+ setIndicatorPosition(targetPosition);
364
+ }
365
+ };
366
+
367
+ fixedAnimationRef.current = {
368
+ rafId: requestAnimationFrame(animate),
369
+ };
370
+ }, [fixedTabs, isSwiping, activeIndex, tabWidth, animationDuration]);
371
+
372
+ // ============================================================
373
+ // Slot-based mode animation: handle activeIndex changes
374
+ // ============================================================
375
+ React.useEffect(() => {
376
+ if (fixedTabs) {
377
+ return; // Skip for fixed tabs mode
378
+ }
379
+
380
+ if (prevActiveIndexRef.current === activeIndex) {
381
+ return;
382
+ }
383
+
384
+ const prevIndex = prevActiveIndexRef.current;
385
+ prevActiveIndexRef.current = activeIndex;
386
+
387
+ // Calculate direction of movement
388
+ const delta = getDelta(navigationMode, activeIndex, prevIndex, itemCount);
389
+
390
+ // Target offset to animate to (then snap to 0)
391
+ const targetOffsetPx = -delta * tabWidth;
392
+
393
+ // Start from current visual position
394
+ const startOffset = isSwiping ? displacement : animatedOffset;
395
+
396
+ // Cancel any existing animation
397
+ if (animationRef.current) {
398
+ cancelAnimationFrame(animationRef.current.rafId);
399
+ }
400
+
401
+ // If already at target (within threshold), snap immediately
402
+ if (Math.abs(startOffset - targetOffsetPx) < 1) {
403
+ setAnimatedOffset(0);
404
+ return;
405
+ }
406
+
407
+ // Start animation
408
+ const startTime = performance.now();
409
+
410
+ const animate = (currentTime: number) => {
411
+ const elapsed = currentTime - startTime;
412
+ const progress = Math.min(elapsed / animationDuration, 1);
413
+ const easedProgress = easeOutExpo(progress);
414
+
415
+ // Interpolate from startOffset toward targetOffset, but we want to end at 0
416
+ // So we animate: startOffset → targetOffset, but since we're rendering with
417
+ // the NEW activeIndex, we need to compensate
418
+ //
419
+ // When activeIndex changes, slots now show different content.
420
+ // We animate the offset from (startOffset - targetOffset) back to 0.
421
+ const compensatedStart = startOffset - targetOffsetPx;
422
+ const currentOffset = compensatedStart * (1 - easedProgress);
423
+
424
+ setAnimatedOffset(currentOffset);
425
+
426
+ if (progress < 1) {
427
+ animationRef.current = {
428
+ startTime,
429
+ startOffset: compensatedStart,
430
+ targetOffset: 0,
431
+ rafId: requestAnimationFrame(animate),
432
+ };
433
+ } else {
434
+ animationRef.current = null;
435
+ setAnimatedOffset(0);
436
+ }
437
+ };
438
+
439
+ animationRef.current = {
440
+ startTime,
441
+ startOffset: startOffset - targetOffsetPx,
442
+ targetOffset: 0,
443
+ rafId: requestAnimationFrame(animate),
444
+ };
445
+ }, [fixedTabs, activeIndex, itemCount, tabWidth, animationDuration, navigationMode, displacement, isSwiping, animatedOffset]);
446
+
447
+ // Update prevActiveIndexRef for fixed tabs mode too
448
+ React.useEffect(() => {
449
+ if (fixedTabs) {
450
+ prevActiveIndexRef.current = activeIndex;
451
+ }
452
+ }, [fixedTabs, activeIndex]);
453
+
454
+ // Cleanup animation on unmount
455
+ React.useEffect(() => {
456
+ return () => {
457
+ if (animationRef.current) {
458
+ cancelAnimationFrame(animationRef.current.rafId);
459
+ }
460
+ if (fixedAnimationRef.current) {
461
+ cancelAnimationFrame(fixedAnimationRef.current.rafId);
462
+ }
463
+ };
464
+ }, []);
465
+
466
+ // Current offset for slot-based mode
467
+ const currentOffset = isSwiping ? displacement : animatedOffset;
468
+ const isAnimating = getIsAnimating(animationRef.current, fixedAnimationRef.current);
469
+
470
+ // Cancel slot animation when swiping starts
471
+ React.useEffect(() => {
472
+ if (isSwiping && animationRef.current) {
473
+ cancelAnimationFrame(animationRef.current.rafId);
474
+ animationRef.current = null;
475
+ setAnimatedOffset(0);
476
+ }
477
+ }, [isSwiping]);
478
+
479
+ // ============================================================
480
+ // Fixed tabs mode: render fixed tabs with sliding indicator
481
+ // ============================================================
482
+ if (fixedTabs) {
483
+ // Calculate total width and centering offset
484
+ const totalTabsWidth = items.length * tabWidth;
485
+ const centeringOffset = (viewportWidth - totalTabsWidth) / 2;
486
+
487
+ return (
488
+ <div
489
+ data-active-id={activeId}
490
+ style={{
491
+ position: "relative",
492
+ width: "100%",
493
+ height: "100%",
494
+ overflow: "hidden",
495
+ display: "flex",
496
+ justifyContent: "center",
497
+ }}
498
+ >
499
+ {/* Sliding indicator (rendered behind tabs) */}
500
+ {renderIndicator?.({
501
+ offsetPx: indicatorPosition,
502
+ tabWidth,
503
+ centerX: centeringOffset,
504
+ isSwiping,
505
+ isAnimating,
506
+ })}
507
+
508
+ {/* Fixed tabs - each tab at its natural position */}
509
+ {items.map((item, index) => (
510
+ <div
511
+ key={item.id}
512
+ data-pivot-tab={item.id}
513
+ data-active={index === activeIndex ? "true" : "false"}
514
+ style={{
515
+ position: "relative",
516
+ width: tabWidth,
517
+ height: "100%",
518
+ flexShrink: 0,
519
+ }}
520
+ >
521
+ {renderTab(item, index === activeIndex, index)}
522
+ </div>
523
+ ))}
524
+ </div>
525
+ );
526
+ }
527
+
528
+ // Slot-based rendering for scrolling tabs (infinite loop support)
529
+ const slots: Array<{
530
+ slotPosition: number;
531
+ itemIndex: number;
532
+ item: { id: TId; label?: string };
533
+ }> = [];
534
+
535
+ for (let pos = -halfRange; pos <= halfRange; pos++) {
536
+ const itemIndex = getItemAtPosition(pos, activeIndex, itemCount, navigationMode);
537
+ if (itemIndex !== null) {
538
+ slots.push({
539
+ slotPosition: pos,
540
+ itemIndex,
541
+ item: items[itemIndex],
542
+ });
543
+ }
544
+ }
545
+
546
+ return (
547
+ <div
548
+ data-active-id={activeId}
549
+ style={{
550
+ position: "relative",
551
+ width: "100%",
552
+ height: "100%",
553
+ overflow: "hidden",
554
+ }}
555
+ >
556
+ {/* Sliding indicator (rendered behind tabs) */}
557
+ {renderIndicator?.({
558
+ offsetPx: currentOffset,
559
+ tabWidth,
560
+ centerX,
561
+ isSwiping,
562
+ isAnimating,
563
+ })}
564
+
565
+ {/* Tab slots */}
566
+ {slots.map(({ slotPosition, itemIndex, item }) => (
567
+ <TabSlot
568
+ key={slotPosition}
569
+ slotPosition={slotPosition}
570
+ item={item}
571
+ itemIndex={itemIndex}
572
+ isActive={itemIndex === activeIndex}
573
+ centerX={centerX}
574
+ tabWidth={tabWidth}
575
+ viewportWidth={viewportWidth}
576
+ offsetPx={currentOffset}
577
+ axis={axis}
578
+ renderTab={renderTab}
579
+ />
580
+ ))}
581
+ </div>
582
+ );
583
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @file Pivot - Headless content switching system
3
+ */
4
+ export { usePivot } from "./usePivot";
5
+ export { scaleInputState } from "./scaleInputState";
6
+ export { SwipePivotTabBar } from "./SwipePivotTabBar";
7
+ export type { SwipePivotTabBarProps, IndicatorRenderProps } from "./SwipePivotTabBar";
8
+ export type { PivotItem, UsePivotOptions, UsePivotResult, PivotItemProps } from "./types";