-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21298 from lukemorawski/lukemorawski19642-blank_a…
…rea_on_scroll Unnecessary blank area is created when scrolling down while keyboard is open #19642
- Loading branch information
Showing
13 changed files
with
359 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,77 @@ | ||
import React, {useEffect, useRef} from 'react'; | ||
import {View} from 'react-native'; | ||
import DomUtils from '@libs/DomUtils'; | ||
import SwipeableViewProps from './types'; | ||
|
||
// Swipeable View is available just on Android/iOS for now. | ||
export default ({children}: SwipeableViewProps) => children; | ||
// Min delta y in px to trigger swipe | ||
const MIN_DELTA_Y = 25; | ||
|
||
function SwipeableView({onSwipeUp, onSwipeDown, style, children}: SwipeableViewProps) { | ||
const ref = useRef<View | null>(null); | ||
const scrollableChildRef = useRef<HTMLElement | null>(null); | ||
const startY = useRef(0); | ||
const isScrolling = useRef(false); | ||
|
||
useEffect(() => { | ||
if (!ref.current) { | ||
return; | ||
} | ||
|
||
const element = ref.current as unknown as HTMLElement; | ||
|
||
const handleTouchStart = (event: TouchEvent) => { | ||
startY.current = event.touches[0].clientY; | ||
}; | ||
|
||
const handleTouchEnd = (event: TouchEvent) => { | ||
const deltaY = event.changedTouches[0].clientY - startY.current; | ||
const isSelecting = DomUtils.isActiveTextSelection(); | ||
let canSwipeDown = true; | ||
let canSwipeUp = true; | ||
if (scrollableChildRef.current) { | ||
canSwipeUp = scrollableChildRef.current.scrollHeight - scrollableChildRef.current.scrollTop === scrollableChildRef.current.clientHeight; | ||
canSwipeDown = scrollableChildRef.current.scrollTop === 0; | ||
} | ||
|
||
if (deltaY > MIN_DELTA_Y && onSwipeDown && !isSelecting && canSwipeDown && !isScrolling.current) { | ||
onSwipeDown(); | ||
} | ||
|
||
if (deltaY < -MIN_DELTA_Y && onSwipeUp && !isSelecting && canSwipeUp && !isScrolling.current) { | ||
onSwipeUp(); | ||
} | ||
isScrolling.current = false; | ||
}; | ||
|
||
const handleScroll = (event: Event) => { | ||
isScrolling.current = true; | ||
if (!event.target || scrollableChildRef.current) { | ||
return; | ||
} | ||
scrollableChildRef.current = event.target as HTMLElement; | ||
}; | ||
|
||
element.addEventListener('touchstart', handleTouchStart); | ||
element.addEventListener('touchend', handleTouchEnd); | ||
element.addEventListener('scroll', handleScroll, true); | ||
|
||
return () => { | ||
element.removeEventListener('touchstart', handleTouchStart); | ||
element.removeEventListener('touchend', handleTouchEnd); | ||
element.removeEventListener('scroll', handleScroll); | ||
}; | ||
}, [onSwipeDown, onSwipeUp]); | ||
|
||
return ( | ||
<View | ||
ref={ref} | ||
style={style} | ||
> | ||
{children} | ||
</View> | ||
); | ||
} | ||
|
||
SwipeableView.displayName = 'SwipeableView'; | ||
|
||
export default SwipeableView; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,18 @@ | ||
import {ReactNode} from 'react'; | ||
import {StyleProp, ViewStyle} from 'react-native'; | ||
|
||
type SwipeableViewProps = { | ||
/** The content to be rendered within the SwipeableView */ | ||
children: ReactNode; | ||
|
||
/** Callback to fire when the user swipes down on the child content */ | ||
onSwipeDown: () => void; | ||
onSwipeDown?: () => void; | ||
|
||
/** Callback to fire when the user swipes up on the child content */ | ||
onSwipeUp?: () => void; | ||
|
||
/** Style for the wrapper View, applied only for the web version. Not used by the native version, as it brakes the layout. */ | ||
style?: StyleProp<ViewStyle>; | ||
}; | ||
|
||
export default SwipeableViewProps; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* A hook that blocks viewport scroll when the keyboard is visible. | ||
* It does this by capturing the current scrollY position when the keyboard is shown, then scrolls back to this position smoothly on 'touchend' event. | ||
* This scroll blocking is removed when the keyboard hides. | ||
* This hook is doing nothing on native platforms. | ||
* | ||
* @example | ||
* useBlockViewportScroll(); | ||
*/ | ||
function useBlockViewportScroll() { | ||
// This hook is doing nothing on native platforms. | ||
// Check index.ts for web implementation. | ||
} | ||
|
||
export default useBlockViewportScroll; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import {useEffect, useRef} from 'react'; | ||
import Keyboard from '@libs/NativeWebKeyboard'; | ||
|
||
/** | ||
* A hook that blocks viewport scroll when the keyboard is visible. | ||
* It does this by capturing the current scrollY position when the keyboard is shown, then scrolls back to this position smoothly on 'touchend' event. | ||
* This scroll blocking is removed when the keyboard hides. | ||
* This hook is doing nothing on native platforms. | ||
* | ||
* @example | ||
* useBlockViewportScroll(); | ||
*/ | ||
function useBlockViewportScroll() { | ||
const optimalScrollY = useRef(0); | ||
const keyboardShowListenerRef = useRef(() => {}); | ||
const keyboardHideListenerRef = useRef(() => {}); | ||
|
||
useEffect(() => { | ||
const handleTouchEnd = () => { | ||
window.scrollTo({top: optimalScrollY.current, behavior: 'smooth'}); | ||
}; | ||
|
||
const handleKeybShow = () => { | ||
optimalScrollY.current = window.scrollY; | ||
window.addEventListener('touchend', handleTouchEnd); | ||
}; | ||
|
||
const handleKeybHide = () => { | ||
window.removeEventListener('touchend', handleTouchEnd); | ||
}; | ||
|
||
keyboardShowListenerRef.current = Keyboard.addListener('keyboardDidShow', handleKeybShow); | ||
keyboardHideListenerRef.current = Keyboard.addListener('keyboardDidHide', handleKeybHide); | ||
|
||
return () => { | ||
keyboardShowListenerRef.current(); | ||
keyboardHideListenerRef.current(); | ||
window.removeEventListener('touchend', handleTouchEnd); | ||
}; | ||
}, []); | ||
} | ||
|
||
export default useBlockViewportScroll; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import {Keyboard} from 'react-native'; | ||
|
||
export default Keyboard; |
Oops, something went wrong.