react-panel-layout 0.5.2 → 0.6.0

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 (463) hide show
  1. package/dist/{FloatingPanelFrame-D9Cp2al1.cjs → FloatingPanelFrame-CEmXDvUA.cjs} +2 -2
  2. package/dist/{FloatingPanelFrame-D9Cp2al1.cjs.map → FloatingPanelFrame-CEmXDvUA.cjs.map} +1 -1
  3. package/dist/{FloatingPanelFrame-lLg-Lpg7.js → FloatingPanelFrame-SgYLc6Ud.js} +11 -11
  4. package/dist/{FloatingPanelFrame-lLg-Lpg7.js.map → FloatingPanelFrame-SgYLc6Ud.js.map} +1 -1
  5. package/dist/FloatingWindow-BpdOpg_L.js +400 -0
  6. package/dist/FloatingWindow-BpdOpg_L.js.map +1 -0
  7. package/dist/FloatingWindow-TCDNY5gE.cjs +2 -0
  8. package/dist/FloatingWindow-TCDNY5gE.cjs.map +1 -0
  9. package/dist/GridLayout-B4VRsC0r.cjs +2 -0
  10. package/dist/GridLayout-B4VRsC0r.cjs.map +1 -0
  11. package/dist/GridLayout-BltqeCPK.js +927 -0
  12. package/dist/GridLayout-BltqeCPK.js.map +1 -0
  13. package/dist/HorizontalDivider-B5Z-KZLk.cjs +2 -0
  14. package/dist/HorizontalDivider-B5Z-KZLk.cjs.map +1 -0
  15. package/dist/HorizontalDivider-WF1k_qND.js +30 -0
  16. package/dist/HorizontalDivider-WF1k_qND.js.map +1 -0
  17. package/dist/PanelSystem-Bs8bQwQF.cjs +3 -0
  18. package/dist/PanelSystem-Bs8bQwQF.cjs.map +1 -0
  19. package/dist/PanelSystem-Dr1TBhxM.js +1946 -0
  20. package/dist/PanelSystem-Dr1TBhxM.js.map +1 -0
  21. package/dist/ResizeHandle-CScipO5l.cjs +2 -0
  22. package/dist/ResizeHandle-CScipO5l.cjs.map +1 -0
  23. package/dist/ResizeHandle-CdA_JYfN.js +120 -0
  24. package/dist/ResizeHandle-CdA_JYfN.js.map +1 -0
  25. package/dist/SwipePivotTabBar-BGO9X94m.js +407 -0
  26. package/dist/SwipePivotTabBar-BGO9X94m.js.map +1 -0
  27. package/dist/SwipePivotTabBar-BrQismcZ.cjs +2 -0
  28. package/dist/SwipePivotTabBar-BrQismcZ.cjs.map +1 -0
  29. package/dist/config.cjs +1 -1
  30. package/dist/config.cjs.map +1 -1
  31. package/dist/config.js +11 -9
  32. package/dist/config.js.map +1 -1
  33. package/dist/constants/styles.d.ts +18 -4
  34. package/dist/floating.cjs +1 -1
  35. package/dist/floating.js +1 -1
  36. package/dist/grid/index.d.ts +58 -0
  37. package/dist/grid.cjs +2 -0
  38. package/dist/grid.cjs.map +1 -0
  39. package/dist/grid.js +13 -0
  40. package/dist/grid.js.map +1 -0
  41. package/dist/hooks/gesture/presets.d.ts +33 -0
  42. package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +110 -0
  43. package/dist/hooks/gesture/thresholdValue.d.ts +44 -0
  44. package/dist/hooks/gesture/types.d.ts +254 -0
  45. package/dist/hooks/gesture/useDirectionalLock.d.ts +20 -0
  46. package/dist/hooks/gesture/useEdgeSwipeInput.d.ts +23 -0
  47. package/dist/hooks/gesture/useNativeGestureGuard.d.ts +23 -0
  48. package/dist/hooks/gesture/usePointerTracking.d.ts +22 -0
  49. package/dist/hooks/gesture/useScrollBoundary.d.ts +23 -0
  50. package/dist/hooks/gesture/useSwipeInput.d.ts +5 -0
  51. package/dist/hooks/gesture/utils.d.ts +40 -0
  52. package/dist/hooks/useAnimatedVisibility.d.ts +58 -0
  53. package/dist/hooks/useAnimationFrame.d.ts +84 -0
  54. package/dist/hooks/useSnapAnimation.d.ts +54 -0
  55. package/dist/hooks/useSwipeContentTransform.d.ts +79 -0
  56. package/dist/index.cjs +1 -2
  57. package/dist/index.cjs.map +1 -1
  58. package/dist/index.d.ts +0 -1
  59. package/dist/index.js +25 -2006
  60. package/dist/index.js.map +1 -1
  61. package/dist/modules/pivot/PivotContent.d.ts +1 -1
  62. package/dist/modules/pivot/SwipePivotContent.d.ts +39 -0
  63. package/dist/modules/pivot/SwipePivotContent.debug.tmp.d.ts +25 -0
  64. package/dist/modules/pivot/SwipePivotContent.test.d.ts +1 -0
  65. package/dist/modules/pivot/SwipePivotTabBar.d.ts +89 -0
  66. package/dist/modules/pivot/index.d.ts +3 -0
  67. package/dist/modules/pivot/scaleInputState.d.ts +37 -0
  68. package/dist/modules/pivot/types.d.ts +67 -2
  69. package/dist/modules/pivot/usePivotSwipeInput.d.ts +68 -0
  70. package/dist/modules/stack/StackContent.d.ts +15 -0
  71. package/dist/modules/stack/SwipeStackContent.d.ts +63 -0
  72. package/dist/modules/stack/SwipeStackOutlet.d.ts +80 -0
  73. package/dist/modules/stack/computeStackContentState.d.ts +99 -0
  74. package/dist/modules/stack/computeSwipeStackTransform.d.ts +76 -0
  75. package/dist/modules/stack/types.d.ts +194 -0
  76. package/dist/modules/stack/useStackAnimationState.d.ts +32 -0
  77. package/dist/modules/stack/useStackNavigation.d.ts +23 -0
  78. package/dist/modules/stack/useStackSwipeInput.d.ts +27 -0
  79. package/dist/panels/index.d.ts +67 -0
  80. package/dist/panels.cjs +2 -0
  81. package/dist/panels.cjs.map +1 -0
  82. package/dist/panels.js +28 -0
  83. package/dist/panels.js.map +1 -0
  84. package/dist/pivot/index.d.ts +3 -0
  85. package/dist/pivot.cjs +1 -1
  86. package/dist/pivot.cjs.map +1 -1
  87. package/dist/pivot.js +20 -2
  88. package/dist/pivot.js.map +1 -1
  89. package/dist/resizer/index.d.ts +57 -0
  90. package/dist/resizer.cjs +2 -0
  91. package/dist/resizer.cjs.map +1 -0
  92. package/dist/resizer.js +8 -0
  93. package/dist/resizer.js.map +1 -0
  94. package/dist/stack/index.d.ts +72 -0
  95. package/dist/stack.cjs +2 -0
  96. package/dist/stack.cjs.map +1 -0
  97. package/dist/stack.js +980 -0
  98. package/dist/stack.js.map +1 -0
  99. package/dist/sticky-header/StickyArea.d.ts +38 -0
  100. package/dist/sticky-header/index.d.ts +4 -4
  101. package/dist/sticky-header/types.d.ts +35 -22
  102. package/dist/sticky-header.cjs +1 -1
  103. package/dist/sticky-header.cjs.map +1 -1
  104. package/dist/sticky-header.js +65 -174
  105. package/dist/sticky-header.js.map +1 -1
  106. package/dist/styles-DPPuJ0sf.js +57 -0
  107. package/dist/styles-DPPuJ0sf.js.map +1 -0
  108. package/dist/styles-qf6ptVLD.cjs +2 -0
  109. package/dist/styles-qf6ptVLD.cjs.map +1 -0
  110. package/dist/useContentCache-CO3LYNmz.js +24 -0
  111. package/dist/useContentCache-CO3LYNmz.js.map +1 -0
  112. package/dist/useContentCache-DqXtLrLs.cjs +2 -0
  113. package/dist/useContentCache-DqXtLrLs.cjs.map +1 -0
  114. package/dist/useDocumentPointerEvents-CKdhGXd0.js +46 -0
  115. package/dist/useDocumentPointerEvents-CKdhGXd0.js.map +1 -0
  116. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs +2 -0
  117. package/dist/useDocumentPointerEvents-ChqrKXDk.cjs.map +1 -0
  118. package/dist/useEffectEvent-Dp7HLCf0.js +13 -0
  119. package/dist/useEffectEvent-Dp7HLCf0.js.map +1 -0
  120. package/dist/useEffectEvent-huSsGUnl.cjs +2 -0
  121. package/dist/useEffectEvent-huSsGUnl.cjs.map +1 -0
  122. package/dist/useFloatingState-C4kRaW_R.cjs +2 -0
  123. package/dist/useFloatingState-C4kRaW_R.cjs.map +1 -0
  124. package/dist/useFloatingState-tEfA_wbc.js +74 -0
  125. package/dist/useFloatingState-tEfA_wbc.js.map +1 -0
  126. package/dist/window/index.d.ts +61 -0
  127. package/dist/window.cjs +2 -0
  128. package/dist/window.cjs.map +1 -0
  129. package/dist/window.js +149 -0
  130. package/dist/window.js.map +1 -0
  131. package/docs/design-tokens.md +405 -0
  132. package/package.json +29 -4
  133. package/src/PanelSystemContext.tsx +88 -0
  134. package/src/components/grid/GridLayerList.tsx +172 -0
  135. package/src/components/grid/GridLayerResizeHandles.tsx +145 -0
  136. package/src/components/grid/GridLayout.spec.tsx +743 -0
  137. package/src/components/grid/GridLayout.tsx +130 -0
  138. package/src/components/grid/GridTrackResizeHandle.tsx +87 -0
  139. package/src/components/paneling/FloatingPanelFrame.tsx +203 -0
  140. package/src/components/panels/DropSuggestOverlay.tsx +131 -0
  141. package/src/components/panels/PanelGroupView.tsx +112 -0
  142. package/src/components/pivot/PivotLayer.tsx +27 -0
  143. package/src/components/resizer/HorizontalDivider.tsx +52 -0
  144. package/src/components/resizer/ResizeHandle.tsx +118 -0
  145. package/src/components/tabs/TabBar.tsx +223 -0
  146. package/src/components/tabs/TabBarTab.tsx +133 -0
  147. package/src/components/tabs/TabDragOverlay.tsx +92 -0
  148. package/src/components/window/DialogOverlay.tsx +180 -0
  149. package/src/components/window/Drawer.tsx +282 -0
  150. package/src/components/window/DrawerLayers.tsx +58 -0
  151. package/src/components/window/FloatingWindow.tsx +95 -0
  152. package/src/components/window/PopupLayerPortal.tsx +218 -0
  153. package/src/config/PanelContentDeclaration.tsx +427 -0
  154. package/src/config/index.tsx +52 -0
  155. package/src/config/panelJsx.spec.tsx +54 -0
  156. package/src/config/panelJsxConfig.spec.tsx +54 -0
  157. package/src/config/panelJsxDrawer.spec.tsx +33 -0
  158. package/src/config/panelRouter.spec.ts +68 -0
  159. package/src/config/panelRouter.tsx +155 -0
  160. package/src/constants/styles.ts +261 -0
  161. package/src/demo/Layout.module.css +258 -0
  162. package/src/demo/Layout.tsx +176 -0
  163. package/src/demo/components/CodeBlock.module.css +54 -0
  164. package/src/demo/components/CodeBlock.tsx +34 -0
  165. package/src/demo/components/CodePreview.module.css +37 -0
  166. package/src/demo/components/CodePreview.tsx +31 -0
  167. package/src/demo/components/DataPreview.module.css +177 -0
  168. package/src/demo/components/DataPreview.tsx +115 -0
  169. package/src/demo/components/Story.module.css +68 -0
  170. package/src/demo/components/Story.tsx +54 -0
  171. package/src/demo/components/layout/CodePanel.module.css +183 -0
  172. package/src/demo/components/layout/CodePanel.tsx +149 -0
  173. package/src/demo/components/layout/DemoPage.module.css +60 -0
  174. package/src/demo/components/layout/DemoPage.tsx +56 -0
  175. package/src/demo/components/layout/SingleSamplePage.module.css +11 -0
  176. package/src/demo/components/layout/SingleSamplePage.tsx +35 -0
  177. package/src/demo/components/layout/SplitDemoLayout.module.css +107 -0
  178. package/src/demo/components/layout/SplitDemoLayout.tsx +218 -0
  179. package/src/demo/components/layout/index.ts +11 -0
  180. package/src/demo/components/tab-styles/ChromeTabBar.module.css +75 -0
  181. package/src/demo/components/tab-styles/ChromeTabBar.tsx +111 -0
  182. package/src/demo/components/tab-styles/GitHubTabBar.module.css +81 -0
  183. package/src/demo/components/tab-styles/GitHubTabBar.tsx +109 -0
  184. package/src/demo/components/tab-styles/VSCodeTabBar.module.css +78 -0
  185. package/src/demo/components/tab-styles/VSCodeTabBar.tsx +109 -0
  186. package/src/demo/components/tab-styles/index.ts +6 -0
  187. package/src/demo/components/ui/DemoButton.module.css +63 -0
  188. package/src/demo/components/ui/DemoButton.tsx +32 -0
  189. package/src/demo/components/ui/DemoCard.module.css +15 -0
  190. package/src/demo/components/ui/DemoCard.tsx +30 -0
  191. package/src/demo/components/ui/DemoContainer.module.css +17 -0
  192. package/src/demo/components/ui/DemoContainer.tsx +30 -0
  193. package/src/demo/components/ui/DemoPanel.module.css +23 -0
  194. package/src/demo/components/ui/DemoPanel.tsx +33 -0
  195. package/src/demo/components/ui/PanelText.module.css +18 -0
  196. package/src/demo/components/ui/PanelText.tsx +29 -0
  197. package/src/demo/components/ui/PanelTitle.module.css +18 -0
  198. package/src/demo/components/ui/PanelTitle.tsx +31 -0
  199. package/src/demo/contexts/TabbarDemoConfig.tsx +218 -0
  200. package/src/demo/demo.css +172 -0
  201. package/src/demo/hooks/useMedia.ts +41 -0
  202. package/src/demo/hooks/useShikiHighlight.ts +55 -0
  203. package/src/demo/index.tsx +293 -0
  204. package/src/demo/pages/Drawer/animations/index.tsx +22 -0
  205. package/src/demo/pages/Drawer/basics/index.tsx +17 -0
  206. package/src/demo/pages/Drawer/components/DrawerAnimations.module.css +125 -0
  207. package/src/demo/pages/Drawer/components/DrawerAnimations.tsx +118 -0
  208. package/src/demo/pages/Drawer/components/DrawerBasics.module.css +55 -0
  209. package/src/demo/pages/Drawer/components/DrawerBasics.tsx +76 -0
  210. package/src/demo/pages/Drawer/components/DrawerMenuLayout.module.css +332 -0
  211. package/src/demo/pages/Drawer/components/DrawerMenuLayout.tsx +199 -0
  212. package/src/demo/pages/Drawer/menu/index.tsx +17 -0
  213. package/src/demo/pages/FloatingPanelFrame/ResizableFloatingPanelsPreview.module.css +163 -0
  214. package/src/demo/pages/FloatingPanelFrame/ResizableFloatingPanelsPreview.tsx +234 -0
  215. package/src/demo/pages/FloatingPanelFrame/basic/index.tsx +17 -0
  216. package/src/demo/pages/FloatingPanelFrame/complex/index.tsx +26 -0
  217. package/src/demo/pages/FloatingPanelFrame/components/BasicPanel.module.css +16 -0
  218. package/src/demo/pages/FloatingPanelFrame/components/BasicPanel.tsx +24 -0
  219. package/src/demo/pages/FloatingPanelFrame/components/ComplexPanel.module.css +54 -0
  220. package/src/demo/pages/FloatingPanelFrame/components/ComplexPanel.tsx +67 -0
  221. package/src/demo/pages/FloatingPanelFrame/components/PanelWithControls.module.css +21 -0
  222. package/src/demo/pages/FloatingPanelFrame/components/PanelWithControls.tsx +41 -0
  223. package/src/demo/pages/FloatingPanelFrame/components/PanelWithMeta.module.css +5 -0
  224. package/src/demo/pages/FloatingPanelFrame/components/PanelWithMeta.tsx +43 -0
  225. package/src/demo/pages/FloatingPanelFrame/components/ScrollablePanel.module.css +11 -0
  226. package/src/demo/pages/FloatingPanelFrame/components/ScrollablePanel.tsx +42 -0
  227. package/src/demo/pages/FloatingPanelFrame/index.tsx +80 -0
  228. package/src/demo/pages/FloatingPanelFrame/scrollable/index.tsx +30 -0
  229. package/src/demo/pages/FloatingPanelFrame/with-controls/index.tsx +30 -0
  230. package/src/demo/pages/FloatingPanelFrame/with-meta/index.tsx +17 -0
  231. package/src/demo/pages/HorizontalDivider/components/PanelsWithRichContent.module.css +112 -0
  232. package/src/demo/pages/HorizontalDivider/components/PanelsWithRichContent.tsx +56 -0
  233. package/src/demo/pages/HorizontalDivider/components/SimpleResizablePanels.module.css +46 -0
  234. package/src/demo/pages/HorizontalDivider/components/SimpleResizablePanels.tsx +29 -0
  235. package/src/demo/pages/HorizontalDivider/components/ThreePanelLayout.module.css +54 -0
  236. package/src/demo/pages/HorizontalDivider/components/ThreePanelLayout.tsx +30 -0
  237. package/src/demo/pages/HorizontalDivider/index.module.css +14 -0
  238. package/src/demo/pages/HorizontalDivider/index.tsx +64 -0
  239. package/src/demo/pages/HorizontalDivider/panels-with-rich-content/index.tsx +21 -0
  240. package/src/demo/pages/HorizontalDivider/simple-resizable-panels/index.tsx +21 -0
  241. package/src/demo/pages/HorizontalDivider/three-panel-layout/index.tsx +21 -0
  242. package/src/demo/pages/PanelLayout/PanelLayoutDemo.module.css +174 -0
  243. package/src/demo/pages/PanelLayout/PanelLayoutDemo.tsx +248 -0
  244. package/src/demo/pages/PanelLayout/components/DashboardLayout.module.css +115 -0
  245. package/src/demo/pages/PanelLayout/components/DashboardLayout.tsx +124 -0
  246. package/src/demo/pages/PanelLayout/components/DraggableOverlays.module.css +101 -0
  247. package/src/demo/pages/PanelLayout/components/DraggableOverlays.tsx +122 -0
  248. package/src/demo/pages/PanelLayout/components/IDELayout.module.css +104 -0
  249. package/src/demo/pages/PanelLayout/components/IDELayout.tsx +143 -0
  250. package/src/demo/pages/PanelLayout/components/SimpleGrid.module.css +19 -0
  251. package/src/demo/pages/PanelLayout/components/SimpleGrid.tsx +62 -0
  252. package/src/demo/pages/PanelLayout/dashboard/index.tsx +22 -0
  253. package/src/demo/pages/PanelLayout/draggable-overlays/index.tsx +22 -0
  254. package/src/demo/pages/PanelLayout/ide-layout/index.tsx +22 -0
  255. package/src/demo/pages/PanelLayout/index.tsx +94 -0
  256. package/src/demo/pages/PanelLayout/simple-grid/index.tsx +22 -0
  257. package/src/demo/pages/PanelSystem/PanelSystemPreview.module.css +20 -0
  258. package/src/demo/pages/PanelSystem/PanelSystemPreview.tsx +101 -0
  259. package/src/demo/pages/PanelSystem/preview/index.tsx +18 -0
  260. package/src/demo/pages/PanelSystem/tabbar/index.tsx +129 -0
  261. package/src/demo/pages/Pivot/basics/index.tsx +17 -0
  262. package/src/demo/pages/Pivot/components/Pivot.module.css +278 -0
  263. package/src/demo/pages/Pivot/components/PivotBasics.tsx +103 -0
  264. package/src/demo/pages/Pivot/components/PivotSidebar.tsx +168 -0
  265. package/src/demo/pages/Pivot/components/PivotTabs.tsx +129 -0
  266. package/src/demo/pages/Pivot/components/PivotTransitions.tsx +120 -0
  267. package/src/demo/pages/Pivot/components/SwipePivot.module.css +114 -0
  268. package/src/demo/pages/Pivot/components/SwipePivot.tsx +193 -0
  269. package/src/demo/pages/Pivot/components/SwipeTabsPivot.module.css +203 -0
  270. package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +289 -0
  271. package/src/demo/pages/Pivot/sidebar/index.tsx +17 -0
  272. package/src/demo/pages/Pivot/swipe/index.tsx +16 -0
  273. package/src/demo/pages/Pivot/swipe-debug/index.tsx +287 -0
  274. package/src/demo/pages/Pivot/swipe-tabs/index.tsx +15 -0
  275. package/src/demo/pages/Pivot/tabs/index.tsx +17 -0
  276. package/src/demo/pages/Pivot/transitions/index.tsx +17 -0
  277. package/src/demo/pages/ResizeHandle/both-directions/index.tsx +17 -0
  278. package/src/demo/pages/ResizeHandle/components/BothDirectionsDemo.module.css +72 -0
  279. package/src/demo/pages/ResizeHandle/components/BothDirectionsDemo.tsx +41 -0
  280. package/src/demo/pages/ResizeHandle/components/HorizontalResizeDemo.module.css +61 -0
  281. package/src/demo/pages/ResizeHandle/components/HorizontalResizeDemo.tsx +33 -0
  282. package/src/demo/pages/ResizeHandle/components/NestedPanelsDemo.module.css +83 -0
  283. package/src/demo/pages/ResizeHandle/components/NestedPanelsDemo.tsx +53 -0
  284. package/src/demo/pages/ResizeHandle/components/VerticalResizeDemo.module.css +68 -0
  285. package/src/demo/pages/ResizeHandle/components/VerticalResizeDemo.tsx +33 -0
  286. package/src/demo/pages/ResizeHandle/horizontal/index.tsx +17 -0
  287. package/src/demo/pages/ResizeHandle/index.module.css +11 -0
  288. package/src/demo/pages/ResizeHandle/index.tsx +71 -0
  289. package/src/demo/pages/ResizeHandle/nested-panels/index.tsx +17 -0
  290. package/src/demo/pages/ResizeHandle/vertical/index.tsx +17 -0
  291. package/src/demo/pages/ResponsiveLayout/adaptive-workspace/index.tsx +22 -0
  292. package/src/demo/pages/ResponsiveLayout/components/ResponsiveWorkspace.module.css +423 -0
  293. package/src/demo/pages/ResponsiveLayout/components/ResponsiveWorkspace.tsx +398 -0
  294. package/src/demo/pages/Stack/basics/index.tsx +22 -0
  295. package/src/demo/pages/Stack/components/Stack.module.css +234 -0
  296. package/src/demo/pages/Stack/components/StackBasics.tsx +217 -0
  297. package/src/demo/pages/Stack/components/StackTablet.module.css +299 -0
  298. package/src/demo/pages/Stack/components/StackTablet.tsx +401 -0
  299. package/src/demo/pages/Stack/tablet/index.tsx +22 -0
  300. package/src/demo/pages/StickyHeader/basics/index.tsx +17 -0
  301. package/src/demo/pages/StickyHeader/components/StickyHeader.module.css +219 -0
  302. package/src/demo/pages/StickyHeader/components/StickyHeaderBasics.tsx +103 -0
  303. package/src/demo/routes.tsx +193 -0
  304. package/src/demo/styles/animations.css +68 -0
  305. package/src/demo/styles/stack-themes.css +35 -0
  306. package/src/demo/utils/createPanelView.tsx +58 -0
  307. package/src/floating/index.ts +24 -0
  308. package/src/grid/index.ts +75 -0
  309. package/src/hooks/ContentCacheContext.tsx +87 -0
  310. package/src/hooks/gesture/presets.spec.ts +86 -0
  311. package/src/hooks/gesture/presets.ts +95 -0
  312. package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +237 -0
  313. package/src/hooks/gesture/testing/createGestureSimulator.ts +310 -0
  314. package/src/hooks/gesture/thresholdValue.spec.ts +103 -0
  315. package/src/hooks/gesture/thresholdValue.ts +77 -0
  316. package/src/hooks/gesture/types.ts +290 -0
  317. package/src/hooks/gesture/useDirectionalLock.spec.ts +271 -0
  318. package/src/hooks/gesture/useDirectionalLock.ts +115 -0
  319. package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +454 -0
  320. package/src/hooks/gesture/useEdgeSwipeInput.ts +131 -0
  321. package/src/hooks/gesture/useNativeGestureGuard.spec.ts +413 -0
  322. package/src/hooks/gesture/useNativeGestureGuard.ts +133 -0
  323. package/src/hooks/gesture/usePointerTracking.spec.ts +364 -0
  324. package/src/hooks/gesture/usePointerTracking.ts +134 -0
  325. package/src/hooks/gesture/useScrollBoundary.spec.ts +249 -0
  326. package/src/hooks/gesture/useScrollBoundary.ts +113 -0
  327. package/src/hooks/gesture/useSwipeInput.spec.ts +592 -0
  328. package/src/hooks/gesture/useSwipeInput.ts +310 -0
  329. package/src/hooks/gesture/utils.spec.ts +152 -0
  330. package/src/hooks/gesture/utils.ts +87 -0
  331. package/src/hooks/useAnimatedVisibility.spec.ts +257 -0
  332. package/src/hooks/useAnimatedVisibility.ts +146 -0
  333. package/src/hooks/useAnimationFrame.ts +200 -0
  334. package/src/hooks/useCSSMatrix.spec.ts +214 -0
  335. package/src/hooks/useCSSMatrix.ts +262 -0
  336. package/src/hooks/useClonedElementPreview.ts +28 -0
  337. package/src/hooks/useContainerScroll.ts +78 -0
  338. package/src/hooks/useContentCache.spec.tsx +232 -0
  339. package/src/hooks/useContentCache.tsx +127 -0
  340. package/src/hooks/useDocumentPointerEvents.ts +137 -0
  341. package/src/hooks/useDocumentScroll.ts +41 -0
  342. package/src/hooks/useEffectEvent.ts +40 -0
  343. package/src/hooks/useElementComponentWrapper.tsx +63 -0
  344. package/src/hooks/useIntersectionObserver.tsx +125 -0
  345. package/src/hooks/useIsomorphicLayoutEffect.ts +29 -0
  346. package/src/hooks/useResizeObserver.tsx +81 -0
  347. package/src/hooks/useScrollContainer.ts +79 -0
  348. package/src/hooks/useSnapAnimation.ts +128 -0
  349. package/src/hooks/useSwipeContentTransform.spec.ts +133 -0
  350. package/src/hooks/useSwipeContentTransform.ts +235 -0
  351. package/src/hooks/useTransitionState.ts +95 -0
  352. package/src/index.tsx +88 -0
  353. package/src/modules/grid/GridLayoutContext.tsx +57 -0
  354. package/src/modules/grid/LayerInstanceContext.tsx +56 -0
  355. package/src/modules/grid/resizeHandles.ts +157 -0
  356. package/src/modules/grid/trackUtils.ts +146 -0
  357. package/src/modules/grid/useGridPlacements.ts +143 -0
  358. package/src/modules/grid/useGridTracks.ts +156 -0
  359. package/src/modules/grid/useLayerDragHandle.ts +16 -0
  360. package/src/modules/grid/useLayerInteractions.tsx +850 -0
  361. package/src/modules/keybindings/KeybindingsProvider.tsx +111 -0
  362. package/src/modules/panels/dom/DomRegistry.tsx +94 -0
  363. package/src/modules/panels/index.ts +45 -0
  364. package/src/modules/panels/interactions/InteractionsContext.test.tsx +330 -0
  365. package/src/modules/panels/interactions/InteractionsContext.tsx +394 -0
  366. package/src/modules/panels/interactions/dnd.ts +28 -0
  367. package/src/modules/panels/keybindings/KeybindingsInstaller.tsx +15 -0
  368. package/src/modules/panels/layout/adapter.ts +124 -0
  369. package/src/modules/panels/rendering/ContentRegistry.spec.tsx +304 -0
  370. package/src/modules/panels/rendering/ContentRegistry.tsx +205 -0
  371. package/src/modules/panels/rendering/GroupContainer.tsx +65 -0
  372. package/src/modules/panels/rendering/RenderBridge.tsx +115 -0
  373. package/src/modules/panels/rendering/RenderContext.tsx +31 -0
  374. package/src/modules/panels/state/PanelSplitHandles.tsx +147 -0
  375. package/src/modules/panels/state/PanelSystemContext.splitLimits.spec.tsx +50 -0
  376. package/src/modules/panels/state/PanelSystemContext.tsx +289 -0
  377. package/src/modules/panels/state/StateContext.tsx +12 -0
  378. package/src/modules/panels/state/cleanup.ts +37 -0
  379. package/src/modules/panels/state/commands.ts +53 -0
  380. package/src/modules/panels/state/focus/Context.tsx +25 -0
  381. package/src/modules/panels/state/focus/logic.ts +57 -0
  382. package/src/modules/panels/state/groups/Context.tsx +25 -0
  383. package/src/modules/panels/state/groups/logic.ts +105 -0
  384. package/src/modules/panels/state/splitLimits.spec.ts +46 -0
  385. package/src/modules/panels/state/splitLimits.ts +90 -0
  386. package/src/modules/panels/state/state.spec.ts +49 -0
  387. package/src/modules/panels/state/tree/Context.tsx +24 -0
  388. package/src/modules/panels/state/tree/logic.spec.ts +34 -0
  389. package/src/modules/panels/state/tree/logic.ts +138 -0
  390. package/src/modules/panels/state/types.ts +142 -0
  391. package/src/modules/panels/system/PanelSystem.empty-tabbar.spec.tsx +53 -0
  392. package/src/modules/panels/system/PanelSystem.tab-click-activates.spec.tsx +44 -0
  393. package/src/modules/panels/system/PanelSystem.tab-reorder.spec.tsx +64 -0
  394. package/src/modules/panels/system/PanelSystem.tabs-no-dup.spec.tsx +57 -0
  395. package/src/modules/panels/system/PanelSystem.tsx +206 -0
  396. package/src/modules/pivot/PivotContent.spec.tsx +179 -0
  397. package/src/modules/pivot/PivotContent.tsx +77 -0
  398. package/src/modules/pivot/SwipePivotContent.debug.tmp.tsx +237 -0
  399. package/src/modules/pivot/SwipePivotContent.position.spec.tsx +167 -0
  400. package/src/modules/pivot/SwipePivotContent.spec.tsx +464 -0
  401. package/src/modules/pivot/SwipePivotContent.test.tsx +502 -0
  402. package/src/modules/pivot/SwipePivotContent.tsx +197 -0
  403. package/src/modules/pivot/SwipePivotTabBar.spec.tsx +865 -0
  404. package/src/modules/pivot/SwipePivotTabBar.tsx +523 -0
  405. package/src/modules/pivot/index.ts +8 -0
  406. package/src/modules/pivot/scaleInputState.spec.ts +210 -0
  407. package/src/modules/pivot/scaleInputState.ts +66 -0
  408. package/src/modules/pivot/types.ts +139 -0
  409. package/src/modules/pivot/usePivot.spec.ts +621 -0
  410. package/src/modules/pivot/usePivot.spec.tsx +186 -0
  411. package/src/modules/pivot/usePivot.tsx +345 -0
  412. package/src/modules/pivot/usePivotSwipeInput.spec.ts +649 -0
  413. package/src/modules/pivot/usePivotSwipeInput.ts +136 -0
  414. package/src/modules/resizer/useResizeDrag.ts +94 -0
  415. package/src/modules/stack/StackContent.spec.tsx +264 -0
  416. package/src/modules/stack/StackContent.tsx +111 -0
  417. package/src/modules/stack/SwipeStackContent.spec.tsx +1277 -0
  418. package/src/modules/stack/SwipeStackContent.tsx +356 -0
  419. package/src/modules/stack/SwipeStackOutlet.spec.tsx +252 -0
  420. package/src/modules/stack/SwipeStackOutlet.tsx +221 -0
  421. package/src/modules/stack/computeStackContentState.spec.ts +281 -0
  422. package/src/modules/stack/computeStackContentState.ts +304 -0
  423. package/src/modules/stack/computeSwipeStackTransform.spec.ts +186 -0
  424. package/src/modules/stack/computeSwipeStackTransform.ts +145 -0
  425. package/src/modules/stack/types.ts +226 -0
  426. package/src/modules/stack/useStackAnimationState.spec.ts +186 -0
  427. package/src/modules/stack/useStackAnimationState.ts +138 -0
  428. package/src/modules/stack/useStackNavigation.spec.ts +477 -0
  429. package/src/modules/stack/useStackNavigation.tsx +336 -0
  430. package/src/modules/stack/useStackSwipeInput.spec.ts +276 -0
  431. package/src/modules/stack/useStackSwipeInput.ts +139 -0
  432. package/src/modules/window/useDrawerState.ts +81 -0
  433. package/src/modules/window/useFloatingState.spec.ts +252 -0
  434. package/src/modules/window/useFloatingState.ts +141 -0
  435. package/src/panels/index.ts +119 -0
  436. package/src/pivot/index.ts +19 -0
  437. package/src/resizer/index.ts +68 -0
  438. package/src/stack/index.ts +91 -0
  439. package/src/sticky-header/StickyArea.tsx +221 -0
  440. package/src/sticky-header/index.ts +18 -0
  441. package/src/sticky-header/types.ts +68 -0
  442. package/src/types.ts +323 -0
  443. package/src/utils/CSSMatrix.ts +321 -0
  444. package/src/utils/css.ts +65 -0
  445. package/src/utils/dialogUtils.ts +43 -0
  446. package/src/utils/math.ts +18 -0
  447. package/src/utils/polyfills/createDialogPolyfill.ts +18 -0
  448. package/src/utils/typedActions.ts +103 -0
  449. package/src/vite-env.d.ts +6 -0
  450. package/src/window/index.ts +67 -0
  451. package/dist/GridLayout-BQQ63eA1.cjs +0 -2
  452. package/dist/GridLayout-BQQ63eA1.cjs.map +0 -1
  453. package/dist/GridLayout-CJTKq7Mp.js +0 -1465
  454. package/dist/GridLayout-CJTKq7Mp.js.map +0 -1
  455. package/dist/sticky-header/StickyHeader.d.ts +0 -53
  456. package/dist/styles-CA2_zLZt.js +0 -52
  457. package/dist/styles-CA2_zLZt.js.map +0 -1
  458. package/dist/styles-PsqGOEJP.cjs +0 -2
  459. package/dist/styles-PsqGOEJP.cjs.map +0 -1
  460. package/dist/usePivot-7ctin_P_.cjs +0 -2
  461. package/dist/usePivot-7ctin_P_.cjs.map +0 -1
  462. package/dist/usePivot-CgQxB8rc.js +0 -124
  463. package/dist/usePivot-CgQxB8rc.js.map +0 -1
@@ -0,0 +1,523 @@
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
+ if (navigationMode === "linear") {
117
+ if (targetIndex < 0 || targetIndex >= itemCount) {
118
+ return null;
119
+ }
120
+ return targetIndex;
121
+ }
122
+
123
+ // Loop mode: wrap around
124
+ return normalizeIndex(targetIndex, itemCount);
125
+ };
126
+
127
+ type TabSlotProps<TId extends string> = {
128
+ slotPosition: number;
129
+ item: { id: TId; label?: string };
130
+ itemIndex: number;
131
+ isActive: boolean;
132
+ centerX: number;
133
+ tabWidth: number;
134
+ viewportWidth: number;
135
+ offsetPx: number;
136
+ axis: GestureAxis;
137
+ renderTab: (item: { id: TId; label?: string }, isActive: boolean, index: number) => React.ReactNode;
138
+ };
139
+
140
+ /**
141
+ * Tab slot component - renders a tab at a fixed slot position.
142
+ * The slot position is stable; only the content changes based on scroll offset.
143
+ */
144
+ const TabSlot = React.memo(<TId extends string>({
145
+ slotPosition,
146
+ item,
147
+ itemIndex,
148
+ isActive,
149
+ centerX,
150
+ tabWidth,
151
+ viewportWidth,
152
+ offsetPx,
153
+ axis,
154
+ renderTab,
155
+ }: TabSlotProps<TId>) => {
156
+ // Calculate visual position: centerX + slotPosition * tabWidth + offsetPx
157
+ const basePx = slotPosition * tabWidth;
158
+ const visualPx = centerX + basePx + offsetPx;
159
+
160
+ // Check if visible in viewport
161
+ const visible = visualPx + tabWidth > 0 && visualPx < viewportWidth;
162
+
163
+ const transformFn = axis === "horizontal" ? "translateX" : "translateY";
164
+
165
+ return (
166
+ <div
167
+ data-pivot-tab={item.id}
168
+ data-slot={slotPosition}
169
+ data-active={isActive ? "true" : "false"}
170
+ style={{
171
+ position: "absolute",
172
+ left: centerX,
173
+ top: 0,
174
+ width: tabWidth,
175
+ height: "100%",
176
+ visibility: visible ? "visible" : "hidden",
177
+ willChange: "transform",
178
+ transform: `${transformFn}(${basePx + offsetPx}px)`,
179
+ }}
180
+ >
181
+ {renderTab(item, isActive, itemIndex)}
182
+ </div>
183
+ );
184
+ }) as <TId extends string>(props: TabSlotProps<TId>) => React.ReactElement;
185
+
186
+ /**
187
+ * Easing function for smooth animation
188
+ */
189
+ const easeOutExpo = (t: number): number => {
190
+ return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
191
+ };
192
+
193
+ export function SwipePivotTabBar<TId extends string = string>({
194
+ items,
195
+ activeId,
196
+ activeIndex,
197
+ itemCount,
198
+ inputState,
199
+ tabWidth,
200
+ viewportWidth,
201
+ navigationMode = "linear",
202
+ axis = "horizontal",
203
+ renderTab,
204
+ animationDuration = DEFAULT_ANIMATION_DURATION,
205
+ fixedTabs = false,
206
+ renderIndicator,
207
+ }: SwipePivotTabBarProps<TId>): React.ReactElement {
208
+ const displacement = getAxisDisplacement(inputState, axis);
209
+ const isSwiping = inputState.phase === "swiping" || inputState.phase === "tracking";
210
+
211
+ // ============================================================
212
+ // Animation state for SLOT-BASED mode (scrolling tabs)
213
+ // ============================================================
214
+ const [animatedOffset, setAnimatedOffset] = React.useState(0);
215
+ const animationRef = React.useRef<{
216
+ startTime: number;
217
+ startOffset: number;
218
+ targetOffset: number;
219
+ rafId: number;
220
+ } | null>(null);
221
+
222
+ // ============================================================
223
+ // Animation state for FIXED TABS mode (iOS-style indicator)
224
+ // Tracks the actual indicator position in pixels
225
+ // ============================================================
226
+ const [indicatorPosition, setIndicatorPosition] = React.useState(activeIndex * tabWidth);
227
+ const indicatorPositionRef = React.useRef(activeIndex * tabWidth); // Track current position for animation start
228
+ const fixedAnimationRef = React.useRef<{
229
+ rafId: number;
230
+ } | null>(null);
231
+ const lastSwipePositionRef = React.useRef<number | null>(null);
232
+
233
+ // Keep ref in sync with state
234
+ React.useEffect(() => {
235
+ indicatorPositionRef.current = indicatorPosition;
236
+ }, [indicatorPosition]);
237
+
238
+ const prevActiveIndexRef = React.useRef(activeIndex);
239
+
240
+ // Calculate the range of slot positions to render
241
+ const halfRange = Math.ceil(viewportWidth / tabWidth / 2) + 1;
242
+
243
+ // Center position for active tab (for slot-based mode)
244
+ const centerX = (viewportWidth - tabWidth) / 2;
245
+
246
+ // ============================================================
247
+ // Fixed tabs mode: track swipe position
248
+ // ============================================================
249
+ React.useEffect(() => {
250
+ if (!fixedTabs || !isSwiping) return;
251
+
252
+ // During swipe, track the visual position
253
+ // Swipe direction is OPPOSITE to indicator movement
254
+ const visualPosition = activeIndex * tabWidth - displacement;
255
+ lastSwipePositionRef.current = visualPosition;
256
+ setIndicatorPosition(visualPosition);
257
+ }, [fixedTabs, isSwiping, activeIndex, tabWidth, displacement]);
258
+
259
+ // ============================================================
260
+ // Fixed tabs mode: animate when swipe ends or tab clicked
261
+ // ============================================================
262
+ React.useEffect(() => {
263
+ if (!fixedTabs || isSwiping) return;
264
+
265
+ // When swipe ends or tab changes via click
266
+ const targetPosition = activeIndex * tabWidth;
267
+ const startPosition = lastSwipePositionRef.current ?? indicatorPositionRef.current;
268
+ lastSwipePositionRef.current = null;
269
+
270
+ // Already at target
271
+ if (Math.abs(startPosition - targetPosition) < 1) {
272
+ setIndicatorPosition(targetPosition);
273
+ return;
274
+ }
275
+
276
+ // Cancel existing animation
277
+ if (fixedAnimationRef.current) {
278
+ cancelAnimationFrame(fixedAnimationRef.current.rafId);
279
+ }
280
+
281
+ // Animate from current position to target
282
+ const startTime = performance.now();
283
+ const animationStartPosition = startPosition; // Capture for closure
284
+
285
+ const animate = (currentTime: number) => {
286
+ const elapsed = currentTime - startTime;
287
+ const progress = Math.min(elapsed / animationDuration, 1);
288
+ const easedProgress = easeOutExpo(progress);
289
+
290
+ const currentPosition = animationStartPosition + (targetPosition - animationStartPosition) * easedProgress;
291
+ setIndicatorPosition(currentPosition);
292
+
293
+ if (progress < 1) {
294
+ fixedAnimationRef.current = {
295
+ rafId: requestAnimationFrame(animate),
296
+ };
297
+ } else {
298
+ fixedAnimationRef.current = null;
299
+ setIndicatorPosition(targetPosition);
300
+ }
301
+ };
302
+
303
+ fixedAnimationRef.current = {
304
+ rafId: requestAnimationFrame(animate),
305
+ };
306
+ }, [fixedTabs, isSwiping, activeIndex, tabWidth, animationDuration]);
307
+
308
+ // ============================================================
309
+ // Slot-based mode animation: handle activeIndex changes
310
+ // ============================================================
311
+ React.useEffect(() => {
312
+ if (fixedTabs) return; // Skip for fixed tabs mode
313
+
314
+ if (prevActiveIndexRef.current === activeIndex) {
315
+ return;
316
+ }
317
+
318
+ const prevIndex = prevActiveIndexRef.current;
319
+ prevActiveIndexRef.current = activeIndex;
320
+
321
+ // Calculate direction of movement
322
+ let delta: number;
323
+ if (navigationMode === "loop") {
324
+ // Use shortest path in loop mode
325
+ const forwardDist = normalizeIndex(activeIndex - prevIndex, itemCount);
326
+ const backwardDist = itemCount - forwardDist;
327
+ delta = forwardDist <= backwardDist ? forwardDist : -backwardDist;
328
+ } else {
329
+ delta = activeIndex - prevIndex;
330
+ }
331
+
332
+ // Target offset to animate to (then snap to 0)
333
+ const targetOffsetPx = -delta * tabWidth;
334
+
335
+ // Start from current visual position
336
+ const startOffset = isSwiping ? displacement : animatedOffset;
337
+
338
+ // Cancel any existing animation
339
+ if (animationRef.current) {
340
+ cancelAnimationFrame(animationRef.current.rafId);
341
+ }
342
+
343
+ // If already at target (within threshold), snap immediately
344
+ if (Math.abs(startOffset - targetOffsetPx) < 1) {
345
+ setAnimatedOffset(0);
346
+ return;
347
+ }
348
+
349
+ // Start animation
350
+ const startTime = performance.now();
351
+
352
+ const animate = (currentTime: number) => {
353
+ const elapsed = currentTime - startTime;
354
+ const progress = Math.min(elapsed / animationDuration, 1);
355
+ const easedProgress = easeOutExpo(progress);
356
+
357
+ // Interpolate from startOffset toward targetOffset, but we want to end at 0
358
+ // So we animate: startOffset → targetOffset, but since we're rendering with
359
+ // the NEW activeIndex, we need to compensate
360
+ //
361
+ // When activeIndex changes, slots now show different content.
362
+ // We animate the offset from (startOffset - targetOffset) back to 0.
363
+ const compensatedStart = startOffset - targetOffsetPx;
364
+ const currentOffset = compensatedStart * (1 - easedProgress);
365
+
366
+ setAnimatedOffset(currentOffset);
367
+
368
+ if (progress < 1) {
369
+ animationRef.current = {
370
+ startTime,
371
+ startOffset: compensatedStart,
372
+ targetOffset: 0,
373
+ rafId: requestAnimationFrame(animate),
374
+ };
375
+ } else {
376
+ animationRef.current = null;
377
+ setAnimatedOffset(0);
378
+ }
379
+ };
380
+
381
+ animationRef.current = {
382
+ startTime,
383
+ startOffset: startOffset - targetOffsetPx,
384
+ targetOffset: 0,
385
+ rafId: requestAnimationFrame(animate),
386
+ };
387
+ }, [fixedTabs, activeIndex, itemCount, tabWidth, animationDuration, navigationMode, displacement, isSwiping, animatedOffset]);
388
+
389
+ // Update prevActiveIndexRef for fixed tabs mode too
390
+ React.useEffect(() => {
391
+ if (fixedTabs) {
392
+ prevActiveIndexRef.current = activeIndex;
393
+ }
394
+ }, [fixedTabs, activeIndex]);
395
+
396
+ // Cleanup animation on unmount
397
+ React.useEffect(() => {
398
+ return () => {
399
+ if (animationRef.current) {
400
+ cancelAnimationFrame(animationRef.current.rafId);
401
+ }
402
+ if (fixedAnimationRef.current) {
403
+ cancelAnimationFrame(fixedAnimationRef.current.rafId);
404
+ }
405
+ };
406
+ }, []);
407
+
408
+ // Current offset for slot-based mode
409
+ const currentOffset = isSwiping ? displacement : animatedOffset;
410
+ const isAnimating = animationRef.current !== null || fixedAnimationRef.current !== null;
411
+
412
+ // Cancel slot animation when swiping starts
413
+ React.useEffect(() => {
414
+ if (isSwiping && animationRef.current) {
415
+ cancelAnimationFrame(animationRef.current.rafId);
416
+ animationRef.current = null;
417
+ setAnimatedOffset(0);
418
+ }
419
+ }, [isSwiping]);
420
+
421
+ // ============================================================
422
+ // Fixed tabs mode: render fixed tabs with sliding indicator
423
+ // ============================================================
424
+ if (fixedTabs) {
425
+ // Calculate total width and centering offset
426
+ const totalTabsWidth = items.length * tabWidth;
427
+ const centeringOffset = (viewportWidth - totalTabsWidth) / 2;
428
+
429
+ return (
430
+ <div
431
+ style={{
432
+ position: "relative",
433
+ width: "100%",
434
+ height: "100%",
435
+ overflow: "hidden",
436
+ display: "flex",
437
+ justifyContent: "center",
438
+ }}
439
+ >
440
+ {/* Sliding indicator (rendered behind tabs) */}
441
+ {renderIndicator?.({
442
+ offsetPx: indicatorPosition,
443
+ tabWidth,
444
+ centerX: centeringOffset,
445
+ isSwiping,
446
+ isAnimating,
447
+ })}
448
+
449
+ {/* Fixed tabs - each tab at its natural position */}
450
+ {items.map((item, index) => (
451
+ <div
452
+ key={item.id}
453
+ data-pivot-tab={item.id}
454
+ data-active={index === activeIndex ? "true" : "false"}
455
+ style={{
456
+ position: "relative",
457
+ width: tabWidth,
458
+ height: "100%",
459
+ flexShrink: 0,
460
+ }}
461
+ >
462
+ {renderTab(item, index === activeIndex, index)}
463
+ </div>
464
+ ))}
465
+ </div>
466
+ );
467
+ }
468
+
469
+ // Slot-based rendering for scrolling tabs (infinite loop support)
470
+ const slots: Array<{
471
+ slotPosition: number;
472
+ itemIndex: number;
473
+ item: { id: TId; label?: string };
474
+ }> = [];
475
+
476
+ for (let pos = -halfRange; pos <= halfRange; pos++) {
477
+ const itemIndex = getItemAtPosition(pos, activeIndex, itemCount, navigationMode);
478
+ if (itemIndex !== null) {
479
+ slots.push({
480
+ slotPosition: pos,
481
+ itemIndex,
482
+ item: items[itemIndex],
483
+ });
484
+ }
485
+ }
486
+
487
+ return (
488
+ <div
489
+ style={{
490
+ position: "relative",
491
+ width: "100%",
492
+ height: "100%",
493
+ overflow: "hidden",
494
+ }}
495
+ >
496
+ {/* Sliding indicator (rendered behind tabs) */}
497
+ {renderIndicator?.({
498
+ offsetPx: currentOffset,
499
+ tabWidth,
500
+ centerX,
501
+ isSwiping,
502
+ isAnimating,
503
+ })}
504
+
505
+ {/* Tab slots */}
506
+ {slots.map(({ slotPosition, itemIndex, item }) => (
507
+ <TabSlot
508
+ key={slotPosition}
509
+ slotPosition={slotPosition}
510
+ item={item}
511
+ itemIndex={itemIndex}
512
+ isActive={itemIndex === activeIndex}
513
+ centerX={centerX}
514
+ tabWidth={tabWidth}
515
+ viewportWidth={viewportWidth}
516
+ offsetPx={currentOffset}
517
+ axis={axis}
518
+ renderTab={renderTab}
519
+ />
520
+ ))}
521
+ </div>
522
+ );
523
+ }
@@ -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";