react-panel-layout 0.5.1 → 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-CEmXDvUA.cjs.map +1 -0
- package/dist/{FloatingPanelFrame-6W5OexYe.js → FloatingPanelFrame-SgYLc6Ud.js} +12 -15
- package/dist/FloatingPanelFrame-SgYLc6Ud.js.map +1 -0
- 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 +73 -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/types.d.ts +12 -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/FloatingPanelFrame-6W5OexYe.js.map +0 -1
- package/dist/FloatingPanelFrame-D9Cp2al1.cjs.map +0 -1
- package/dist/GridLayout-BzrIDrC9.js +0 -1465
- package/dist/GridLayout-BzrIDrC9.js.map +0 -1
- package/dist/GridLayout-ZrOhoLLB.cjs +0 -2
- package/dist/GridLayout-ZrOhoLLB.cjs.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-BS-DGfwd.cjs +0 -2
- package/dist/usePivot-BS-DGfwd.cjs.map +0 -1
- package/dist/usePivot-BvOGxLQQ.js +0 -124
- package/dist/usePivot-BvOGxLQQ.js.map +0 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Shared useIntersectionObserver hook with cached observer instances.
|
|
3
|
+
*/
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
const createIdGenerator = () => {
|
|
7
|
+
const map = new Map<object, number>();
|
|
8
|
+
return (ref: object | null | undefined) => {
|
|
9
|
+
if (!ref) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const existing = map.get(ref);
|
|
13
|
+
if (existing !== undefined) {
|
|
14
|
+
return existing;
|
|
15
|
+
}
|
|
16
|
+
const nextId = map.size;
|
|
17
|
+
map.set(ref, nextId);
|
|
18
|
+
return nextId;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const getId = createIdGenerator();
|
|
23
|
+
type Unobserve = () => void;
|
|
24
|
+
type Callback = (entry: IntersectionObserverEntry) => void;
|
|
25
|
+
type SharedObserver = {
|
|
26
|
+
observe: (target: Element, callback: Callback) => Unobserve;
|
|
27
|
+
};
|
|
28
|
+
const observerCache = new Map<string, SharedObserver>();
|
|
29
|
+
const getSharedObserver = (options: IntersectionObserverInit) => {
|
|
30
|
+
const observerKey = `ovs-threshold:${options.threshold}-rootMargin:${options.rootMargin}-root:${getId(options.root)}`;
|
|
31
|
+
|
|
32
|
+
if (observerCache.has(observerKey)) {
|
|
33
|
+
return observerCache.get(observerKey)!;
|
|
34
|
+
}
|
|
35
|
+
const observer = new (class {
|
|
36
|
+
#callbackMap = new Map<Element, Callback>();
|
|
37
|
+
#intersectionObserver = new IntersectionObserver((entries) => {
|
|
38
|
+
entries.forEach((entry) => {
|
|
39
|
+
const callback = this.#callbackMap.get(entry.target);
|
|
40
|
+
if (callback) {
|
|
41
|
+
callback(entry);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}, options);
|
|
45
|
+
observe(target: Element, callback: Callback) {
|
|
46
|
+
this.#callbackMap.set(target, callback);
|
|
47
|
+
this.#intersectionObserver.observe(target);
|
|
48
|
+
return () => {
|
|
49
|
+
this.#callbackMap.delete(target);
|
|
50
|
+
this.#intersectionObserver.unobserve(target);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
54
|
+
observerCache.set(observerKey, observer);
|
|
55
|
+
|
|
56
|
+
return observer;
|
|
57
|
+
};
|
|
58
|
+
const voidClientRect = Object.freeze({
|
|
59
|
+
x: 0,
|
|
60
|
+
y: 0,
|
|
61
|
+
width: 0,
|
|
62
|
+
height: 0,
|
|
63
|
+
top: 0,
|
|
64
|
+
right: 0,
|
|
65
|
+
bottom: 0,
|
|
66
|
+
left: 0,
|
|
67
|
+
}) as DOMRectReadOnly;
|
|
68
|
+
/**
|
|
69
|
+
* Observe intersection changes for a given element reference using shared observers.
|
|
70
|
+
*
|
|
71
|
+
* @param ref - Ref holding the element to observe.
|
|
72
|
+
* @param options - Intersection observer configuration.
|
|
73
|
+
* @returns Latest intersection entry snapshot with sensible defaults.
|
|
74
|
+
*/
|
|
75
|
+
export function useIntersectionObserver<T extends HTMLElement>(
|
|
76
|
+
ref: React.RefObject<T | null>,
|
|
77
|
+
{ threshold = 0, rootMargin = "0px", root = null }: IntersectionObserverInit,
|
|
78
|
+
): {
|
|
79
|
+
readonly boundingClientRect: DOMRectReadOnly;
|
|
80
|
+
readonly intersectionRatio: number;
|
|
81
|
+
readonly intersectionRect: DOMRectReadOnly;
|
|
82
|
+
readonly isIntersecting: boolean;
|
|
83
|
+
readonly rootBounds: DOMRectReadOnly | null;
|
|
84
|
+
readonly target: Element | null;
|
|
85
|
+
readonly time: DOMHighResTimeStamp;
|
|
86
|
+
} {
|
|
87
|
+
const [intersection, setIntersection] = React.useState<IntersectionObserverEntry | null>(null);
|
|
88
|
+
|
|
89
|
+
React.useEffect(() => {
|
|
90
|
+
const target = ref.current;
|
|
91
|
+
if (!target) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const observer = getSharedObserver({
|
|
96
|
+
threshold,
|
|
97
|
+
rootMargin,
|
|
98
|
+
root,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return observer.observe(target, (entry) => {
|
|
102
|
+
setIntersection({
|
|
103
|
+
isIntersecting: entry.isIntersecting,
|
|
104
|
+
boundingClientRect: entry.boundingClientRect,
|
|
105
|
+
intersectionRatio: entry.intersectionRatio,
|
|
106
|
+
intersectionRect: entry.intersectionRect,
|
|
107
|
+
rootBounds: entry.rootBounds,
|
|
108
|
+
target: entry.target,
|
|
109
|
+
time: entry.time,
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}, [ref, threshold, rootMargin, root]);
|
|
113
|
+
|
|
114
|
+
return React.useMemo(() => {
|
|
115
|
+
return {
|
|
116
|
+
isIntersecting: intersection?.isIntersecting ?? false,
|
|
117
|
+
boundingClientRect: intersection?.boundingClientRect ?? voidClientRect,
|
|
118
|
+
intersectionRatio: intersection?.intersectionRatio ?? 0,
|
|
119
|
+
intersectionRect: intersection?.intersectionRect ?? voidClientRect,
|
|
120
|
+
rootBounds: intersection?.rootBounds ?? null,
|
|
121
|
+
target: intersection?.target ?? ref.current,
|
|
122
|
+
time: intersection?.time ?? 0,
|
|
123
|
+
};
|
|
124
|
+
}, [intersection, ref]);
|
|
125
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useIsomorphicLayoutEffect - SSR-safe version of useLayoutEffect
|
|
3
|
+
*
|
|
4
|
+
* Uses useLayoutEffect on the client and useEffect on the server to avoid warnings
|
|
5
|
+
* during server-side rendering (e.g., with Next.js)
|
|
6
|
+
*/
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if we're running in a browser environment
|
|
11
|
+
*/
|
|
12
|
+
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* SSR-safe version of useLayoutEffect
|
|
16
|
+
*
|
|
17
|
+
* - Client: Uses useLayoutEffect for synchronous layout updates
|
|
18
|
+
* - Server: Uses useEffect to avoid SSR warnings
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* useIsomorphicLayoutEffect(() => {
|
|
23
|
+
* // This runs synchronously after DOM mutations on the client
|
|
24
|
+
* // but safely falls back to useEffect on the server
|
|
25
|
+
* measureElement();
|
|
26
|
+
* }, [deps]);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export const useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Shared useResizeObserver hook with cached observer instances.
|
|
3
|
+
*/
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
type Unobserve = () => void;
|
|
7
|
+
type Callback = (entry: ResizeObserverEntry, observer: ResizeObserver) => void;
|
|
8
|
+
type SharedObserver = {
|
|
9
|
+
observe: (target: Element, callback: Callback) => Unobserve;
|
|
10
|
+
};
|
|
11
|
+
const observerCache = new Map<string, SharedObserver>();
|
|
12
|
+
const getSharedObserver = (options: ResizeObserverOptions) => {
|
|
13
|
+
const { box = "content-box" } = options;
|
|
14
|
+
const observerKey = `resize-box:${box}`;
|
|
15
|
+
const cached = observerCache.get(observerKey);
|
|
16
|
+
if (cached) {
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
const observer = new (class {
|
|
20
|
+
#callbackMap = new Map<Element, Callback>();
|
|
21
|
+
#resizeObserver = new ResizeObserver((entries, observer) => {
|
|
22
|
+
entries.forEach((entry) => {
|
|
23
|
+
const callback = this.#callbackMap.get(entry.target);
|
|
24
|
+
if (callback) {
|
|
25
|
+
callback(entry, observer);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
observe(target: Element, callback: Callback) {
|
|
30
|
+
this.#callbackMap.set(target, callback);
|
|
31
|
+
this.#resizeObserver.observe(target, options);
|
|
32
|
+
return () => {
|
|
33
|
+
this.#callbackMap.delete(target);
|
|
34
|
+
this.#resizeObserver.unobserve(target);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
})();
|
|
38
|
+
observerCache.set(observerKey, observer);
|
|
39
|
+
|
|
40
|
+
return observer;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Observe size changes for a given element reference using shared resize observers.
|
|
44
|
+
*
|
|
45
|
+
* @param ref - Ref holding the element whose size to monitor.
|
|
46
|
+
* @param options - Resize observer configuration.
|
|
47
|
+
* @returns Latest resize entry and a derived DOMRect snapshot.
|
|
48
|
+
*/
|
|
49
|
+
export function useResizeObserver<T extends HTMLElement>(
|
|
50
|
+
ref: React.RefObject<T | null>,
|
|
51
|
+
{ box }: ResizeObserverOptions,
|
|
52
|
+
) {
|
|
53
|
+
const [entry, setEntry] = React.useState<ResizeObserverEntry | null>(null);
|
|
54
|
+
const target = ref.current;
|
|
55
|
+
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
if (!target) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const observer = getSharedObserver({ box });
|
|
62
|
+
return observer.observe(target, (nextEntry) => {
|
|
63
|
+
setEntry(nextEntry);
|
|
64
|
+
});
|
|
65
|
+
}, [box, target]);
|
|
66
|
+
|
|
67
|
+
const rect = React.useMemo(() => {
|
|
68
|
+
if (!entry) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (entry.borderBoxSize?.length > 0) {
|
|
73
|
+
const size = entry.borderBoxSize[0];
|
|
74
|
+
return new DOMRect(0, 0, size.inlineSize, size.blockSize);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return entry.contentRect;
|
|
78
|
+
}, [entry]);
|
|
79
|
+
|
|
80
|
+
return { entry, rect };
|
|
81
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useScrollContainer - Detect the nearest scrollable ancestor
|
|
3
|
+
*
|
|
4
|
+
* Traverses the DOM tree to find the nearest ancestor with overflow: scroll/auto.
|
|
5
|
+
* Returns null if the document is the scroll container.
|
|
6
|
+
*/
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if an element is a scroll container.
|
|
11
|
+
*/
|
|
12
|
+
function isScrollContainer(element: Element): boolean {
|
|
13
|
+
const style = getComputedStyle(element);
|
|
14
|
+
const overflowY = style.overflowY;
|
|
15
|
+
const overflowX = style.overflowX;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
overflowY === "scroll" ||
|
|
19
|
+
overflowY === "auto" ||
|
|
20
|
+
overflowX === "scroll" ||
|
|
21
|
+
overflowX === "auto"
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Find the nearest scrollable ancestor of an element.
|
|
27
|
+
*
|
|
28
|
+
* @param element - Starting element to search from
|
|
29
|
+
* @returns The nearest scrollable ancestor, or null if document is the container
|
|
30
|
+
*/
|
|
31
|
+
function findScrollContainer(element: Element | null): HTMLElement | null {
|
|
32
|
+
if (!element) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// eslint-disable-next-line no-restricted-syntax -- DOM traversal requires let
|
|
37
|
+
let current = element.parentElement;
|
|
38
|
+
|
|
39
|
+
while (current) {
|
|
40
|
+
if (isScrollContainer(current)) {
|
|
41
|
+
return current;
|
|
42
|
+
}
|
|
43
|
+
current = current.parentElement;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Hook to detect the nearest scrollable ancestor of a ref element.
|
|
51
|
+
*
|
|
52
|
+
* @param ref - Ref to the element to find scroll container for
|
|
53
|
+
* @returns The nearest scrollable ancestor element, or null if document is the container
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* const elementRef = useRef<HTMLDivElement>(null);
|
|
58
|
+
* const scrollContainer = useScrollContainer(elementRef);
|
|
59
|
+
* // scrollContainer is HTMLElement if in nested scroll, null if document scroll
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function useScrollContainer<T extends HTMLElement>(
|
|
63
|
+
ref: React.RefObject<T | null>,
|
|
64
|
+
): HTMLElement | null {
|
|
65
|
+
const [container, setContainer] = React.useState<HTMLElement | null>(null);
|
|
66
|
+
|
|
67
|
+
React.useEffect(() => {
|
|
68
|
+
const element = ref.current;
|
|
69
|
+
if (!element) {
|
|
70
|
+
setContainer(null);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const scrollContainer = findScrollContainer(element);
|
|
75
|
+
setContainer(scrollContainer);
|
|
76
|
+
}, [ref]);
|
|
77
|
+
|
|
78
|
+
return container;
|
|
79
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Hook for snapping element transform animation.
|
|
3
|
+
*
|
|
4
|
+
* Built on top of useAnimationFrame, provides a convenient API
|
|
5
|
+
* for animating element transforms from one position to another.
|
|
6
|
+
*/
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import { useAnimationFrame, interpolate, easings } from "./useAnimationFrame.js";
|
|
9
|
+
import type { EasingFunction } from "./useAnimationFrame.js";
|
|
10
|
+
|
|
11
|
+
// Re-export easings for convenience
|
|
12
|
+
export { easings } from "./useAnimationFrame.js";
|
|
13
|
+
export type { EasingFunction } from "./useAnimationFrame.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for useSnapAnimation hook.
|
|
17
|
+
*/
|
|
18
|
+
export type UseSnapAnimationOptions = {
|
|
19
|
+
/** Reference to the element to animate */
|
|
20
|
+
elementRef: React.RefObject<HTMLElement | null>;
|
|
21
|
+
/** Duration of animation in milliseconds */
|
|
22
|
+
duration?: number;
|
|
23
|
+
/** Easing function for the animation */
|
|
24
|
+
easing?: EasingFunction;
|
|
25
|
+
/** Callback when animation completes */
|
|
26
|
+
onComplete?: () => void;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Result from useSnapAnimation hook.
|
|
31
|
+
*/
|
|
32
|
+
export type UseSnapAnimationResult = {
|
|
33
|
+
/** Whether animation is currently running */
|
|
34
|
+
isAnimating: boolean;
|
|
35
|
+
/** Start a new animation from current value to target */
|
|
36
|
+
animate: (from: number, to: number, axis: "x" | "y") => void;
|
|
37
|
+
/** Cancel any running animation */
|
|
38
|
+
cancel: () => void;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Default animation duration in ms */
|
|
42
|
+
const DEFAULT_DURATION = 300;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Hook for snapping element transform animation.
|
|
46
|
+
*
|
|
47
|
+
* Animates an element's transform from one position to another
|
|
48
|
+
* using requestAnimationFrame for smooth animation.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* const elementRef = useRef<HTMLDivElement>(null);
|
|
53
|
+
* const { animate, isAnimating } = useSnapAnimation({
|
|
54
|
+
* elementRef,
|
|
55
|
+
* duration: 300,
|
|
56
|
+
* easing: easings.easeOutExpo,
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* // Start animation
|
|
60
|
+
* animate(currentX, targetX, "x");
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function useSnapAnimation(options: UseSnapAnimationOptions): UseSnapAnimationResult {
|
|
64
|
+
const {
|
|
65
|
+
elementRef,
|
|
66
|
+
duration = DEFAULT_DURATION,
|
|
67
|
+
easing = easings.easeOutExpo,
|
|
68
|
+
onComplete,
|
|
69
|
+
} = options;
|
|
70
|
+
|
|
71
|
+
// Store animation parameters in refs for use in onFrame callback
|
|
72
|
+
const animParamsRef = React.useRef<{
|
|
73
|
+
from: number;
|
|
74
|
+
to: number;
|
|
75
|
+
axis: "x" | "y";
|
|
76
|
+
} | null>(null);
|
|
77
|
+
|
|
78
|
+
const handleFrame = React.useCallback(
|
|
79
|
+
({ easedProgress }: { easedProgress: number }) => {
|
|
80
|
+
const element = elementRef.current;
|
|
81
|
+
const params = animParamsRef.current;
|
|
82
|
+
if (!element || !params) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const currentValue = interpolate(params.from, params.to, easedProgress);
|
|
87
|
+
const translateFn = params.axis === "x" ? "translateX" : "translateY";
|
|
88
|
+
element.style.transform = `${translateFn}(${currentValue}px)`;
|
|
89
|
+
},
|
|
90
|
+
[elementRef],
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const { isAnimating, start, cancel } = useAnimationFrame({
|
|
94
|
+
duration,
|
|
95
|
+
easing,
|
|
96
|
+
onFrame: handleFrame,
|
|
97
|
+
onComplete,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const animate = React.useCallback(
|
|
101
|
+
(from: number, to: number, axis: "x" | "y") => {
|
|
102
|
+
const element = elementRef.current;
|
|
103
|
+
if (!element) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// If from and to are the same, no animation needed
|
|
108
|
+
if (Math.abs(from - to) < 0.5) {
|
|
109
|
+
const translateFn = axis === "x" ? "translateX" : "translateY";
|
|
110
|
+
element.style.transform = `${translateFn}(${to}px)`;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Store animation parameters
|
|
115
|
+
animParamsRef.current = { from, to, axis };
|
|
116
|
+
|
|
117
|
+
// Start animation
|
|
118
|
+
start();
|
|
119
|
+
},
|
|
120
|
+
[elementRef, start],
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
isAnimating,
|
|
125
|
+
animate,
|
|
126
|
+
cancel,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tests for useSwipeContentTransform hook.
|
|
3
|
+
*/
|
|
4
|
+
import { renderHook } from "@testing-library/react";
|
|
5
|
+
import { useSwipeContentTransform } from "./useSwipeContentTransform.js";
|
|
6
|
+
|
|
7
|
+
describe("useSwipeContentTransform", () => {
|
|
8
|
+
const createMockElementRef = (): { element: HTMLDivElement; ref: React.RefObject<HTMLDivElement> } => {
|
|
9
|
+
const element = document.createElement("div");
|
|
10
|
+
return { element, ref: { current: element } };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
it("returns initial state correctly", () => {
|
|
14
|
+
const { ref } = createMockElementRef();
|
|
15
|
+
const { result } = renderHook(() =>
|
|
16
|
+
useSwipeContentTransform({
|
|
17
|
+
elementRef: ref,
|
|
18
|
+
targetPx: 0,
|
|
19
|
+
displacement: 0,
|
|
20
|
+
isSwiping: false,
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
expect(result.current.isAnimating).toBe(false);
|
|
25
|
+
expect(result.current.currentPx).toBe(0);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("updates element transform during swipe", () => {
|
|
29
|
+
const { element, ref } = createMockElementRef();
|
|
30
|
+
const { rerender } = renderHook(
|
|
31
|
+
({ displacement, isSwiping }) =>
|
|
32
|
+
useSwipeContentTransform({
|
|
33
|
+
elementRef: ref,
|
|
34
|
+
targetPx: 0,
|
|
35
|
+
displacement,
|
|
36
|
+
isSwiping,
|
|
37
|
+
}),
|
|
38
|
+
{
|
|
39
|
+
initialProps: { displacement: 0, isSwiping: true },
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Simulate swipe movement
|
|
44
|
+
rerender({ displacement: 100, isSwiping: true });
|
|
45
|
+
|
|
46
|
+
expect(element.style.transform).toBe("translateX(100px)");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("uses correct transform function for vertical axis", () => {
|
|
50
|
+
const { element, ref } = createMockElementRef();
|
|
51
|
+
const { rerender } = renderHook(
|
|
52
|
+
({ displacement, isSwiping }) =>
|
|
53
|
+
useSwipeContentTransform({
|
|
54
|
+
elementRef: ref,
|
|
55
|
+
targetPx: 0,
|
|
56
|
+
displacement,
|
|
57
|
+
isSwiping,
|
|
58
|
+
axis: "vertical",
|
|
59
|
+
}),
|
|
60
|
+
{
|
|
61
|
+
initialProps: { displacement: 0, isSwiping: true },
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
rerender({ displacement: 50, isSwiping: true });
|
|
66
|
+
|
|
67
|
+
expect(element.style.transform).toBe("translateY(50px)");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("applies target position with displacement", () => {
|
|
71
|
+
const { element, ref } = createMockElementRef();
|
|
72
|
+
const { rerender } = renderHook(
|
|
73
|
+
({ displacement, isSwiping }) =>
|
|
74
|
+
useSwipeContentTransform({
|
|
75
|
+
elementRef: ref,
|
|
76
|
+
targetPx: -300, // target is off-screen left
|
|
77
|
+
displacement,
|
|
78
|
+
isSwiping,
|
|
79
|
+
}),
|
|
80
|
+
{
|
|
81
|
+
initialProps: { displacement: 0, isSwiping: true },
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// During swipe, position = targetPx + displacement
|
|
86
|
+
rerender({ displacement: 100, isSwiping: true });
|
|
87
|
+
|
|
88
|
+
expect(element.style.transform).toBe("translateX(-200px)");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("does not update transform when element ref is null", () => {
|
|
92
|
+
const nullRef: React.RefObject<HTMLElement | null> = { current: null };
|
|
93
|
+
|
|
94
|
+
const { rerender } = renderHook(
|
|
95
|
+
({ displacement, isSwiping }) =>
|
|
96
|
+
useSwipeContentTransform({
|
|
97
|
+
elementRef: nullRef,
|
|
98
|
+
targetPx: 0,
|
|
99
|
+
displacement,
|
|
100
|
+
isSwiping,
|
|
101
|
+
}),
|
|
102
|
+
{
|
|
103
|
+
initialProps: { displacement: 0, isSwiping: true },
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Should not throw
|
|
108
|
+
expect(() => {
|
|
109
|
+
rerender({ displacement: 100, isSwiping: true });
|
|
110
|
+
}).not.toThrow();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("snaps to target when target changes and not swiping", () => {
|
|
114
|
+
const { element, ref } = createMockElementRef();
|
|
115
|
+
const { rerender } = renderHook(
|
|
116
|
+
({ targetPx }) =>
|
|
117
|
+
useSwipeContentTransform({
|
|
118
|
+
elementRef: ref,
|
|
119
|
+
targetPx,
|
|
120
|
+
displacement: 0,
|
|
121
|
+
isSwiping: false,
|
|
122
|
+
}),
|
|
123
|
+
{
|
|
124
|
+
initialProps: { targetPx: 0 },
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Change target without swiping - should snap
|
|
129
|
+
rerender({ targetPx: 300 });
|
|
130
|
+
|
|
131
|
+
expect(element.style.transform).toBe("translateX(300px)");
|
|
132
|
+
});
|
|
133
|
+
});
|