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,179 @@
1
+ /**
2
+ * @file Tests for PivotContent component - animation behavior.
3
+ *
4
+ * TDD: Pivotのアニメーションが動作しない問題を再現するテスト
5
+ *
6
+ * 要件:
7
+ * 1. アクティブなパネルのみ表示、非アクティブは display: none
8
+ * 2. leaveアニメーション中は display: block を維持(アニメーション完了後に none)
9
+ * 3. 非アクティブパネルはポインターイベントを受け付けない
10
+ */
11
+ import { render, screen, fireEvent } from "@testing-library/react";
12
+ import * as React from "react";
13
+ import { PivotContent } from "./PivotContent.js";
14
+ import { PIVOT_ANIMATION_ENTER, PIVOT_ANIMATION_LEAVE } from "../../constants/styles.js";
15
+
16
+ describe("PivotContent", () => {
17
+ describe("display control", () => {
18
+ it("shows active content (display: block)", () => {
19
+ render(
20
+ <PivotContent id="test" isActive={true} transitionMode="css">
21
+ <div data-testid="content">Content</div>
22
+ </PivotContent>,
23
+ );
24
+
25
+ const wrapper = screen.getByTestId("content").parentElement;
26
+ expect(wrapper?.style?.display).toBe("block");
27
+ });
28
+
29
+ it("hides inactive content when initially inactive (display: none)", () => {
30
+ render(
31
+ <PivotContent id="test" isActive={false} transitionMode="css">
32
+ <div data-testid="content">Content</div>
33
+ </PivotContent>,
34
+ );
35
+
36
+ const wrapper = screen.getByTestId("content").parentElement;
37
+ expect(wrapper?.style?.display).toBe("none");
38
+ });
39
+
40
+ it("keeps content displayed during leave animation", () => {
41
+ const { rerender } = render(
42
+ <PivotContent id="test" isActive={true} transitionMode="css">
43
+ <div data-testid="content">Content</div>
44
+ </PivotContent>,
45
+ );
46
+
47
+ const wrapper = screen.getByTestId("content").parentElement;
48
+ expect(wrapper?.style?.display).toBe("block");
49
+
50
+ // 非アクティブに変更(leaveアニメーション開始)
51
+ rerender(
52
+ <PivotContent id="test" isActive={false} transitionMode="css">
53
+ <div data-testid="content">Content</div>
54
+ </PivotContent>,
55
+ );
56
+
57
+ // leaveアニメーション中は display: block を維持
58
+ expect(wrapper?.style?.display).toBe("block");
59
+ });
60
+
61
+ it("hides content after leave animation ends", () => {
62
+ const { rerender } = render(
63
+ <PivotContent id="test" isActive={true} transitionMode="css">
64
+ <div data-testid="content">Content</div>
65
+ </PivotContent>,
66
+ );
67
+
68
+ const wrapper = screen.getByTestId("content").parentElement!;
69
+
70
+ // 非アクティブに変更
71
+ rerender(
72
+ <PivotContent id="test" isActive={false} transitionMode="css">
73
+ <div data-testid="content">Content</div>
74
+ </PivotContent>,
75
+ );
76
+
77
+ // アニメーション完了をシミュレート
78
+ fireEvent.animationEnd(wrapper);
79
+
80
+ // アニメーション完了後は display: none
81
+ expect(wrapper.style.display).toBe("none");
82
+ });
83
+ });
84
+
85
+ describe("animation when transitionMode=css", () => {
86
+ it("applies enter animation when active", () => {
87
+ render(
88
+ <PivotContent id="test" isActive={true} transitionMode="css">
89
+ <div data-testid="content">Content</div>
90
+ </PivotContent>,
91
+ );
92
+
93
+ const wrapper = screen.getByTestId("content").parentElement;
94
+ expect(wrapper?.style?.animation).toBe(PIVOT_ANIMATION_ENTER);
95
+ });
96
+
97
+ it("applies leave animation when becoming inactive", () => {
98
+ const { rerender } = render(
99
+ <PivotContent id="test" isActive={true} transitionMode="css">
100
+ <div data-testid="content">Content</div>
101
+ </PivotContent>,
102
+ );
103
+
104
+ rerender(
105
+ <PivotContent id="test" isActive={false} transitionMode="css">
106
+ <div data-testid="content">Content</div>
107
+ </PivotContent>,
108
+ );
109
+
110
+ const wrapper = screen.getByTestId("content").parentElement;
111
+ expect(wrapper?.style?.animation).toBe(PIVOT_ANIMATION_LEAVE);
112
+ });
113
+ });
114
+
115
+ describe("no animation when transitionMode=none", () => {
116
+ it("does not apply animation property", () => {
117
+ render(
118
+ <PivotContent id="test" isActive={true} transitionMode="none">
119
+ <div data-testid="content">Content</div>
120
+ </PivotContent>,
121
+ );
122
+
123
+ const wrapper = screen.getByTestId("content").parentElement;
124
+ expect(wrapper?.style?.animation).toBeFalsy();
125
+ });
126
+
127
+ it("hides inactive content immediately (no animation)", () => {
128
+ render(
129
+ <PivotContent id="test" isActive={false} transitionMode="none">
130
+ <div data-testid="content">Content</div>
131
+ </PivotContent>,
132
+ );
133
+
134
+ const wrapper = screen.getByTestId("content").parentElement;
135
+ expect(wrapper?.style?.display).toBe("none");
136
+ });
137
+ });
138
+
139
+ describe("pointer events", () => {
140
+ it("enables pointer events for active content", () => {
141
+ render(
142
+ <PivotContent id="test" isActive={true} transitionMode="css">
143
+ <div data-testid="content">Content</div>
144
+ </PivotContent>,
145
+ );
146
+
147
+ const wrapper = screen.getByTestId("content").parentElement;
148
+ expect(wrapper?.style?.pointerEvents).toBe("auto");
149
+ });
150
+
151
+ it("disables pointer events for inactive content", () => {
152
+ render(
153
+ <PivotContent id="test" isActive={false} transitionMode="css">
154
+ <div data-testid="content">Content</div>
155
+ </PivotContent>,
156
+ );
157
+
158
+ const wrapper = screen.getByTestId("content").parentElement;
159
+ expect(wrapper?.style?.pointerEvents).toBe("none");
160
+ });
161
+
162
+ it("disables pointer events during leave animation", () => {
163
+ const { rerender } = render(
164
+ <PivotContent id="test" isActive={true} transitionMode="css">
165
+ <div data-testid="content">Content</div>
166
+ </PivotContent>,
167
+ );
168
+
169
+ rerender(
170
+ <PivotContent id="test" isActive={false} transitionMode="css">
171
+ <div data-testid="content">Content</div>
172
+ </PivotContent>,
173
+ );
174
+
175
+ const wrapper = screen.getByTestId("content").parentElement;
176
+ expect(wrapper?.style?.pointerEvents).toBe("none");
177
+ });
178
+ });
179
+ });
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @file PivotContent component for rendering pivot items with CSS animations.
3
+ *
4
+ * Override via CSS custom properties:
5
+ * - --rpl-pivot-animation-enter: Animation when becoming active
6
+ * - --rpl-pivot-animation-leave: Animation when becoming inactive
7
+ *
8
+ * User defines @keyframes in their CSS and references via these tokens.
9
+ * Example:
10
+ * @keyframes pivotEnter {
11
+ * from { opacity: 0; }
12
+ * to { opacity: 1; }
13
+ * }
14
+ * :root { --rpl-pivot-animation-enter: pivotEnter 150ms ease-out forwards; }
15
+ */
16
+ import * as React from "react";
17
+ import { PIVOT_ANIMATION_ENTER, PIVOT_ANIMATION_LEAVE } from "../../constants/styles";
18
+ import { useAnimatedVisibility } from "../../hooks/useAnimatedVisibility";
19
+
20
+ export type PivotContentProps = {
21
+ id: string;
22
+ isActive: boolean;
23
+ transitionMode: "css" | "none";
24
+ children: React.ReactNode;
25
+ };
26
+
27
+ const baseStyle: React.CSSProperties = {
28
+ position: "absolute",
29
+ inset: 0,
30
+ width: "100%",
31
+ height: "100%",
32
+ };
33
+
34
+ /**
35
+ * Renders pivot content with CSS animation support.
36
+ *
37
+ * When transitionMode="css": Applies enter/leave animations with display:none when hidden.
38
+ * When transitionMode="none": Uses React.Activity for memory optimization.
39
+ */
40
+ export const PivotContent: React.FC<PivotContentProps> = React.memo(({ id, isActive, transitionMode, children }) => {
41
+ const visibility = useAnimatedVisibility({
42
+ isVisible: isActive,
43
+ leaveAnimation: transitionMode === "css" ? PIVOT_ANIMATION_LEAVE : undefined,
44
+ skipAnimation: transitionMode !== "css",
45
+ });
46
+
47
+ const style = React.useMemo<React.CSSProperties>(() => {
48
+ const s: React.CSSProperties = {
49
+ ...baseStyle,
50
+ ...visibility.style,
51
+ pointerEvents: isActive ? "auto" : "none",
52
+ };
53
+
54
+ if (transitionMode === "css") {
55
+ s.animation = isActive ? PIVOT_ANIMATION_ENTER : PIVOT_ANIMATION_LEAVE;
56
+ }
57
+
58
+ return s;
59
+ }, [isActive, transitionMode, visibility.style]);
60
+
61
+ const content = (
62
+ <div
63
+ data-pivot-content={id}
64
+ data-active={isActive ? "true" : "false"}
65
+ style={style}
66
+ {...visibility.props}
67
+ >
68
+ {children}
69
+ </div>
70
+ );
71
+
72
+ if (transitionMode === "none") {
73
+ return <React.Activity mode={isActive ? "visible" : "hidden"}>{content}</React.Activity>;
74
+ }
75
+
76
+ return content;
77
+ });
@@ -0,0 +1,237 @@
1
+ /**
2
+ * @file Debug version of SwipePivotContent to investigate iOS swipe issue
3
+ *
4
+ * Issue: On iOS, when swiping pages, the next page sticks to the current position.
5
+ *
6
+ * Hypotheses to test:
7
+ * 1. currentPxRef initialization issue when position/containerSize changes
8
+ * 2. isAnimating timing issue (setIsAnimating is async)
9
+ * 3. useLayoutEffect execution order issue
10
+ * 4. pointercancel handling on iOS
11
+ */
12
+ import * as React from "react";
13
+ import { useAnimationFrame, interpolate, easings } from "../../hooks/useAnimationFrame.js";
14
+ import type { SwipeInputState, GestureAxis } from "../../hooks/gesture/types.js";
15
+
16
+ const DEFAULT_ANIMATION_DURATION = 300;
17
+
18
+ export type SwipePivotContentProps = {
19
+ id: string;
20
+ isActive: boolean;
21
+ position: -1 | 0 | 1;
22
+ inputState: SwipeInputState;
23
+ axis?: GestureAxis;
24
+ containerSize: number;
25
+ children: React.ReactNode;
26
+ canNavigate?: boolean;
27
+ animationDuration?: number;
28
+ };
29
+
30
+ const baseStyle: React.CSSProperties = {
31
+ position: "absolute",
32
+ inset: 0,
33
+ width: "100%",
34
+ height: "100%",
35
+ };
36
+
37
+ const getDisplacement = (inputState: SwipeInputState, axis: GestureAxis): number => {
38
+ if (inputState.phase === "idle") {
39
+ return 0;
40
+ }
41
+ return axis === "horizontal" ? inputState.displacement.x : inputState.displacement.y;
42
+ };
43
+
44
+ const shouldBeVisible = (
45
+ isActive: boolean,
46
+ position: -1 | 0 | 1,
47
+ inputState: SwipeInputState,
48
+ canNavigate: boolean,
49
+ isAnimating: boolean,
50
+ ): boolean => {
51
+ if (isActive) {
52
+ return true;
53
+ }
54
+ if (isAnimating) {
55
+ return true;
56
+ }
57
+ if (!canNavigate) {
58
+ return false;
59
+ }
60
+ if (inputState.phase === "idle") {
61
+ return false;
62
+ }
63
+ // Show adjacent content based on swipe direction
64
+ if (position === -1 && inputState.direction === 1) {
65
+ return true;
66
+ }
67
+ if (position === 1 && inputState.direction === -1) {
68
+ return true;
69
+ }
70
+ return false;
71
+ };
72
+
73
+ // Debug logging
74
+ const DEBUG = true;
75
+ const log = (id: string, ...args: unknown[]) => {
76
+ if (DEBUG) {
77
+ console.log(`[SwipePivotContent:${id}]`, ...args);
78
+ }
79
+ };
80
+
81
+ export const SwipePivotContentDebug: React.FC<SwipePivotContentProps> = React.memo(({
82
+ id,
83
+ isActive,
84
+ position,
85
+ inputState,
86
+ axis = "horizontal",
87
+ containerSize,
88
+ children,
89
+ canNavigate = true,
90
+ animationDuration = DEFAULT_ANIMATION_DURATION,
91
+ }) => {
92
+ const elementRef = React.useRef<HTMLDivElement>(null);
93
+
94
+ // BUG HYPOTHESIS 1: currentPxRef is only initialized once at mount
95
+ // If position or containerSize changes, this won't be updated
96
+ const currentPxRef = React.useRef<number>(position * containerSize);
97
+ const animRef = React.useRef<{ from: number; to: number } | null>(null);
98
+
99
+ // Track previous values for debugging
100
+ const prevPositionRef = React.useRef(position);
101
+ const prevContainerSizeRef = React.useRef(containerSize);
102
+
103
+ const targetPx = position * containerSize;
104
+ const displacement = getDisplacement(inputState, axis);
105
+ const isSwiping = inputState.phase === "swiping" || inputState.phase === "tracking";
106
+
107
+ // Debug: Log when position or containerSize changes
108
+ React.useEffect(() => {
109
+ if (prevPositionRef.current !== position) {
110
+ log(id, "position changed:", prevPositionRef.current, "->", position);
111
+ log(id, " currentPxRef.current:", currentPxRef.current);
112
+ log(id, " new targetPx:", targetPx);
113
+ prevPositionRef.current = position;
114
+ }
115
+ if (prevContainerSizeRef.current !== containerSize) {
116
+ log(id, "containerSize changed:", prevContainerSizeRef.current, "->", containerSize);
117
+ prevContainerSizeRef.current = containerSize;
118
+ }
119
+ }, [id, position, containerSize, targetPx]);
120
+
121
+ // Debug: Log inputState changes
122
+ React.useEffect(() => {
123
+ log(id, "inputState:", inputState.phase, "direction:", inputState.direction, "displacement:", displacement);
124
+ }, [id, inputState, displacement]);
125
+
126
+ // Animation frame handler
127
+ const handleFrame = React.useCallback(
128
+ ({ easedProgress }: { easedProgress: number }) => {
129
+ const element = elementRef.current;
130
+ const anim = animRef.current;
131
+ if (!element || !anim) {
132
+ return;
133
+ }
134
+ const value = interpolate(anim.from, anim.to, easedProgress);
135
+ currentPxRef.current = value;
136
+ const fn = axis === "horizontal" ? "translateX" : "translateY";
137
+ element.style.transform = `${fn}(${value}px)`;
138
+ },
139
+ [axis],
140
+ );
141
+
142
+ const handleComplete = React.useCallback(() => {
143
+ log(id, "animation complete, targetPx:", targetPx);
144
+ animRef.current = null;
145
+ currentPxRef.current = targetPx;
146
+ }, [id, targetPx]);
147
+
148
+ const { isAnimating, start, cancel } = useAnimationFrame({
149
+ duration: animationDuration,
150
+ easing: easings.easeOutExpo,
151
+ onFrame: handleFrame,
152
+ onComplete: handleComplete,
153
+ });
154
+
155
+ // When swipe ends, animate to target
156
+ React.useLayoutEffect(() => {
157
+ // If swiping, don't start animation
158
+ if (isSwiping) {
159
+ cancel();
160
+ return;
161
+ }
162
+
163
+ // Check if we need to animate
164
+ const currentPx = currentPxRef.current;
165
+ const distance = Math.abs(currentPx - targetPx);
166
+
167
+ log(id, "useLayoutEffect[animation]:", {
168
+ isSwiping,
169
+ currentPx,
170
+ targetPx,
171
+ distance,
172
+ willAnimate: distance > 1,
173
+ });
174
+
175
+ if (distance > 1) {
176
+ // Need to animate from current to target
177
+ animRef.current = { from: currentPx, to: targetPx };
178
+ log(id, "starting animation:", animRef.current);
179
+ start();
180
+ }
181
+ }, [id, isSwiping, targetPx, start, cancel]);
182
+
183
+ // Direct DOM update
184
+ React.useLayoutEffect(() => {
185
+ const element = elementRef.current;
186
+ if (!element) {
187
+ return;
188
+ }
189
+
190
+ if (isAnimating) {
191
+ log(id, "useLayoutEffect[dom]: skipped (isAnimating)");
192
+ return; // Animation handles transform
193
+ }
194
+
195
+ const fn = axis === "horizontal" ? "translateX" : "translateY";
196
+ const displayPx = targetPx + displacement;
197
+
198
+ log(id, "useLayoutEffect[dom]:", {
199
+ targetPx,
200
+ displacement,
201
+ displayPx,
202
+ prevCurrentPx: currentPxRef.current,
203
+ });
204
+
205
+ currentPxRef.current = displayPx;
206
+ element.style.transform = `${fn}(${displayPx}px)`;
207
+ }, [id, targetPx, displacement, axis, isAnimating]);
208
+
209
+ // Visibility
210
+ const visible = shouldBeVisible(isActive, position, inputState, canNavigate, isAnimating);
211
+
212
+ React.useLayoutEffect(() => {
213
+ const element = elementRef.current;
214
+ if (element) {
215
+ element.style.visibility = visible ? "visible" : "hidden";
216
+ log(id, "visibility:", visible);
217
+ }
218
+ }, [id, visible]);
219
+
220
+ const staticStyle = React.useMemo<React.CSSProperties>(() => ({
221
+ ...baseStyle,
222
+ pointerEvents: isActive ? "auto" : "none",
223
+ willChange: "transform",
224
+ }), [isActive]);
225
+
226
+ return (
227
+ <div
228
+ ref={elementRef}
229
+ data-pivot-content={id}
230
+ data-active={isActive ? "true" : "false"}
231
+ data-position={position}
232
+ style={staticStyle}
233
+ >
234
+ {children}
235
+ </div>
236
+ );
237
+ });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * @file Tests for SwipePivotContent position handling.
3
+ *
4
+ * TDD: Swipe NavigationでPage2,3が重なる問題を再現するテスト
5
+ *
6
+ * 問題: Page 1でスワイプすると、Page 2とPage 3が同じ位置に表示される
7
+ * 原因: getPosition関数が-1, 0, 1しか返さないため、離れたページも同じpositionになる
8
+ * 解決: 親コンポーネントで隣接するアイテムのみをレンダリングする
9
+ */
10
+ import { render, screen } from "@testing-library/react";
11
+ import * as React from "react";
12
+ import { SwipePivotContent } from "./SwipePivotContent.js";
13
+ import type { SwipeInputState } from "../../hooks/gesture/types.js";
14
+
15
+ /**
16
+ * Helper function to calculate position offset (not clamped)
17
+ * This is the fix: calculate actual offset, then filter
18
+ */
19
+ const getPositionOffset = (itemIndex: number, activeIndex: number): number => {
20
+ return itemIndex - activeIndex;
21
+ };
22
+
23
+ /**
24
+ * Helper to determine if item should be rendered
25
+ */
26
+ const shouldRenderItem = (offset: number): boolean => {
27
+ return Math.abs(offset) <= 1;
28
+ };
29
+
30
+ /**
31
+ * Helper to convert offset to display position
32
+ */
33
+ const toDisplayPosition = (offset: number): -1 | 0 | 1 => {
34
+ if (offset < 0) return -1;
35
+ if (offset > 0) return 1;
36
+ return 0;
37
+ };
38
+
39
+ describe("SwipePivotContent position handling", () => {
40
+ const containerSize = 300;
41
+
42
+ const swipingLeftState: SwipeInputState = {
43
+ phase: "swiping",
44
+ displacement: { x: -50, y: 0 },
45
+ velocity: { x: -0.5, y: 0 },
46
+ direction: -1,
47
+ };
48
+
49
+ describe("correct position offset calculation", () => {
50
+ it("calculates correct offset for each item", () => {
51
+ // When on Page 1 (index 0)
52
+ const activeIndex = 0;
53
+
54
+ expect(getPositionOffset(0, activeIndex)).toBe(0); // Page 1
55
+ expect(getPositionOffset(1, activeIndex)).toBe(1); // Page 2
56
+ expect(getPositionOffset(2, activeIndex)).toBe(2); // Page 3 - offset is 2!
57
+ });
58
+
59
+ it("filters out non-adjacent items", () => {
60
+ const activeIndex = 0;
61
+
62
+ expect(shouldRenderItem(getPositionOffset(0, activeIndex))).toBe(true); // Page 1
63
+ expect(shouldRenderItem(getPositionOffset(1, activeIndex))).toBe(true); // Page 2
64
+ expect(shouldRenderItem(getPositionOffset(2, activeIndex))).toBe(false); // Page 3 - should NOT render
65
+ });
66
+ });
67
+
68
+ describe("rendering only adjacent items", () => {
69
+ it("only renders Page 1 and Page 2 when on Page 1", () => {
70
+ // Simulating correct behavior in parent component
71
+ const items = [
72
+ { id: "page1", index: 0 },
73
+ { id: "page2", index: 1 },
74
+ { id: "page3", index: 2 },
75
+ ];
76
+ const activeIndex = 0;
77
+
78
+ // Filter to only adjacent items BEFORE rendering
79
+ const itemsToRender = items.filter(item =>
80
+ shouldRenderItem(getPositionOffset(item.index, activeIndex))
81
+ );
82
+
83
+ render(
84
+ <>
85
+ {itemsToRender.map((item) => {
86
+ const offset = getPositionOffset(item.index, activeIndex);
87
+ return (
88
+ <SwipePivotContent
89
+ key={item.id}
90
+ id={item.id}
91
+ isActive={offset === 0}
92
+ position={toDisplayPosition(offset)}
93
+ inputState={swipingLeftState}
94
+ containerSize={containerSize}
95
+ canNavigate={true}
96
+ >
97
+ <div data-testid={item.id}>{item.id}</div>
98
+ </SwipePivotContent>
99
+ );
100
+ })}
101
+ </>,
102
+ );
103
+
104
+ expect(screen.queryByTestId("page1")).toBeInTheDocument();
105
+ expect(screen.queryByTestId("page2")).toBeInTheDocument();
106
+ expect(screen.queryByTestId("page3")).not.toBeInTheDocument(); // Page 3 not rendered
107
+ });
108
+
109
+ it("only renders Page 2, Page 3 when on Page 2", () => {
110
+ const items = [
111
+ { id: "page1", index: 0 },
112
+ { id: "page2", index: 1 },
113
+ { id: "page3", index: 2 },
114
+ ];
115
+ const activeIndex = 1; // On Page 2
116
+
117
+ const itemsToRender = items.filter(item =>
118
+ shouldRenderItem(getPositionOffset(item.index, activeIndex))
119
+ );
120
+
121
+ render(
122
+ <>
123
+ {itemsToRender.map((item) => {
124
+ const offset = getPositionOffset(item.index, activeIndex);
125
+ return (
126
+ <SwipePivotContent
127
+ key={item.id}
128
+ id={item.id}
129
+ isActive={offset === 0}
130
+ position={toDisplayPosition(offset)}
131
+ inputState={swipingLeftState}
132
+ containerSize={containerSize}
133
+ canNavigate={true}
134
+ >
135
+ <div data-testid={item.id}>{item.id}</div>
136
+ </SwipePivotContent>
137
+ );
138
+ })}
139
+ </>,
140
+ );
141
+
142
+ expect(screen.queryByTestId("page1")).toBeInTheDocument(); // Previous
143
+ expect(screen.queryByTestId("page2")).toBeInTheDocument(); // Active
144
+ expect(screen.queryByTestId("page3")).toBeInTheDocument(); // Next
145
+ });
146
+ });
147
+
148
+ describe("canNavigate for boundary items", () => {
149
+ it("non-adjacent items should not be visible when canNavigate=false", () => {
150
+ render(
151
+ <SwipePivotContent
152
+ id="page3"
153
+ isActive={false}
154
+ position={1}
155
+ inputState={swipingLeftState}
156
+ containerSize={containerSize}
157
+ canNavigate={false}
158
+ >
159
+ <div data-testid="page3">Page 3</div>
160
+ </SwipePivotContent>,
161
+ );
162
+
163
+ const wrapper = screen.getByTestId("page3").parentElement;
164
+ expect(wrapper?.style?.visibility).toBe("hidden");
165
+ });
166
+ });
167
+ });