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,257 @@
1
+ /**
2
+ * @file Tests for useAnimatedVisibility hook.
3
+ *
4
+ * アニメーション完了待機パターンのテスト:
5
+ * 1. アニメーションなし → 即座にdisplay:none
6
+ * 2. アニメーションあり → 完了待ってdisplay:none
7
+ */
8
+ import { renderHook, act } from "@testing-library/react";
9
+ import { useAnimatedVisibility } from "./useAnimatedVisibility.js";
10
+
11
+ describe("useAnimatedVisibility", () => {
12
+ describe("initial state", () => {
13
+ it("displays when initially visible", () => {
14
+ const { result } = renderHook(() =>
15
+ useAnimatedVisibility({
16
+ isVisible: true,
17
+ leaveAnimation: "fadeOut 200ms ease-out",
18
+ }),
19
+ );
20
+
21
+ expect(result.current.style.display).toBe("block");
22
+ expect(result.current.state.shouldDisplay).toBe(true);
23
+ expect(result.current.state.isAnimatingOut).toBe(false);
24
+ });
25
+
26
+ it("hides when initially not visible", () => {
27
+ const { result } = renderHook(() =>
28
+ useAnimatedVisibility({
29
+ isVisible: false,
30
+ leaveAnimation: "fadeOut 200ms ease-out",
31
+ }),
32
+ );
33
+
34
+ expect(result.current.style.display).toBe("none");
35
+ expect(result.current.state.shouldDisplay).toBe(false);
36
+ expect(result.current.state.isAnimatingOut).toBe(false);
37
+ });
38
+ });
39
+
40
+ describe("no animation", () => {
41
+ it("hides immediately when leaveAnimation is undefined", () => {
42
+ const { result, rerender } = renderHook(
43
+ ({ isVisible }) => useAnimatedVisibility({ isVisible }),
44
+ { initialProps: { isVisible: true } },
45
+ );
46
+
47
+ expect(result.current.style.display).toBe("block");
48
+
49
+ rerender({ isVisible: false });
50
+
51
+ // Should hide immediately (no animation to wait for)
52
+ expect(result.current.style.display).toBe("none");
53
+ expect(result.current.state.isAnimatingOut).toBe(false);
54
+ });
55
+
56
+ it("hides immediately when leaveAnimation is 'none'", () => {
57
+ const { result, rerender } = renderHook(
58
+ ({ isVisible }) =>
59
+ useAnimatedVisibility({
60
+ isVisible,
61
+ leaveAnimation: "none",
62
+ }),
63
+ { initialProps: { isVisible: true } },
64
+ );
65
+
66
+ rerender({ isVisible: false });
67
+
68
+ expect(result.current.style.display).toBe("none");
69
+ expect(result.current.state.isAnimatingOut).toBe(false);
70
+ });
71
+
72
+ it("hides immediately when skipAnimation is true", () => {
73
+ const { result, rerender } = renderHook(
74
+ ({ isVisible }) =>
75
+ useAnimatedVisibility({
76
+ isVisible,
77
+ leaveAnimation: "fadeOut 200ms ease-out",
78
+ skipAnimation: true,
79
+ }),
80
+ { initialProps: { isVisible: true } },
81
+ );
82
+
83
+ rerender({ isVisible: false });
84
+
85
+ expect(result.current.style.display).toBe("none");
86
+ expect(result.current.state.isAnimatingOut).toBe(false);
87
+ });
88
+ });
89
+
90
+ describe("with animation", () => {
91
+ it("stays visible during leave animation", () => {
92
+ const { result, rerender } = renderHook(
93
+ ({ isVisible }) =>
94
+ useAnimatedVisibility({
95
+ isVisible,
96
+ leaveAnimation: "fadeOut 200ms ease-out",
97
+ }),
98
+ { initialProps: { isVisible: true } },
99
+ );
100
+
101
+ rerender({ isVisible: false });
102
+
103
+ // Should stay visible while animating out
104
+ expect(result.current.style.display).toBe("block");
105
+ expect(result.current.state.isAnimatingOut).toBe(true);
106
+ });
107
+
108
+ it("hides after animationEnd event", () => {
109
+ const { result, rerender } = renderHook(
110
+ ({ isVisible }) =>
111
+ useAnimatedVisibility({
112
+ isVisible,
113
+ leaveAnimation: "fadeOut 200ms ease-out",
114
+ }),
115
+ { initialProps: { isVisible: true } },
116
+ );
117
+
118
+ rerender({ isVisible: false });
119
+ expect(result.current.style.display).toBe("block");
120
+
121
+ // Simulate animationend event
122
+ const sharedElement = document.createElement("div");
123
+ const mockEvent = {
124
+ target: sharedElement,
125
+ currentTarget: sharedElement,
126
+ } as unknown as React.AnimationEvent;
127
+
128
+ act(() => {
129
+ result.current.props.onAnimationEnd(mockEvent);
130
+ });
131
+
132
+ // Now should be hidden
133
+ expect(result.current.style.display).toBe("none");
134
+ expect(result.current.state.isAnimatingOut).toBe(false);
135
+ });
136
+
137
+ it("ignores animationEnd from child elements", () => {
138
+ const { result, rerender } = renderHook(
139
+ ({ isVisible }) =>
140
+ useAnimatedVisibility({
141
+ isVisible,
142
+ leaveAnimation: "fadeOut 200ms ease-out",
143
+ }),
144
+ { initialProps: { isVisible: true } },
145
+ );
146
+
147
+ rerender({ isVisible: false });
148
+
149
+ // Simulate animationend from a child element (target !== currentTarget)
150
+ const parent = document.createElement("div");
151
+ const child = document.createElement("div");
152
+ const mockEvent = {
153
+ target: child,
154
+ currentTarget: parent,
155
+ } as unknown as React.AnimationEvent;
156
+
157
+ act(() => {
158
+ result.current.props.onAnimationEnd(mockEvent);
159
+ });
160
+
161
+ // Should still be visible (event was from child)
162
+ expect(result.current.style.display).toBe("block");
163
+ expect(result.current.state.isAnimatingOut).toBe(true);
164
+ });
165
+ });
166
+
167
+ describe("rapid state changes", () => {
168
+ it("shows again if made visible during leave animation", () => {
169
+ const { result, rerender } = renderHook(
170
+ ({ isVisible }) =>
171
+ useAnimatedVisibility({
172
+ isVisible,
173
+ leaveAnimation: "fadeOut 200ms ease-out",
174
+ }),
175
+ { initialProps: { isVisible: true } },
176
+ );
177
+
178
+ // Start hiding
179
+ rerender({ isVisible: false });
180
+ expect(result.current.state.isAnimatingOut).toBe(true);
181
+
182
+ // Immediately show again
183
+ rerender({ isVisible: true });
184
+ expect(result.current.style.display).toBe("block");
185
+ expect(result.current.state.isAnimatingOut).toBe(false);
186
+ });
187
+ });
188
+
189
+ describe("timeout fallback", () => {
190
+ it("hides after timeout if animationEnd never fires", async () => {
191
+ vi.useFakeTimers();
192
+
193
+ const { result, rerender } = renderHook(
194
+ ({ isVisible }) =>
195
+ useAnimatedVisibility({
196
+ isVisible,
197
+ leaveAnimation: "fadeOut 200ms ease-out",
198
+ animationTimeout: 500,
199
+ }),
200
+ { initialProps: { isVisible: true } },
201
+ );
202
+
203
+ rerender({ isVisible: false });
204
+ expect(result.current.style.display).toBe("block");
205
+ expect(result.current.state.isAnimatingOut).toBe(true);
206
+
207
+ // Advance time past timeout
208
+ await act(async () => {
209
+ vi.advanceTimersByTime(600);
210
+ });
211
+
212
+ // Should be hidden now (fallback triggered)
213
+ expect(result.current.style.display).toBe("none");
214
+ expect(result.current.state.isAnimatingOut).toBe(false);
215
+
216
+ vi.useRealTimers();
217
+ });
218
+
219
+ it("clears timeout when animationEnd fires before timeout", async () => {
220
+ vi.useFakeTimers();
221
+
222
+ const { result, rerender } = renderHook(
223
+ ({ isVisible }) =>
224
+ useAnimatedVisibility({
225
+ isVisible,
226
+ leaveAnimation: "fadeOut 200ms ease-out",
227
+ animationTimeout: 500,
228
+ }),
229
+ { initialProps: { isVisible: true } },
230
+ );
231
+
232
+ rerender({ isVisible: false });
233
+
234
+ // Fire animationEnd before timeout
235
+ const sharedElement = document.createElement("div");
236
+ const mockEvent = {
237
+ target: sharedElement,
238
+ currentTarget: sharedElement,
239
+ } as unknown as React.AnimationEvent;
240
+
241
+ act(() => {
242
+ result.current.props.onAnimationEnd(mockEvent);
243
+ });
244
+
245
+ expect(result.current.style.display).toBe("none");
246
+
247
+ // Advance past timeout - should not affect state
248
+ await act(async () => {
249
+ vi.advanceTimersByTime(600);
250
+ });
251
+
252
+ expect(result.current.style.display).toBe("none");
253
+
254
+ vi.useRealTimers();
255
+ });
256
+ });
257
+ });
@@ -0,0 +1,146 @@
1
+ /**
2
+ * @file Hook for animation-aware visibility control.
3
+ *
4
+ * Common pattern for showing/hiding elements with CSS animations:
5
+ * - If animation exists: wait for animationend before hiding
6
+ * - If no animation: hide immediately
7
+ * - Uses display:none for performance (removes from layout)
8
+ * - Includes timeout fallback in case animationend doesn't fire
9
+ */
10
+ import * as React from "react";
11
+
12
+ /** Default timeout for animation fallback (ms) */
13
+ const DEFAULT_ANIMATION_TIMEOUT = 1000;
14
+
15
+ type AnimatedVisibilityState = {
16
+ /** Whether element should be displayed (display: block/none) */
17
+ shouldDisplay: boolean;
18
+ /** Whether element is currently animating out */
19
+ isAnimatingOut: boolean;
20
+ };
21
+
22
+ type UseAnimatedVisibilityOptions = {
23
+ /** Whether the element is logically visible */
24
+ isVisible: boolean;
25
+ /** CSS animation value for leave animation (e.g., CSS variable) */
26
+ leaveAnimation?: string;
27
+ /** Skip animation and hide immediately */
28
+ skipAnimation?: boolean;
29
+ /** Timeout for animation fallback in ms (default: 1000ms) */
30
+ animationTimeout?: number;
31
+ };
32
+
33
+ type UseAnimatedVisibilityResult = {
34
+ /** Current visibility state */
35
+ state: AnimatedVisibilityState;
36
+ /** Props to spread on the animated element */
37
+ props: {
38
+ onAnimationEnd: (e: React.AnimationEvent) => void;
39
+ };
40
+ /** Style to apply for display control */
41
+ style: {
42
+ display: "block" | "none";
43
+ };
44
+ };
45
+
46
+ /**
47
+ * Hook for animation-aware visibility control.
48
+ *
49
+ * @example
50
+ * const { state, props, style } = useAnimatedVisibility({
51
+ * isVisible: isActive,
52
+ * leaveAnimation: PIVOT_ANIMATION_LEAVE,
53
+ * });
54
+ *
55
+ * return (
56
+ * <div
57
+ * style={{ ...baseStyle, ...style, animation: isActive ? enterAnim : leaveAnim }}
58
+ * {...props}
59
+ * >
60
+ * {children}
61
+ * </div>
62
+ * );
63
+ */
64
+ export function useAnimatedVisibility({
65
+ isVisible,
66
+ leaveAnimation,
67
+ skipAnimation = false,
68
+ animationTimeout = DEFAULT_ANIMATION_TIMEOUT,
69
+ }: UseAnimatedVisibilityOptions): UseAnimatedVisibilityResult {
70
+ const prevVisibleRef = React.useRef(isVisible);
71
+ const [isAnimatingOut, setIsAnimatingOut] = React.useState(false);
72
+ const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
73
+
74
+ // Clear timeout on unmount
75
+ React.useEffect(() => {
76
+ return () => {
77
+ if (timeoutRef.current) {
78
+ clearTimeout(timeoutRef.current);
79
+ }
80
+ };
81
+ }, []);
82
+
83
+ React.useEffect(() => {
84
+ const wasVisible = prevVisibleRef.current;
85
+ prevVisibleRef.current = isVisible;
86
+
87
+ // Clear any pending timeout
88
+ if (timeoutRef.current) {
89
+ clearTimeout(timeoutRef.current);
90
+ timeoutRef.current = null;
91
+ }
92
+
93
+ if (wasVisible && !isVisible) {
94
+ // Transitioning from visible to hidden
95
+ if (skipAnimation || !leaveAnimation || leaveAnimation === "none") {
96
+ // No animation, hide immediately
97
+ setIsAnimatingOut(false);
98
+ } else {
99
+ // Has animation, mark as animating out
100
+ setIsAnimatingOut(true);
101
+
102
+ // Set timeout fallback in case animationend doesn't fire
103
+ // (e.g., CSS variable resolves to "none", or animation is very short)
104
+ timeoutRef.current = setTimeout(() => {
105
+ setIsAnimatingOut(false);
106
+ }, animationTimeout);
107
+ }
108
+ } else if (!wasVisible && isVisible) {
109
+ // Transitioning from hidden to visible
110
+ setIsAnimatingOut(false);
111
+ }
112
+ }, [isVisible, leaveAnimation, skipAnimation, animationTimeout]);
113
+
114
+ const handleAnimationEnd = React.useCallback(
115
+ (e: React.AnimationEvent) => {
116
+ // Only handle animation end for this element (not bubbled from children)
117
+ if (e.target === e.currentTarget && isAnimatingOut) {
118
+ // Clear timeout since animation completed normally
119
+ if (timeoutRef.current) {
120
+ clearTimeout(timeoutRef.current);
121
+ timeoutRef.current = null;
122
+ }
123
+ setIsAnimatingOut(false);
124
+ }
125
+ },
126
+ [isAnimatingOut],
127
+ );
128
+
129
+ // Element should be displayed if:
130
+ // - It's visible, OR
131
+ // - It's animating out (leave animation in progress)
132
+ const shouldDisplay = isVisible || isAnimatingOut;
133
+
134
+ return {
135
+ state: {
136
+ shouldDisplay,
137
+ isAnimatingOut,
138
+ },
139
+ props: {
140
+ onAnimationEnd: handleAnimationEnd,
141
+ },
142
+ style: {
143
+ display: shouldDisplay ? "block" : "none",
144
+ },
145
+ };
146
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * @file Generic requestAnimationFrame-based animation hook.
3
+ *
4
+ * Provides a reusable animation loop with easing support.
5
+ * This is the foundation for more specific animation hooks.
6
+ */
7
+ import * as React from "react";
8
+
9
+ /**
10
+ * Easing function type.
11
+ * Takes a progress value (0-1) and returns an eased value (0-1).
12
+ */
13
+ export type EasingFunction = (t: number) => number;
14
+
15
+ /**
16
+ * Built-in easing functions.
17
+ */
18
+ export const easings = {
19
+ /** Linear (no easing) */
20
+ linear: (t: number): number => t,
21
+
22
+ /** Ease out cubic */
23
+ easeOutCubic: (t: number): number => 1 - Math.pow(1 - t, 3),
24
+
25
+ /** Ease out expo (similar to cubic-bezier(0.22, 1, 0.36, 1)) */
26
+ easeOutExpo: (t: number): number => {
27
+ if (t === 1) {
28
+ return 1;
29
+ }
30
+ return 1 - Math.pow(2, -10 * t);
31
+ },
32
+
33
+ /** Ease out quart */
34
+ easeOutQuart: (t: number): number => 1 - Math.pow(1 - t, 4),
35
+
36
+ /** Ease in out cubic */
37
+ easeInOutCubic: (t: number): number => {
38
+ if (t < 0.5) {
39
+ return 4 * t * t * t;
40
+ }
41
+ return 1 - Math.pow(-2 * t + 2, 3) / 2;
42
+ },
43
+ } as const;
44
+
45
+ /**
46
+ * Animation state passed to callbacks.
47
+ */
48
+ export type AnimationState = {
49
+ /** Raw progress (0-1) */
50
+ progress: number;
51
+ /** Eased progress (0-1) */
52
+ easedProgress: number;
53
+ /** Elapsed time in ms */
54
+ elapsed: number;
55
+ /** Whether animation is complete */
56
+ isComplete: boolean;
57
+ };
58
+
59
+ /**
60
+ * Options for useAnimationFrame hook.
61
+ */
62
+ export type UseAnimationFrameOptions = {
63
+ /** Duration of animation in milliseconds */
64
+ duration?: number;
65
+ /** Easing function for the animation */
66
+ easing?: EasingFunction;
67
+ /** Callback called every frame with animation state */
68
+ onFrame?: (state: AnimationState) => void;
69
+ /** Callback when animation completes */
70
+ onComplete?: () => void;
71
+ };
72
+
73
+ /**
74
+ * Result from useAnimationFrame hook.
75
+ */
76
+ export type UseAnimationFrameResult = {
77
+ /** Whether animation is currently running */
78
+ isAnimating: boolean;
79
+ /** Start the animation */
80
+ start: () => void;
81
+ /** Cancel the animation */
82
+ cancel: () => void;
83
+ };
84
+
85
+ /** Default animation duration in ms */
86
+ const DEFAULT_DURATION = 300;
87
+
88
+ /**
89
+ * Generic requestAnimationFrame-based animation hook.
90
+ *
91
+ * Provides a reusable animation loop with progress calculation and easing.
92
+ * Use this as a building block for specific animation behaviors.
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * const { start, isAnimating } = useAnimationFrame({
97
+ * duration: 300,
98
+ * easing: easings.easeOutExpo,
99
+ * onFrame: ({ easedProgress }) => {
100
+ * const value = fromValue + (toValue - fromValue) * easedProgress;
101
+ * element.style.transform = `translateX(${value}px)`;
102
+ * },
103
+ * onComplete: () => console.log('Done!'),
104
+ * });
105
+ *
106
+ * // Start animation
107
+ * start();
108
+ * ```
109
+ */
110
+ export function useAnimationFrame(options: UseAnimationFrameOptions): UseAnimationFrameResult {
111
+ const {
112
+ duration = DEFAULT_DURATION,
113
+ easing = easings.easeOutExpo,
114
+ onFrame,
115
+ onComplete,
116
+ } = options;
117
+
118
+ const [isAnimating, setIsAnimating] = React.useState(false);
119
+ const rafIdRef = React.useRef<number | null>(null);
120
+ const startTimeRef = React.useRef<number | null>(null);
121
+
122
+ // Use refs for callbacks to avoid stale closures
123
+ const onFrameRef = React.useRef(onFrame);
124
+ const onCompleteRef = React.useRef(onComplete);
125
+ React.useEffect(() => {
126
+ onFrameRef.current = onFrame;
127
+ onCompleteRef.current = onComplete;
128
+ }, [onFrame, onComplete]);
129
+
130
+ const cancel = React.useCallback(() => {
131
+ if (rafIdRef.current !== null) {
132
+ cancelAnimationFrame(rafIdRef.current);
133
+ rafIdRef.current = null;
134
+ }
135
+ startTimeRef.current = null;
136
+ setIsAnimating(false);
137
+ }, []);
138
+
139
+ const start = React.useCallback(() => {
140
+ // Cancel any existing animation
141
+ cancel();
142
+
143
+ setIsAnimating(true);
144
+ startTimeRef.current = null;
145
+
146
+ const step = (timestamp: number) => {
147
+ if (startTimeRef.current === null) {
148
+ startTimeRef.current = timestamp;
149
+ }
150
+
151
+ const elapsed = timestamp - startTimeRef.current;
152
+ const progress = Math.min(elapsed / duration, 1);
153
+ const easedProgress = easing(progress);
154
+ const isComplete = progress >= 1;
155
+
156
+ const state: AnimationState = {
157
+ progress,
158
+ easedProgress,
159
+ elapsed,
160
+ isComplete,
161
+ };
162
+
163
+ onFrameRef.current?.(state);
164
+
165
+ if (!isComplete) {
166
+ rafIdRef.current = requestAnimationFrame(step);
167
+ } else {
168
+ // Animation complete
169
+ rafIdRef.current = null;
170
+ startTimeRef.current = null;
171
+ setIsAnimating(false);
172
+ onCompleteRef.current?.();
173
+ }
174
+ };
175
+
176
+ rafIdRef.current = requestAnimationFrame(step);
177
+ }, [duration, easing, cancel]);
178
+
179
+ // Cleanup on unmount
180
+ React.useEffect(() => {
181
+ return () => {
182
+ if (rafIdRef.current !== null) {
183
+ cancelAnimationFrame(rafIdRef.current);
184
+ }
185
+ };
186
+ }, []);
187
+
188
+ return {
189
+ isAnimating,
190
+ start,
191
+ cancel,
192
+ };
193
+ }
194
+
195
+ /**
196
+ * Interpolate between two values using eased progress.
197
+ */
198
+ export function interpolate(from: number, to: number, easedProgress: number): number {
199
+ return from + (to - from) * easedProgress;
200
+ }