Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
>
<TabScreen label="Explore" icon="compass">
<ExploreWitHookExamples />
Expand All @@ -103,6 +104,7 @@ function Example() {
// onPress={() => {
// console.log('onPress explore');
// }}
// TabHeaderComponent={MyCustomHeader} // render a custom header
>
<View style={{ backgroundColor: 'red', flex:1 }} />
</TabScreen>
Expand Down
2 changes: 2 additions & 0 deletions src/Swiper.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function SwiperNative(props: SwiperProps) {
disableSwipe,
tabHeaderStyle,
tabLabelStyle,
TabHeaderComponent,
} = props;
const { index, goTo } = React.useContext(TabsContext);
const indexRef = React.useRef<number>(index || 0);
Expand Down Expand Up @@ -82,6 +83,7 @@ function SwiperNative(props: SwiperProps) {
mode,
tabHeaderStyle,
tabLabelStyle,
TabHeaderComponent,
};
return (
<>
Expand Down
2 changes: 2 additions & 0 deletions src/Swiper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function Swiper(props: SwiperProps) {
mode,
tabHeaderStyle,
tabLabelStyle,
TabHeaderComponent,
} = props;

const index = useTabIndex();
Expand All @@ -41,6 +42,7 @@ function Swiper(props: SwiperProps) {
mode,
tabHeaderStyle,
tabLabelStyle,
TabHeaderComponent,
};

return (
Expand Down
2 changes: 2 additions & 0 deletions src/TabScreen.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions src/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -18,6 +19,7 @@ function Tabs({
disableSwipe = false,
tabHeaderStyle,
tabLabelStyle,
TabHeaderComponent,
...rest
}: {
children: any;
Expand All @@ -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);

Expand All @@ -48,6 +51,7 @@ function Tabs({
disableSwipe={disableSwipe}
tabHeaderStyle={tabHeaderStyle}
tabLabelStyle={tabLabelStyle}
TabHeaderComponent={TabHeaderComponent}
>
{children}
</Swiper>
Expand Down
6 changes: 2 additions & 4 deletions src/TabsHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function TabsHeader({
mode,
tabHeaderStyle,
tabLabelStyle,
TabHeaderComponent,
children,
}: SwiperRenderProps) {
const { index, goTo } = React.useContext(TabsContext);
Expand Down Expand Up @@ -174,7 +175,6 @@ export default function TabsHeader({
style={[
{ backgroundColor, elevation },
restStyle,
styles.tabs,
iconPosition === 'top' && styles.tabsTopIcon,
]}
onLayout={onTabsLayout}
Expand Down Expand Up @@ -218,6 +218,7 @@ export default function TabsHeader({
showTextLabel={showTextLabel}
mode={mode}
tabLabelStyle={tabLabelStyle}
TabHeaderComponent={TabHeaderComponent}
/>
))}
<Animated.View
Expand Down Expand Up @@ -260,9 +261,6 @@ const styles = StyleSheet.create({
scrollablePadding: {
width: 52,
},
tabs: {
height: 48,
},
tabsTopIcon: {
height: 72,
},
Expand Down
173 changes: 103 additions & 70 deletions src/TabsHeaderItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function TabsHeaderItem({
iconPosition,
showTextLabel,
tabLabelStyle,
TabHeaderComponent,
}: {
tab: ReactElement<TabScreenProps>;
tabIndex: number;
Expand All @@ -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(
Expand All @@ -74,6 +76,99 @@ export default function TabsHeaderItem({

const badgeWithoutContent = typeof tab.props.badge === 'boolean';

const CustomHeader = tab.props.TabHeaderComponent || TabHeaderComponent;

const HeaderItem = CustomHeader ? (
<CustomHeader
theme={theme}
tabIndex={tabIndex}
tab={tab as ReactElement<TabScreenProps>}
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 ? (
<View
style={[
styles.iconContainer,
iconPosition !== 'top' && styles.marginRight,
]}
>
{tab.props.icon ? (
Platform.OS === 'android' ? (
<Animated.View style={{ opacity }}>
<MaterialCommunityIcon
selectable={false}
accessibilityElementsHidden={true}
importantForAccessibility="no"
name={tab.props.icon}
color={textColor}
size={24}
/>
</Animated.View>
) : (
<MaterialCommunityIcon
selectable={false}
accessibilityElementsHidden={true}
importantForAccessibility="no"
name={tab.props.icon}
style={{ color, opacity }}
size={24}
/>
)
) : null}
</View>
) : null}
{badgeIsFilled ? (
<View
style={[
styles.badgeContainer,
{
right:
(badgeWithoutContent
? String(tab.props.badge).length * -2
: 0) - 2,
},
]}
>
{badgeWithoutContent ? (
<Badge visible={true} size={8} theme={theme} />
) : (
<Badge visible={true} size={16} theme={theme}>
{tab.props.badge as any}
</Badge>
)}
</View>
) : null}
{showTextLabel ? (
<AnimatedText
selectable={false}
style={[
styles.text,
iconPosition === 'top' && styles.textTop,
{ ...theme.fonts.titleSmall, color, opacity },
tabLabelStyle,
]}
>
{uppercase && !theme.isV3
? tab.props.label.toUpperCase()
: tab.props.label}
</AnimatedText>
) : null}
</>
);
return (
<View
key={tab.props.label}
Expand Down Expand Up @@ -108,74 +203,7 @@ export default function TabsHeaderItem({
iconPosition === 'top' && styles.touchableRippleInnerTop,
]}
>
{tab.props.icon ? (
<View
style={[
styles.iconContainer,
iconPosition !== 'top' && styles.marginRight,
]}
>
{tab.props.icon ? (
Platform.OS === 'android' ? (
<Animated.View style={{ opacity }}>
<MaterialCommunityIcon
selectable={false}
accessibilityElementsHidden={true}
importantForAccessibility="no"
name={tab.props.icon}
color={textColor}
size={24}
/>
</Animated.View>
) : (
<MaterialCommunityIcon
selectable={false}
accessibilityElementsHidden={true}
importantForAccessibility="no"
name={tab.props.icon}
style={{ color, opacity }}
size={24}
/>
)
) : null}
</View>
) : null}
{badgeIsFilled ? (
<View
style={[
styles.badgeContainer,
{
right:
(badgeWithoutContent
? String(tab.props.badge).length * -2
: 0) - 2,
},
]}
>
{badgeWithoutContent ? (
<Badge visible={true} size={8} theme={theme} />
) : (
<Badge visible={true} size={16} theme={theme}>
{tab.props.badge as any}
</Badge>
)}
</View>
) : null}
{showTextLabel ? (
<AnimatedText
selectable={false}
style={[
styles.text,
iconPosition === 'top' && styles.textTop,
{ ...theme.fonts.titleSmall, color, opacity },
tabLabelStyle,
]}
>
{uppercase && !theme.isV3
? tab.props.label.toUpperCase()
: tab.props.label}
</AnimatedText>
) : null}
{HeaderItem}
</View>
</TouchableRipple>
</View>
Expand All @@ -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',
Expand Down
3 changes: 3 additions & 0 deletions src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<StyleProp<ViewStyle>>;
export type AnimatedTextStyle = Animated.AnimatedProps<StyleProp<TextStyle>>;
export type Mode = 'fixed' | 'scrollable';
Expand All @@ -27,6 +28,7 @@ export interface SwiperRenderProps {
mode: Mode;
tabHeaderStyle: ViewStyle | undefined;
tabLabelStyle: TextStyle | undefined;
TabHeaderComponent?: typeof TabsHeaderItem;
}

export interface SwiperProps {
Expand All @@ -42,6 +44,7 @@ export interface SwiperProps {
disableSwipe?: boolean;
tabHeaderStyle: ViewStyle | undefined;
tabLabelStyle: TextStyle | undefined;
TabHeaderComponent?: typeof TabsHeaderItem;
}

export interface OffsetScrollArgs {
Expand Down