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