feat(tabbar): add onTabItemPress event and enhance TabBar functionality

- Introduced onTabItemPress event in TabBarRootView to handle tab item presses.
- Updated TabBarModule to include the new event in the definition.
- Modified TabBarRootWrapper to accept onTabItemPress and selectedIndex props.
- Enhanced TabRoot to utilize the onTabItemPress callback for scroll-to-top functionality.

This update improves user interaction with the tab bar by allowing for additional actions on tab item presses.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-09-22 22:26:39 +08:00
parent a9d5cbd519
commit a350950672
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
5 changed files with 60 additions and 23 deletions

View File

@ -9,29 +9,29 @@ import ExpoModulesCore
public class TabBarModule: Module {
public func definition() -> ModuleDefinition {
Name("TabBarRoot")
public func definition() -> ModuleDefinition {
Name("TabBarRoot")
Function("switchTab") { [weak self] (reactTag: Int, index: Int) in
DispatchQueue.main.async {
guard let bridge = self?.appContext?.reactBridge else {
return
}
if let sourceView = bridge.uiManager.view(forReactTag: NSNumber(value: reactTag)) as! TabBarRootView?
{
sourceView.switchToTab(index: index)
}
}
Function("switchTab") { [weak self] (reactTag: Int, index: Int) in
DispatchQueue.main.async {
guard let bridge = self?.appContext?.reactBridge else {
return
}
View(TabBarRootView.self) {
Prop("selectedIndex") { (view, index: Int) in
view.switchToTab(index: index)
}
Events("onTabIndexChange")
if let sourceView = bridge.uiManager.view(forReactTag: NSNumber(value: reactTag))
as! TabBarRootView?
{
sourceView.switchToTab(index: index)
}
}
}
View(TabBarRootView.self) {
Prop("selectedIndex") { (view, index: Int) in
view.switchToTab(index: index)
}
Events("onTabIndexChange", "onTabItemPress")
}
}
}

View File

@ -37,6 +37,7 @@ class TabBarRootView: ExpoView {
private var tabViewControllers: [UIViewController] = []
private let onTabIndexChange = EventDispatcher()
private let onTabItemPress = EventDispatcher()
required init(appContext: AppContext? = nil) {
super.init(appContext: appContext)
@ -133,6 +134,14 @@ class TabBarRootView: ExpoView {
// MARK: - UITabBarControllerDelegate
extension TabBarRootView: UITabBarControllerDelegate {
func tabBarController(
_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController
) -> Bool {
if let index = tabViewControllers.firstIndex(of: viewController) {
onTabItemPress(["index": index, "currentIndex": tabBarController.selectedIndex])
}
return true
}
func tabBarController(
_ tabBarController: UITabBarController, didSelect viewController: UIViewController
) {

View File

@ -1,9 +1,12 @@
import { useTypeScriptHappyCallback } from "@follow/hooks"
import { useAtom } from "jotai"
import type { FC, PropsWithChildren } from "react"
import * as React from "react"
import { use, useCallback, useMemo } from "react"
import { StyleSheet } from "react-native"
import { useNavigationScrollToTop } from "@/src/components/layouts/tabbar/hooks"
import { BottomTabContext } from "./BottomTabContext"
import { TabBarRootWrapper } from "./native"
import { TabScreen } from "./TabScreen"
@ -13,6 +16,8 @@ export const TabRoot: FC<PropsWithChildren> = ({ children }) => {
const { currentIndexAtom } = use(BottomTabContext)
const [tabIndex, setTabIndex] = useAtom(currentIndexAtom)
const scrollToTop = useNavigationScrollToTop()
const MapChildren = useMemo(() => {
let cnt = 0
return React.Children.map(children, (child) => {
@ -33,6 +38,15 @@ export const TabRoot: FC<PropsWithChildren> = ({ children }) => {
},
[setTabIndex],
)}
onTabItemPress={useTypeScriptHappyCallback(
(e) => {
const { index, currentIndex } = e.nativeEvent
if (index === currentIndex) {
scrollToTop()
}
},
[scrollToTop],
)}
selectedIndex={tabIndex}
>
{MapChildren}

View File

@ -16,6 +16,11 @@ export const TabScreenWrapper = ({ title, icon, activeIcon, ...rest }: TabScreen
return <View {...rest} />
}
export const TabBarRootWrapper = (props: TabBarRootWrapperProps) => {
export const TabBarRootWrapper = ({
onTabIndexChange,
onTabItemPress,
selectedIndex,
...props
}: TabBarRootWrapperProps) => {
return <View {...props} />
}

View File

@ -28,7 +28,16 @@ export interface ResolvedTabScreenProps extends Omit<TabScreenProps, "icon" | "a
icon: (props: TabbarIconProps) => React.ReactNode
}
export type TabBarRootWrapperProps = {
export interface TabBarRootWrapperProps extends ViewProps {
/**
*
* iOS only
*/
onTabIndexChange: (e: NativeSyntheticEvent<{ index: number }>) => void
/**
* iOS only
*/
onTabItemPress?: (e: NativeSyntheticEvent<{ index: number; currentIndex: number }>) => void
selectedIndex: number
} & ViewProps
}