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,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file SwipeStackContent component for Stack panels with direct DOM manipulation.
|
|
3
|
+
*
|
|
4
|
+
* Provides iOS-style swipe-to-go-back behavior:
|
|
5
|
+
* - Active panel follows the finger directly
|
|
6
|
+
* - Behind panel reveals from -30% with parallax effect
|
|
7
|
+
*
|
|
8
|
+
* Uses useSwipeContentTransform for immediate DOM updates.
|
|
9
|
+
*/
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
import { useSwipeContentTransform } from "../../hooks/useSwipeContentTransform.js";
|
|
12
|
+
import type { SwipeInputState, GestureAxis } from "../../hooks/gesture/types.js";
|
|
13
|
+
import type { StackDisplayMode } from "./types.js";
|
|
14
|
+
import {
|
|
15
|
+
computeActiveTargetPx,
|
|
16
|
+
computeBehindTargetPx,
|
|
17
|
+
computeSwipeVisibility,
|
|
18
|
+
determineSwipePanelRole,
|
|
19
|
+
DEFAULT_BEHIND_OFFSET,
|
|
20
|
+
} from "./computeSwipeStackTransform.js";
|
|
21
|
+
|
|
22
|
+
const DEFAULT_ANIMATION_DURATION = 300;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Scale factor per depth level for "stack" display mode.
|
|
26
|
+
* Each level behind reduces scale by this amount.
|
|
27
|
+
*/
|
|
28
|
+
const STACK_SCALE_FACTOR = 0.05;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Offset percentage per depth level for "stack" display mode.
|
|
32
|
+
*/
|
|
33
|
+
const STACK_OFFSET_PERCENT = 5;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Maximum dimming opacity for behind panels in iOS-style navigation.
|
|
37
|
+
*/
|
|
38
|
+
const MAX_DIM_OPACITY = 0.1;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Props for SwipeStackContent component.
|
|
42
|
+
*/
|
|
43
|
+
export type SwipeStackContentProps = {
|
|
44
|
+
/** Panel ID */
|
|
45
|
+
id: string;
|
|
46
|
+
/** Panel depth in the stack */
|
|
47
|
+
depth: number;
|
|
48
|
+
/** Current navigation depth (active panel) */
|
|
49
|
+
navigationDepth: number;
|
|
50
|
+
/** Whether this panel is currently active */
|
|
51
|
+
isActive: boolean;
|
|
52
|
+
/** Swipe input state from useStackSwipeInput */
|
|
53
|
+
inputState: SwipeInputState;
|
|
54
|
+
/** Container size in pixels (width for horizontal, height for vertical) */
|
|
55
|
+
containerSize: number;
|
|
56
|
+
/** Gesture axis. @default "horizontal" */
|
|
57
|
+
axis?: GestureAxis;
|
|
58
|
+
/** Behind panel offset ratio. @default -0.3 */
|
|
59
|
+
behindOffset?: number;
|
|
60
|
+
/** Animation duration in ms. @default 300 */
|
|
61
|
+
animationDuration?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Whether to animate when first mounted as active.
|
|
64
|
+
* Set to true for push navigation animations.
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
animateOnMount?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Whether to show iOS-style edge shadow on active panel.
|
|
70
|
+
* @default true
|
|
71
|
+
*/
|
|
72
|
+
showShadow?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Display mode for visual styling.
|
|
75
|
+
* - "overlay": panels overlay, no scale (iOS style)
|
|
76
|
+
* - "slide": panels slide with parallax
|
|
77
|
+
* - "stack": panels scale down and offset (stacked cards style)
|
|
78
|
+
* @default "overlay"
|
|
79
|
+
*/
|
|
80
|
+
displayMode?: StackDisplayMode;
|
|
81
|
+
/**
|
|
82
|
+
* Whether to show dimming overlay on behind panels.
|
|
83
|
+
* Creates iOS-style darkening effect that fades during swipe.
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
showDimming?: boolean;
|
|
87
|
+
/** Content to render */
|
|
88
|
+
children: React.ReactNode;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const BASE_STYLE: React.CSSProperties = {
|
|
92
|
+
position: "absolute",
|
|
93
|
+
inset: 0,
|
|
94
|
+
width: "100%",
|
|
95
|
+
height: "100%",
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get displacement from input state for the given axis.
|
|
100
|
+
*/
|
|
101
|
+
const getAxisDisplacement = (inputState: SwipeInputState, axis: GestureAxis): number => {
|
|
102
|
+
if (inputState.phase === "idle") {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
return axis === "horizontal" ? inputState.displacement.x : inputState.displacement.y;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* SwipeStackContent renders a single stack panel with swipe gesture support.
|
|
110
|
+
*
|
|
111
|
+
* Key behaviors:
|
|
112
|
+
* - Active panel: follows finger directly (translateX = displacement)
|
|
113
|
+
* - Behind panel: reveals from -30% with parallax (slower movement)
|
|
114
|
+
* - Hidden panels: not rendered during swipe
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```tsx
|
|
118
|
+
* <SwipeStackContent
|
|
119
|
+
* id="detail"
|
|
120
|
+
* depth={1}
|
|
121
|
+
* navigationDepth={1}
|
|
122
|
+
* isActive={true}
|
|
123
|
+
* inputState={swipeInput.inputState}
|
|
124
|
+
* containerSize={containerWidth}
|
|
125
|
+
* >
|
|
126
|
+
* <DetailPanel />
|
|
127
|
+
* </SwipeStackContent>
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
// iOS-style left edge shadow for active panels
|
|
131
|
+
const ACTIVE_PANEL_SHADOW = "-5px 0 15px rgba(0, 0, 0, 0.1)";
|
|
132
|
+
|
|
133
|
+
export const SwipeStackContent: React.FC<SwipeStackContentProps> = React.memo(
|
|
134
|
+
({
|
|
135
|
+
id,
|
|
136
|
+
depth,
|
|
137
|
+
navigationDepth,
|
|
138
|
+
isActive,
|
|
139
|
+
inputState,
|
|
140
|
+
containerSize,
|
|
141
|
+
axis = "horizontal",
|
|
142
|
+
behindOffset = DEFAULT_BEHIND_OFFSET,
|
|
143
|
+
animationDuration = DEFAULT_ANIMATION_DURATION,
|
|
144
|
+
animateOnMount = false,
|
|
145
|
+
showShadow = true,
|
|
146
|
+
displayMode = "overlay",
|
|
147
|
+
showDimming = true,
|
|
148
|
+
children,
|
|
149
|
+
}) => {
|
|
150
|
+
const elementRef = React.useRef<HTMLDivElement>(null);
|
|
151
|
+
const isFirstMountRef = React.useRef<boolean>(true);
|
|
152
|
+
|
|
153
|
+
const displacement = getAxisDisplacement(inputState, axis);
|
|
154
|
+
const isSwiping = inputState.phase === "swiping" || inputState.phase === "tracking";
|
|
155
|
+
|
|
156
|
+
// Determine panel role
|
|
157
|
+
const role = determineSwipePanelRole(depth, navigationDepth);
|
|
158
|
+
|
|
159
|
+
// Track first mount for push animation
|
|
160
|
+
const isFirstMount = isFirstMountRef.current;
|
|
161
|
+
if (isFirstMountRef.current) {
|
|
162
|
+
isFirstMountRef.current = false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Compute target position based on role
|
|
166
|
+
const targetPx = React.useMemo(() => {
|
|
167
|
+
switch (role) {
|
|
168
|
+
case "active":
|
|
169
|
+
// Active panel rests at 0
|
|
170
|
+
return 0;
|
|
171
|
+
case "behind":
|
|
172
|
+
// Behind panel rests at offset position
|
|
173
|
+
return behindOffset * containerSize;
|
|
174
|
+
case "hidden":
|
|
175
|
+
// Hidden panels are off-screen
|
|
176
|
+
return containerSize;
|
|
177
|
+
}
|
|
178
|
+
}, [role, behindOffset, containerSize]);
|
|
179
|
+
|
|
180
|
+
// Compute displacement for this panel
|
|
181
|
+
const panelDisplacement = React.useMemo(() => {
|
|
182
|
+
if (displacement <= 0) {
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
switch (role) {
|
|
187
|
+
case "active":
|
|
188
|
+
// Active panel follows finger directly
|
|
189
|
+
return computeActiveTargetPx(displacement);
|
|
190
|
+
case "behind": {
|
|
191
|
+
// Behind panel uses parallax - compute offset from base position
|
|
192
|
+
const currentPos = computeBehindTargetPx(displacement, containerSize, behindOffset);
|
|
193
|
+
const basePos = behindOffset * containerSize;
|
|
194
|
+
return currentPos - basePos;
|
|
195
|
+
}
|
|
196
|
+
case "hidden":
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
}, [role, displacement, containerSize, behindOffset]);
|
|
200
|
+
|
|
201
|
+
// Compute initial position for push animation
|
|
202
|
+
// When animateOnMount is true and panel is first mounted as "active",
|
|
203
|
+
// it should animate in from off-screen
|
|
204
|
+
// Root panel (depth=0) should not animate on mount
|
|
205
|
+
const initialPx = React.useMemo(() => {
|
|
206
|
+
if (!isFirstMount || !animateOnMount) {
|
|
207
|
+
return undefined; // Only relevant on first mount with animateOnMount
|
|
208
|
+
}
|
|
209
|
+
if (role === "active" && depth > 0) {
|
|
210
|
+
// New active panel (not root): start from off-screen right
|
|
211
|
+
return containerSize;
|
|
212
|
+
}
|
|
213
|
+
// Root panel or other roles: start at their natural position
|
|
214
|
+
return undefined;
|
|
215
|
+
}, [isFirstMount, animateOnMount, role, depth, containerSize]);
|
|
216
|
+
|
|
217
|
+
// Use shared transform hook for DOM manipulation
|
|
218
|
+
const { isAnimating } = useSwipeContentTransform({
|
|
219
|
+
elementRef,
|
|
220
|
+
targetPx,
|
|
221
|
+
displacement: panelDisplacement,
|
|
222
|
+
isSwiping,
|
|
223
|
+
axis,
|
|
224
|
+
animationDuration,
|
|
225
|
+
containerSize,
|
|
226
|
+
// Animate when targetPx changes (button navigation)
|
|
227
|
+
animateOnTargetChange: true,
|
|
228
|
+
// For push animation: start from off-screen
|
|
229
|
+
initialPx,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Compute visibility
|
|
233
|
+
const visible = computeSwipeVisibility({
|
|
234
|
+
depth,
|
|
235
|
+
navigationDepth,
|
|
236
|
+
isActive,
|
|
237
|
+
isSwiping,
|
|
238
|
+
isAnimating,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Compute swipe progress for scale and dimming interpolation
|
|
242
|
+
const swipeProgress = React.useMemo(() => {
|
|
243
|
+
if (containerSize <= 0 || displacement <= 0) {
|
|
244
|
+
return 0;
|
|
245
|
+
}
|
|
246
|
+
return Math.min(displacement / containerSize, 1);
|
|
247
|
+
}, [displacement, containerSize]);
|
|
248
|
+
|
|
249
|
+
// Compute scale for "stack" display mode
|
|
250
|
+
// Behind panels are scaled down, and scale interpolates during swipe
|
|
251
|
+
const scale = React.useMemo(() => {
|
|
252
|
+
if (displayMode !== "stack") {
|
|
253
|
+
return 1; // No scale for overlay/slide modes
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const depthDiff = navigationDepth - depth;
|
|
257
|
+
|
|
258
|
+
if (role === "active") {
|
|
259
|
+
return 1; // Active panel is always at full scale
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (role === "behind") {
|
|
263
|
+
// Base scale for behind panel
|
|
264
|
+
const baseScale = 1 - depthDiff * STACK_SCALE_FACTOR;
|
|
265
|
+
// During swipe, interpolate toward 1
|
|
266
|
+
return baseScale + swipeProgress * (1 - baseScale);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return 1;
|
|
270
|
+
}, [displayMode, role, depth, navigationDepth, swipeProgress]);
|
|
271
|
+
|
|
272
|
+
// Compute dimming opacity for behind panels
|
|
273
|
+
// Full dimming at rest, fades to 0 during swipe
|
|
274
|
+
const dimmingOpacity = React.useMemo(() => {
|
|
275
|
+
if (!showDimming || role !== "behind") {
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
// Fade from MAX_DIM_OPACITY to 0 as swipe progresses
|
|
279
|
+
return MAX_DIM_OPACITY * (1 - swipeProgress);
|
|
280
|
+
}, [showDimming, role, swipeProgress]);
|
|
281
|
+
|
|
282
|
+
// Update visibility via direct DOM manipulation
|
|
283
|
+
React.useLayoutEffect(() => {
|
|
284
|
+
const element = elementRef.current;
|
|
285
|
+
if (element) {
|
|
286
|
+
element.style.visibility = visible ? "visible" : "hidden";
|
|
287
|
+
}
|
|
288
|
+
}, [visible]);
|
|
289
|
+
|
|
290
|
+
// Update scale via direct DOM manipulation for smooth animation
|
|
291
|
+
React.useLayoutEffect(() => {
|
|
292
|
+
const element = elementRef.current;
|
|
293
|
+
if (!element || displayMode !== "stack") {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Get current transform (translateX) and append scale
|
|
298
|
+
const currentTransform = element.style.transform;
|
|
299
|
+
if (currentTransform.includes("translateX")) {
|
|
300
|
+
// Extract translateX value and combine with scale
|
|
301
|
+
const translateMatch = currentTransform.match(/translateX\([^)]+\)/);
|
|
302
|
+
if (translateMatch) {
|
|
303
|
+
element.style.transform = `${translateMatch[0]} scale(${scale})`;
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
element.style.transform = `scale(${scale})`;
|
|
307
|
+
}
|
|
308
|
+
}, [scale, displayMode]);
|
|
309
|
+
|
|
310
|
+
// Compute shadow for active panel
|
|
311
|
+
// Shadow is shown on panels at depth > 0 when they're active or animating
|
|
312
|
+
const shouldShowShadow = showShadow && depth > 0 && role === "active";
|
|
313
|
+
|
|
314
|
+
// Static style - transform is handled entirely by useSwipeContentTransform
|
|
315
|
+
// to ensure smooth animations
|
|
316
|
+
const staticStyle = React.useMemo<React.CSSProperties>(
|
|
317
|
+
() => ({
|
|
318
|
+
...BASE_STYLE,
|
|
319
|
+
pointerEvents: isActive ? "auto" : "none",
|
|
320
|
+
willChange: "transform",
|
|
321
|
+
zIndex: depth,
|
|
322
|
+
visibility: visible ? "visible" : "hidden",
|
|
323
|
+
boxShadow: shouldShowShadow ? ACTIVE_PANEL_SHADOW : undefined,
|
|
324
|
+
}),
|
|
325
|
+
[isActive, depth, visible, shouldShowShadow],
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
// Dimming overlay style for behind panels
|
|
329
|
+
const dimmingStyle = React.useMemo<React.CSSProperties | null>(() => {
|
|
330
|
+
if (dimmingOpacity <= 0) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
position: "absolute",
|
|
335
|
+
inset: 0,
|
|
336
|
+
backgroundColor: `rgba(0, 0, 0, ${dimmingOpacity})`,
|
|
337
|
+
pointerEvents: "none",
|
|
338
|
+
zIndex: 1,
|
|
339
|
+
};
|
|
340
|
+
}, [dimmingOpacity]);
|
|
341
|
+
|
|
342
|
+
return (
|
|
343
|
+
<div
|
|
344
|
+
ref={elementRef}
|
|
345
|
+
data-stack-content={id}
|
|
346
|
+
data-depth={depth}
|
|
347
|
+
data-active={isActive ? "true" : "false"}
|
|
348
|
+
data-role={role}
|
|
349
|
+
style={staticStyle}
|
|
350
|
+
>
|
|
351
|
+
{children}
|
|
352
|
+
{dimmingStyle != null && <div style={dimmingStyle} data-dimming-overlay />}
|
|
353
|
+
</div>
|
|
354
|
+
);
|
|
355
|
+
},
|
|
356
|
+
);
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tests for SwipeStackOutlet component.
|
|
3
|
+
*/
|
|
4
|
+
import { render } from "@testing-library/react";
|
|
5
|
+
import { SwipeStackOutlet } from "./SwipeStackOutlet.js";
|
|
6
|
+
import type { SwipeInputState } from "../../hooks/gesture/types.js";
|
|
7
|
+
import type { StackPanel, StackNavigationState } from "./types.js";
|
|
8
|
+
|
|
9
|
+
const IDLE_STATE: SwipeInputState = {
|
|
10
|
+
phase: "idle",
|
|
11
|
+
displacement: { x: 0, y: 0 },
|
|
12
|
+
velocity: { x: 0, y: 0 },
|
|
13
|
+
direction: 0,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const createSwipingState = (displacementX: number): SwipeInputState => ({
|
|
17
|
+
phase: "swiping",
|
|
18
|
+
displacement: { x: displacementX, y: 0 },
|
|
19
|
+
velocity: { x: 0.5, y: 0 },
|
|
20
|
+
direction: displacementX > 0 ? 1 : displacementX < 0 ? -1 : 0,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const createPanels = (): StackPanel[] => [
|
|
24
|
+
{ id: "home", title: "Home", content: <div>Home Content</div> },
|
|
25
|
+
{ id: "list", title: "List", content: <div>List Content</div> },
|
|
26
|
+
{ id: "detail", title: "Detail", content: <div>Detail Content</div> },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
describe("SwipeStackOutlet", () => {
|
|
30
|
+
describe("rendering", () => {
|
|
31
|
+
it("renders container with correct attributes", () => {
|
|
32
|
+
const navigationState: StackNavigationState = {
|
|
33
|
+
stack: ["home"],
|
|
34
|
+
depth: 0,
|
|
35
|
+
isRevealing: false,
|
|
36
|
+
revealDepth: null,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const { container } = render(
|
|
40
|
+
<SwipeStackOutlet
|
|
41
|
+
panels={createPanels()}
|
|
42
|
+
navigationState={navigationState}
|
|
43
|
+
inputState={IDLE_STATE}
|
|
44
|
+
containerSize={400}
|
|
45
|
+
/>,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const stackContainer = container.querySelector("[data-swipe-stack-container]");
|
|
49
|
+
expect(stackContainer).toBeInTheDocument();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("renders only active panel at root depth", () => {
|
|
53
|
+
const navigationState: StackNavigationState = {
|
|
54
|
+
stack: ["home"],
|
|
55
|
+
depth: 0,
|
|
56
|
+
isRevealing: false,
|
|
57
|
+
revealDepth: null,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const { container, getByText } = render(
|
|
61
|
+
<SwipeStackOutlet
|
|
62
|
+
panels={createPanels()}
|
|
63
|
+
navigationState={navigationState}
|
|
64
|
+
inputState={IDLE_STATE}
|
|
65
|
+
containerSize={400}
|
|
66
|
+
/>,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(getByText("Home Content")).toBeInTheDocument();
|
|
70
|
+
|
|
71
|
+
// Should only have one SwipeStackContent
|
|
72
|
+
const stackContents = container.querySelectorAll("[data-stack-content]");
|
|
73
|
+
expect(stackContents).toHaveLength(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("renders active and behind panels when depth > 0", () => {
|
|
77
|
+
const navigationState: StackNavigationState = {
|
|
78
|
+
stack: ["home", "list"],
|
|
79
|
+
depth: 1,
|
|
80
|
+
isRevealing: false,
|
|
81
|
+
revealDepth: null,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const { container, getByText } = render(
|
|
85
|
+
<SwipeStackOutlet
|
|
86
|
+
panels={createPanels()}
|
|
87
|
+
navigationState={navigationState}
|
|
88
|
+
inputState={IDLE_STATE}
|
|
89
|
+
containerSize={400}
|
|
90
|
+
/>,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(getByText("Home Content")).toBeInTheDocument();
|
|
94
|
+
expect(getByText("List Content")).toBeInTheDocument();
|
|
95
|
+
|
|
96
|
+
const stackContents = container.querySelectorAll("[data-stack-content]");
|
|
97
|
+
expect(stackContents).toHaveLength(2);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("assigns correct roles to panels", () => {
|
|
101
|
+
const navigationState: StackNavigationState = {
|
|
102
|
+
stack: ["home", "list"],
|
|
103
|
+
depth: 1,
|
|
104
|
+
isRevealing: false,
|
|
105
|
+
revealDepth: null,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const { container } = render(
|
|
109
|
+
<SwipeStackOutlet
|
|
110
|
+
panels={createPanels()}
|
|
111
|
+
navigationState={navigationState}
|
|
112
|
+
inputState={IDLE_STATE}
|
|
113
|
+
containerSize={400}
|
|
114
|
+
/>,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const homePanel = container.querySelector('[data-stack-content="home"]');
|
|
118
|
+
const listPanel = container.querySelector('[data-stack-content="list"]');
|
|
119
|
+
|
|
120
|
+
expect(homePanel?.getAttribute("data-role")).toBe("behind");
|
|
121
|
+
expect(listPanel?.getAttribute("data-role")).toBe("active");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("swipe behavior", () => {
|
|
126
|
+
it("passes input state to panels during swipe", () => {
|
|
127
|
+
const navigationState: StackNavigationState = {
|
|
128
|
+
stack: ["home", "list"],
|
|
129
|
+
depth: 1,
|
|
130
|
+
isRevealing: false,
|
|
131
|
+
revealDepth: null,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const swipeState = createSwipingState(200);
|
|
135
|
+
|
|
136
|
+
const { container, rerender } = render(
|
|
137
|
+
<SwipeStackOutlet
|
|
138
|
+
panels={createPanels()}
|
|
139
|
+
navigationState={navigationState}
|
|
140
|
+
inputState={IDLE_STATE}
|
|
141
|
+
containerSize={400}
|
|
142
|
+
/>,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Start swiping
|
|
146
|
+
rerender(
|
|
147
|
+
<SwipeStackOutlet
|
|
148
|
+
panels={createPanels()}
|
|
149
|
+
navigationState={navigationState}
|
|
150
|
+
inputState={swipeState}
|
|
151
|
+
containerSize={400}
|
|
152
|
+
/>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const listPanel = container.querySelector('[data-stack-content="list"]') as HTMLElement;
|
|
156
|
+
const homePanel = container.querySelector('[data-stack-content="home"]') as HTMLElement;
|
|
157
|
+
|
|
158
|
+
// Active panel should be at displacement position
|
|
159
|
+
expect(listPanel.style.transform).toBe("translateX(200px)");
|
|
160
|
+
|
|
161
|
+
// Behind panel should be at parallax position (-60px, halfway from -120 to 0)
|
|
162
|
+
expect(homePanel.style.transform).toBe("translateX(-60px)");
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("cached content", () => {
|
|
167
|
+
it("uses getCachedContent when provided", () => {
|
|
168
|
+
const navigationState: StackNavigationState = {
|
|
169
|
+
stack: ["home"],
|
|
170
|
+
depth: 0,
|
|
171
|
+
isRevealing: false,
|
|
172
|
+
revealDepth: null,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const calledWith: string[] = [];
|
|
176
|
+
const getCachedContent = (panelId: string): React.ReactNode => {
|
|
177
|
+
calledWith.push(panelId);
|
|
178
|
+
if (panelId === "home") {
|
|
179
|
+
return <div>Cached Home Content</div>;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const { getByText, queryByText } = render(
|
|
185
|
+
<SwipeStackOutlet
|
|
186
|
+
panels={createPanels()}
|
|
187
|
+
navigationState={navigationState}
|
|
188
|
+
inputState={IDLE_STATE}
|
|
189
|
+
containerSize={400}
|
|
190
|
+
getCachedContent={getCachedContent}
|
|
191
|
+
/>,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
expect(getByText("Cached Home Content")).toBeInTheDocument();
|
|
195
|
+
expect(queryByText("Home Content")).not.toBeInTheDocument();
|
|
196
|
+
expect(calledWith).toContain("home");
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("falls back to panel content when getCachedContent returns null", () => {
|
|
200
|
+
const navigationState: StackNavigationState = {
|
|
201
|
+
stack: ["home"],
|
|
202
|
+
depth: 0,
|
|
203
|
+
isRevealing: false,
|
|
204
|
+
revealDepth: null,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const getCachedContent = (): React.ReactNode => null;
|
|
208
|
+
|
|
209
|
+
const { getByText } = render(
|
|
210
|
+
<SwipeStackOutlet
|
|
211
|
+
panels={createPanels()}
|
|
212
|
+
navigationState={navigationState}
|
|
213
|
+
inputState={IDLE_STATE}
|
|
214
|
+
containerSize={400}
|
|
215
|
+
getCachedContent={getCachedContent}
|
|
216
|
+
/>,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(getByText("Home Content")).toBeInTheDocument();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("deep navigation", () => {
|
|
224
|
+
it("only renders active and behind panels, not deeper ones", () => {
|
|
225
|
+
const navigationState: StackNavigationState = {
|
|
226
|
+
stack: ["home", "list", "detail"],
|
|
227
|
+
depth: 2,
|
|
228
|
+
isRevealing: false,
|
|
229
|
+
revealDepth: null,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const { container, getByText, queryByText } = render(
|
|
233
|
+
<SwipeStackOutlet
|
|
234
|
+
panels={createPanels()}
|
|
235
|
+
navigationState={navigationState}
|
|
236
|
+
inputState={IDLE_STATE}
|
|
237
|
+
containerSize={400}
|
|
238
|
+
/>,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
// Should render detail (active) and list (behind)
|
|
242
|
+
expect(getByText("Detail Content")).toBeInTheDocument();
|
|
243
|
+
expect(getByText("List Content")).toBeInTheDocument();
|
|
244
|
+
|
|
245
|
+
// Should NOT render home (too deep)
|
|
246
|
+
expect(queryByText("Home Content")).not.toBeInTheDocument();
|
|
247
|
+
|
|
248
|
+
const stackContents = container.querySelectorAll("[data-stack-content]");
|
|
249
|
+
expect(stackContents).toHaveLength(2);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
});
|