diff --git a/src/components/tabController/TabPage.tsx b/src/components/tabController/TabPage.tsx index 20a22bfbb5..365af95101 100644 --- a/src/components/tabController/TabPage.tsx +++ b/src/components/tabController/TabPage.tsx @@ -1,5 +1,5 @@ -import React, {PropsWithChildren, useCallback, useContext, useState, useMemo} from 'react'; -import {type StyleProp, StyleSheet, type ViewStyle} from 'react-native'; +import React, {PropsWithChildren, useCallback, useContext, useState, useMemo, useRef, useEffect} from 'react'; +import {type StyleProp, StyleSheet, type ViewStyle, findNodeHandle, AccessibilityInfo} from 'react-native'; import Reanimated, {useAnimatedStyle, useAnimatedReaction, runOnJS} from 'react-native-reanimated'; // import {Freeze} from 'react-freeze'; import TabBarContext from './TabBarContext'; @@ -31,6 +31,8 @@ export interface TabControllerPageProps { style?: StyleProp; } +const READER_FOCUS_DELAY = 300; + /** * @description: TabController's TabPage * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx @@ -46,6 +48,8 @@ export default function TabPage({ }: PropsWithChildren) { const {currentPage, asCarousel, nestedInScrollView, containerWidth} = useContext(TabBarContext); const [shouldLoad, setLoaded] = useState(!lazy); + const [isActive, setIsActive] = useState(false); + const pageRef = useRef(null); // const [focused, setFocused] = useState(false); const lazyLoad = useCallback(() => { @@ -60,13 +64,13 @@ export default function TabPage({ return currentPage.value; }, (currentPage /* , previousPage */) => { - const isActive = currentPage === index; - // const wasActive = previousPage === index; - // const nearActive = asCarousel && (currentPage - 1 === index || currentPage + 1 === index); - // const wasNearActive = - // asCarousel && previousPage !== null && (previousPage - 1 === index || previousPage + 1 === index); + const newIsActive = currentPage === index; + + if (newIsActive !== isActive) { + runOnJS(setIsActive)(newIsActive); + } - if (isActive) { + if (newIsActive) { runOnJS(lazyLoad)(); } @@ -76,7 +80,25 @@ export default function TabPage({ // runOnJS(setFocused)(false); // } }, - [currentPage, lazyLoad]); + [currentPage, lazyLoad, isActive]); + + useEffect(() => { + let timeoutId: NodeJS.Timeout; + if (isActive && pageRef.current && shouldLoad) { + timeoutId = setTimeout(() => { + const node = findNodeHandle(pageRef.current); + if (node) { + AccessibilityInfo.setAccessibilityFocus(node); + } + }, READER_FOCUS_DELAY); + } + + return () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + }, [isActive, shouldLoad]); const animatedPageStyle = useAnimatedStyle(() => { const isActive = Math.round(currentPage.value) === index; @@ -99,7 +121,7 @@ export default function TabPage({ }, [asCarousel, animatedPageStyle, containerWidth, style]); return ( - + {!shouldLoad && renderLoading?.()} {shouldLoad && props.children} {/* {props.children} */}