From b421b4eb1f634005b5103d7019d1a3c44f86a96f Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Sun, 3 Dec 2023 13:27:50 +0700 Subject: [PATCH 1/3] #19642 fix scroll on vitual view when keyboard open --- src/components/Composer/index.js | 16 +++++++++-- src/hooks/useScrollBarVisible/index.native.ts | 7 +++++ src/hooks/useScrollBarVisible/index.ts | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/hooks/useScrollBarVisible/index.native.ts create mode 100644 src/hooks/useScrollBarVisible/index.ts diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index 4bb3df5c1b85..9bb849f23af6 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -8,6 +8,7 @@ import RNTextInput from '@components/RNTextInput'; import Text from '@components/Text'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withNavigation from '@components/withNavigation'; +import useScrollBarVisible from '@hooks/useScrollBarVisible'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; import compose from '@libs/compose'; @@ -180,6 +181,7 @@ function Composer({ const [caretContent, setCaretContent] = useState(''); const [valueBeforeCaret, setValueBeforeCaret] = useState(''); const [textInputWidth, setTextInputWidth] = useState(''); + const isScrollBarVisible = useScrollBarVisible(textInput, value); useEffect(() => { if (!shouldClear) { @@ -418,18 +420,26 @@ function Composer({ ); - const inputStyleMemo = useMemo( - () => [ + const scrollStyleMemo = useMemo(() => { + if (Browser.isMobileSafari()) { + return isScrollBarVisible ? [styles.overflowScroll, styles.overscrollBehaviorContain] : styles.overflowHidden; + } + return [ // We are hiding the scrollbar to prevent it from reducing the text input width, // so we can get the correct scroll height while calculating the number of lines. numberOfLines < maxLines ? styles.overflowHidden : {}, + ]; + }, [isScrollBarVisible, maxLines, numberOfLines, styles.overflowHidden, styles.overflowScroll, styles.overscrollBehaviorContain]); + const inputStyleMemo = useMemo( + () => [ StyleSheet.flatten([style, {outline: 'none'}]), StyleUtils.getComposeTextAreaPadding(numberOfLines, isComposerFullSize), Browser.isMobileSafari() || Browser.isSafari() ? styles.rtlTextRenderForSafari : {}, + scrollStyleMemo, ], - [numberOfLines, maxLines, styles.overflowHidden, styles.rtlTextRenderForSafari, style, StyleUtils, isComposerFullSize], + [numberOfLines, scrollStyleMemo, styles.rtlTextRenderForSafari, style, StyleUtils, isComposerFullSize], ); return ( diff --git a/src/hooks/useScrollBarVisible/index.native.ts b/src/hooks/useScrollBarVisible/index.native.ts new file mode 100644 index 000000000000..37df393cbc6e --- /dev/null +++ b/src/hooks/useScrollBarVisible/index.native.ts @@ -0,0 +1,7 @@ +/** + * Native doesn't have the DOM, so we just return null. + * + */ +const useScrollBarVisible = () => null; + +export default useScrollBarVisible; diff --git a/src/hooks/useScrollBarVisible/index.ts b/src/hooks/useScrollBarVisible/index.ts new file mode 100644 index 000000000000..91ccf0d7ba8b --- /dev/null +++ b/src/hooks/useScrollBarVisible/index.ts @@ -0,0 +1,28 @@ +import {useCallback, useEffect, useState} from 'react'; + +const useScrollBarVisible = (ref: React.RefObject, value: string) => { + const [isScrollBarVisible, setIsScrollBarVisible] = useState(false); + + const handleResize = useCallback(() => { + if (!ref.current) { + return; + } + const {scrollHeight, clientHeight} = ref.current; + setIsScrollBarVisible(scrollHeight > clientHeight); + }, [ref]); + + useEffect(() => { + if (!ref.current || !('ResizeObserver' in (window || {}))) { + return; + } + + const resizeObserver = new ResizeObserver(handleResize); + resizeObserver.observe(ref.current); + return () => { + resizeObserver.disconnect(); + }; + }, [handleResize, ref, value]); + return isScrollBarVisible; +}; + +export default useScrollBarVisible; From ae6ffbaca5a68cd46d03db73d0e28775927e8f30 Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Tue, 12 Dec 2023 22:55:25 +0700 Subject: [PATCH 2/3] add shouldScrollContain props for composer --- src/components/Composer/index.js | 9 +++++++-- .../ComposerWithSuggestions/ComposerWithSuggestions.js | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index 9bb849f23af6..f152573645d1 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -87,6 +87,9 @@ const propTypes = { /** Whether the sull composer is open */ isComposerFullSize: PropTypes.bool, + /** Should set scroll behavior is contain for composer */ + shouldScrollContain: PropTypes.bool, + ...withLocalizePropTypes, }; @@ -114,6 +117,7 @@ const defaultProps = { checkComposerVisibility: () => false, isReportActionCompose: false, isComposerFullSize: false, + shouldScrollContain: false, }; /** @@ -165,6 +169,7 @@ function Composer({ selection: selectionProp, isReportActionCompose, isComposerFullSize, + shouldScrollContain, ...props }) { const theme = useTheme(); @@ -421,7 +426,7 @@ function Composer({ ); const scrollStyleMemo = useMemo(() => { - if (Browser.isMobileSafari()) { + if (shouldScrollContain) { return isScrollBarVisible ? [styles.overflowScroll, styles.overscrollBehaviorContain] : styles.overflowHidden; } return [ @@ -429,7 +434,7 @@ function Composer({ // so we can get the correct scroll height while calculating the number of lines. numberOfLines < maxLines ? styles.overflowHidden : {}, ]; - }, [isScrollBarVisible, maxLines, numberOfLines, styles.overflowHidden, styles.overflowScroll, styles.overscrollBehaviorContain]); + }, [shouldScrollContain, isScrollBarVisible, maxLines, numberOfLines, styles.overflowHidden, styles.overflowScroll, styles.overscrollBehaviorContain]); const inputStyleMemo = useMemo( () => [ diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index f5a321f72799..4bd600c761bf 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -10,6 +10,7 @@ import useDebounce from '@hooks/useDebounce'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as Browser from '@libs/Browser'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import compose from '@libs/compose'; import * as ComposerUtils from '@libs/ComposerUtils'; @@ -556,6 +557,7 @@ function ComposerWithSuggestions({ setComposerHeight(composerLayoutHeight); }} onScroll={hideSuggestionMenu} + shouldScrollContain={Browser.isMobileSafari()} /> From 83a1e4c80bbd3a796a10675eb0c5145848bb508c Mon Sep 17 00:00:00 2001 From: Cong Pham Date: Wed, 13 Dec 2023 01:51:24 +0700 Subject: [PATCH 3/3] update naming code review --- src/components/Composer/index.js | 16 ++++++++-------- src/hooks/useIsScrollBarVisible/index.native.ts | 7 +++++++ .../index.ts | 4 ++-- src/hooks/useScrollBarVisible/index.native.ts | 7 ------- .../ComposerWithSuggestions.js | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 src/hooks/useIsScrollBarVisible/index.native.ts rename src/hooks/{useScrollBarVisible => useIsScrollBarVisible}/index.ts (83%) delete mode 100644 src/hooks/useScrollBarVisible/index.native.ts diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index f152573645d1..713439671162 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -8,7 +8,7 @@ import RNTextInput from '@components/RNTextInput'; import Text from '@components/Text'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withNavigation from '@components/withNavigation'; -import useScrollBarVisible from '@hooks/useScrollBarVisible'; +import useIsScrollBarVisible from '@hooks/useIsScrollBarVisible'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Browser from '@libs/Browser'; import compose from '@libs/compose'; @@ -87,8 +87,8 @@ const propTypes = { /** Whether the sull composer is open */ isComposerFullSize: PropTypes.bool, - /** Should set scroll behavior is contain for composer */ - shouldScrollContain: PropTypes.bool, + /** Should make the input only scroll inside the element avoid scroll out to parent */ + shouldContainScroll: PropTypes.bool, ...withLocalizePropTypes, }; @@ -117,7 +117,7 @@ const defaultProps = { checkComposerVisibility: () => false, isReportActionCompose: false, isComposerFullSize: false, - shouldScrollContain: false, + shouldContainScroll: false, }; /** @@ -169,7 +169,7 @@ function Composer({ selection: selectionProp, isReportActionCompose, isComposerFullSize, - shouldScrollContain, + shouldContainScroll, ...props }) { const theme = useTheme(); @@ -186,7 +186,7 @@ function Composer({ const [caretContent, setCaretContent] = useState(''); const [valueBeforeCaret, setValueBeforeCaret] = useState(''); const [textInputWidth, setTextInputWidth] = useState(''); - const isScrollBarVisible = useScrollBarVisible(textInput, value); + const isScrollBarVisible = useIsScrollBarVisible(textInput, value); useEffect(() => { if (!shouldClear) { @@ -426,7 +426,7 @@ function Composer({ ); const scrollStyleMemo = useMemo(() => { - if (shouldScrollContain) { + if (shouldContainScroll) { return isScrollBarVisible ? [styles.overflowScroll, styles.overscrollBehaviorContain] : styles.overflowHidden; } return [ @@ -434,7 +434,7 @@ function Composer({ // so we can get the correct scroll height while calculating the number of lines. numberOfLines < maxLines ? styles.overflowHidden : {}, ]; - }, [shouldScrollContain, isScrollBarVisible, maxLines, numberOfLines, styles.overflowHidden, styles.overflowScroll, styles.overscrollBehaviorContain]); + }, [shouldContainScroll, isScrollBarVisible, maxLines, numberOfLines, styles.overflowHidden, styles.overflowScroll, styles.overscrollBehaviorContain]); const inputStyleMemo = useMemo( () => [ diff --git a/src/hooks/useIsScrollBarVisible/index.native.ts b/src/hooks/useIsScrollBarVisible/index.native.ts new file mode 100644 index 000000000000..a461e1b7b074 --- /dev/null +++ b/src/hooks/useIsScrollBarVisible/index.native.ts @@ -0,0 +1,7 @@ +/** + * Native doesn't have the DOM, so we just return null. + * + */ +const useIsScrollBarVisible = () => null; + +export default useIsScrollBarVisible; diff --git a/src/hooks/useScrollBarVisible/index.ts b/src/hooks/useIsScrollBarVisible/index.ts similarity index 83% rename from src/hooks/useScrollBarVisible/index.ts rename to src/hooks/useIsScrollBarVisible/index.ts index 91ccf0d7ba8b..4ab3a7bb24db 100644 --- a/src/hooks/useScrollBarVisible/index.ts +++ b/src/hooks/useIsScrollBarVisible/index.ts @@ -1,6 +1,6 @@ import {useCallback, useEffect, useState} from 'react'; -const useScrollBarVisible = (ref: React.RefObject, value: string) => { +const useIsScrollBarVisible = (ref: React.RefObject, value: string) => { const [isScrollBarVisible, setIsScrollBarVisible] = useState(false); const handleResize = useCallback(() => { @@ -25,4 +25,4 @@ const useScrollBarVisible = (ref: React.RefObject null; - -export default useScrollBarVisible; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index 4bd600c761bf..2ff7506b50a8 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -557,7 +557,7 @@ function ComposerWithSuggestions({ setComposerHeight(composerLayoutHeight); }} onScroll={hideSuggestionMenu} - shouldScrollContain={Browser.isMobileSafari()} + shouldContainScroll={Browser.isMobileSafari()} />