diff --git a/README.md b/README.md index 0f92b78..824ee84 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ function Example() { // disableSwipe={false} // (default=false) disable swipe to left/right gestures // tabHeaderStyle // style object, can be animated properties as well in // tabLabelStyle // style object + // TabHeaderComponent={MyCustomHeader} // render a custom header > @@ -103,6 +104,7 @@ function Example() { // onPress={() => { // console.log('onPress explore'); // }} + // TabHeaderComponent={MyCustomHeader} // render a custom header > diff --git a/src/Swiper.native.tsx b/src/Swiper.native.tsx index aa405df..58286e1 100644 --- a/src/Swiper.native.tsx +++ b/src/Swiper.native.tsx @@ -26,6 +26,7 @@ function SwiperNative(props: SwiperProps) { disableSwipe, tabHeaderStyle, tabLabelStyle, + TabHeaderComponent, } = props; const { index, goTo } = React.useContext(TabsContext); const indexRef = React.useRef(index || 0); @@ -82,6 +83,7 @@ function SwiperNative(props: SwiperProps) { mode, tabHeaderStyle, tabLabelStyle, + TabHeaderComponent, }; return ( <> diff --git a/src/Swiper.tsx b/src/Swiper.tsx index d3e80d6..ca776a3 100644 --- a/src/Swiper.tsx +++ b/src/Swiper.tsx @@ -17,6 +17,7 @@ function Swiper(props: SwiperProps) { mode, tabHeaderStyle, tabLabelStyle, + TabHeaderComponent, } = props; const index = useTabIndex(); @@ -41,6 +42,7 @@ function Swiper(props: SwiperProps) { mode, tabHeaderStyle, tabLabelStyle, + TabHeaderComponent, }; return ( diff --git a/src/TabScreen.tsx b/src/TabScreen.tsx index 4e10082..8285538 100644 --- a/src/TabScreen.tsx +++ b/src/TabScreen.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import type { GestureResponderEvent } from 'react-native'; +import type TabsHeaderItem from './TabsHeaderItem'; export interface TabScreenProps { label: string; @@ -9,6 +10,7 @@ export interface TabScreenProps { onPress?: (event: GestureResponderEvent) => void; onPressIn?: (event: GestureResponderEvent) => void; disabled?: boolean; + TabHeaderComponent?: typeof TabsHeaderItem | undefined; } export default function TabScreen({ children }: TabScreenProps) { diff --git a/src/Tabs.tsx b/src/Tabs.tsx index 3be6a3f..a1c0e88 100644 --- a/src/Tabs.tsx +++ b/src/Tabs.tsx @@ -5,6 +5,7 @@ import Swiper from './Swiper'; import type { MD3LightTheme } from 'react-native-paper'; import type { IconPosition, Mode } from './utils'; +import type TabsHeaderItem from './TabsHeaderItem'; function Tabs({ theme, @@ -18,6 +19,7 @@ function Tabs({ disableSwipe = false, tabHeaderStyle, tabLabelStyle, + TabHeaderComponent, ...rest }: { children: any; @@ -32,6 +34,7 @@ function Tabs({ disableSwipe?: boolean; tabHeaderStyle?: ViewStyle | undefined; tabLabelStyle?: TextStyle | undefined; + TabHeaderComponent?: typeof TabsHeaderItem; }) { const children = React.Children.toArray(rest.children).filter(Boolean); @@ -48,6 +51,7 @@ function Tabs({ disableSwipe={disableSwipe} tabHeaderStyle={tabHeaderStyle} tabLabelStyle={tabLabelStyle} + TabHeaderComponent={TabHeaderComponent} > {children} diff --git a/src/TabsHeader.tsx b/src/TabsHeader.tsx index 1dd29e3..36ad40c 100644 --- a/src/TabsHeader.tsx +++ b/src/TabsHeader.tsx @@ -27,6 +27,7 @@ export default function TabsHeader({ mode, tabHeaderStyle, tabLabelStyle, + TabHeaderComponent, children, }: SwiperRenderProps) { const { index, goTo } = React.useContext(TabsContext); @@ -174,7 +175,6 @@ export default function TabsHeader({ style={[ { backgroundColor, elevation }, restStyle, - styles.tabs, iconPosition === 'top' && styles.tabsTopIcon, ]} onLayout={onTabsLayout} @@ -218,6 +218,7 @@ export default function TabsHeader({ showTextLabel={showTextLabel} mode={mode} tabLabelStyle={tabLabelStyle} + TabHeaderComponent={TabHeaderComponent} /> ))} ; tabIndex: number; @@ -48,6 +49,7 @@ export default function TabsHeaderItem({ showTextLabel?: boolean; mode: Mode; tabLabelStyle?: TextStyle | undefined; + TabHeaderComponent?: typeof TabsHeaderItem; }) { const baseColor = theme.colors.primary; const rippleColor = React.useMemo( @@ -74,6 +76,99 @@ export default function TabsHeaderItem({ const badgeWithoutContent = typeof tab.props.badge === 'boolean'; + const CustomHeader = tab.props.TabHeaderComponent || TabHeaderComponent; + + const HeaderItem = CustomHeader ? ( + } + active={active} + onTabLayout={onTabLayout} + goTo={goTo} + activeColor={activeColor} + textColor={textColor} + position={position} + offset={offset} + childrenCount={childrenCount} + uppercase={uppercase} + iconPosition={iconPosition} + showTextLabel={showTextLabel} + mode={mode} + tabLabelStyle={tabLabelStyle} + /> + ) : ( + <> + {tab.props.icon ? ( + + {tab.props.icon ? ( + Platform.OS === 'android' ? ( + + + + ) : ( + + ) + ) : null} + + ) : null} + {badgeIsFilled ? ( + + {badgeWithoutContent ? ( + + ) : ( + + {tab.props.badge as any} + + )} + + ) : null} + {showTextLabel ? ( + + {uppercase && !theme.isV3 + ? tab.props.label.toUpperCase() + : tab.props.label} + + ) : null} + + ); return ( - {tab.props.icon ? ( - - {tab.props.icon ? ( - Platform.OS === 'android' ? ( - - - - ) : ( - - ) - ) : null} - - ) : null} - {badgeIsFilled ? ( - - {badgeWithoutContent ? ( - - ) : ( - - {tab.props.badge as any} - - )} - - ) : null} - {showTextLabel ? ( - - {uppercase && !theme.isV3 - ? tab.props.label.toUpperCase() - : tab.props.label} - - ) : null} + {HeaderItem} @@ -188,10 +216,15 @@ const styles = StyleSheet.create({ left: 0, top: -2, }, - tabRoot: { position: 'relative' }, + tabRoot: { + position: 'relative', + justifyContent: 'center', + flexDirection: 'row', + alignItems: 'stretch', + }, tabRootFixed: { flex: 1 }, touchableRipple: { - height: 48, + minHeight: 48, justifyContent: 'center', alignItems: 'center', overflow: 'hidden', diff --git a/src/utils.tsx b/src/utils.tsx index 1c5437b..9db50e9 100644 --- a/src/utils.tsx +++ b/src/utils.tsx @@ -8,6 +8,7 @@ import type { } from 'react-native'; import type { MD3LightTheme } from 'react-native-paper'; import type { MutableRefObject, RefObject } from 'react'; +import type TabsHeaderItem from './TabsHeaderItem'; export type AnimatedViewStyle = Animated.AnimatedProps>; export type AnimatedTextStyle = Animated.AnimatedProps>; export type Mode = 'fixed' | 'scrollable'; @@ -27,6 +28,7 @@ export interface SwiperRenderProps { mode: Mode; tabHeaderStyle: ViewStyle | undefined; tabLabelStyle: TextStyle | undefined; + TabHeaderComponent?: typeof TabsHeaderItem; } export interface SwiperProps { @@ -42,6 +44,7 @@ export interface SwiperProps { disableSwipe?: boolean; tabHeaderStyle: ViewStyle | undefined; tabLabelStyle: TextStyle | undefined; + TabHeaderComponent?: typeof TabsHeaderItem; } export interface OffsetScrollArgs {