diff --git a/packages/vkui/src/components/View/View.test.tsx b/packages/vkui/src/components/View/View.test.tsx index e279d5ffcd..5ad9b598d8 100644 --- a/packages/vkui/src/components/View/View.test.tsx +++ b/packages/vkui/src/components/View/View.test.tsx @@ -249,7 +249,8 @@ describe('View', () => { expect(document.getElementById('p1')).toBeTruthy(); expect(document.getElementById('p2')).toBeNull(); }); - it('restores scroll after swipeBack', () => { + + it('restores scroll after cancelled swipeBack (mouse up during the move)', () => { let y = 101; scrollsCache['scroll']['p1'] = 22; const [MockScroll, scrollTo] = mockScrollContext(() => y); @@ -262,7 +263,21 @@ describe('View', () => { }); fireEvent.mouseUp(view); rerender(); - expect(scrollTo).toBeCalledWith(0, 22); + expect(scrollTo).toHaveBeenCalledWith(0, 22); + }); + + it('restores scroll when swipeBack cancelled because user moves panel back to starting point', () => { + const currentScrollPosition = 22; + const startPosition = { clientX: 0, clientY: 100 }; + const [MockScroll, scrollTo] = mockScrollContext(() => currentScrollPosition); + const { view, rerender, SwipeBack } = setupSwipeBack({ Wrapper: MockScroll }); + fireEvent.mouseDown(view, startPosition); + fireEvent.mouseMove(view, { clientX: SWIPE_BACK_SHIFT_THRESHOLD, clientY: 100 }); + fireEvent.mouseMove(view, startPosition); + fireEvent.mouseUp(view); + + rerender(); + expect(scrollTo).toHaveBeenCalledWith(0, currentScrollPosition); }); describe('horizontal scrollable elements', () => { diff --git a/packages/vkui/src/components/View/View.tsx b/packages/vkui/src/components/View/View.tsx index 2ee5b871f0..37b644c266 100644 --- a/packages/vkui/src/components/View/View.tsx +++ b/packages/vkui/src/components/View/View.tsx @@ -113,6 +113,7 @@ export const View = ({ const prevSwipingBack = usePrevious(swipingBack); const prevBrowserSwipe = usePrevious(browserSwipe); const prevSwipeBackResult = usePrevious(swipeBackResult); + const prevSwipeBackShift = usePrevious(swipeBackShift); const prevSwipeBackPrevPanel = usePrevious(swipeBackPrevPanel); const prevOnTransition = usePrevious(onTransition); @@ -439,11 +440,6 @@ export const View = ({ ); } - // Если свайп назад отменился (когда пользователь недостаточно сильно свайпнул) - if (prevSwipeBackResult === 'fail' && !swipeBackResult && activePanel !== null) { - scroll?.scrollTo(0, scrolls.current[activePanel]); - } - // Закончился Safari свайп if (prevActivePanel !== activePanelProp && browserSwipe) { setBrowserSwipe(false); @@ -476,6 +472,33 @@ export const View = ({ waitTransitionFinish, ]); + React.useEffect( + function restoreScrollPositionWhenSwipeBackIsCancelled() { + // Если свайп назад отменился (когда пользователь недостаточно сильно свайпнул) + const swipeBackCancelledInTheMiddleOfAction = + prevSwipeBackResult === 'fail' && !swipeBackResult; + const swipeBackCancelledByMovingPanelBackToInitialPoint = + prevSwipingBack && !swipingBack && prevSwipeBackShift === 0; + + if ( + (swipeBackCancelledInTheMiddleOfAction || + swipeBackCancelledByMovingPanelBackToInitialPoint) && + activePanel !== null + ) { + scroll?.scrollTo(0, scrolls.current[activePanel]); + } + }, + [ + prevSwipeBackResult, + swipeBackResult, + prevSwipingBack, + swipingBack, + prevSwipeBackShift, + activePanel, + scroll, + ], + ); + return (