diff --git a/.changeset/public-foxes-cheer.md b/.changeset/public-foxes-cheer.md new file mode 100644 index 00000000..9a4be2ae --- /dev/null +++ b/.changeset/public-foxes-cheer.md @@ -0,0 +1,6 @@ +--- +'react-native-bottom-tabs': minor +'@bottom-tabs/react-navigation': minor +--- + +feat: add ios tab roles diff --git a/apps/example/ios/Podfile.lock b/apps/example/ios/Podfile.lock index b0a038e8..ce8d0a32 100644 --- a/apps/example/ios/Podfile.lock +++ b/apps/example/ios/Podfile.lock @@ -1205,71 +1205,7 @@ PODS: - SwiftUIIntrospect (~> 1.0) - Yoga - react-native-safe-area-context (5.2.0): - - DoubleConversion - - glog - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - react-native-safe-area-context/common (= 5.2.0) - - react-native-safe-area-context/fabric (= 5.2.0) - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-safe-area-context/common (5.2.0): - - DoubleConversion - - glog - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - - react-native-safe-area-context/fabric (5.2.0): - - DoubleConversion - - glog - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - react-native-safe-area-context/common - - React-NativeModulesApple - - React-RCTFabric - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - Yoga - React-nativeconfig (0.77.1) - React-NativeModulesApple (0.77.1): - glog @@ -1557,7 +1493,6 @@ PODS: - React-ImageManager - React-jsi - React-NativeModulesApple - - React-RCTAppDelegate - React-RCTFabric - React-rendererdebug - React-utils @@ -1591,29 +1526,6 @@ PODS: - ReactCommon/turbomodule/core - Yoga - RNScreens (4.9.1): - - DoubleConversion - - glog - - RCT-Folly (= 2024.11.18.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-RCTImage - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNScreens/common (= 4.9.1) - - Yoga - - RNScreens/common (4.9.1): - DoubleConversion - glog - RCT-Folly (= 2024.11.18.00) @@ -1695,7 +1607,6 @@ DEPENDENCIES: - React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) - React-jsc (from `../node_modules/react-native/ReactCommon/jsc`) - - React-jsc/Fabric (from `../node_modules/react-native/ReactCommon/jsc`) - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) @@ -1902,71 +1813,71 @@ SPEC CHECKSUMS: FBLazyVector: 79c4b7ec726447eec5f8593379466bd9fde1aa14 fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: eb93e2f488219332457c3c4eafd2738ddc7e80b8 - RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 + RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 RCTDeprecation: 664055db806cce35c3c1b43c84414dd66e117ae6 RCTRequired: dc9a83fa1012054f94430d210337ca3a1afe6fc0 RCTTypeSafety: 031cefa254a1df313a196f105b8fcffdab1c5ab6 React: 8edfc46c315852ec88ea4a29d5e79019af3dc667 React-callinvoker: 4450b01574dfc7a8f074f7e29e6965ac04859c8f - React-Core: a318cda2bd04acffe4f70703098625b201aa2237 - React-CoreModules: ebe93fa403bbd4d0909de105ffd34eeaad355083 - React-cxxreact: 6fe3b8f8e8baf6a22fc39c8c4b4a0e1b5ae3e374 + React-Core: 1fcd0d52ae09bdf7cf1fe96dd94b082208e43b86 + React-CoreModules: 78e04d2319b1b61e0d4ed7fcd3e366d461819279 + React-cxxreact: f9ca69323c1a9c22756ad1a4ed629fb6b44b2a18 React-debug: b0f7271aeacc2eb9e34f863397dcfc204ef721c0 - React-defaultsnativemodule: 07704c5e30a5e66065a46aa11d4014941917a8ee - React-domnativemodule: dc3492f56861c82e658ea7db60316f7f1a8bace7 - React-Fabric: 7a3d2abb0607f100881cc6c8e54484324c109260 - React-FabricComponents: 4206a041e4671277d45deaf89e52f20951a8dd7d - React-FabricImage: b05580c4f17de740c38dd5e54d289e913f5bc5df + React-defaultsnativemodule: e790bf1d1a300a23504257f306a6629d2a60d845 + React-domnativemodule: adcfebf6c3b7882b28a061ed2789777f3d337a18 + React-Fabric: ecf387c8cca0e7ed2ef5104cd2725f38409163b2 + React-FabricComponents: f7cd4fab1308f52c418b474dc877b94f7acc5672 + React-FabricImage: e5c94c5679f2fec2261e76809aa86765fb4e0322 React-featureflags: 23d3dcdac6c9badeeb631db8a0883c7a3108d580 - React-featureflagsnativemodule: 75c84559ad5fe3f26ccccdd34ee0320bbf191e4b - React-graphics: 61380f6d01a225af9a3808dfd0f16622d2b6f90d - React-idlecallbacksnativemodule: 58ef763402c13067c0e85c615caf7b389d661435 - React-ImageManager: bab699b4ed44ce23b23d5bcab1cdc376eb69d583 + React-featureflagsnativemodule: 7fc7346e83f792b6cbc851be03cbf201601a81fc + React-graphics: 348400b8ba57611d552af6db5dc7d42ccf132d08 + React-idlecallbacksnativemodule: 8a111e8e0be17ef628ea58ce1c1b1587e331fc51 + React-ImageManager: ee8526b1af93152133709104c6d649d5dada63b3 React-jsc: 1f6b8e576f2858c5479683647c081de145ce8055 - React-jserrorhandler: f46bec9688c2fd853d101e7fb39ee48e162d5077 - React-jsi: 917f26392eaec18d7ce4e197eb87f680ca87e426 - React-jsiexecutor: 73a715d55e8ee66377ffe831063c5b7bb1a81448 - React-jsinspector: e4ba333f8ed2bf14f0f5459482a28ea922145169 - React-jsitracing: 838bbd073e24e84cf936354f085721cbc9204d70 - React-logger: 1935d6e6461e9c8be4c87af56c56a4876021171e - React-Mapbuffer: 212171f037e3b22e6c2df839aa826806da480b85 - React-microtasksnativemodule: a0ffd165c39251512d8cf51e9e8f5719dabc38b6 - react-native-bottom-tabs: 6ee03990297f7e37f5c1dd6f5259cee851733d4f - react-native-safe-area-context: e54b360402f089600c2fb0d825d1d3d918b99e15 + React-jserrorhandler: 51806588d8259e44cab7f35e72468be6ab8f6798 + React-jsi: a75033c737fbcb46d80c80fc20b9475bfbf8d2bf + React-jsiexecutor: ed2125b6786f75b40cc5e3da791d7ab78a13e711 + React-jsinspector: 22c5bd5e056328a95678e8f8df9e757171dd21fd + React-jsitracing: 9e7066f99151f99ed588f2055e011845b12a1bf6 + React-logger: e7eeebaed32b88dcc29b10901aa8c5822dc397c4 + React-Mapbuffer: 73dd1210c4ecf0dfb4e2d4e06f2a13f824a801a9 + React-microtasksnativemodule: dece61f766f8d326099d217603b1ebb50d6bb707 + react-native-bottom-tabs: 537b63b00fc50b296da66c1e5199cde4efce9e88 + react-native-safe-area-context: 3e33e7c43c8b74dba436a5a32651cb8d7064c740 React-nativeconfig: cb207ebba7cafce30657c7ad9f1587a8f32e4564 - React-NativeModulesApple: 76a5d35322908fbc88871e6dd20433bea2b8b2db - React-perflogger: 8152bab3f0eb4b8751f282f9af7caed2c823a9ea - React-performancetimeline: 3ef4a640b56f9c7ec5f52bd93217b9b607c37cf4 + React-NativeModulesApple: 38f252170af5351c88bc2e94d697359cd8c031e6 + React-perflogger: c4c3b7c18f8a50cdbe2bcdd2f15705ba029a5a02 + React-performancetimeline: 38bda258bd9f9da19b27615e8edfbec064aa42cc React-RCTActionSheet: 0fdf55fb8724856d63ca8c63cdb4e2325e15e8ec - React-RCTAnimation: b93f5a1675cc2599e96851fec13c909fdfb1d6bb - React-RCTAppDelegate: e3127aff7db7100ee0000e3f67956e9c6cbaa13f - React-RCTBlob: 53dc2afa8ccdc1b6d6885d81f6862fcb918a1875 - React-RCTFabric: 0a4c2a18d0ef3368f900dc08ea15ab532dd3dcf3 - React-RCTFBReactNativeSpec: ec50e74af2993fb51c1f9991cc7226fea21aaa26 - React-RCTImage: 028171a4d7017ea96a2e605c817cd76f01ed3836 - React-RCTLinking: e3f5431ab5f8f56b82387d41a2c484a278a8e645 - React-RCTNetwork: 6de20da228ffe8bd9c9e3bafe3f7d1dfe1d7bd55 - React-RCTSettings: 433c9f6a070bcecbe5a44d5009326b4d6f3b0667 - React-RCTText: 46249950f8d8738b90a60883d19b5bef09f0a296 - React-RCTVibration: 8f41e85ab6d40c7db6111ca9e8c7492c8de374fb + React-RCTAnimation: b2fcc7c462f1fb5e195a5547f6e405ec9a60d80f + React-RCTAppDelegate: 7691b8fe13c1c329cfcb44252cd1abf4409c0fcf + React-RCTBlob: f6620374c96915ce1762405b1504e607e239c518 + React-RCTFabric: 8b25a4b9e5b62b4e263a2e85e10bd3215bee6b32 + React-RCTFBReactNativeSpec: bc3fb34e6f7736cabda3c3de2444b928e3a3dd55 + React-RCTImage: f189ae651e3c97879b4cdefcba1d4cffe55439da + React-RCTLinking: 759ac5e4aed95ac3c29849f98ff3f3b5ece830ed + React-RCTNetwork: ce1f38434a70eb1e228344f7632e636c3ceca03b + React-RCTSettings: 3602ea3adf9009f6d09461bf05f7e392414c32d8 + React-RCTText: e48b4b54eab3f4cfea9be1228b5ef9ad3b8172c1 + React-RCTVibration: 2e4dc335dd1e57c7004bcc07e7f5319e5968d5cf React-rendererconsistency: c766ce7261ab6ed6be7bc155c403e29436d4f156 - React-rendererdebug: 1f619b295f346242842f3accee23e8394b995d3c + React-rendererdebug: f8bf864b2646944c3f7c41555dbed0b5d7aea5d1 React-rncore: cafe45e14d870bbecbbf4bd89e12ef3b596e1f2d - React-RuntimeApple: 51303fe6715be3596bf0479c1b34ce56d61ac81f - React-RuntimeCore: a0fe52c5f42a65f9d636ee4fbee14322865eb530 + React-RuntimeApple: 6b67a8f0109a5289ccef380d14ba099aeadcef0e + React-RuntimeCore: 056d99b829e1de4afed419e17e95639cf72799f1 React-runtimeexecutor: 201311bdafb53b5c30292782c8ee90193af86d91 - React-runtimescheduler: 89a12fb995740bf1c1d768d3c6732e709913dbeb + React-runtimescheduler: 32e558eb10b88ea398bb974b74d2230e5a71f30e React-timing: 127d8598b5a15ae5b29ebd0ec474d590285c6f2f - React-utils: 5157cba7e171651af2113558b0c6cd562d4271bf - ReactAppDependencyProvider: e7e92253013754a8c35ebdbf8ad700f4e8956f62 - ReactCodegen: 6efd314e2f59c2eae0898c6d1e0d933876a1666c - ReactCommon: cec0154a884747940be235f16acbd4fc9c959f89 - ReactNativeHost: 9796a3872d3b2777a87acbe62d666dec521eda7b - ReactTestApp-DevSupport: ba03e8b8d2c87ed4b631ae8dc25765925f37e4a7 + React-utils: d5269d138fd5b7b93a7f03e697f25d482e64d399 + ReactAppDependencyProvider: 41e9fb63606c32cce924653d2d410cb01ec81286 + ReactCodegen: 9cf993a8cfdffca67d5abe1ba056020fc48fc0d4 + ReactCommon: ede76856e587ac3fd7ce70ca2387e571bc947d14 + ReactNativeHost: 8ef54ff7c6c17c1242e287c286699f8955f22e45 + ReactTestApp-DevSupport: f23bacc6d21da29a7d8d248bb6ee8cc9ad241a48 ReactTestApp-Resources: 4f6dff3b157f879757cd750caccd1d34a7eda647 - RNGestureHandler: 3bd32689c176c81dd57bb1e3b3804e7352994000 - RNScreens: be44c347c9ae035bc78da28e283f484fa37e916f - RNVectorIcons: 5987b681d1ad97637f67e4e7af2902b9d4c3f5d6 + RNGestureHandler: 00e35ccf0f6ee047d10d726a6b2c1cf85de54946 + RNScreens: 62107db9c6147f19a6d0e8989f355acae46a99c5 + RNVectorIcons: 5c1b761ed018ba598757d876380e584414befada SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8 SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 diff --git a/apps/example/src/Examples/SFSymbols.tsx b/apps/example/src/Examples/SFSymbols.tsx index 1e7b48ca..f9490fbb 100644 --- a/apps/example/src/Examples/SFSymbols.tsx +++ b/apps/example/src/Examples/SFSymbols.tsx @@ -41,6 +41,7 @@ export default function SFSymbols() { ? require('../../assets/icons/person_dark.png') : { sfSymbol: 'person.fill' }, title: 'Contacts', + role: 'search', }, ]); diff --git a/docs/docs/docs/guides/standalone-usage.md b/docs/docs/docs/guides/standalone-usage.md index 545b9f25..72ddb643 100644 --- a/docs/docs/docs/guides/standalone-usage.md +++ b/docs/docs/docs/guides/standalone-usage.md @@ -212,6 +212,7 @@ Each route in the `routes` array can have the following properties: - `activeTintColor`: Custom active tint color for this specific tab - `lazy`: Whether to lazy load this tab's content - `freezeOnBlur`: Whether to freeze the tab's content when it's not visible +- `role`: A value that defines the purpose of the tab ### Helper Props @@ -260,3 +261,9 @@ Function to determine if a tab should be hidden. Function to get the test ID for a tab item. - Default: Uses `route.testID` + +#### `getRole` + +Function to get the role for a tab item. + +- Default: Uses `route.role` diff --git a/docs/docs/docs/guides/usage-with-react-navigation.mdx b/docs/docs/docs/guides/usage-with-react-navigation.mdx index c04a8c95..97a16d2b 100644 --- a/docs/docs/docs/guides/usage-with-react-navigation.mdx +++ b/docs/docs/docs/guides/usage-with-react-navigation.mdx @@ -326,6 +326,14 @@ It's working separately from `enableFreeze()` in `react-native-screens`. So sett Test ID for the tab item. This can be used to find the tab item in the native view hierarchy. +#### `role` + +A value that defines the purpose of the tab. This can be used to pin and separate search tabs + +Available options: + +- `search` - The search role. + ### Events The navigator can emit events on certain actions. Supported events are: diff --git a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt index 0e6df67b..5a16130f 100644 --- a/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt +++ b/packages/react-native-bottom-tabs/android/src/main/java/com/rcttabview/RCTTabViewImpl.kt @@ -16,7 +16,7 @@ data class TabInfo( val badge: String?, val activeTintColor: Int?, val hidden: Boolean, - val testID: String? + val testID: String?, ) class RCTTabViewImpl { diff --git a/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm b/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm index ff511604..1cfc0167 100644 --- a/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm +++ b/packages/react-native-bottom-tabs/ios/Fabric/RCTTabViewComponentView.mm @@ -37,7 +37,8 @@ lhs.badge == rhs.badge && lhs.activeTintColor == rhs.activeTintColor && lhs.hidden == rhs.hidden && - lhs.testID == rhs.testID; + lhs.testID == rhs.testID && + lhs.role == rhs.role; } bool operator!=(const RNCTabViewItemsStruct& lhs, const RNCTabViewItemsStruct& rhs) { @@ -201,7 +202,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & sfSymbol:RCTNSStringFromStringNilIfEmpty(item.sfSymbol) activeTintColor:RCTUIColorFromSharedColor(item.activeTintColor) hidden:item.hidden - testID:RCTNSStringFromStringNilIfEmpty(item.testID)]; + testID:RCTNSStringFromStringNilIfEmpty(item.testID) + role:RCTNSStringFromStringNilIfEmpty(item.role)]; [result addObject:tabInfo]; } diff --git a/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift b/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift index 01754e79..750a9be4 100644 --- a/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift +++ b/packages/react-native-bottom-tabs/ios/TabView/NewTabView.swift @@ -17,6 +17,7 @@ struct NewTabView: AnyTabView { if !tabData.hidden || isFocused { let icon = props.icons[index] + let role = tabData.role?.convert() ?? nil let platformChild = props.children[safe: index] ?? PlatformView() let child = RepresentableView(view: platformChild) @@ -28,7 +29,7 @@ struct NewTabView: AnyTabView { onSelect: onSelect ) - Tab(value: tabData.key) { + Tab(value: tabData.key, role: role) { child .ignoresSafeArea(.container, edges: .all) .tabAppear(using: context) diff --git a/packages/react-native-bottom-tabs/ios/TabViewProps.swift b/packages/react-native-bottom-tabs/ios/TabViewProps.swift index 833aca94..28d1063e 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewProps.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewProps.swift @@ -23,6 +23,18 @@ internal enum MinimizeBehavior: String { #endif } +public enum TabBarRole: String { + case search + + @available(iOS 18, macOS 15, visionOS 2, tvOS 18, *) + func convert() -> TabRole { + switch self { + case .search: + return .search + } + } +} + /** Props that component accepts. SwiftUI view gets re-rendered when ObservableObject changes. */ diff --git a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift index 9a304713..7914f231 100644 --- a/packages/react-native-bottom-tabs/ios/TabViewProvider.swift +++ b/packages/react-native-bottom-tabs/ios/TabViewProvider.swift @@ -13,6 +13,7 @@ public final class TabInfo: NSObject { public let activeTintColor: PlatformColor? public let hidden: Bool public let testID: String? + public let role: TabBarRole? public init( key: String, @@ -21,7 +22,8 @@ public final class TabInfo: NSObject { sfSymbol: String, activeTintColor: PlatformColor?, hidden: Bool, - testID: String? + testID: String?, + role: String? ) { self.key = key self.title = title @@ -30,6 +32,7 @@ public final class TabInfo: NSObject { self.activeTintColor = activeTintColor self.hidden = hidden self.testID = testID + self.role = TabBarRole(rawValue: role ?? "") super.init() } } @@ -273,7 +276,8 @@ public final class TabInfo: NSObject { sfSymbol: itemDict["sfSymbol"] as? String ?? "", activeTintColor: RCTConvert.uiColor(itemDict["activeTintColor"] as? NSNumber), hidden: itemDict["hidden"] as? Bool ?? false, - testID: itemDict["testID"] as? String ?? "" + testID: itemDict["testID"] as? String ?? "", + role: itemDict["role"] as? String, ) ) } diff --git a/packages/react-native-bottom-tabs/src/TabView.tsx b/packages/react-native-bottom-tabs/src/TabView.tsx index ed30dd13..15b28b69 100644 --- a/packages/react-native-bottom-tabs/src/TabView.tsx +++ b/packages/react-native-bottom-tabs/src/TabView.tsx @@ -20,7 +20,7 @@ import { BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext'; import type { ImageSource } from 'react-native/Libraries/Image/ImageSource'; import NativeTabView from './TabViewNativeComponent'; import useLatestCallback from 'use-latest-callback'; -import type { BaseRoute, NavigationState } from './types'; +import type { BaseRoute, NavigationState, TabRole } from './types'; import DelayedFreeze from './DelayedFreeze'; const isAppleSymbol = (icon: any): icon is { sfSymbol: string } => @@ -124,6 +124,11 @@ interface Props { */ getTestID?: (props: { route: Route }) => string | undefined; + /** + * Get role for the tab, uses `route.role` by default. (iOS only) + */ + getRole?: (props: { route: Route }) => TabRole | undefined; + /** * Custom tab bar to render. Set to `null` to hide the tab bar completely. */ @@ -190,6 +195,7 @@ const TabView = ({ getHidden = ({ route }: { route: Route }) => route.hidden, getActiveTintColor = ({ route }: { route: Route }) => route.activeTintColor, getTestID = ({ route }: { route: Route }) => route.testID, + getRole = ({ route }: { route: Route }) => route.role, hapticFeedbackEnabled = false, // Android's native behavior is to show labels when there are less than 4 tabs. We leave it as undefined to use the platform default behavior. labeled = Platform.OS !== 'android' ? true : undefined, @@ -261,6 +267,7 @@ const TabView = ({ activeTintColor: processColor(getActiveTintColor({ route })), hidden: getHidden?.({ route }), testID: getTestID?.({ route }), + role: getRole?.({ route }), }; }), [ @@ -271,6 +278,7 @@ const TabView = ({ getActiveTintColor, getHidden, getTestID, + getRole, ] ); diff --git a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts index 4eca038b..a91a6ed9 100644 --- a/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts +++ b/packages/react-native-bottom-tabs/src/TabViewNativeComponent.ts @@ -30,6 +30,7 @@ export type TabViewItems = ReadonlyArray<{ activeTintColor?: ProcessedColorValue | null; hidden?: boolean; testID?: string; + role?: string; }>; export interface TabViewProps extends ViewProps { diff --git a/packages/react-native-bottom-tabs/src/index.tsx b/packages/react-native-bottom-tabs/src/index.tsx index 58090804..408f08f0 100644 --- a/packages/react-native-bottom-tabs/src/index.tsx +++ b/packages/react-native-bottom-tabs/src/index.tsx @@ -15,4 +15,4 @@ export { BottomTabBarHeightContext } from './utils/BottomTabBarHeightContext'; /** * Types */ -export type { AppleIcon } from './types'; +export type { AppleIcon, TabRole } from './types'; diff --git a/packages/react-native-bottom-tabs/src/types.ts b/packages/react-native-bottom-tabs/src/types.ts index 744af8df..bd47f95d 100644 --- a/packages/react-native-bottom-tabs/src/types.ts +++ b/packages/react-native-bottom-tabs/src/types.ts @@ -5,6 +5,8 @@ export type IconSource = string | ImageSourcePropType; export type AppleIcon = { sfSymbol: SFSymbol }; +export type TabRole = 'search'; + export type BaseRoute = { key: string; title?: string; @@ -15,6 +17,7 @@ export type BaseRoute = { activeTintColor?: string; hidden?: boolean; testID?: string; + role?: TabRole; freezeOnBlur?: boolean; }; diff --git a/packages/react-navigation/src/types.ts b/packages/react-navigation/src/types.ts index 4a5192fa..32c48064 100644 --- a/packages/react-navigation/src/types.ts +++ b/packages/react-navigation/src/types.ts @@ -9,7 +9,7 @@ import type { } from '@react-navigation/native'; import type { ImageSourcePropType } from 'react-native'; import type TabView from 'react-native-bottom-tabs'; -import type { AppleIcon } from 'react-native-bottom-tabs'; +import type { AppleIcon, TabRole } from 'react-native-bottom-tabs'; export type NativeBottomTabNavigationEventMap = { /** @@ -92,6 +92,11 @@ export type NativeBottomTabNavigationOptions = { */ tabBarButtonTestID?: string; + /** + * Role for the tab. (iOS only) + */ + role?: TabRole; + /** * Whether inactive screens should be suspended from re-rendering. Defaults to `false`. */ @@ -131,6 +136,7 @@ export type NativeBottomTabNavigationConfig = Partial< | 'onTabLongPress' | 'getActiveTintColor' | 'getTestID' + | 'getRole' | 'tabBar' | 'getFreezeOnBlur' > diff --git a/packages/react-navigation/src/views/NativeBottomTabView.tsx b/packages/react-navigation/src/views/NativeBottomTabView.tsx index b3965d16..ffc3b41e 100644 --- a/packages/react-navigation/src/views/NativeBottomTabView.tsx +++ b/packages/react-navigation/src/views/NativeBottomTabView.tsx @@ -49,6 +49,7 @@ export default function NativeBottomTabView({ getTestID={({ route }) => descriptors[route.key]?.options.tabBarButtonTestID } + getRole={({ route }) => descriptors[route.key]?.options.role} tabBar={ tabBar ? () => tabBar({ state, descriptors, navigation }) : undefined }