react-panel-layout 0.5.2 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (541) hide show
  1. package/dist/{FloatingPanelFrame-lLg-Lpg7.js → FloatingPanelFrame-3eU9AwPo.js} +11 -11
  2. package/dist/{FloatingPanelFrame-lLg-Lpg7.js.map → FloatingPanelFrame-3eU9AwPo.js.map} +1 -1
  3. package/dist/{FloatingPanelFrame-D9Cp2al1.cjs → FloatingPanelFrame-CEmXDvUA.cjs} +2 -2
  4. package/dist/{FloatingPanelFrame-D9Cp2al1.cjs.map → FloatingPanelFrame-CEmXDvUA.cjs.map} +1 -1
  5. package/dist/FloatingWindow-CUXnEtrb.js +827 -0
  6. package/dist/FloatingWindow-CUXnEtrb.js.map +1 -0
  7. package/dist/FloatingWindow-DMwyK0eK.cjs +2 -0
  8. package/dist/FloatingWindow-DMwyK0eK.cjs.map +1 -0
  9. package/dist/GridLayout-DKTg_N61.cjs +2 -0
  10. package/dist/GridLayout-DKTg_N61.cjs.map +1 -0
  11. package/dist/GridLayout-UWNxXw77.js +926 -0
  12. package/dist/GridLayout-UWNxXw77.js.map +1 -0
  13. package/dist/HorizontalDivider-DdxzfV0l.js +30 -0
  14. package/dist/HorizontalDivider-DdxzfV0l.js.map +1 -0
  15. package/dist/HorizontalDivider-_pgV4Mcv.cjs +2 -0
  16. package/dist/HorizontalDivider-_pgV4Mcv.cjs.map +1 -0
  17. package/dist/PanelSystem-BqUzNtf2.js +1946 -0
  18. package/dist/PanelSystem-BqUzNtf2.js.map +1 -0
  19. package/dist/PanelSystem-D603LKKv.cjs +3 -0
  20. package/dist/PanelSystem-D603LKKv.cjs.map +1 -0
  21. package/dist/ResizeHandle-CBcAS918.cjs +2 -0
  22. package/dist/ResizeHandle-CBcAS918.cjs.map +1 -0
  23. package/dist/ResizeHandle-CXjc1meV.js +119 -0
  24. package/dist/ResizeHandle-CXjc1meV.js.map +1 -0
  25. package/dist/SwipePivotTabBar-DWrCuwEI.js +411 -0
  26. package/dist/SwipePivotTabBar-DWrCuwEI.js.map +1 -0
  27. package/dist/SwipePivotTabBar-fjjXkpj7.cjs +2 -0
  28. package/dist/SwipePivotTabBar-fjjXkpj7.cjs.map +1 -0
  29. package/dist/components/gesture/SwipeSafeZone.d.ts +40 -0
  30. package/dist/components/window/Drawer.d.ts +3 -1
  31. package/dist/components/window/DrawerLayers.d.ts +1 -1
  32. package/dist/components/window/drawerStyles.d.ts +69 -0
  33. package/dist/components/window/drawerSwipeConfig.d.ts +29 -0
  34. package/dist/components/window/useDrawerSwipeTransform.d.ts +23 -0
  35. package/dist/config.cjs +1 -1
  36. package/dist/config.cjs.map +1 -1
  37. package/dist/config.js +11 -9
  38. package/dist/config.js.map +1 -1
  39. package/dist/constants/styles.d.ts +35 -4
  40. package/dist/dialog/index.d.ts +69 -0
  41. package/dist/floating.cjs +1 -1
  42. package/dist/floating.js +1 -1
  43. package/dist/grid/index.d.ts +58 -0
  44. package/dist/grid.cjs +2 -0
  45. package/dist/grid.cjs.map +1 -0
  46. package/dist/grid.js +13 -0
  47. package/dist/grid.js.map +1 -0
  48. package/dist/hooks/gesture/presets.d.ts +33 -0
  49. package/dist/hooks/gesture/testing/createGestureSimulator.d.ts +117 -0
  50. package/dist/hooks/gesture/thresholdValue.d.ts +44 -0
  51. package/dist/hooks/gesture/types.d.ts +297 -0
  52. package/dist/hooks/gesture/useDirectionalLock.d.ts +20 -0
  53. package/dist/hooks/gesture/useEdgeSwipeInput.d.ts +23 -0
  54. package/dist/hooks/gesture/useNativeGestureGuard.d.ts +23 -0
  55. package/dist/hooks/gesture/usePointerTracking.d.ts +22 -0
  56. package/dist/hooks/gesture/useScrollBoundary.d.ts +23 -0
  57. package/dist/hooks/gesture/useSwipeInput.d.ts +5 -0
  58. package/dist/hooks/gesture/utils.d.ts +59 -0
  59. package/dist/hooks/useAnimatedVisibility.d.ts +58 -0
  60. package/dist/hooks/useAnimationFrame.d.ts +86 -0
  61. package/dist/hooks/useOperationContinuity.d.ts +64 -0
  62. package/dist/hooks/useResizeObserver.d.ts +33 -1
  63. package/dist/hooks/useSharedElementTransition.d.ts +112 -0
  64. package/dist/hooks/useSnapAnimation.d.ts +54 -0
  65. package/dist/hooks/useSwipeContentTransform.d.ts +86 -0
  66. package/dist/index.cjs +1 -2
  67. package/dist/index.cjs.map +1 -1
  68. package/dist/index.d.ts +0 -1
  69. package/dist/index.js +25 -2006
  70. package/dist/index.js.map +1 -1
  71. package/dist/modules/dialog/AlertDialog.d.ts +9 -0
  72. package/dist/modules/dialog/DialogContainer.d.ts +37 -0
  73. package/dist/modules/dialog/Modal.d.ts +26 -0
  74. package/dist/modules/dialog/SwipeDialogContainer.d.ts +16 -0
  75. package/dist/modules/dialog/dialogAnimationUtils.d.ts +113 -0
  76. package/dist/modules/dialog/types.d.ts +183 -0
  77. package/dist/modules/dialog/useDialog.d.ts +39 -0
  78. package/dist/modules/dialog/useDialogContainer.d.ts +47 -0
  79. package/dist/modules/dialog/useDialogSwipeInput.d.ts +70 -0
  80. package/dist/modules/dialog/useDialogTransform.d.ts +82 -0
  81. package/dist/modules/drawer/types.d.ts +74 -0
  82. package/dist/modules/drawer/useDrawerSwipeInput.d.ts +24 -0
  83. package/dist/modules/pivot/PivotContent.d.ts +1 -1
  84. package/dist/modules/pivot/SwipePivotContent.d.ts +39 -0
  85. package/dist/modules/pivot/SwipePivotContent.debug.tmp.d.ts +25 -0
  86. package/dist/modules/pivot/SwipePivotContent.test.d.ts +1 -0
  87. package/dist/modules/pivot/SwipePivotTabBar.d.ts +92 -0
  88. package/dist/modules/pivot/index.d.ts +3 -0
  89. package/dist/modules/pivot/scaleInputState.d.ts +37 -0
  90. package/dist/modules/pivot/types.d.ts +67 -2
  91. package/dist/modules/pivot/usePivotSwipeInput.d.ts +68 -0
  92. package/dist/modules/stack/StackContent.d.ts +15 -0
  93. package/dist/modules/stack/SwipeStackContent.d.ts +66 -0
  94. package/dist/modules/stack/SwipeStackOutlet.d.ts +80 -0
  95. package/dist/modules/stack/computeStackContentState.d.ts +99 -0
  96. package/dist/modules/stack/computeSwipeStackTransform.d.ts +76 -0
  97. package/dist/modules/stack/types.d.ts +194 -0
  98. package/dist/modules/stack/useStackAnimationState.d.ts +32 -0
  99. package/dist/modules/stack/useStackNavigation.d.ts +23 -0
  100. package/dist/modules/stack/useStackSwipeInput.d.ts +27 -0
  101. package/dist/panels/index.d.ts +67 -0
  102. package/dist/panels.cjs +2 -0
  103. package/dist/panels.cjs.map +1 -0
  104. package/dist/panels.js +28 -0
  105. package/dist/panels.js.map +1 -0
  106. package/dist/pivot/index.d.ts +3 -0
  107. package/dist/pivot.cjs +1 -1
  108. package/dist/pivot.cjs.map +1 -1
  109. package/dist/pivot.js +20 -2
  110. package/dist/pivot.js.map +1 -1
  111. package/dist/resizer/index.d.ts +57 -0
  112. package/dist/resizer.cjs +2 -0
  113. package/dist/resizer.cjs.map +1 -0
  114. package/dist/resizer.js +8 -0
  115. package/dist/resizer.js.map +1 -0
  116. package/dist/stack/index.d.ts +72 -0
  117. package/dist/stack.cjs +2 -0
  118. package/dist/stack.cjs.map +1 -0
  119. package/dist/stack.js +721 -0
  120. package/dist/stack.js.map +1 -0
  121. package/dist/sticky-header/StickyArea.d.ts +38 -0
  122. package/dist/sticky-header/calculateStickyMetrics.d.ts +28 -0
  123. package/dist/sticky-header/index.d.ts +4 -4
  124. package/dist/sticky-header/types.d.ts +35 -22
  125. package/dist/sticky-header.cjs +1 -1
  126. package/dist/sticky-header.cjs.map +1 -1
  127. package/dist/sticky-header.js +73 -174
  128. package/dist/sticky-header.js.map +1 -1
  129. package/dist/styles-NkjuMOVS.js +57 -0
  130. package/dist/styles-NkjuMOVS.js.map +1 -0
  131. package/dist/styles-qf6ptVLD.cjs +2 -0
  132. package/dist/styles-qf6ptVLD.cjs.map +1 -0
  133. package/dist/types.d.ts +16 -0
  134. package/dist/useContentCache-CO3LYNmz.js +24 -0
  135. package/dist/useContentCache-CO3LYNmz.js.map +1 -0
  136. package/dist/useContentCache-DqXtLrLs.cjs +2 -0
  137. package/dist/useContentCache-DqXtLrLs.cjs.map +1 -0
  138. package/dist/useDocumentPointerEvents-DXxw3qWj.js +54 -0
  139. package/dist/useDocumentPointerEvents-DXxw3qWj.js.map +1 -0
  140. package/dist/useDocumentPointerEvents-DxDSOtip.cjs +2 -0
  141. package/dist/useDocumentPointerEvents-DxDSOtip.cjs.map +1 -0
  142. package/dist/useFloatingState-C4kRaW_R.cjs +2 -0
  143. package/dist/useFloatingState-C4kRaW_R.cjs.map +1 -0
  144. package/dist/useFloatingState-tEfA_wbc.js +74 -0
  145. package/dist/useFloatingState-tEfA_wbc.js.map +1 -0
  146. package/dist/useNativeGestureGuard-C7TSqEkr.cjs +2 -0
  147. package/dist/useNativeGestureGuard-C7TSqEkr.cjs.map +1 -0
  148. package/dist/useNativeGestureGuard-CGYo6O0r.js +347 -0
  149. package/dist/useNativeGestureGuard-CGYo6O0r.js.map +1 -0
  150. package/dist/window/index.d.ts +63 -0
  151. package/dist/window.cjs +2 -0
  152. package/dist/window.cjs.map +1 -0
  153. package/dist/window.js +160 -0
  154. package/dist/window.js.map +1 -0
  155. package/docs/design-tokens.md +405 -0
  156. package/package.json +34 -4
  157. package/src/PanelSystemContext.tsx +88 -0
  158. package/src/components/gesture/SwipeSafeZone.tsx +69 -0
  159. package/src/components/grid/GridLayerList.tsx +172 -0
  160. package/src/components/grid/GridLayerResizeHandles.tsx +145 -0
  161. package/src/components/grid/GridLayout.spec.tsx +743 -0
  162. package/src/components/grid/GridLayout.tsx +130 -0
  163. package/src/components/grid/GridTrackResizeHandle.tsx +87 -0
  164. package/src/components/paneling/FloatingPanelFrame.tsx +203 -0
  165. package/src/components/panels/DropSuggestOverlay.tsx +131 -0
  166. package/src/components/panels/PanelGroupView.tsx +112 -0
  167. package/src/components/pivot/PivotLayer.tsx +27 -0
  168. package/src/components/resizer/HorizontalDivider.tsx +52 -0
  169. package/src/components/resizer/ResizeHandle.tsx +118 -0
  170. package/src/components/tabs/TabBar.tsx +223 -0
  171. package/src/components/tabs/TabBarTab.tsx +133 -0
  172. package/src/components/tabs/TabDragOverlay.tsx +92 -0
  173. package/src/components/window/DialogOverlay.tsx +180 -0
  174. package/src/components/window/Drawer.tsx +369 -0
  175. package/src/components/window/DrawerLayers.tsx +68 -0
  176. package/src/components/window/FloatingWindow.tsx +95 -0
  177. package/src/components/window/PopupLayerPortal.tsx +218 -0
  178. package/src/components/window/drawerStyles.spec.ts +263 -0
  179. package/src/components/window/drawerStyles.ts +228 -0
  180. package/src/components/window/drawerSwipeConfig.spec.ts +131 -0
  181. package/src/components/window/drawerSwipeConfig.ts +112 -0
  182. package/src/components/window/useDrawerSwipeTransform.spec.ts +234 -0
  183. package/src/components/window/useDrawerSwipeTransform.ts +129 -0
  184. package/src/config/PanelContentDeclaration.tsx +427 -0
  185. package/src/config/index.tsx +52 -0
  186. package/src/config/panelJsx.spec.tsx +54 -0
  187. package/src/config/panelJsxConfig.spec.tsx +54 -0
  188. package/src/config/panelJsxDrawer.spec.tsx +33 -0
  189. package/src/config/panelRouter.spec.ts +68 -0
  190. package/src/config/panelRouter.tsx +155 -0
  191. package/src/constants/styles.ts +280 -0
  192. package/src/demo/Layout.module.css +258 -0
  193. package/src/demo/Layout.tsx +176 -0
  194. package/src/demo/components/CodeBlock.module.css +54 -0
  195. package/src/demo/components/CodeBlock.tsx +34 -0
  196. package/src/demo/components/CodePreview.module.css +37 -0
  197. package/src/demo/components/CodePreview.tsx +31 -0
  198. package/src/demo/components/DataPreview.module.css +177 -0
  199. package/src/demo/components/DataPreview.tsx +115 -0
  200. package/src/demo/components/Story.module.css +68 -0
  201. package/src/demo/components/Story.tsx +54 -0
  202. package/src/demo/components/layout/CodePanel.module.css +183 -0
  203. package/src/demo/components/layout/CodePanel.tsx +149 -0
  204. package/src/demo/components/layout/DemoPage.module.css +60 -0
  205. package/src/demo/components/layout/DemoPage.tsx +56 -0
  206. package/src/demo/components/layout/SingleSamplePage.module.css +11 -0
  207. package/src/demo/components/layout/SingleSamplePage.tsx +35 -0
  208. package/src/demo/components/layout/SplitDemoLayout.module.css +107 -0
  209. package/src/demo/components/layout/SplitDemoLayout.tsx +218 -0
  210. package/src/demo/components/layout/index.ts +11 -0
  211. package/src/demo/components/tab-styles/ChromeTabBar.module.css +75 -0
  212. package/src/demo/components/tab-styles/ChromeTabBar.tsx +111 -0
  213. package/src/demo/components/tab-styles/GitHubTabBar.module.css +81 -0
  214. package/src/demo/components/tab-styles/GitHubTabBar.tsx +109 -0
  215. package/src/demo/components/tab-styles/VSCodeTabBar.module.css +78 -0
  216. package/src/demo/components/tab-styles/VSCodeTabBar.tsx +109 -0
  217. package/src/demo/components/tab-styles/index.ts +6 -0
  218. package/src/demo/components/ui/DemoButton.module.css +63 -0
  219. package/src/demo/components/ui/DemoButton.tsx +32 -0
  220. package/src/demo/components/ui/DemoCard.module.css +15 -0
  221. package/src/demo/components/ui/DemoCard.tsx +30 -0
  222. package/src/demo/components/ui/DemoContainer.module.css +17 -0
  223. package/src/demo/components/ui/DemoContainer.tsx +30 -0
  224. package/src/demo/components/ui/DemoPanel.module.css +23 -0
  225. package/src/demo/components/ui/DemoPanel.tsx +33 -0
  226. package/src/demo/components/ui/PanelText.module.css +18 -0
  227. package/src/demo/components/ui/PanelText.tsx +29 -0
  228. package/src/demo/components/ui/PanelTitle.module.css +18 -0
  229. package/src/demo/components/ui/PanelTitle.tsx +31 -0
  230. package/src/demo/contexts/TabbarDemoConfig.tsx +218 -0
  231. package/src/demo/demo.css +172 -0
  232. package/src/demo/hooks/useMedia.ts +41 -0
  233. package/src/demo/hooks/useShikiHighlight.ts +55 -0
  234. package/src/demo/index.tsx +293 -0
  235. package/src/demo/pages/Dialog/alerts/index.tsx +22 -0
  236. package/src/demo/pages/Dialog/card/index.tsx +22 -0
  237. package/src/demo/pages/Dialog/components/AlertDialogDemo.tsx +124 -0
  238. package/src/demo/pages/Dialog/components/CardExpandDemo.module.css +243 -0
  239. package/src/demo/pages/Dialog/components/CardExpandDemo.tsx +204 -0
  240. package/src/demo/pages/Dialog/components/CustomAlertDialogDemo.tsx +219 -0
  241. package/src/demo/pages/Dialog/components/DialogDemos.module.css +77 -0
  242. package/src/demo/pages/Dialog/components/ModalBasics.tsx +45 -0
  243. package/src/demo/pages/Dialog/components/SwipeDialogDemo.module.css +77 -0
  244. package/src/demo/pages/Dialog/components/SwipeDialogDemo.tsx +181 -0
  245. package/src/demo/pages/Dialog/custom-alert/index.tsx +22 -0
  246. package/src/demo/pages/Dialog/modal/index.tsx +17 -0
  247. package/src/demo/pages/Dialog/swipe/index.tsx +22 -0
  248. package/src/demo/pages/Drawer/animations/index.tsx +22 -0
  249. package/src/demo/pages/Drawer/basics/index.tsx +17 -0
  250. package/src/demo/pages/Drawer/components/DrawerAnimations.module.css +125 -0
  251. package/src/demo/pages/Drawer/components/DrawerAnimations.tsx +118 -0
  252. package/src/demo/pages/Drawer/components/DrawerBasics.module.css +55 -0
  253. package/src/demo/pages/Drawer/components/DrawerBasics.tsx +76 -0
  254. package/src/demo/pages/Drawer/components/DrawerMenuLayout.module.css +332 -0
  255. package/src/demo/pages/Drawer/components/DrawerMenuLayout.tsx +199 -0
  256. package/src/demo/pages/Drawer/components/DrawerSwipe.module.css +316 -0
  257. package/src/demo/pages/Drawer/components/DrawerSwipe.tsx +178 -0
  258. package/src/demo/pages/Drawer/menu/index.tsx +17 -0
  259. package/src/demo/pages/Drawer/swipe/index.tsx +17 -0
  260. package/src/demo/pages/FloatingPanelFrame/ResizableFloatingPanelsPreview.module.css +163 -0
  261. package/src/demo/pages/FloatingPanelFrame/ResizableFloatingPanelsPreview.tsx +234 -0
  262. package/src/demo/pages/FloatingPanelFrame/basic/index.tsx +17 -0
  263. package/src/demo/pages/FloatingPanelFrame/complex/index.tsx +26 -0
  264. package/src/demo/pages/FloatingPanelFrame/components/BasicPanel.module.css +16 -0
  265. package/src/demo/pages/FloatingPanelFrame/components/BasicPanel.tsx +24 -0
  266. package/src/demo/pages/FloatingPanelFrame/components/ComplexPanel.module.css +54 -0
  267. package/src/demo/pages/FloatingPanelFrame/components/ComplexPanel.tsx +67 -0
  268. package/src/demo/pages/FloatingPanelFrame/components/PanelWithControls.module.css +21 -0
  269. package/src/demo/pages/FloatingPanelFrame/components/PanelWithControls.tsx +41 -0
  270. package/src/demo/pages/FloatingPanelFrame/components/PanelWithMeta.module.css +5 -0
  271. package/src/demo/pages/FloatingPanelFrame/components/PanelWithMeta.tsx +43 -0
  272. package/src/demo/pages/FloatingPanelFrame/components/ScrollablePanel.module.css +11 -0
  273. package/src/demo/pages/FloatingPanelFrame/components/ScrollablePanel.tsx +42 -0
  274. package/src/demo/pages/FloatingPanelFrame/index.tsx +80 -0
  275. package/src/demo/pages/FloatingPanelFrame/scrollable/index.tsx +30 -0
  276. package/src/demo/pages/FloatingPanelFrame/with-controls/index.tsx +30 -0
  277. package/src/demo/pages/FloatingPanelFrame/with-meta/index.tsx +17 -0
  278. package/src/demo/pages/HorizontalDivider/components/PanelsWithRichContent.module.css +112 -0
  279. package/src/demo/pages/HorizontalDivider/components/PanelsWithRichContent.tsx +56 -0
  280. package/src/demo/pages/HorizontalDivider/components/SimpleResizablePanels.module.css +46 -0
  281. package/src/demo/pages/HorizontalDivider/components/SimpleResizablePanels.tsx +29 -0
  282. package/src/demo/pages/HorizontalDivider/components/ThreePanelLayout.module.css +54 -0
  283. package/src/demo/pages/HorizontalDivider/components/ThreePanelLayout.tsx +30 -0
  284. package/src/demo/pages/HorizontalDivider/index.module.css +14 -0
  285. package/src/demo/pages/HorizontalDivider/index.tsx +64 -0
  286. package/src/demo/pages/HorizontalDivider/panels-with-rich-content/index.tsx +21 -0
  287. package/src/demo/pages/HorizontalDivider/simple-resizable-panels/index.tsx +21 -0
  288. package/src/demo/pages/HorizontalDivider/three-panel-layout/index.tsx +21 -0
  289. package/src/demo/pages/PanelLayout/PanelLayoutDemo.module.css +174 -0
  290. package/src/demo/pages/PanelLayout/PanelLayoutDemo.tsx +248 -0
  291. package/src/demo/pages/PanelLayout/components/DashboardLayout.module.css +115 -0
  292. package/src/demo/pages/PanelLayout/components/DashboardLayout.tsx +124 -0
  293. package/src/demo/pages/PanelLayout/components/DraggableOverlays.module.css +101 -0
  294. package/src/demo/pages/PanelLayout/components/DraggableOverlays.tsx +122 -0
  295. package/src/demo/pages/PanelLayout/components/IDELayout.module.css +104 -0
  296. package/src/demo/pages/PanelLayout/components/IDELayout.tsx +143 -0
  297. package/src/demo/pages/PanelLayout/components/SimpleGrid.module.css +19 -0
  298. package/src/demo/pages/PanelLayout/components/SimpleGrid.tsx +62 -0
  299. package/src/demo/pages/PanelLayout/dashboard/index.tsx +22 -0
  300. package/src/demo/pages/PanelLayout/draggable-overlays/index.tsx +22 -0
  301. package/src/demo/pages/PanelLayout/ide-layout/index.tsx +22 -0
  302. package/src/demo/pages/PanelLayout/index.tsx +94 -0
  303. package/src/demo/pages/PanelLayout/simple-grid/index.tsx +22 -0
  304. package/src/demo/pages/PanelSystem/PanelSystemPreview.module.css +20 -0
  305. package/src/demo/pages/PanelSystem/PanelSystemPreview.tsx +101 -0
  306. package/src/demo/pages/PanelSystem/preview/index.tsx +18 -0
  307. package/src/demo/pages/PanelSystem/tabbar/index.tsx +129 -0
  308. package/src/demo/pages/Pivot/basics/index.tsx +17 -0
  309. package/src/demo/pages/Pivot/components/Pivot.module.css +278 -0
  310. package/src/demo/pages/Pivot/components/PivotBasics.tsx +103 -0
  311. package/src/demo/pages/Pivot/components/PivotSidebar.tsx +168 -0
  312. package/src/demo/pages/Pivot/components/PivotTabs.tsx +129 -0
  313. package/src/demo/pages/Pivot/components/PivotTransitions.tsx +120 -0
  314. package/src/demo/pages/Pivot/components/SwipePivot.module.css +114 -0
  315. package/src/demo/pages/Pivot/components/SwipePivot.tsx +193 -0
  316. package/src/demo/pages/Pivot/components/SwipeTabsPivot.module.css +203 -0
  317. package/src/demo/pages/Pivot/components/SwipeTabsPivot.tsx +320 -0
  318. package/src/demo/pages/Pivot/sidebar/index.tsx +17 -0
  319. package/src/demo/pages/Pivot/swipe/index.tsx +16 -0
  320. package/src/demo/pages/Pivot/swipe-debug/index.tsx +287 -0
  321. package/src/demo/pages/Pivot/swipe-tabs/index.tsx +15 -0
  322. package/src/demo/pages/Pivot/tabs/index.tsx +17 -0
  323. package/src/demo/pages/Pivot/transitions/index.tsx +17 -0
  324. package/src/demo/pages/ResizeHandle/both-directions/index.tsx +17 -0
  325. package/src/demo/pages/ResizeHandle/components/BothDirectionsDemo.module.css +72 -0
  326. package/src/demo/pages/ResizeHandle/components/BothDirectionsDemo.tsx +41 -0
  327. package/src/demo/pages/ResizeHandle/components/HorizontalResizeDemo.module.css +61 -0
  328. package/src/demo/pages/ResizeHandle/components/HorizontalResizeDemo.tsx +33 -0
  329. package/src/demo/pages/ResizeHandle/components/NestedPanelsDemo.module.css +83 -0
  330. package/src/demo/pages/ResizeHandle/components/NestedPanelsDemo.tsx +53 -0
  331. package/src/demo/pages/ResizeHandle/components/VerticalResizeDemo.module.css +68 -0
  332. package/src/demo/pages/ResizeHandle/components/VerticalResizeDemo.tsx +33 -0
  333. package/src/demo/pages/ResizeHandle/horizontal/index.tsx +17 -0
  334. package/src/demo/pages/ResizeHandle/index.module.css +11 -0
  335. package/src/demo/pages/ResizeHandle/index.tsx +71 -0
  336. package/src/demo/pages/ResizeHandle/nested-panels/index.tsx +17 -0
  337. package/src/demo/pages/ResizeHandle/vertical/index.tsx +17 -0
  338. package/src/demo/pages/ResponsiveLayout/adaptive-workspace/index.tsx +22 -0
  339. package/src/demo/pages/ResponsiveLayout/components/ResponsiveWorkspace.module.css +423 -0
  340. package/src/demo/pages/ResponsiveLayout/components/ResponsiveWorkspace.tsx +398 -0
  341. package/src/demo/pages/Stack/basics/index.tsx +22 -0
  342. package/src/demo/pages/Stack/components/Stack.module.css +234 -0
  343. package/src/demo/pages/Stack/components/StackBasics.spec.tsx +152 -0
  344. package/src/demo/pages/Stack/components/StackBasics.tsx +301 -0
  345. package/src/demo/pages/Stack/components/StackTablet.module.css +299 -0
  346. package/src/demo/pages/Stack/components/StackTablet.spec.tsx +120 -0
  347. package/src/demo/pages/Stack/components/StackTablet.tsx +422 -0
  348. package/src/demo/pages/Stack/tablet/index.tsx +22 -0
  349. package/src/demo/pages/StickyHeader/basics/index.tsx +17 -0
  350. package/src/demo/pages/StickyHeader/components/StickyHeader.module.css +219 -0
  351. package/src/demo/pages/StickyHeader/components/StickyHeaderBasics.tsx +103 -0
  352. package/src/demo/routes.tsx +214 -0
  353. package/src/demo/styles/animations.css +68 -0
  354. package/src/demo/styles/stack-themes.css +35 -0
  355. package/src/demo/utils/createPanelView.tsx +58 -0
  356. package/src/dialog/index.ts +85 -0
  357. package/src/floating/index.ts +24 -0
  358. package/src/grid/index.ts +75 -0
  359. package/src/hooks/ContentCacheContext.tsx +87 -0
  360. package/src/hooks/gesture/presets.spec.ts +86 -0
  361. package/src/hooks/gesture/presets.ts +95 -0
  362. package/src/hooks/gesture/testing/createGestureSimulator.spec.ts +241 -0
  363. package/src/hooks/gesture/testing/createGestureSimulator.ts +385 -0
  364. package/src/hooks/gesture/thresholdValue.spec.ts +103 -0
  365. package/src/hooks/gesture/thresholdValue.ts +77 -0
  366. package/src/hooks/gesture/types.ts +367 -0
  367. package/src/hooks/gesture/useDirectionalLock.spec.ts +271 -0
  368. package/src/hooks/gesture/useDirectionalLock.ts +115 -0
  369. package/src/hooks/gesture/useEdgeSwipeInput.spec.ts +462 -0
  370. package/src/hooks/gesture/useEdgeSwipeInput.ts +131 -0
  371. package/src/hooks/gesture/useNativeGestureGuard.spec.ts +473 -0
  372. package/src/hooks/gesture/useNativeGestureGuard.ts +135 -0
  373. package/src/hooks/gesture/usePointerTracking.spec.ts +364 -0
  374. package/src/hooks/gesture/usePointerTracking.ts +134 -0
  375. package/src/hooks/gesture/useScrollBoundary.spec.ts +249 -0
  376. package/src/hooks/gesture/useScrollBoundary.ts +113 -0
  377. package/src/hooks/gesture/useSwipeInput.spec.ts +592 -0
  378. package/src/hooks/gesture/useSwipeInput.ts +310 -0
  379. package/src/hooks/gesture/utils.spec.ts +152 -0
  380. package/src/hooks/gesture/utils.ts +178 -0
  381. package/src/hooks/useAnimatedVisibility.spec.ts +277 -0
  382. package/src/hooks/useAnimatedVisibility.ts +172 -0
  383. package/src/hooks/useAnimationFrame.ts +208 -0
  384. package/src/hooks/useCSSMatrix.spec.ts +214 -0
  385. package/src/hooks/useCSSMatrix.ts +262 -0
  386. package/src/hooks/useClonedElementPreview.ts +28 -0
  387. package/src/hooks/useContainerScroll.ts +78 -0
  388. package/src/hooks/useContentCache.spec.tsx +232 -0
  389. package/src/hooks/useContentCache.tsx +127 -0
  390. package/src/hooks/useDocumentPointerEvents.ts +137 -0
  391. package/src/hooks/useDocumentScroll.ts +41 -0
  392. package/src/hooks/useEffectEvent.ts +40 -0
  393. package/src/hooks/useElementComponentWrapper.tsx +63 -0
  394. package/src/hooks/useIntersectionObserver.tsx +125 -0
  395. package/src/hooks/useIsomorphicLayoutEffect.ts +29 -0
  396. package/src/hooks/useOperationContinuity.spec.ts +387 -0
  397. package/src/hooks/useOperationContinuity.ts +135 -0
  398. package/src/hooks/useResizeObserver.spec.tsx +277 -0
  399. package/src/hooks/useResizeObserver.tsx +150 -0
  400. package/src/hooks/useScrollContainer.ts +73 -0
  401. package/src/hooks/useSharedElementTransition.ts +333 -0
  402. package/src/hooks/useSnapAnimation.ts +128 -0
  403. package/src/hooks/useSwipeContentTransform.spec.ts +133 -0
  404. package/src/hooks/useSwipeContentTransform.ts +373 -0
  405. package/src/hooks/useTransitionState.ts +95 -0
  406. package/src/index.tsx +88 -0
  407. package/src/modules/dialog/AlertDialog.spec.tsx +387 -0
  408. package/src/modules/dialog/AlertDialog.tsx +221 -0
  409. package/src/modules/dialog/DialogContainer.spec.tsx +228 -0
  410. package/src/modules/dialog/DialogContainer.tsx +188 -0
  411. package/src/modules/dialog/Modal.spec.tsx +220 -0
  412. package/src/modules/dialog/Modal.tsx +182 -0
  413. package/src/modules/dialog/SwipeDialogContainer.tsx +208 -0
  414. package/src/modules/dialog/dialogAnimationUtils.spec.ts +253 -0
  415. package/src/modules/dialog/dialogAnimationUtils.ts +297 -0
  416. package/src/modules/dialog/types.ts +186 -0
  417. package/src/modules/dialog/useDialog.spec.tsx +447 -0
  418. package/src/modules/dialog/useDialog.ts +214 -0
  419. package/src/modules/dialog/useDialogContainer.spec.ts +331 -0
  420. package/src/modules/dialog/useDialogContainer.ts +150 -0
  421. package/src/modules/dialog/useDialogSwipeInput.spec.ts +157 -0
  422. package/src/modules/dialog/useDialogSwipeInput.ts +319 -0
  423. package/src/modules/dialog/useDialogTransform.spec.ts +370 -0
  424. package/src/modules/dialog/useDialogTransform.ts +407 -0
  425. package/src/modules/drawer/types.ts +102 -0
  426. package/src/modules/drawer/useDrawerSwipeInput.spec.ts +566 -0
  427. package/src/modules/drawer/useDrawerSwipeInput.ts +399 -0
  428. package/src/modules/grid/GridLayoutContext.tsx +57 -0
  429. package/src/modules/grid/LayerInstanceContext.tsx +56 -0
  430. package/src/modules/grid/resizeHandles.ts +157 -0
  431. package/src/modules/grid/trackUtils.ts +146 -0
  432. package/src/modules/grid/useGridPlacements.ts +143 -0
  433. package/src/modules/grid/useGridTracks.ts +156 -0
  434. package/src/modules/grid/useLayerDragHandle.ts +16 -0
  435. package/src/modules/grid/useLayerInteractions.tsx +850 -0
  436. package/src/modules/keybindings/KeybindingsProvider.tsx +111 -0
  437. package/src/modules/panels/dom/DomRegistry.tsx +94 -0
  438. package/src/modules/panels/index.ts +45 -0
  439. package/src/modules/panels/interactions/InteractionsContext.test.tsx +330 -0
  440. package/src/modules/panels/interactions/InteractionsContext.tsx +394 -0
  441. package/src/modules/panels/interactions/dnd.ts +28 -0
  442. package/src/modules/panels/keybindings/KeybindingsInstaller.tsx +15 -0
  443. package/src/modules/panels/layout/adapter.ts +124 -0
  444. package/src/modules/panels/rendering/ContentRegistry.spec.tsx +311 -0
  445. package/src/modules/panels/rendering/ContentRegistry.tsx +205 -0
  446. package/src/modules/panels/rendering/GroupContainer.tsx +65 -0
  447. package/src/modules/panels/rendering/RenderBridge.tsx +115 -0
  448. package/src/modules/panels/rendering/RenderContext.tsx +31 -0
  449. package/src/modules/panels/state/PanelSplitHandles.tsx +147 -0
  450. package/src/modules/panels/state/PanelSystemContext.splitLimits.spec.tsx +50 -0
  451. package/src/modules/panels/state/PanelSystemContext.tsx +289 -0
  452. package/src/modules/panels/state/StateContext.tsx +12 -0
  453. package/src/modules/panels/state/cleanup.ts +37 -0
  454. package/src/modules/panels/state/commands.ts +53 -0
  455. package/src/modules/panels/state/focus/Context.tsx +25 -0
  456. package/src/modules/panels/state/focus/logic.ts +57 -0
  457. package/src/modules/panels/state/groups/Context.tsx +25 -0
  458. package/src/modules/panels/state/groups/logic.ts +105 -0
  459. package/src/modules/panels/state/splitLimits.spec.ts +46 -0
  460. package/src/modules/panels/state/splitLimits.ts +90 -0
  461. package/src/modules/panels/state/state.spec.ts +49 -0
  462. package/src/modules/panels/state/tree/Context.tsx +24 -0
  463. package/src/modules/panels/state/tree/logic.spec.ts +34 -0
  464. package/src/modules/panels/state/tree/logic.ts +138 -0
  465. package/src/modules/panels/state/types.ts +142 -0
  466. package/src/modules/panels/system/PanelSystem.empty-tabbar.spec.tsx +53 -0
  467. package/src/modules/panels/system/PanelSystem.tab-click-activates.spec.tsx +44 -0
  468. package/src/modules/panels/system/PanelSystem.tab-reorder.spec.tsx +64 -0
  469. package/src/modules/panels/system/PanelSystem.tabs-no-dup.spec.tsx +57 -0
  470. package/src/modules/panels/system/PanelSystem.tsx +206 -0
  471. package/src/modules/pivot/PivotContent.spec.tsx +179 -0
  472. package/src/modules/pivot/PivotContent.tsx +77 -0
  473. package/src/modules/pivot/SwipePivotContent.debug.tmp.tsx +237 -0
  474. package/src/modules/pivot/SwipePivotContent.position.spec.tsx +171 -0
  475. package/src/modules/pivot/SwipePivotContent.spec.tsx +494 -0
  476. package/src/modules/pivot/SwipePivotContent.test.tsx +502 -0
  477. package/src/modules/pivot/SwipePivotContent.tsx +197 -0
  478. package/src/modules/pivot/SwipePivotTabBar.spec.tsx +882 -0
  479. package/src/modules/pivot/SwipePivotTabBar.tsx +583 -0
  480. package/src/modules/pivot/index.ts +8 -0
  481. package/src/modules/pivot/scaleInputState.spec.ts +219 -0
  482. package/src/modules/pivot/scaleInputState.ts +66 -0
  483. package/src/modules/pivot/types.ts +139 -0
  484. package/src/modules/pivot/usePivot.spec.ts +635 -0
  485. package/src/modules/pivot/usePivot.spec.tsx +186 -0
  486. package/src/modules/pivot/usePivot.tsx +345 -0
  487. package/src/modules/pivot/usePivotSwipeInput.spec.ts +708 -0
  488. package/src/modules/pivot/usePivotSwipeInput.ts +136 -0
  489. package/src/modules/resizer/useResizeDrag.ts +94 -0
  490. package/src/modules/stack/StackContent.spec.tsx +264 -0
  491. package/src/modules/stack/StackContent.tsx +111 -0
  492. package/src/modules/stack/SwipeStackContent.spec.tsx +1564 -0
  493. package/src/modules/stack/SwipeStackContent.tsx +366 -0
  494. package/src/modules/stack/SwipeStackOutlet.spec.tsx +250 -0
  495. package/src/modules/stack/SwipeStackOutlet.tsx +221 -0
  496. package/src/modules/stack/computeStackContentState.spec.ts +281 -0
  497. package/src/modules/stack/computeStackContentState.ts +304 -0
  498. package/src/modules/stack/computeSwipeStackTransform.spec.ts +186 -0
  499. package/src/modules/stack/computeSwipeStackTransform.ts +145 -0
  500. package/src/modules/stack/swipeTransitionContinuity.spec.tsx +1133 -0
  501. package/src/modules/stack/types.ts +226 -0
  502. package/src/modules/stack/useStackAnimationState.spec.ts +188 -0
  503. package/src/modules/stack/useStackAnimationState.ts +143 -0
  504. package/src/modules/stack/useStackNavigation.spec.ts +672 -0
  505. package/src/modules/stack/useStackNavigation.tsx +393 -0
  506. package/src/modules/stack/useStackSwipeInput.spec.ts +309 -0
  507. package/src/modules/stack/useStackSwipeInput.ts +139 -0
  508. package/src/modules/window/useDrawerState.ts +81 -0
  509. package/src/modules/window/useFloatingState.spec.ts +252 -0
  510. package/src/modules/window/useFloatingState.ts +141 -0
  511. package/src/panels/index.ts +119 -0
  512. package/src/pivot/index.ts +19 -0
  513. package/src/resizer/index.ts +68 -0
  514. package/src/stack/index.ts +91 -0
  515. package/src/sticky-header/StickyArea.tsx +193 -0
  516. package/src/sticky-header/calculateStickyMetrics.spec.ts +105 -0
  517. package/src/sticky-header/calculateStickyMetrics.ts +50 -0
  518. package/src/sticky-header/index.ts +18 -0
  519. package/src/sticky-header/types.ts +68 -0
  520. package/src/types.ts +341 -0
  521. package/src/utils/CSSMatrix.ts +321 -0
  522. package/src/utils/css.ts +65 -0
  523. package/src/utils/dialogUtils.ts +43 -0
  524. package/src/utils/math.ts +18 -0
  525. package/src/utils/polyfills/createDialogPolyfill.ts +18 -0
  526. package/src/utils/typedActions.ts +103 -0
  527. package/src/vite-env.d.ts +6 -0
  528. package/src/window/index.ts +69 -0
  529. package/dist/GridLayout-BQQ63eA1.cjs +0 -2
  530. package/dist/GridLayout-BQQ63eA1.cjs.map +0 -1
  531. package/dist/GridLayout-CJTKq7Mp.js +0 -1465
  532. package/dist/GridLayout-CJTKq7Mp.js.map +0 -1
  533. package/dist/sticky-header/StickyHeader.d.ts +0 -53
  534. package/dist/styles-CA2_zLZt.js +0 -52
  535. package/dist/styles-CA2_zLZt.js.map +0 -1
  536. package/dist/styles-PsqGOEJP.cjs +0 -2
  537. package/dist/styles-PsqGOEJP.cjs.map +0 -1
  538. package/dist/usePivot-7ctin_P_.cjs +0 -2
  539. package/dist/usePivot-7ctin_P_.cjs.map +0 -1
  540. package/dist/usePivot-CgQxB8rc.js +0 -124
  541. package/dist/usePivot-CgQxB8rc.js.map +0 -1
@@ -0,0 +1,366 @@
1
+ /**
2
+ * @file SwipeStackContent component for Stack panels with direct DOM manipulation.
3
+ *
4
+ * Provides iOS-style swipe-to-go-back behavior:
5
+ * - Active panel follows the finger directly
6
+ * - Behind panel reveals from -30% with parallax effect
7
+ *
8
+ * Uses useSwipeContentTransform for immediate DOM updates.
9
+ */
10
+ import * as React from "react";
11
+ import { useSwipeContentTransform } from "../../hooks/useSwipeContentTransform.js";
12
+ import { useOperationContinuity } from "../../hooks/useOperationContinuity.js";
13
+ import type { ContinuousOperationState, GestureAxis } from "../../hooks/gesture/types.js";
14
+ import type { StackDisplayMode } from "./types.js";
15
+ import {
16
+ computeActiveTargetPx,
17
+ computeBehindTargetPx,
18
+ computeSwipeVisibility,
19
+ determineSwipePanelRole,
20
+ DEFAULT_BEHIND_OFFSET,
21
+ } from "./computeSwipeStackTransform.js";
22
+
23
+ const DEFAULT_ANIMATION_DURATION = 300;
24
+
25
+ /**
26
+ * Scale factor per depth level for "stack" display mode.
27
+ * Each level behind reduces scale by this amount.
28
+ */
29
+ const STACK_SCALE_FACTOR = 0.05;
30
+
31
+ /**
32
+ * Maximum dimming opacity for behind panels in iOS-style navigation.
33
+ */
34
+ const MAX_DIM_OPACITY = 0.1;
35
+
36
+ /**
37
+ * Props for SwipeStackContent component.
38
+ *
39
+ * This component accepts ContinuousOperationState, meaning it responds uniformly
40
+ * to any continuous operation (whether human gesture or system animation).
41
+ */
42
+ export type SwipeStackContentProps = {
43
+ /** Panel ID */
44
+ id: string;
45
+ /** Panel depth in the stack */
46
+ depth: number;
47
+ /** Current navigation depth (active panel) */
48
+ navigationDepth: number;
49
+ /** Whether this panel is currently active */
50
+ isActive: boolean;
51
+ /** Continuous operation state (from gesture input or animation system) */
52
+ operationState: ContinuousOperationState;
53
+ /** Container size in pixels (width for horizontal, height for vertical) */
54
+ containerSize: number;
55
+ /** Gesture axis. @default "horizontal" */
56
+ axis?: GestureAxis;
57
+ /** Behind panel offset ratio. @default -0.3 */
58
+ behindOffset?: number;
59
+ /** Animation duration in ms. @default 300 */
60
+ animationDuration?: number;
61
+ /**
62
+ * Whether to animate when first mounted as active.
63
+ * Set to true for push navigation animations.
64
+ * @default false
65
+ */
66
+ animateOnMount?: boolean;
67
+ /**
68
+ * Whether to show iOS-style edge shadow on active panel.
69
+ * @default true
70
+ */
71
+ showShadow?: boolean;
72
+ /**
73
+ * Display mode for visual styling.
74
+ * - "overlay": panels overlay, no scale (iOS style)
75
+ * - "slide": panels slide with parallax
76
+ * - "stack": panels scale down and offset (stacked cards style)
77
+ * @default "overlay"
78
+ */
79
+ displayMode?: StackDisplayMode;
80
+ /**
81
+ * Whether to show dimming overlay on behind panels.
82
+ * Creates iOS-style darkening effect that fades during swipe.
83
+ * @default true
84
+ */
85
+ showDimming?: boolean;
86
+ /** Content to render */
87
+ children: React.ReactNode;
88
+ };
89
+
90
+ const BASE_STYLE: React.CSSProperties = {
91
+ position: "absolute",
92
+ inset: 0,
93
+ width: "100%",
94
+ height: "100%",
95
+ };
96
+
97
+ /**
98
+ * Get displacement from operation state for the given axis.
99
+ */
100
+ const getAxisDisplacement = (state: ContinuousOperationState, axis: GestureAxis): number => {
101
+ if (state.phase === "idle") {
102
+ return 0;
103
+ }
104
+ return axis === "horizontal" ? state.displacement.x : state.displacement.y;
105
+ };
106
+
107
+ /**
108
+ * SwipeStackContent renders a single stack panel with swipe gesture support.
109
+ *
110
+ * Key behaviors:
111
+ * - Active panel: follows finger directly (translateX = displacement)
112
+ * - Behind panel: reveals from -30% with parallax (slower movement)
113
+ * - Hidden panels: not rendered during swipe
114
+ *
115
+ * @example
116
+ * ```tsx
117
+ * <SwipeStackContent
118
+ * id="detail"
119
+ * depth={1}
120
+ * navigationDepth={1}
121
+ * isActive={true}
122
+ * inputState={swipeInput.inputState}
123
+ * containerSize={containerWidth}
124
+ * >
125
+ * <DetailPanel />
126
+ * </SwipeStackContent>
127
+ * ```
128
+ */
129
+ // iOS-style left edge shadow for active panels
130
+ const ACTIVE_PANEL_SHADOW = "-5px 0 15px rgba(0, 0, 0, 0.1)";
131
+
132
+ export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
133
+ ({
134
+ id,
135
+ depth,
136
+ navigationDepth,
137
+ isActive,
138
+ operationState,
139
+ containerSize,
140
+ axis = "horizontal",
141
+ behindOffset = DEFAULT_BEHIND_OFFSET,
142
+ animationDuration = DEFAULT_ANIMATION_DURATION,
143
+ animateOnMount = false,
144
+ showShadow = true,
145
+ displayMode = "overlay",
146
+ showDimming = true,
147
+ children,
148
+ }) => {
149
+ const elementRef = React.useRef<HTMLDivElement>(null);
150
+
151
+ const displacement = getAxisDisplacement(operationState, axis);
152
+ const isOperating = operationState.phase === "operating";
153
+
154
+ // Determine panel role
155
+ const role = determineSwipePanelRole(depth, navigationDepth);
156
+
157
+ // Maintain role continuity during swipe operations.
158
+ // When navigation changes before the gesture ends (e.g., role changes from
159
+ // "behind" to "active"), we keep using the previous role for position
160
+ // calculations to prevent visual jumps.
161
+ // changedDuringOperation tells us if the role changed during the operation,
162
+ // which we use to skip backward target change animation (over-swipe case).
163
+ const { value: effectiveRole, changedDuringOperation } = useOperationContinuity(
164
+ role,
165
+ displacement > 0,
166
+ );
167
+
168
+ // Compute target position based on effective role
169
+ const targetPx = React.useMemo(() => {
170
+ switch (effectiveRole) {
171
+ case "active":
172
+ // Active panel rests at 0
173
+ return 0;
174
+ case "behind":
175
+ // Behind panel rests at offset position
176
+ return behindOffset * containerSize;
177
+ case "hidden":
178
+ // Hidden panels are off-screen
179
+ return containerSize;
180
+ }
181
+ }, [effectiveRole, behindOffset, containerSize]);
182
+
183
+ // Compute displacement for this panel
184
+ const panelDisplacement = React.useMemo(() => {
185
+ if (displacement <= 0) {
186
+ return 0;
187
+ }
188
+
189
+ switch (effectiveRole) {
190
+ case "active":
191
+ // Active panel follows finger directly
192
+ return computeActiveTargetPx(displacement);
193
+ case "behind": {
194
+ // Behind panel uses parallax - compute offset from base position
195
+ const currentPos = computeBehindTargetPx(displacement, containerSize, behindOffset);
196
+ const basePos = behindOffset * containerSize;
197
+ return currentPos - basePos;
198
+ }
199
+ case "hidden":
200
+ return 0;
201
+ }
202
+ }, [effectiveRole, displacement, containerSize, behindOffset]);
203
+
204
+ // Compute initial position for push animation
205
+ // When animateOnMount is true and panel is first mounted as "active",
206
+ // it should animate in from off-screen
207
+ // Root panel (depth=0) should not animate on mount
208
+ // Note: useSwipeContentTransform handles first-mount tracking internally,
209
+ // so we just declare the initial position; the hook consumes it only once.
210
+ const initialPx = React.useMemo(() => {
211
+ if (!animateOnMount) {
212
+ return undefined;
213
+ }
214
+ if (role === "active" && depth > 0) {
215
+ // New active panel (not root): start from off-screen right
216
+ return containerSize;
217
+ }
218
+ // Root panel or other roles: start at their natural position
219
+ return undefined;
220
+ }, [animateOnMount, role, depth, containerSize]);
221
+
222
+ // Use shared transform hook for DOM manipulation
223
+ const { isAnimating } = useSwipeContentTransform({
224
+ elementRef,
225
+ targetPx,
226
+ displacement: panelDisplacement,
227
+ isOperating,
228
+ axis,
229
+ animationDuration,
230
+ containerSize,
231
+ // Animate when targetPx changes (button navigation)
232
+ animateOnTargetChange: true,
233
+ // For push animation: start from off-screen
234
+ initialPx,
235
+ // Skip backward animation if role changed during the operation.
236
+ // This handles over-swipe where panel moves beyond 100% and needs to snap back.
237
+ // useSwipeContentTransform allows forward animations (normal swipe-to-complete)
238
+ // but skips backward animations (over-swipe snap).
239
+ skipTargetChangeAnimation: changedDuringOperation,
240
+ });
241
+
242
+ // Compute visibility
243
+ const visible = computeSwipeVisibility({
244
+ depth,
245
+ navigationDepth,
246
+ isActive,
247
+ isOperating,
248
+ isAnimating,
249
+ });
250
+
251
+ // Compute swipe progress for scale and dimming interpolation
252
+ const swipeProgress = React.useMemo(() => {
253
+ if (containerSize <= 0 || displacement <= 0) {
254
+ return 0;
255
+ }
256
+ return Math.min(displacement / containerSize, 1);
257
+ }, [displacement, containerSize]);
258
+
259
+ // Compute scale for "stack" display mode
260
+ // Behind panels are scaled down, and scale interpolates during swipe
261
+ const scale = React.useMemo(() => {
262
+ if (displayMode !== "stack") {
263
+ return 1; // No scale for overlay/slide modes
264
+ }
265
+
266
+ const depthDiff = navigationDepth - depth;
267
+
268
+ if (role === "active") {
269
+ return 1; // Active panel is always at full scale
270
+ }
271
+
272
+ if (role === "behind") {
273
+ // Base scale for behind panel
274
+ const baseScale = 1 - depthDiff * STACK_SCALE_FACTOR;
275
+ // During swipe, interpolate toward 1
276
+ return baseScale + swipeProgress * (1 - baseScale);
277
+ }
278
+
279
+ return 1;
280
+ }, [displayMode, role, depth, navigationDepth, swipeProgress]);
281
+
282
+ // Compute dimming opacity for behind panels
283
+ // Full dimming at rest, fades to 0 during swipe
284
+ const dimmingOpacity = React.useMemo(() => {
285
+ if (!showDimming || role !== "behind") {
286
+ return 0;
287
+ }
288
+ // Fade from MAX_DIM_OPACITY to 0 as swipe progresses
289
+ return MAX_DIM_OPACITY * (1 - swipeProgress);
290
+ }, [showDimming, role, swipeProgress]);
291
+
292
+ // Update visibility via direct DOM manipulation
293
+ React.useLayoutEffect(() => {
294
+ const element = elementRef.current;
295
+ if (element) {
296
+ element.style.visibility = visible ? "visible" : "hidden";
297
+ }
298
+ }, [visible]);
299
+
300
+ // Update scale via direct DOM manipulation for smooth animation
301
+ React.useLayoutEffect(() => {
302
+ const element = elementRef.current;
303
+ if (!element || displayMode !== "stack") {
304
+ return;
305
+ }
306
+
307
+ // Get current transform (translateX) and append scale
308
+ const currentTransform = element.style.transform;
309
+ if (currentTransform.includes("translateX")) {
310
+ // Extract translateX value and combine with scale
311
+ const translateMatch = currentTransform.match(/translateX\([^)]+\)/);
312
+ if (translateMatch) {
313
+ element.style.transform = `${translateMatch[0]} scale(${scale})`;
314
+ }
315
+ } else {
316
+ element.style.transform = `scale(${scale})`;
317
+ }
318
+ }, [scale, displayMode]);
319
+
320
+ // Compute shadow for active panel
321
+ // Shadow is shown on panels at depth > 0 when they're active or animating
322
+ const shouldShowShadow = showShadow ? depth > 0 && role === "active" : false;
323
+
324
+ // Static style - transform is handled entirely by useSwipeContentTransform
325
+ // to ensure smooth animations
326
+ const staticStyle = React.useMemo<React.CSSProperties>(
327
+ () => ({
328
+ ...BASE_STYLE,
329
+ pointerEvents: isActive ? "auto" : "none",
330
+ willChange: "transform",
331
+ zIndex: depth,
332
+ visibility: visible ? "visible" : "hidden",
333
+ boxShadow: shouldShowShadow ? ACTIVE_PANEL_SHADOW : undefined,
334
+ }),
335
+ [isActive, depth, visible, shouldShowShadow],
336
+ );
337
+
338
+ // Dimming overlay style for behind panels
339
+ const dimmingStyle = React.useMemo<React.CSSProperties | null>(() => {
340
+ if (dimmingOpacity <= 0) {
341
+ return null;
342
+ }
343
+ return {
344
+ position: "absolute",
345
+ inset: 0,
346
+ backgroundColor: `rgba(0, 0, 0, ${dimmingOpacity})`,
347
+ pointerEvents: "none",
348
+ zIndex: 1,
349
+ };
350
+ }, [dimmingOpacity]);
351
+
352
+ return (
353
+ <div
354
+ ref={elementRef}
355
+ data-stack-content={id}
356
+ data-depth={depth}
357
+ data-active={isActive ? "true" : "false"}
358
+ data-role={role}
359
+ style={staticStyle}
360
+ >
361
+ {children}
362
+ {dimmingStyle != null ? <div style={dimmingStyle} data-dimming-overlay /> : null}
363
+ </div>
364
+ );
365
+ },
366
+ );
@@ -0,0 +1,250 @@
1
+ /**
2
+ * @file Tests for SwipeStackOutlet component.
3
+ */
4
+ import { render } from "@testing-library/react";
5
+ import { SwipeStackOutlet } from "./SwipeStackOutlet.js";
6
+ import type { ContinuousOperationState } from "../../hooks/gesture/types.js";
7
+ import type { StackPanel, StackNavigationState } from "./types.js";
8
+
9
+ const IDLE_STATE: ContinuousOperationState = {
10
+ phase: "idle",
11
+ displacement: { x: 0, y: 0 },
12
+ velocity: { x: 0, y: 0 },
13
+ };
14
+
15
+ const createOperatingState = (displacementX: number): ContinuousOperationState => ({
16
+ phase: "operating",
17
+ displacement: { x: displacementX, y: 0 },
18
+ velocity: { x: 0.5, y: 0 },
19
+ });
20
+
21
+ const createPanels = (): StackPanel[] => [
22
+ { id: "home", title: "Home", content: <div>Home Content</div> },
23
+ { id: "list", title: "List", content: <div>List Content</div> },
24
+ { id: "detail", title: "Detail", content: <div>Detail Content</div> },
25
+ ];
26
+
27
+ describe("SwipeStackOutlet", () => {
28
+ describe("rendering", () => {
29
+ it("renders container with correct attributes", () => {
30
+ const navigationState: StackNavigationState = {
31
+ stack: ["home"],
32
+ depth: 0,
33
+ isRevealing: false,
34
+ revealDepth: null,
35
+ };
36
+
37
+ const { container } = render(
38
+ <SwipeStackOutlet
39
+ panels={createPanels()}
40
+ navigationState={navigationState}
41
+ operationState={IDLE_STATE}
42
+ containerSize={400}
43
+ />,
44
+ );
45
+
46
+ const stackContainer = container.querySelector("[data-swipe-stack-container]");
47
+ expect(stackContainer).toBeInTheDocument();
48
+ });
49
+
50
+ it("renders only active panel at root depth", () => {
51
+ const navigationState: StackNavigationState = {
52
+ stack: ["home"],
53
+ depth: 0,
54
+ isRevealing: false,
55
+ revealDepth: null,
56
+ };
57
+
58
+ const { container, getByText } = render(
59
+ <SwipeStackOutlet
60
+ panels={createPanels()}
61
+ navigationState={navigationState}
62
+ operationState={IDLE_STATE}
63
+ containerSize={400}
64
+ />,
65
+ );
66
+
67
+ expect(getByText("Home Content")).toBeInTheDocument();
68
+
69
+ // Should only have one SwipeStackContent
70
+ const stackContents = container.querySelectorAll("[data-stack-content]");
71
+ expect(stackContents).toHaveLength(1);
72
+ });
73
+
74
+ it("renders active and behind panels when depth > 0", () => {
75
+ const navigationState: StackNavigationState = {
76
+ stack: ["home", "list"],
77
+ depth: 1,
78
+ isRevealing: false,
79
+ revealDepth: null,
80
+ };
81
+
82
+ const { container, getByText } = render(
83
+ <SwipeStackOutlet
84
+ panels={createPanels()}
85
+ navigationState={navigationState}
86
+ operationState={IDLE_STATE}
87
+ containerSize={400}
88
+ />,
89
+ );
90
+
91
+ expect(getByText("Home Content")).toBeInTheDocument();
92
+ expect(getByText("List Content")).toBeInTheDocument();
93
+
94
+ const stackContents = container.querySelectorAll("[data-stack-content]");
95
+ expect(stackContents).toHaveLength(2);
96
+ });
97
+
98
+ it("assigns correct roles to panels", () => {
99
+ const navigationState: StackNavigationState = {
100
+ stack: ["home", "list"],
101
+ depth: 1,
102
+ isRevealing: false,
103
+ revealDepth: null,
104
+ };
105
+
106
+ const { container } = render(
107
+ <SwipeStackOutlet
108
+ panels={createPanels()}
109
+ navigationState={navigationState}
110
+ operationState={IDLE_STATE}
111
+ containerSize={400}
112
+ />,
113
+ );
114
+
115
+ const homePanel = container.querySelector('[data-stack-content="home"]');
116
+ const listPanel = container.querySelector('[data-stack-content="list"]');
117
+
118
+ expect(homePanel?.getAttribute("data-role")).toBe("behind");
119
+ expect(listPanel?.getAttribute("data-role")).toBe("active");
120
+ });
121
+ });
122
+
123
+ describe("swipe behavior", () => {
124
+ it("passes input state to panels during swipe", () => {
125
+ const navigationState: StackNavigationState = {
126
+ stack: ["home", "list"],
127
+ depth: 1,
128
+ isRevealing: false,
129
+ revealDepth: null,
130
+ };
131
+
132
+ const swipeState = createOperatingState(200);
133
+
134
+ const { container, rerender } = render(
135
+ <SwipeStackOutlet
136
+ panels={createPanels()}
137
+ navigationState={navigationState}
138
+ operationState={IDLE_STATE}
139
+ containerSize={400}
140
+ />,
141
+ );
142
+
143
+ // Start swiping
144
+ rerender(
145
+ <SwipeStackOutlet
146
+ panels={createPanels()}
147
+ navigationState={navigationState}
148
+ operationState={swipeState}
149
+ containerSize={400}
150
+ />,
151
+ );
152
+
153
+ const listPanel = container.querySelector('[data-stack-content="list"]') as HTMLElement;
154
+ const homePanel = container.querySelector('[data-stack-content="home"]') as HTMLElement;
155
+
156
+ // Active panel should be at displacement position
157
+ expect(listPanel.style.transform).toBe("translateX(200px)");
158
+
159
+ // Behind panel should be at parallax position (-60px, halfway from -120 to 0)
160
+ expect(homePanel.style.transform).toBe("translateX(-60px)");
161
+ });
162
+ });
163
+
164
+ describe("cached content", () => {
165
+ it("uses getCachedContent when provided", () => {
166
+ const navigationState: StackNavigationState = {
167
+ stack: ["home"],
168
+ depth: 0,
169
+ isRevealing: false,
170
+ revealDepth: null,
171
+ };
172
+
173
+ const calledWith: string[] = [];
174
+ const getCachedContent = (panelId: string): React.ReactNode => {
175
+ calledWith.push(panelId);
176
+ if (panelId === "home") {
177
+ return <div>Cached Home Content</div>;
178
+ }
179
+ return null;
180
+ };
181
+
182
+ const { getByText, queryByText } = render(
183
+ <SwipeStackOutlet
184
+ panels={createPanels()}
185
+ navigationState={navigationState}
186
+ operationState={IDLE_STATE}
187
+ containerSize={400}
188
+ getCachedContent={getCachedContent}
189
+ />,
190
+ );
191
+
192
+ expect(getByText("Cached Home Content")).toBeInTheDocument();
193
+ expect(queryByText("Home Content")).not.toBeInTheDocument();
194
+ expect(calledWith).toContain("home");
195
+ });
196
+
197
+ it("falls back to panel content when getCachedContent returns null", () => {
198
+ const navigationState: StackNavigationState = {
199
+ stack: ["home"],
200
+ depth: 0,
201
+ isRevealing: false,
202
+ revealDepth: null,
203
+ };
204
+
205
+ const getCachedContent = (): React.ReactNode => null;
206
+
207
+ const { getByText } = render(
208
+ <SwipeStackOutlet
209
+ panels={createPanels()}
210
+ navigationState={navigationState}
211
+ operationState={IDLE_STATE}
212
+ containerSize={400}
213
+ getCachedContent={getCachedContent}
214
+ />,
215
+ );
216
+
217
+ expect(getByText("Home Content")).toBeInTheDocument();
218
+ });
219
+ });
220
+
221
+ describe("deep navigation", () => {
222
+ it("only renders active and behind panels, not deeper ones", () => {
223
+ const navigationState: StackNavigationState = {
224
+ stack: ["home", "list", "detail"],
225
+ depth: 2,
226
+ isRevealing: false,
227
+ revealDepth: null,
228
+ };
229
+
230
+ const { container, getByText, queryByText } = render(
231
+ <SwipeStackOutlet
232
+ panels={createPanels()}
233
+ navigationState={navigationState}
234
+ operationState={IDLE_STATE}
235
+ containerSize={400}
236
+ />,
237
+ );
238
+
239
+ // Should render detail (active) and list (behind)
240
+ expect(getByText("Detail Content")).toBeInTheDocument();
241
+ expect(getByText("List Content")).toBeInTheDocument();
242
+
243
+ // Should NOT render home (too deep)
244
+ expect(queryByText("Home Content")).not.toBeInTheDocument();
245
+
246
+ const stackContents = container.querySelectorAll("[data-stack-content]");
247
+ expect(stackContents).toHaveLength(2);
248
+ });
249
+ });
250
+ });