Skip to content

Commit

Permalink
feat: implement tab-view new api
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski committed Aug 22, 2023
1 parent 24c0392 commit 414c1dc
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 259 deletions.
32 changes: 18 additions & 14 deletions example/src/Screens/TabView/CustomIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,29 +108,33 @@ export const CustomIndicator = () => {
);
};

const renderIcon = ({ route }: { route: Route }) => (
<Ionicons name={route.icon} size={24} style={styles.icon} />
const renderBadge = React.useCallback(
() => (
<View style={styles.badge}>
<Text style={styles.count}>42</Text>
</View>
),
[]
);

const renderBadge = ({ route }: { route: Route }) => {
if (route.key === 'albums') {
return (
<View style={styles.badge}>
<Text style={styles.count}>42</Text>
</View>
);
}
return null;
};
const renderIcon = React.useCallback((props: { route: Route }) => {
return <Ionicons name={props.route.icon} style={styles.icon} {...props} />;
}, []);

const renderTabBar = (
props: SceneRendererProps & { navigationState: State }
) => (
<View style={[styles.tabbar, { paddingBottom: insets.bottom }]}>
<TabBar
{...props}
renderIcon={renderIcon}
renderBadge={renderBadge}
commonOptions={{
icon: renderIcon,
}}
options={{
albums: {
badge: renderBadge,
},
}}
renderIndicator={renderIndicator}
style={styles.tabbar}
contentContainerStyle={styles.tabbarContentContainer}
Expand Down
10 changes: 6 additions & 4 deletions example/src/Screens/TabView/TabBarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,19 @@ export const TabBarIcon = () => {
{ key: 'article', icon: 'md-list' },
]);

const renderIcon = ({ route, color }: { route: Route; color: string }) => (
<Ionicons name={route.icon} size={24} color={color} />
);
const renderIcon = React.useCallback((props: { route: Route }) => {
return <Ionicons name={props.route.icon} {...props} />;
}, []);

const renderTabBar = (
props: SceneRendererProps & { navigationState: State }
) => (
<TabBar
{...props}
indicatorStyle={styles.indicator}
renderIcon={renderIcon}
commonOptions={{
icon: renderIcon,
}}
style={styles.tabbar}
contentContainerStyle={styles.tabbarContentContainer}
gap={20}
Expand Down
12 changes: 5 additions & 7 deletions packages/material-top-tabs/src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ export type MaterialTopTabNavigationOptions = {
/**
* A function that given { focused: boolean, color: string } returns a React.Node to display in the tab bar.
*/
tabBarIcon?: (props: { focused: boolean; color: string }) => React.ReactNode;
tabBarIcon?: (props: {
focused: boolean;
color: string;
}) => React.ReactElement;

/**
* Whether the tab icon should be visible. Defaults to `false`.
Expand All @@ -118,7 +121,7 @@ export type MaterialTopTabNavigationOptions = {
/**
* Function that returns a React element to use as a badge for the tab.
*/
tabBarBadge?: () => React.ReactNode;
tabBarBadge?: () => React.ReactElement;

/**
* Function that returns a React element as the tab bar indicator.
Expand Down Expand Up @@ -179,11 +182,6 @@ export type MaterialTopTabNavigationOptions = {
*/
tabBarScrollEnabled?: boolean;

/**
* Style object for the tab icon container.
*/
tabBarIconStyle?: StyleProp<ViewStyle>;

/**
* Style object for the tab label.
*/
Expand Down
129 changes: 54 additions & 75 deletions packages/material-top-tabs/src/views/MaterialTopTabBar.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,51 @@
import {
ParamListBase,
Route,
TabNavigationState,
useTheme,
} from '@react-navigation/native';
import Color from 'color';
import * as React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { TabBar, TabBarIndicator } from 'react-native-tab-view';
import { StyleProp, StyleSheet, Text, ViewStyle } from 'react-native';
import { TabBar, TabBarIndicator, TabDescriptor } from 'react-native-tab-view';

import type { MaterialTopTabBarProps } from '../types';

type MaterialLabelType = {
color: string;
label?: string;
labelStyle?: StyleProp<ViewStyle>;
allowScaling?: boolean;
};

const MaterialLabel = ({
color,
label,
labelStyle,
allowScaling,
}: MaterialLabelType) => {
const { fonts } = useTheme();

return (
<Text
style={[{ color }, fonts.regular, styles.label, labelStyle]}
allowFontScaling={allowScaling}
>
{label}
</Text>
);
};

const renderLabel = (props: MaterialLabelType) => {
return <MaterialLabel {...props} />;
};

export function MaterialTopTabBar({
state,
navigation,
descriptors,
...rest
}: MaterialTopTabBarProps) {
const { colors, fonts } = useTheme();
const { colors } = useTheme();

const focusedOptions = descriptors[state.routes[state.index].key].options;

Expand All @@ -26,6 +54,27 @@ export function MaterialTopTabBar({
focusedOptions.tabBarInactiveTintColor ??
Color(activeColor).alpha(0.5).rgb().string();

const tabBarOptions: Record<string, TabDescriptor<{ key: string }>> = {};

state.routes.forEach((route) => {
const { options } = descriptors[route.key];
tabBarOptions[route.key] = {
testID: options.tabBarButtonTestID,
accessibilityLabel: options.tabBarAccessibilityLabel,
badge: options.tabBarBadge,
icon: options.tabBarShowIcon === false ? undefined : options.tabBarIcon,
label: options.tabBarShowLabel === false ? undefined : renderLabel,
labelAllowFontScaling: options.tabBarAllowFontScaling,
labelStyle: options.tabBarLabelStyle,
labelText:
options.tabBarShowLabel === false
? undefined
: options.title !== undefined
? options.title
: route.name,
};
});

return (
<TabBar
{...rest}
Expand All @@ -46,12 +95,6 @@ export function MaterialTopTabBar({
indicatorContainerStyle={focusedOptions.tabBarIndicatorContainerStyle}
contentContainerStyle={focusedOptions.tabBarContentContainerStyle}
style={[{ backgroundColor: colors.card }, focusedOptions.tabBarStyle]}
getAccessibilityLabel={({ route }) =>
descriptors[route.key].options.tabBarAccessibilityLabel
}
getTestID={({ route }) =>
descriptors[route.key].options.tabBarButtonTestID
}
onTabPress={({ route, preventDefault }) => {
const event = navigation.emit({
type: 'tabPress',
Expand All @@ -69,67 +112,7 @@ export function MaterialTopTabBar({
target: route.key,
})
}
renderIcon={({ route, focused, color }) => {
const { options } = descriptors[route.key];

if (options.tabBarShowIcon === false) {
return null;
}

if (options.tabBarIcon !== undefined) {
const icon = options.tabBarIcon({ focused, color });

return (
<View style={[styles.icon, options.tabBarIconStyle]}>{icon}</View>
);
}

return null;
}}
renderLabel={({ route, focused, color }) => {
const { options } = descriptors[route.key];

if (options.tabBarShowLabel === false) {
return null;
}

const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: (route as Route<string>).name;

if (typeof label === 'string') {
return (
<Text
style={[
{ color },
fonts.regular,
styles.label,
options.tabBarLabelStyle,
]}
allowFontScaling={options.tabBarAllowFontScaling}
>
{label}
</Text>
);
}

const children =
typeof options.tabBarLabel === 'string'
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;

return label({ focused, color, children });
}}
renderBadge={({ route }) => {
const { tabBarBadge } = descriptors[route.key].options;

return tabBarBadge?.() ?? null;
}}
options={tabBarOptions}
renderIndicator={({ navigationState: state, ...rest }) => {
return focusedOptions.tabBarIndicator ? (
focusedOptions.tabBarIndicator({
Expand All @@ -145,10 +128,6 @@ export function MaterialTopTabBar({
}

const styles = StyleSheet.create({
icon: {
height: 24,
width: 24,
},
label: {
textAlign: 'center',
textTransform: 'uppercase',
Expand Down
Loading

0 comments on commit 414c1dc

Please sign in to comment.