diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx index a3afd3febbde..9d5357bfaf00 100644 --- a/src/components/ButtonWithDropdownMenu/index.tsx +++ b/src/components/ButtonWithDropdownMenu/index.tsx @@ -17,7 +17,7 @@ import mergeRefs from '@libs/mergeRefs'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import type {AnchorPosition} from '@src/styles'; -import type {ButtonWithDropdownMenuProps, ButtonWithDropdownMenuRef} from './types'; +import type {ButtonWithDropdownMenuProps} from './types'; const defaultAnchorAlignment = { horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT, @@ -25,7 +25,7 @@ const defaultAnchorAlignment = { vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, }; -function ButtonWithDropdownMenuInner({ref, ...props}: ButtonWithDropdownMenuProps) { +function ButtonWithDropdownMenu({ref, ...props}: ButtonWithDropdownMenuProps) { const { success = true, isSplitButton = true, @@ -296,8 +296,6 @@ function ButtonWithDropdownMenuInner({ref, ...props}: ButtonWithDrop ); } -ButtonWithDropdownMenuInner.displayName = 'ButtonWithDropdownMenu'; -const ButtonWithDropdownMenu = ButtonWithDropdownMenuInner as ( - props: ButtonWithDropdownMenuProps & {ref?: React.Ref}, -) => ReturnType; +ButtonWithDropdownMenu.displayName = 'ButtonWithDropdownMenu'; + export default ButtonWithDropdownMenu; diff --git a/src/components/EmojiPicker/EmojiPicker.tsx b/src/components/EmojiPicker/EmojiPicker.tsx index c05e09ced506..0b19f051db92 100644 --- a/src/components/EmojiPicker/EmojiPicker.tsx +++ b/src/components/EmojiPicker/EmojiPicker.tsx @@ -1,5 +1,5 @@ /* eslint-disable react-compiler/react-compiler */ -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef, RefObject} from 'react'; import {Dimensions, View} from 'react-native'; import type {Emoji} from '@assets/emojis/types'; @@ -28,9 +28,10 @@ const DEFAULT_ANCHOR_ORIGIN = { type EmojiPickerProps = { viewportOffsetTop: number; + ref?: ForwardedRef; }; -function EmojiPicker({viewportOffsetTop}: EmojiPickerProps, ref: ForwardedRef) { +function EmojiPicker({viewportOffsetTop, ref}: EmojiPickerProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const [isEmojiPickerVisible, setIsEmojiPickerVisible] = useState(false); @@ -246,4 +247,4 @@ function EmojiPicker({viewportOffsetTop}: EmojiPickerProps, ref: ForwardedRef; withoutOverlay?: boolean; + ref?: ForwardedRef; }; function EmojiPickerButtonDropdown( // eslint-disable-next-line @typescript-eslint/no-unused-vars - {isDisabled = false, withoutOverlay = false, onModalHide, onInputChange, value, disabled, style, ...otherProps}: EmojiPickerButtonDropdownProps, + {isDisabled = false, withoutOverlay = false, onModalHide, onInputChange, value, disabled, style, ref, ...otherProps}: EmojiPickerButtonDropdownProps, // eslint-disable-next-line @typescript-eslint/no-unused-vars - ref: ForwardedRef, ) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -105,4 +105,4 @@ function EmojiPickerButtonDropdown( EmojiPickerButtonDropdown.displayName = 'EmojiPickerButtonDropdown'; -export default React.forwardRef(EmojiPickerButtonDropdown); +export default EmojiPickerButtonDropdown; diff --git a/src/components/EmojiPicker/EmojiPickerMenu/BaseEmojiPickerMenu.tsx b/src/components/EmojiPicker/EmojiPickerMenu/BaseEmojiPickerMenu.tsx index 3b489b08b16f..de937238c108 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/BaseEmojiPickerMenu.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/BaseEmojiPickerMenu.tsx @@ -40,6 +40,9 @@ type BaseEmojiPickerMenuProps = { /** Whether the list should always bounce vertically */ alwaysBounceVertical?: boolean; + + /** Reference to the outer element */ + ref?: ForwardedRef>; }; /** @@ -77,10 +80,18 @@ function ListEmptyComponent() { return {translate('common.noResultsFound')}; } -function BaseEmojiPickerMenu( - {headerEmojis, scrollToHeader, isFiltered, listWrapperStyle = [], data, renderItem, stickyHeaderIndices = [], extraData = [], alwaysBounceVertical = false}: BaseEmojiPickerMenuProps, - ref: ForwardedRef>, -) { +function BaseEmojiPickerMenu({ + headerEmojis, + scrollToHeader, + isFiltered, + listWrapperStyle = [], + data, + renderItem, + stickyHeaderIndices = [], + extraData = [], + alwaysBounceVertical = false, + ref, +}: BaseEmojiPickerMenuProps) { const styles = useThemeStyles(); return ( <> @@ -123,4 +134,4 @@ function BaseEmojiPickerMenu( BaseEmojiPickerMenu.displayName = 'BaseEmojiPickerMenu'; -export default React.forwardRef(BaseEmojiPickerMenu); +export default BaseEmojiPickerMenu; diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx index 692677a26752..630b09c63dd4 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.native.tsx @@ -1,13 +1,11 @@ import type {ListRenderItem} from '@shopify/flash-list'; import lodashDebounce from 'lodash/debounce'; import React, {useCallback} from 'react'; -import type {ForwardedRef} from 'react'; import {InteractionManager, View} from 'react-native'; import type {Emoji} from '@assets/emojis/types'; import EmojiPickerMenuItem from '@components/EmojiPicker/EmojiPickerMenuItem'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; -import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSingleExecution from '@hooks/useSingleExecution'; @@ -23,7 +21,7 @@ import type EmojiPickerMenuProps from './types'; import useEmojiPickerMenu from './useEmojiPickerMenu'; // eslint-disable-next-line @typescript-eslint/no-unused-vars -function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, ref: ForwardedRef) { +function EmojiPickerMenu({onEmojiSelected, activeEmoji, ref}: EmojiPickerMenuProps) { const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); const {shouldUseNarrowLayout} = useResponsiveLayout(); @@ -151,4 +149,4 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r } EmojiPickerMenu.displayName = 'EmojiPickerMenu'; -export default React.forwardRef(EmojiPickerMenu); +export default EmojiPickerMenu; diff --git a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx index 9629c740ae38..542f615e686b 100755 --- a/src/components/EmojiPicker/EmojiPickerMenu/index.tsx +++ b/src/components/EmojiPicker/EmojiPickerMenu/index.tsx @@ -1,7 +1,6 @@ import type {ListRenderItem} from '@shopify/flash-list'; import throttle from 'lodash/throttle'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import type {ForwardedRef} from 'react'; import {View} from 'react-native'; import EmojiPickerMenuItem from '@components/EmojiPicker/EmojiPickerMenuItem'; import Text from '@components/Text'; @@ -30,7 +29,7 @@ import useEmojiPickerMenu from './useEmojiPickerMenu'; const throttleTime = isMobile() ? 200 : 50; -function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, ref: ForwardedRef) { +function EmojiPickerMenu({onEmojiSelected, activeEmoji, ref}: EmojiPickerMenuProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {windowWidth} = useWindowDimensions(); @@ -361,4 +360,4 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r } EmojiPickerMenu.displayName = 'EmojiPickerMenu'; -export default React.forwardRef(EmojiPickerMenu); +export default EmojiPickerMenu; diff --git a/src/components/EmojiPicker/EmojiPickerMenu/types.ts b/src/components/EmojiPicker/EmojiPickerMenu/types.ts index b821cc89aeea..7db5821d2e12 100644 --- a/src/components/EmojiPicker/EmojiPickerMenu/types.ts +++ b/src/components/EmojiPicker/EmojiPickerMenu/types.ts @@ -1,10 +1,15 @@ +import type {ForwardedRef} from 'react'; import type {Emoji} from '@assets/emojis/types'; +import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; type EmojiPickerMenuProps = { /** Function to add the selected emoji to the main compose text input */ onEmojiSelected: (emoji: string, emojiObject: Emoji) => void; activeEmoji?: string; + + /** Reference to the outer element */ + ref?: ForwardedRef; }; export default EmojiPickerMenuProps; diff --git a/src/components/FlatList/index.android.tsx b/src/components/FlatList/index.android.tsx index 261d10488098..edc9f28cb5f8 100644 --- a/src/components/FlatList/index.android.tsx +++ b/src/components/FlatList/index.android.tsx @@ -1,12 +1,12 @@ import {useFocusEffect} from '@react-navigation/native'; -import type {ForwardedRef} from 'react'; -import React, {forwardRef, useCallback, useRef} from 'react'; -import type {FlatListProps, NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; +import React, {useCallback, useRef} from 'react'; +import type {NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; import {FlatList} from 'react-native'; +import type {CustomFlatListProps} from './index'; // FlatList wrapped with the freeze component will lose its scroll state when frozen (only for Android). // CustomFlatList saves the offset and use it for scrollToOffset() when unfrozen. -function CustomFlatList(props: FlatListProps, ref: ForwardedRef) { +function CustomFlatList({ref, ...props}: CustomFlatListProps) { const lastScrollOffsetRef = useRef(0); const onScreenFocus = useCallback(() => { @@ -44,4 +44,4 @@ function CustomFlatList(props: FlatListProps, ref: ForwardedRef) } CustomFlatList.displayName = 'CustomFlatListWithRef'; -export default forwardRef(CustomFlatList); +export default CustomFlatList; diff --git a/src/components/FlatList/index.ios.tsx b/src/components/FlatList/index.ios.tsx index b01ed7eaf071..4c73b4417d18 100644 --- a/src/components/FlatList/index.ios.tsx +++ b/src/components/FlatList/index.ios.tsx @@ -1,10 +1,9 @@ -import type {ForwardedRef} from 'react'; -import React, {forwardRef, useCallback, useState} from 'react'; -import type {FlatListProps} from 'react-native'; +import React, {useCallback, useState} from 'react'; import {FlatList} from 'react-native'; +import type {CustomFlatListProps} from './index'; // On iOS, we have to unset maintainVisibleContentPosition while the user is scrolling to prevent jumping to the beginning issue -function CustomFlatList(props: FlatListProps, ref: ForwardedRef) { +function CustomFlatList({ref, ...props}: CustomFlatListProps) { const {maintainVisibleContentPosition: originalMaintainVisibleContentPosition, ...rest} = props; const [isScrolling, setIsScrolling] = useState(false); @@ -31,4 +30,4 @@ function CustomFlatList(props: FlatListProps, ref: ForwardedRef) } CustomFlatList.displayName = 'CustomFlatListWithRef'; -export default forwardRef(CustomFlatList); +export default CustomFlatList; diff --git a/src/components/FlatList/index.tsx b/src/components/FlatList/index.tsx index 1901d5a99da8..267f127600f9 100644 --- a/src/components/FlatList/index.tsx +++ b/src/components/FlatList/index.tsx @@ -1,5 +1,5 @@ /* eslint-disable es/no-optional-chaining, es/no-nullish-coalescing-operators, react/prop-types */ -import type {ForwardedRef, MutableRefObject} from 'react'; +import type {ForwardedRef, RefObject} from 'react'; import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import type {FlatListProps, NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; import {FlatList} from 'react-native'; @@ -9,8 +9,8 @@ import {isMobileSafari} from '@libs/Browser'; // We do a best effort to avoid content jumping by using some hacks on mobile Safari only. const IS_MOBILE_SAFARI = isMobileSafari(); -function mergeRefs(...args: Array | ForwardedRef | null>) { - return function forwardRef(node: FlatList) { +function mergeRefs(...args: Array | ForwardedRef | null>) { + return function (node: FlatList) { args.forEach((ref) => { if (ref == null) { return; @@ -29,7 +29,7 @@ function mergeRefs(...args: Array | ForwardedRef | ForwardedRef | null>) { +function useMergeRefs(...args: Array | ForwardedRef | null>) { return useMemo( () => mergeRefs(...args), // eslint-disable-next-line @@ -41,7 +41,11 @@ function getScrollableNode(flatList: FlatList | null): HTMLElement | undefined { return flatList?.getScrollableNode() as HTMLElement | undefined; } -function MVCPFlatList({maintainVisibleContentPosition, horizontal = false, onScroll, ...props}: FlatListProps, ref: ForwardedRef) { +type CustomFlatListProps = FlatListProps & { + ref?: ForwardedRef; +}; + +function MVCPFlatList({maintainVisibleContentPosition, horizontal = false, onScroll, ref, ...props}: CustomFlatListProps) { const {minIndexForVisible: mvcpMinIndexForVisible, autoscrollToTopThreshold: mvcpAutoscrollToTopThreshold} = maintainVisibleContentPosition ?? {}; const scrollRef = useRef(null); const prevFirstVisibleOffsetRef = useRef(0); @@ -197,7 +201,7 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false }; }, [prepareForMaintainVisibleContentPosition, setupMutationObserver]); - const setMergedRef = useMergeRefs(scrollRef, ref); + const setMergedRef = useMergeRefs(scrollRef, ref as ForwardedRef); const onRef = useCallback( (newRef: FlatList) => { @@ -253,4 +257,6 @@ function MVCPFlatList({maintainVisibleContentPosition, horizontal = false MVCPFlatList.displayName = 'MVCPFlatList'; -export default React.forwardRef(MVCPFlatList); +export default MVCPFlatList; + +export type {CustomFlatListProps}; diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx b/src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx index 9f014d04e960..35184f5320ef 100644 --- a/src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx +++ b/src/components/InvertedFlatList/BaseInvertedFlatList/index.e2e.tsx @@ -1,15 +1,16 @@ -import React, {forwardRef, useMemo} from 'react'; +import React, {useMemo} from 'react'; import type {FlatListProps, ScrollViewProps, ViewToken} from 'react-native'; import {DeviceEventEmitter, FlatList} from 'react-native'; import type {ReportAction} from '@src/types/onyx'; type BaseInvertedFlatListProps = FlatListProps & { shouldEnableAutoScrollToTopThreshold?: boolean; + ref?: React.ForwardedRef>; }; const AUTOSCROLL_TO_TOP_THRESHOLD = 128; -function BaseInvertedFlatListE2e(props: BaseInvertedFlatListProps, ref: React.ForwardedRef>) { +function BaseInvertedFlatListE2e({ref, ...props}: BaseInvertedFlatListProps) { const {shouldEnableAutoScrollToTopThreshold, ...rest} = props; const handleViewableItemsChanged = useMemo( @@ -47,4 +48,4 @@ function BaseInvertedFlatListE2e(props: BaseInvertedFlatListProps, ref: React.Fo BaseInvertedFlatListE2e.displayName = 'BaseInvertedFlatListE2e'; -export default forwardRef(BaseInvertedFlatListE2e); +export default BaseInvertedFlatListE2e; diff --git a/src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx b/src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx index 5cccc596a522..47274b133c6f 100644 --- a/src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx +++ b/src/components/InvertedFlatList/BaseInvertedFlatList/index.tsx @@ -1,5 +1,5 @@ import type {ForwardedRef} from 'react'; -import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {FlatListProps, ListRenderItem, ListRenderItemInfo, FlatList as RNFlatList, ScrollViewProps} from 'react-native'; import FlatList from '@components/FlatList'; import usePrevious from '@hooks/usePrevious'; @@ -24,11 +24,12 @@ type BaseInvertedFlatListProps = Omit, 'data' | 'renderItem' data: T[]; renderItem: ListRenderItem; initialScrollKey?: string | null; + ref?: ForwardedRef; }; const AUTOSCROLL_TO_TOP_THRESHOLD = 250; -function BaseInvertedFlatList(props: BaseInvertedFlatListProps, ref: ForwardedRef) { +function BaseInvertedFlatList({ref, ...props}: BaseInvertedFlatListProps) { const {shouldEnableAutoScrollToTopThreshold, initialScrollKey, data, onStartReached, renderItem, keyExtractor = defaultKeyExtractor, ...rest} = props; // `initialScrollIndex` doesn't work properly with FlatList, this uses an alternative approach to achieve the same effect. // What we do is start rendering the list from `initialScrollKey` and then whenever we reach the start we render more @@ -141,7 +142,7 @@ function BaseInvertedFlatList(props: BaseInvertedFlatListProps, ref: Forwa BaseInvertedFlatList.displayName = 'BaseInvertedFlatList'; -export default forwardRef(BaseInvertedFlatList); +export default BaseInvertedFlatList; export {AUTOSCROLL_TO_TOP_THRESHOLD}; diff --git a/src/components/InvertedFlatList/index.native.tsx b/src/components/InvertedFlatList/index.native.tsx index 68110627c3b6..50e6fb1d5cc1 100644 --- a/src/components/InvertedFlatList/index.native.tsx +++ b/src/components/InvertedFlatList/index.native.tsx @@ -1,11 +1,9 @@ -import type {ForwardedRef} from 'react'; -import React, {forwardRef} from 'react'; -import type {FlatList} from 'react-native'; +import React from 'react'; import BaseInvertedFlatList from './BaseInvertedFlatList'; import type {BaseInvertedFlatListProps} from './BaseInvertedFlatList'; import CellRendererComponent from './CellRendererComponent'; -function BaseInvertedFlatListWithRef(props: BaseInvertedFlatListProps, ref: ForwardedRef) { +function BaseInvertedFlatListWithRef({ref, ...props}: BaseInvertedFlatListProps) { return ( (props: BaseInvertedFlatListProps, ref BaseInvertedFlatListWithRef.displayName = 'BaseInvertedFlatListWithRef'; -export default forwardRef(BaseInvertedFlatListWithRef); +export default BaseInvertedFlatListWithRef; diff --git a/src/components/InvertedFlatList/index.tsx b/src/components/InvertedFlatList/index.tsx index 9eb4b327dfcf..40b1451e1fed 100644 --- a/src/components/InvertedFlatList/index.tsx +++ b/src/components/InvertedFlatList/index.tsx @@ -1,6 +1,5 @@ -import type {ForwardedRef} from 'react'; -import React, {forwardRef, useEffect, useRef} from 'react'; -import type {FlatList, NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; +import React, {useEffect, useRef} from 'react'; +import type {NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; import {DeviceEventEmitter} from 'react-native'; import CONST from '@src/CONST'; import BaseInvertedFlatList from './BaseInvertedFlatList'; @@ -9,7 +8,7 @@ import CellRendererComponent from './CellRendererComponent'; // This is adapted from https://codesandbox.io/s/react-native-dsyse // It's a HACK alert since FlatList has inverted scrolling on web -function InvertedFlatList({onScroll: onScrollProp = () => {}, ...props}: BaseInvertedFlatListProps, ref: ForwardedRef) { +function InvertedFlatList({onScroll: onScrollProp = () => {}, ref, ...props}: BaseInvertedFlatListProps) { const lastScrollEvent = useRef(null); const scrollEndTimeout = useRef(null); const updateInProgress = useRef(false); @@ -96,4 +95,4 @@ function InvertedFlatList({onScroll: onScrollProp = () => {}, ...props}: Base InvertedFlatList.displayName = 'InvertedFlatList'; -export default forwardRef(InvertedFlatList); +export default InvertedFlatList; diff --git a/src/components/Modal/BaseModal.tsx b/src/components/Modal/BaseModal.tsx index ceda868d53a0..253f5812a0bb 100644 --- a/src/components/Modal/BaseModal.tsx +++ b/src/components/Modal/BaseModal.tsx @@ -1,4 +1,4 @@ -import React, {forwardRef, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; // Animated required for side panel navigation // eslint-disable-next-line no-restricted-imports @@ -26,51 +26,49 @@ import ModalContext from './ModalContext'; import ReanimatedModal from './ReanimatedModal'; import type BaseModalProps from './types'; -function BaseModal( - { - isVisible, - onClose, - shouldSetModalVisibility = true, - onModalHide = () => {}, - type, - popoverAnchorPosition = {}, - innerContainerStyle = {}, - outerStyle, - onModalShow = () => {}, - onModalWillShow, - onModalWillHide, - fullscreen = true, - animationIn, - animationOut, - hideModalContentWhileAnimating = false, - animationInTiming, - animationOutTiming, - animationInDelay, - statusBarTranslucent = true, - navigationBarTranslucent = true, - onLayout, - avoidKeyboard = false, - children, - shouldUseCustomBackdrop = false, - onBackdropPress, - modalId, - shouldEnableNewFocusManagement = false, - restoreFocusType, - shouldUseModalPaddingStyle = true, - initialFocus = false, - swipeThreshold = 150, - swipeDirection, - shouldPreventScrollOnFocus = false, - enableEdgeToEdgeBottomSafeAreaPadding, - shouldApplySidePanelOffset = type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED, - hasBackdrop, - backdropOpacity, - shouldDisableBottomSafeAreaPadding = false, - shouldIgnoreBackHandlerDuringTransition = false, - forwardedFSClass = CONST.FULLSTORY.CLASS.UNMASK, - }: BaseModalProps, - ref: React.ForwardedRef, -) { +function BaseModal({ + isVisible, + onClose, + shouldSetModalVisibility = true, + onModalHide = () => {}, + type, + popoverAnchorPosition = {}, + innerContainerStyle = {}, + outerStyle, + onModalShow = () => {}, + onModalWillShow, + onModalWillHide, + fullscreen = true, + animationIn, + animationOut, + hideModalContentWhileAnimating = false, + animationInTiming, + animationOutTiming, + animationInDelay, + statusBarTranslucent = true, + navigationBarTranslucent = true, + onLayout, + avoidKeyboard = false, + children, + shouldUseCustomBackdrop = false, + onBackdropPress, + modalId, + shouldEnableNewFocusManagement = false, + restoreFocusType, + shouldUseModalPaddingStyle = true, + initialFocus = false, + swipeThreshold = 150, + swipeDirection, + shouldPreventScrollOnFocus = false, + enableEdgeToEdgeBottomSafeAreaPadding, + shouldApplySidePanelOffset = type === CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED, + hasBackdrop, + backdropOpacity, + shouldDisableBottomSafeAreaPadding = false, + shouldIgnoreBackHandlerDuringTransition = false, + forwardedFSClass = CONST.FULLSTORY.CLASS.UNMASK, + ref, +}: BaseModalProps) { // When the `enableEdgeToEdgeBottomSafeAreaPadding` prop is explicitly set, we enable edge-to-edge mode. const isUsingEdgeToEdgeMode = enableEdgeToEdgeBottomSafeAreaPadding !== undefined; const theme = useTheme(); @@ -368,4 +366,4 @@ function BaseModal( BaseModal.displayName = 'BaseModalWithRef'; -export default forwardRef(BaseModal); +export default BaseModal; diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 86fd8590b7a6..2a112f6987ea 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,5 +1,6 @@ import type {FocusTrapProps} from 'focus-trap-react'; -import type {ViewStyle} from 'react-native'; +import type {ForwardedRef} from 'react'; +import type {View, ViewStyle} from 'react-native'; import type {ValueOf} from 'type-fest'; import type {ForwardedFSClassProps} from '@libs/Fullstory/types'; import type CONST from '@src/CONST'; @@ -113,6 +114,11 @@ type BaseModalProps = Partial & * Disables the bottom safe area padding in the modal. Used in for scrollable FeatureTrainingModal. */ shouldDisableBottomSafeAreaPadding?: boolean; + + /** + * Reference to the outer element. + */ + ref?: ForwardedRef; }; export default BaseModalProps; diff --git a/src/components/QRShare/QRShareWithDownload/index.native.tsx b/src/components/QRShare/QRShareWithDownload/index.native.tsx index 44950d9bdfa7..72d78611b0c1 100644 --- a/src/components/QRShare/QRShareWithDownload/index.native.tsx +++ b/src/components/QRShare/QRShareWithDownload/index.native.tsx @@ -1,15 +1,13 @@ -import type {ForwardedRef} from 'react'; -import React, {forwardRef, useImperativeHandle, useRef} from 'react'; +import React, {useImperativeHandle, useRef} from 'react'; import ViewShot from 'react-native-view-shot'; import getQrCodeFileName from '@components/QRShare/getQrCodeDownloadFileName'; -import type {QRShareProps} from '@components/QRShare/types'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import fileDownload from '@libs/fileDownload'; import QRShare from '..'; -import type QRShareWithDownloadHandle from './types'; +import type {QRShareWithDownloadProps} from './types'; -function QRShareWithDownload(props: QRShareProps, ref: ForwardedRef) { +function QRShareWithDownload({ref, ...props}: QRShareWithDownloadProps) { const {isOffline} = useNetwork(); const {translate} = useLocalize(); @@ -37,4 +35,4 @@ function QRShareWithDownload(props: QRShareProps, ref: ForwardedRef) { +function QRShareWithDownload({ref, ...props}: QRShareWithDownloadProps) { const {isOffline} = useNetwork(); const qrShareRef = useRef(null); @@ -41,4 +40,4 @@ function QRShareWithDownload(props: QRShareProps, ref: ForwardedRef Promise | undefined; }; -export default QRShareWithDownloadHandle; +type QRShareWithDownloadProps = Omit & { + ref?: ForwardedRef; +}; + +export type {QRShareWithDownloadProps, QRShareWithDownloadHandle}; diff --git a/src/components/QRShare/index.tsx b/src/components/QRShare/index.tsx index dd392aecfc0b..d7d60e0ccd3a 100644 --- a/src/components/QRShare/index.tsx +++ b/src/components/QRShare/index.tsx @@ -1,5 +1,4 @@ -import type {ForwardedRef} from 'react'; -import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; +import React, {useImperativeHandle, useRef, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; import {View} from 'react-native'; import type {Svg} from 'react-native-svg'; @@ -12,12 +11,23 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import variables from '@styles/variables'; -import type {QRShareHandle, QRShareProps} from './types'; +import type {QRShareProps} from './types'; -function QRShare( - {url, title, subtitle, logo, svgLogo, svgLogoFillColor, logoBackgroundColor, logoRatio, logoMarginRatio, shouldShowExpensifyLogo = true, additionalStyles, size}: QRShareProps, - ref: ForwardedRef, -) { +function QRShare({ + url, + title, + subtitle, + logo, + svgLogo, + svgLogoFillColor, + logoBackgroundColor, + logoRatio, + logoMarginRatio, + shouldShowExpensifyLogo = true, + additionalStyles, + size, + ref, +}: QRShareProps) { const styles = useThemeStyles(); const theme = useTheme(); const {shouldUseNarrowLayout} = useResponsiveLayout(); @@ -94,4 +104,4 @@ function QRShare( QRShare.displayName = 'QRShare'; -export default forwardRef(QRShare); +export default QRShare; diff --git a/src/components/QRShare/types.ts b/src/components/QRShare/types.ts index 97e2fcd2a87b..9520cbde4d6b 100644 --- a/src/components/QRShare/types.ts +++ b/src/components/QRShare/types.ts @@ -1,4 +1,5 @@ import type React from 'react'; +import type {ForwardedRef} from 'react'; import type {ImageSourcePropType, StyleProp, ViewStyle} from 'react-native'; import type {Svg, SvgProps} from 'react-native-svg'; import type {ValueOf} from 'type-fest'; @@ -66,6 +67,9 @@ type QRShareProps = { * The size of the QR code */ size?: ValueOf; + + /** Reference to the outer element */ + ref?: ForwardedRef; }; type QRShareHandle = { diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 0735a7a45ede..ff60ab2d6097 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,6 +1,6 @@ /* eslint-disable rulesdir/no-acc-spread-in-reduce */ import type {ForwardedRef, RefObject} from 'react'; -import React, {forwardRef, useCallback, useEffect, useMemo} from 'react'; +import React, {useCallback, useEffect, useMemo} from 'react'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {View} from 'react-native'; import Animated, {interpolateColor, useAnimatedStyle, useSharedValue} from 'react-native-reanimated'; @@ -67,30 +67,31 @@ type SearchAutocompleteInputProps = { /** Map of autocomplete suggestions. Required for highlighting to work properly */ substitutionMap: SubstitutionMap; + + /** Reference to the outer element */ + ref?: ForwardedRef; } & Pick; -function SearchAutocompleteInput( - { - value, - onSearchQueryChange, - onSubmit = () => {}, - autocompleteListRef, - isFullWidth, - disabled = false, - shouldShowOfflineMessage = false, - autoFocus = true, - onFocus, - onBlur, - caretHidden = false, - wrapperStyle, - wrapperFocusedStyle = {}, - outerWrapperStyle, - isSearchingForReports, - selection, - substitutionMap, - }: SearchAutocompleteInputProps, - forwardedRef: ForwardedRef, -) { +function SearchAutocompleteInput({ + value, + onSearchQueryChange, + onSubmit = () => {}, + autocompleteListRef, + isFullWidth, + disabled = false, + shouldShowOfflineMessage = false, + autoFocus = true, + onFocus, + onBlur, + caretHidden = false, + wrapperStyle, + wrapperFocusedStyle = {}, + outerWrapperStyle, + isSearchingForReports, + selection, + substitutionMap, + ref, +}: SearchAutocompleteInputProps) { const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); @@ -231,7 +232,7 @@ function SearchAutocompleteInput( onBlur?.(); }} isLoading={isSearchingForReports} - ref={forwardedRef} + ref={ref} type="markdown" multiline={false} parser={parser} @@ -255,4 +256,4 @@ function SearchAutocompleteInput( SearchAutocompleteInput.displayName = 'SearchAutocompleteInput'; export type {SearchAutocompleteInputProps}; -export default forwardRef(SearchAutocompleteInput); +export default SearchAutocompleteInput; diff --git a/src/components/Search/SearchAutocompleteList.tsx b/src/components/Search/SearchAutocompleteList.tsx index eab001ccbc26..01311dd6ebd5 100644 --- a/src/components/Search/SearchAutocompleteList.tsx +++ b/src/components/Search/SearchAutocompleteList.tsx @@ -1,6 +1,6 @@ import {accountIDSelector, emailSelector} from '@selectors/Session'; import type {ForwardedRef} from 'react'; -import React, {forwardRef, useCallback, useEffect, useMemo, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useState} from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import * as Expensicons from '@components/Icon/Expensicons'; import {useOptionsList} from '@components/OptionListContextProvider'; @@ -102,6 +102,9 @@ type SearchAutocompleteListProps = { /** All cards */ allCards: CardList; + + /** Reference to the outer element */ + ref?: ForwardedRef; }; const defaultListOptions = { @@ -153,25 +156,23 @@ function SearchRouterItem(props: UserListItemProps | SearchQueryList ); } -function SearchAutocompleteList( - { - autocompleteQueryValue, - handleSearch, - searchQueryItem, - getAdditionalSections, - onListItemPress, - setTextQuery, - updateAutocompleteSubstitutions, - shouldSubscribeToArrowKeyEvents = true, - onHighlightFirstItem, - textInputRef, - personalDetails, - reports, - allFeeds, - allCards, - }: SearchAutocompleteListProps, - ref: ForwardedRef, -) { +function SearchAutocompleteList({ + autocompleteQueryValue, + handleSearch, + searchQueryItem, + getAdditionalSections, + onListItemPress, + setTextQuery, + updateAutocompleteSubstitutions, + shouldSubscribeToArrowKeyEvents = true, + onHighlightFirstItem, + textInputRef, + personalDetails, + reports, + allFeeds, + allCards, + ref, +}: SearchAutocompleteListProps) { const styles = useThemeStyles(); const {translate, localeCompare} = useLocalize(); const {shouldUseNarrowLayout} = useResponsiveLayout(); @@ -796,6 +797,6 @@ function SearchAutocompleteList( ); } -export default forwardRef(SearchAutocompleteList); +export default SearchAutocompleteList; export {SearchRouterItem}; export type {GetAdditionalSectionsCallback}; diff --git a/src/components/Search/SearchInputSelectionWrapper/index.native.tsx b/src/components/Search/SearchInputSelectionWrapper/index.native.tsx index 41b46329b36b..14f5816fd731 100644 --- a/src/components/Search/SearchInputSelectionWrapper/index.native.tsx +++ b/src/components/Search/SearchInputSelectionWrapper/index.native.tsx @@ -1,12 +1,10 @@ -import type {ForwardedRef} from 'react'; -import React, {forwardRef, Suspense} from 'react'; +import React, {Suspense} from 'react'; import Deferred from '@components/Deferred'; import SearchAutocompleteInput from '@components/Search/SearchAutocompleteInput'; import type {SearchAutocompleteInputProps} from '@components/Search/SearchAutocompleteInput'; import SearchInputSelectionSkeleton from '@components/Skeletons/SearchInputSelectionSkeleton'; -import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; -function SearchInputSelectionWrapper(props: SearchAutocompleteInputProps, ref: ForwardedRef) { +function SearchInputSelectionWrapper({ref, ...props}: SearchAutocompleteInputProps) { return ( }> @@ -23,4 +21,4 @@ function SearchInputSelectionWrapper(props: SearchAutocompleteInputProps, ref: F SearchInputSelectionWrapper.displayName = 'SearchInputSelectionWrapper'; -export default forwardRef(SearchInputSelectionWrapper); +export default SearchInputSelectionWrapper; diff --git a/src/components/Search/SearchInputSelectionWrapper/index.tsx b/src/components/Search/SearchInputSelectionWrapper/index.tsx index 0dcf3282b032..1ec945ad3f96 100644 --- a/src/components/Search/SearchInputSelectionWrapper/index.tsx +++ b/src/components/Search/SearchInputSelectionWrapper/index.tsx @@ -1,10 +1,8 @@ -import type {ForwardedRef} from 'react'; -import React, {forwardRef} from 'react'; +import React from 'react'; import SearchAutocompleteInput from '@components/Search/SearchAutocompleteInput'; import type {SearchAutocompleteInputProps} from '@components/Search/SearchAutocompleteInput'; -import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types'; -function SearchInputSelectionWrapper({selection, ...props}: SearchAutocompleteInputProps, ref: ForwardedRef) { +function SearchInputSelectionWrapper({selection, ref, ...props}: SearchAutocompleteInputProps) { return ( , 'onScroll' | 'conten /** Violations indexed by transaction ID */ violations?: Record | undefined; + + /** Reference to the outer element */ + ref?: ForwardedRef; }; const keyExtractor = (item: SearchListItem, index: number) => item.keyForList ?? `${index}`; @@ -133,36 +136,34 @@ function isTransactionMatchWithGroupItem(transaction: Transaction, groupItem: Se return false; } -function SearchList( - { - data, - ListItem, - SearchTableHeader, - onSelectRow, - onCheckboxPress, - canSelectMultiple, - onScroll = () => {}, - onAllCheckboxPress, - contentContainerStyle, - onEndReachedThreshold, - onEndReached, - containerStyle, - ListFooterComponent, - shouldPreventDefaultFocusOnSelectRow, - shouldPreventLongPressRow, - queryJSON, - columns, - isFocused, - onViewableItemsChanged, - onLayout, - shouldAnimate, - isMobileSelectionModeEnabled, - areAllOptionalColumnsHidden, - newTransactions = [], - violations, - }: SearchListProps, - ref: ForwardedRef, -) { +function SearchList({ + data, + ListItem, + SearchTableHeader, + onSelectRow, + onCheckboxPress, + canSelectMultiple, + onScroll = () => {}, + onAllCheckboxPress, + contentContainerStyle, + onEndReachedThreshold, + onEndReached, + containerStyle, + ListFooterComponent, + shouldPreventDefaultFocusOnSelectRow, + shouldPreventLongPressRow, + queryJSON, + columns, + isFocused, + onViewableItemsChanged, + onLayout, + shouldAnimate, + isMobileSelectionModeEnabled, + areAllOptionalColumnsHidden, + newTransactions = [], + violations, + ref, +}: SearchListProps) { const styles = useThemeStyles(); const {hash, groupBy} = queryJSON; @@ -429,4 +430,4 @@ function SearchList( ); } -export default forwardRef(SearchList); +export default SearchList; diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 6cf462d3592f..92dcf29609b8 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -1,6 +1,6 @@ import {findFocusedRoute} from '@react-navigation/native'; import {deepEqual} from 'fast-equals'; -import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {TextInputProps} from 'react-native'; import {InteractionManager, View} from 'react-native'; import type {ValueOf} from 'type-fest'; @@ -81,9 +81,10 @@ type SearchRouterProps = { onRouterClose: () => void; shouldHideInputCaret?: TextInputProps['caretHidden']; isSearchRouterDisplayed?: boolean; + ref?: React.Ref; }; -function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDisplayed}: SearchRouterProps, ref: React.Ref) { +function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDisplayed, ref}: SearchRouterProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {setShouldResetSearchQuery} = useSearchContext(); @@ -532,4 +533,4 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla SearchRouter.displayName = 'SearchRouter'; -export default forwardRef(SearchRouter); +export default SearchRouter; diff --git a/src/components/withNavigationTransitionEnd.tsx b/src/components/withNavigationTransitionEnd.tsx index 0bb6f1ffa448..ac0350c67c8d 100644 --- a/src/components/withNavigationTransitionEnd.tsx +++ b/src/components/withNavigationTransitionEnd.tsx @@ -1,5 +1,5 @@ import {useNavigation} from '@react-navigation/native'; -import type {ComponentType, ForwardedRef, RefAttributes} from 'react'; +import type {ComponentType} from 'react'; import React, {useEffect, useState} from 'react'; import getComponentDisplayName from '@libs/getComponentDisplayName'; import type {PlatformStackNavigationProp} from '@libs/Navigation/PlatformStackNavigation/types'; @@ -7,8 +7,8 @@ import type {RootNavigatorParamList} from '@libs/Navigation/types'; type WithNavigationTransitionEndProps = {didScreenTransitionEnd: boolean}; -export default function (WrappedComponent: ComponentType>): React.ComponentType> { - function WithNavigationTransitionEnd(props: TProps, ref: ForwardedRef) { +export default function (WrappedComponent: ComponentType): React.ComponentType { + function WithNavigationTransitionEnd(props: TProps) { const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false); const navigation = useNavigation>(); @@ -26,14 +26,13 @@ export default function (WrappedComponent: ComponentType ); } WithNavigationTransitionEnd.displayName = `WithNavigationTransitionEnd(${getComponentDisplayName(WrappedComponent)})`; - return React.forwardRef(WithNavigationTransitionEnd); + return WithNavigationTransitionEnd; } export type {WithNavigationTransitionEndProps}; diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index b1718018314b..8c6734a5802e 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -9,7 +9,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; -import type QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; +import type {QRShareWithDownloadHandle} from '@components/QRShare/QRShareWithDownload/types'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; diff --git a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/Camera.tsx b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/Camera.tsx index 8a3fab671ab6..e9e9141d8768 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/Camera.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/Camera.tsx @@ -1,11 +1,10 @@ import {useIsFocused} from '@react-navigation/native'; import React from 'react'; -import type {ForwardedRef} from 'react'; import {Camera as VisionCamera} from 'react-native-vision-camera'; import type {NavigationAwareCameraNativeProps} from './types'; // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -function Camera({cameraTabIndex, ...props}: NavigationAwareCameraNativeProps, ref: ForwardedRef) { +function Camera({cameraTabIndex, ref, ...props}: NavigationAwareCameraNativeProps) { const isCameraActive = useIsFocused(); return ( @@ -21,4 +20,4 @@ function Camera({cameraTabIndex, ...props}: NavigationAwareCameraNativeProps, re Camera.displayName = 'NavigationAwareCamera'; -export default React.forwardRef(Camera); +export default Camera; diff --git a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/WebCamera.tsx b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/WebCamera.tsx index 82a8edb71faf..2914ceea7229 100644 --- a/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/WebCamera.tsx +++ b/src/pages/iou/request/step/IOURequestStepScan/NavigationAwareCamera/WebCamera.tsx @@ -2,13 +2,12 @@ import {useIsFocused} from '@react-navigation/native'; import React, {useState} from 'react'; import type {ForwardedRef} from 'react'; import {View} from 'react-native'; -import type {Camera} from 'react-native-vision-camera'; import Webcam from 'react-webcam'; import useThemeStyles from '@hooks/useThemeStyles'; import type {NavigationAwareCameraProps} from './types'; // Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. -function WebCamera(props: NavigationAwareCameraProps, ref: ForwardedRef) { +function WebCamera({itemRef, ref, ...props}: NavigationAwareCameraProps) { const [isInitialized, setIsInitialized] = useState(false); const shouldShowCamera = useIsFocused(); const styles = useThemeStyles(); @@ -32,4 +31,4 @@ function WebCamera(props: NavigationAwareCameraProps, ref: ForwardedRef; +}; type NavigationAwareCameraNativeProps = Omit & { cameraTabIndex: number; + ref?: ForwardedRef; }; export type {NavigationAwareCameraProps, NavigationAwareCameraNativeProps}; diff --git a/src/pages/workspace/WorkspaceOverviewSharePage.tsx b/src/pages/workspace/WorkspaceOverviewSharePage.tsx index 5fa9e6f0b957..a44d184e5cff 100644 --- a/src/pages/workspace/WorkspaceOverviewSharePage.tsx +++ b/src/pages/workspace/WorkspaceOverviewSharePage.tsx @@ -8,7 +8,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import {useSession} from '@components/OnyxListItemProvider'; import QRShareWithDownload from '@components/QRShare/QRShareWithDownload'; -import type QRShareWithDownloadHandle from '@components/QRShare/QRShareWithDownload/types'; +import type {QRShareWithDownloadHandle} from '@components/QRShare/QRShareWithDownload/types'; import RenderHTML from '@components/RenderHTML'; import ScreenWrapper from '@components/ScreenWrapper'; import ScrollView from '@components/ScrollView';