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,226 @@
1
+ /**
2
+ * @file Type definitions for the Stack navigation component system.
3
+ *
4
+ * Stack manages hierarchical navigation where panels stack as you drill down.
5
+ * Similar to iOS/iPadOS navigation controllers.
6
+ */
7
+ import type * as React from "react";
8
+ import type { SwipeInputState } from "../../hooks/gesture/types.js";
9
+
10
+ /**
11
+ * Display mode for stack panels.
12
+ * - "overlay": Panels fully overlap (mobile style)
13
+ * - "slide": Panels slide in from edge
14
+ * - "stack": Panels stack with visual depth (tablet style)
15
+ */
16
+ export type StackDisplayMode = "overlay" | "slide" | "stack";
17
+
18
+ /**
19
+ * Transition mode for stack animations.
20
+ * - "css": Uses CSS transitions/animations
21
+ * - "none": No transitions (uses React.Activity for memory optimization)
22
+ */
23
+ export type StackTransitionMode = "css" | "none";
24
+
25
+ /**
26
+ * A panel in the stack.
27
+ */
28
+ export type StackPanel<TId extends string = string> = {
29
+ /** Unique identifier for this panel */
30
+ id: TId;
31
+ /** Human-readable title for the panel (used in headers, back buttons) */
32
+ title?: string;
33
+ /** The content to render */
34
+ content: React.ReactNode;
35
+ /**
36
+ * Enable content caching to preserve React component state.
37
+ * When disabled (default), content is re-created on each render.
38
+ * @default false
39
+ */
40
+ cache?: boolean;
41
+ };
42
+
43
+ /**
44
+ * State of the stack navigation.
45
+ */
46
+ export type StackNavigationState<TId extends string = string> = {
47
+ /** Stack of panel IDs representing the navigation hierarchy */
48
+ stack: ReadonlyArray<TId>;
49
+ /** Current depth in the stack (0-indexed) */
50
+ depth: number;
51
+ /** Whether a parent panel is being revealed (peeking) */
52
+ isRevealing: boolean;
53
+ /** Depth of the revealed parent, if any */
54
+ revealDepth: number | null;
55
+ };
56
+
57
+ /**
58
+ * Props returned by getPanelProps for panel elements.
59
+ */
60
+ export type StackPanelProps = {
61
+ "data-stack-panel": string;
62
+ "data-depth": number;
63
+ "data-active": "true" | "false";
64
+ "aria-hidden": boolean;
65
+ };
66
+
67
+ /**
68
+ * Props returned by getBackButtonProps for back navigation.
69
+ */
70
+ export type StackBackButtonProps = {
71
+ onClick: () => void;
72
+ disabled: boolean;
73
+ "aria-label": string;
74
+ };
75
+
76
+ /**
77
+ * Options for useStackNavigation hook.
78
+ */
79
+ export type UseStackNavigationOptions<TId extends string = string> = {
80
+ /** Array of available panels */
81
+ panels: ReadonlyArray<StackPanel<TId>>;
82
+ /** Initial panel ID to display. @default first panel */
83
+ initialPanelId?: TId;
84
+ /** Display mode for panels */
85
+ displayMode: StackDisplayMode;
86
+ /** Transition mode for animations. @default "css" */
87
+ transitionMode?: StackTransitionMode;
88
+ /** Callback when panel changes */
89
+ onPanelChange?: (id: TId, depth: number) => void;
90
+ };
91
+
92
+ /**
93
+ * Result from useStackNavigation hook.
94
+ */
95
+ export type UseStackNavigationResult<TId extends string = string> = {
96
+ /** Current navigation state */
97
+ state: StackNavigationState<TId>;
98
+
99
+ // Navigation operations
100
+
101
+ /**
102
+ * Push a new panel onto the stack.
103
+ * @param id - Panel ID to push
104
+ */
105
+ push: (id: TId) => void;
106
+
107
+ /**
108
+ * Navigate in a direction relative to current depth.
109
+ * @param direction - Number of steps: -1=back, -2=back2, etc.
110
+ */
111
+ go: (direction: number) => void;
112
+
113
+ /**
114
+ * Move to an absolute depth in the stack.
115
+ * @param depth - Target depth (0-indexed)
116
+ */
117
+ move: (depth: number) => void;
118
+
119
+ /**
120
+ * Replace the current panel with a new one.
121
+ * @param id - Panel ID to replace with
122
+ */
123
+ replace: (id: TId) => void;
124
+
125
+ // Parent reveal operations
126
+
127
+ /**
128
+ * Temporarily reveal a parent panel.
129
+ * @param depth - Depth to reveal to (defaults to depth-1)
130
+ */
131
+ revealParent: (depth?: number) => void;
132
+
133
+ /**
134
+ * Reveal the root panel.
135
+ */
136
+ revealRoot: () => void;
137
+
138
+ /**
139
+ * Dismiss the parent reveal and return to current panel.
140
+ */
141
+ dismissReveal: () => void;
142
+
143
+ // Props and state getters
144
+
145
+ /**
146
+ * Get props for a panel element.
147
+ * @param id - Panel ID
148
+ */
149
+ getPanelProps: (id: TId) => StackPanelProps;
150
+
151
+ /**
152
+ * Get props for a back button.
153
+ */
154
+ getBackButtonProps: () => StackBackButtonProps;
155
+
156
+ /**
157
+ * Check if navigation in a direction is possible.
158
+ * @param direction - Direction to check
159
+ */
160
+ canGo: (direction: number) => boolean;
161
+
162
+ /** Current panel ID (top of stack) */
163
+ currentPanelId: TId;
164
+
165
+ /** Previous panel ID (one step back), or null if at root */
166
+ previousPanelId: TId | null;
167
+
168
+ // Rendering
169
+
170
+ /** Outlet component that renders the stack content */
171
+ Outlet: React.FC;
172
+ };
173
+
174
+ /**
175
+ * Options for useStackSwipeInput hook.
176
+ */
177
+ export type UseStackSwipeInputOptions = {
178
+ /** Reference to the swipe container element */
179
+ containerRef: React.RefObject<HTMLElement | null>;
180
+ /** Stack navigation result from useStackNavigation */
181
+ navigation: Pick<UseStackNavigationResult, "go" | "canGo" | "revealParent" | "dismissReveal" | "state">;
182
+ /** Edge to detect swipes from. @default "left" */
183
+ edge?: "left" | "right";
184
+ /** Width of the edge detection zone. @default 20 */
185
+ edgeWidth?: number;
186
+ /** Whether swipe input is enabled. @default true */
187
+ enabled?: boolean;
188
+ };
189
+
190
+ /**
191
+ * Result from useStackSwipeInput hook.
192
+ */
193
+ export type UseStackSwipeInputResult = {
194
+ /** Whether currently swiping from edge */
195
+ isEdgeSwiping: boolean;
196
+ /** Swipe progress (0-1) for animation */
197
+ progress: number;
198
+ /** Full swipe input state for direct DOM manipulation */
199
+ inputState: SwipeInputState;
200
+ /** Props to spread on the container element */
201
+ containerProps: React.HTMLAttributes<HTMLElement> & {
202
+ style: React.CSSProperties;
203
+ };
204
+ };
205
+
206
+ /**
207
+ * Props for StackContent component.
208
+ */
209
+ export type StackContentProps = {
210
+ /** Panel ID */
211
+ id: string;
212
+ /** Panel depth in the stack */
213
+ depth: number;
214
+ /** Whether this panel is currently active (top of stack) */
215
+ isActive: boolean;
216
+ /** Display mode */
217
+ displayMode: StackDisplayMode;
218
+ /** Transition mode */
219
+ transitionMode: StackTransitionMode;
220
+ /** Current navigation state */
221
+ navigationState: StackNavigationState;
222
+ /** Swipe progress for animation (0-1) */
223
+ swipeProgress?: number;
224
+ /** Content to render */
225
+ children: React.ReactNode;
226
+ };
@@ -0,0 +1,186 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { computeAnimatedPanels, type AnimatedPanel } from "./useStackAnimationState.js";
3
+
4
+ describe("computeAnimatedPanels", () => {
5
+ describe("initial state", () => {
6
+ it("should mark all panels as active when there is no previous stack", () => {
7
+ const prevPanels: AnimatedPanel[] = [];
8
+ const prevStack: string[] = [];
9
+ const currentStack = ["root"];
10
+
11
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
12
+
13
+ expect(result).toEqual([{ id: "root", depth: 0, phase: "active" }]);
14
+ });
15
+
16
+ it("should handle multiple initial panels", () => {
17
+ const prevPanels: AnimatedPanel[] = [];
18
+ const prevStack: string[] = [];
19
+ const currentStack = ["root", "general"];
20
+
21
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
22
+
23
+ expect(result).toEqual([
24
+ { id: "root", depth: 0, phase: "active" },
25
+ { id: "general", depth: 1, phase: "active" },
26
+ ]);
27
+ });
28
+ });
29
+
30
+ describe("push operation", () => {
31
+ it("should mark new panel as entering when pushed", () => {
32
+ const prevPanels: AnimatedPanel[] = [{ id: "root", depth: 0, phase: "active" }];
33
+ const prevStack = ["root"];
34
+ const currentStack = ["root", "general"];
35
+
36
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
37
+
38
+ expect(result).toEqual([
39
+ { id: "root", depth: 0, phase: "active" },
40
+ { id: "general", depth: 1, phase: "entering" },
41
+ ]);
42
+ });
43
+
44
+ it("should mark deeply nested push as entering", () => {
45
+ const prevPanels: AnimatedPanel[] = [
46
+ { id: "root", depth: 0, phase: "active" },
47
+ { id: "general", depth: 1, phase: "active" },
48
+ ];
49
+ const prevStack = ["root", "general"];
50
+ const currentStack = ["root", "general", "about"];
51
+
52
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
53
+
54
+ expect(result).toEqual([
55
+ { id: "root", depth: 0, phase: "active" },
56
+ { id: "general", depth: 1, phase: "active" },
57
+ { id: "about", depth: 2, phase: "entering" },
58
+ ]);
59
+ });
60
+ });
61
+
62
+ describe("pop operation", () => {
63
+ it("should mark popped panel as exiting", () => {
64
+ const prevPanels: AnimatedPanel[] = [
65
+ { id: "root", depth: 0, phase: "active" },
66
+ { id: "general", depth: 1, phase: "active" },
67
+ ];
68
+ const prevStack = ["root", "general"];
69
+ const currentStack = ["root"];
70
+
71
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
72
+
73
+ expect(result).toEqual([
74
+ { id: "root", depth: 0, phase: "active" },
75
+ { id: "general", depth: 1, phase: "exiting" },
76
+ ]);
77
+ });
78
+
79
+ it("should handle multiple panels popped at once", () => {
80
+ const prevPanels: AnimatedPanel[] = [
81
+ { id: "root", depth: 0, phase: "active" },
82
+ { id: "general", depth: 1, phase: "active" },
83
+ { id: "about", depth: 2, phase: "active" },
84
+ ];
85
+ const prevStack = ["root", "general", "about"];
86
+ const currentStack = ["root"];
87
+
88
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
89
+
90
+ expect(result).toEqual([
91
+ { id: "root", depth: 0, phase: "active" },
92
+ { id: "general", depth: 1, phase: "exiting" },
93
+ { id: "about", depth: 2, phase: "exiting" },
94
+ ]);
95
+ });
96
+
97
+ it("should preserve depth for exiting panels", () => {
98
+ const prevPanels: AnimatedPanel[] = [
99
+ { id: "root", depth: 0, phase: "active" },
100
+ { id: "general", depth: 1, phase: "active" },
101
+ { id: "about", depth: 2, phase: "active" },
102
+ { id: "name", depth: 3, phase: "active" },
103
+ ];
104
+ const prevStack = ["root", "general", "about", "name"];
105
+ const currentStack = ["root", "general"];
106
+
107
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
108
+
109
+ expect(result).toContainEqual({ id: "about", depth: 2, phase: "exiting" });
110
+ expect(result).toContainEqual({ id: "name", depth: 3, phase: "exiting" });
111
+ });
112
+ });
113
+
114
+ describe("exiting panel persistence", () => {
115
+ it("should keep already exiting panels", () => {
116
+ const prevPanels: AnimatedPanel[] = [
117
+ { id: "root", depth: 0, phase: "active" },
118
+ { id: "general", depth: 1, phase: "exiting" },
119
+ ];
120
+ const prevStack = ["root"];
121
+ const currentStack = ["root"];
122
+
123
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
124
+
125
+ expect(result).toContainEqual({ id: "general", depth: 1, phase: "exiting" });
126
+ });
127
+
128
+ it("should keep multiple exiting panels", () => {
129
+ const prevPanels: AnimatedPanel[] = [
130
+ { id: "root", depth: 0, phase: "active" },
131
+ { id: "general", depth: 1, phase: "exiting" },
132
+ { id: "about", depth: 2, phase: "exiting" },
133
+ ];
134
+ const prevStack = ["root"];
135
+ const currentStack = ["root"];
136
+
137
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
138
+
139
+ expect(result).toContainEqual({ id: "general", depth: 1, phase: "exiting" });
140
+ expect(result).toContainEqual({ id: "about", depth: 2, phase: "exiting" });
141
+ });
142
+ });
143
+
144
+ describe("entering panel transition", () => {
145
+ it("should keep entering phase on subsequent renders", () => {
146
+ const prevPanels: AnimatedPanel[] = [
147
+ { id: "root", depth: 0, phase: "active" },
148
+ { id: "general", depth: 1, phase: "entering" },
149
+ ];
150
+ const prevStack = ["root", "general"];
151
+ const currentStack = ["root", "general"];
152
+
153
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
154
+
155
+ expect(result).toEqual([
156
+ { id: "root", depth: 0, phase: "active" },
157
+ { id: "general", depth: 1, phase: "entering" },
158
+ ]);
159
+ });
160
+ });
161
+
162
+ describe("edge cases", () => {
163
+ it("should handle empty stacks", () => {
164
+ const prevPanels: AnimatedPanel[] = [];
165
+ const prevStack: string[] = [];
166
+ const currentStack: string[] = [];
167
+
168
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
169
+
170
+ expect(result).toEqual([]);
171
+ });
172
+
173
+ it("should handle no change in stack", () => {
174
+ const prevPanels: AnimatedPanel[] = [
175
+ { id: "root", depth: 0, phase: "active" },
176
+ { id: "general", depth: 1, phase: "active" },
177
+ ];
178
+ const prevStack = ["root", "general"];
179
+ const currentStack = ["root", "general"];
180
+
181
+ const result = computeAnimatedPanels(prevPanels, prevStack, currentStack);
182
+
183
+ expect(result).toEqual(prevPanels);
184
+ });
185
+ });
186
+ });
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @file Hook for managing stack animation state.
3
+ *
4
+ * Tracks panel lifecycle: entering → active → exiting → removed
5
+ * Provides stable state for animations regardless of React render timing.
6
+ */
7
+ import * as React from "react";
8
+
9
+ export type PanelAnimationPhase = "entering" | "active" | "exiting";
10
+
11
+ export type AnimatedPanel = {
12
+ id: string;
13
+ depth: number;
14
+ phase: PanelAnimationPhase;
15
+ };
16
+
17
+ export type UseStackAnimationStateOptions = {
18
+ /** Current stack from navigation */
19
+ stack: ReadonlyArray<string>;
20
+ /** Called when an exiting panel's animation completes */
21
+ onExitComplete?: (id: string) => void;
22
+ };
23
+
24
+ export type UseStackAnimationStateResult = {
25
+ /** All panels to render (active + exiting) */
26
+ panels: ReadonlyArray<AnimatedPanel>;
27
+ /** Mark a panel's enter animation as complete */
28
+ markEnterComplete: (id: string) => void;
29
+ /** Mark a panel's exit animation as complete */
30
+ markExitComplete: (id: string) => void;
31
+ };
32
+
33
+ /**
34
+ * Computes animated panels from current and previous stack.
35
+ * Pure function for easy testing.
36
+ */
37
+ export function computeAnimatedPanels(
38
+ prevPanels: ReadonlyArray<AnimatedPanel>,
39
+ prevStack: ReadonlyArray<string>,
40
+ currentStack: ReadonlyArray<string>,
41
+ ): ReadonlyArray<AnimatedPanel> {
42
+ const result: AnimatedPanel[] = [];
43
+ const currentStackSet = new Set(currentStack);
44
+
45
+ // Add panels from current stack
46
+ for (let i = 0; i < currentStack.length; i++) {
47
+ const id = currentStack[i];
48
+ const wasInPrevStack = prevStack.includes(id);
49
+ const prevPanel = prevPanels.find((p) => p.id === id);
50
+
51
+ let phase: PanelAnimationPhase;
52
+ if (!wasInPrevStack && prevStack.length > 0) {
53
+ // New panel pushed onto stack
54
+ phase = "entering";
55
+ } else if (prevPanel?.phase === "entering") {
56
+ // Was entering, keep entering until animation completes
57
+ phase = "entering";
58
+ } else {
59
+ phase = "active";
60
+ }
61
+
62
+ result.push({ id, depth: i, phase });
63
+ }
64
+
65
+ // Add exiting panels (were in prev stack but not in current)
66
+ for (let i = 0; i < prevStack.length; i++) {
67
+ const id = prevStack[i];
68
+ if (!currentStackSet.has(id)) {
69
+ // This panel was popped
70
+ const prevPanel = prevPanels.find((p) => p.id === id);
71
+ // Keep depth from previous position for animation
72
+ const depth = prevPanel?.depth ?? i;
73
+ result.push({ id, depth, phase: "exiting" });
74
+ }
75
+ }
76
+
77
+ // Also keep any panels that were already exiting
78
+ for (const panel of prevPanels) {
79
+ if (panel.phase === "exiting" && !result.some((p) => p.id === panel.id)) {
80
+ result.push(panel);
81
+ }
82
+ }
83
+
84
+ return result;
85
+ }
86
+
87
+ /**
88
+ * Hook for managing stack animation state.
89
+ *
90
+ * Uses a ref-based approach to compute panels synchronously during render,
91
+ * avoiding the flash that would occur with useEffect.
92
+ */
93
+ export function useStackAnimationState(
94
+ options: UseStackAnimationStateOptions,
95
+ ): UseStackAnimationStateResult {
96
+ const { stack, onExitComplete } = options;
97
+
98
+ // Use refs for synchronous computation during render
99
+ const prevStackRef = React.useRef<ReadonlyArray<string>>(stack);
100
+ const panelsRef = React.useRef<ReadonlyArray<AnimatedPanel>>(
101
+ stack.map((id, i) => ({ id, depth: i, phase: "active" as const })),
102
+ );
103
+
104
+ // Compute panels synchronously during render
105
+ const prevStack = prevStackRef.current;
106
+ const stackChanged =
107
+ prevStack.length !== stack.length || prevStack.some((id, i) => stack[i] !== id);
108
+
109
+ if (stackChanged) {
110
+ panelsRef.current = computeAnimatedPanels(panelsRef.current, prevStack, stack);
111
+ prevStackRef.current = stack;
112
+ }
113
+
114
+ // Force re-render when panels change (for animation completion callbacks)
115
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
116
+
117
+ const markEnterComplete = React.useCallback((id: string) => {
118
+ panelsRef.current = panelsRef.current.map((p) =>
119
+ p.id === id && p.phase === "entering" ? { ...p, phase: "active" } : p,
120
+ );
121
+ forceUpdate();
122
+ }, []);
123
+
124
+ const markExitComplete = React.useCallback(
125
+ (id: string) => {
126
+ panelsRef.current = panelsRef.current.filter((p) => p.id !== id);
127
+ onExitComplete?.(id);
128
+ forceUpdate();
129
+ },
130
+ [onExitComplete],
131
+ );
132
+
133
+ return {
134
+ panels: panelsRef.current,
135
+ markEnterComplete,
136
+ markExitComplete,
137
+ };
138
+ }