Files
react-navigation/packages/drawer/src/views/DrawerItem.tsx
pedrobern 1c4bd6813b fix: add flex: 1 to DrawerItem style (#8701)
# Summary 

In the [DrawerItem](https://github.com/react-navigation/react-navigation/blob/main/packages/drawer/src/views/DrawerItem.tsx), It's not possible to provide style for the [label wrapper](https://github.com/react-navigation/react-navigation/blob/main/packages/drawer/src/views/DrawerItem.tsx#L167), only for the label, so the label wrapper must at least be `flex: 1` in order to be more flexible for custom components, like in the pictures below.

In the below examples, providing `flex: 1` inside the custom component view is not enough, because the wrapper does not grow. This PR will fix it.

An alternative would be creating a new prop `labelStyleWrapper`, but I don't see any motivation for doing so, as this PR would fix it.

## Before

![before](https://user-images.githubusercontent.com/39778068/89813734-81b45a80-db18-11ea-8d53-0ddcfdbb04c1.jpeg)

## After

![after](https://user-images.githubusercontent.com/39778068/89813748-8711a500-db18-11ea-92b4-3db982c1eba5.jpeg)
2020-09-02 16:22:31 +02:00

214 lines
5.0 KiB
TypeScript

import * as React from 'react';
import {
Text,
View,
StyleSheet,
StyleProp,
ViewStyle,
TextStyle,
Platform,
TouchableWithoutFeedbackProps,
} from 'react-native';
import { Link, useTheme } from '@react-navigation/native';
import Color from 'color';
import TouchableItem from './TouchableItem';
type Props = {
/**
* The label text of the item.
*/
label:
| string
| ((props: { focused: boolean; color: string }) => React.ReactNode);
/**
* Icon to display for the `DrawerItem`.
*/
icon?: (props: {
focused: boolean;
size: number;
color: string;
}) => React.ReactNode;
/**
* URL to use for the link to the tab.
*/
to?: string;
/**
* Whether to highlight the drawer item as active.
*/
focused?: boolean;
/**
* Function to execute on press.
*/
onPress: () => void;
/**
* Color for the icon and label when the item is active.
*/
activeTintColor?: string;
/**
* Color for the icon and label when the item is inactive.
*/
inactiveTintColor?: string;
/**
* Background color for item when its active.
*/
activeBackgroundColor?: string;
/**
* Background color for item when its inactive.
*/
inactiveBackgroundColor?: string;
/**
* Style object for the label element.
*/
labelStyle?: StyleProp<TextStyle>;
/**
* Style object for the wrapper element.
*/
style?: StyleProp<ViewStyle>;
};
const Touchable = ({
children,
style,
onPress,
to,
accessibilityRole,
delayPressIn,
...rest
}: TouchableWithoutFeedbackProps & {
to?: string;
children: React.ReactNode;
onPress?: () => void;
}) => {
if (Platform.OS === 'web' && to) {
// React Native Web doesn't forward `onClick` if we use `TouchableWithoutFeedback`.
// We need to use `onClick` to be able to prevent default browser handling of links.
return (
<Link
{...rest}
to={to}
style={[styles.button, style]}
onPress={(e: any) => {
if (
!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
(e.button == null || e.button === 0) // ignore everything but left clicks
) {
e.preventDefault();
onPress?.(e);
}
}}
>
{children}
</Link>
);
} else {
return (
<TouchableItem
{...rest}
accessibilityRole={accessibilityRole}
delayPressIn={delayPressIn}
onPress={onPress}
>
<View style={style}>{children}</View>
</TouchableItem>
);
}
};
/**
* A component used to show an action item with an icon and a label in a navigation drawer.
*/
export default function DrawerItem(props: Props) {
const { colors } = useTheme();
const {
icon,
label,
labelStyle,
to,
focused = false,
activeTintColor = colors.primary,
inactiveTintColor = Color(colors.text).alpha(0.68).rgb().string(),
activeBackgroundColor = Color(activeTintColor).alpha(0.12).rgb().string(),
inactiveBackgroundColor = 'transparent',
style,
onPress,
...rest
} = props;
const { borderRadius = 4 } = StyleSheet.flatten(style || {});
const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;
const iconNode = icon ? icon({ size: 24, focused, color }) : null;
return (
<View
collapsable={false}
{...rest}
style={[styles.container, { borderRadius, backgroundColor }, style]}
>
<Touchable
delayPressIn={0}
onPress={onPress}
style={[styles.wrapper, { borderRadius }]}
accessibilityTraits={focused ? ['button', 'selected'] : 'button'}
accessibilityComponentType="button"
accessibilityRole="button"
accessibilityState={{ selected: focused }}
// @ts-expect-error: keep for compatibility with older React Native versions
accessibilityStates={focused ? ['selected'] : []}
to={to}
>
<React.Fragment>
{iconNode}
<View
style={[
styles.label,
{ marginLeft: iconNode ? 32 : 0, marginVertical: 5 },
]}
>
{typeof label === 'string' ? (
<Text
numberOfLines={1}
style={[
{
color,
fontWeight: '500',
},
labelStyle,
]}
>
{label}
</Text>
) : (
label({ color, focused })
)}
</View>
</React.Fragment>
</Touchable>
</View>
);
}
const styles = StyleSheet.create({
container: {
marginHorizontal: 10,
marginVertical: 4,
overflow: 'hidden',
},
wrapper: {
flexDirection: 'row',
alignItems: 'center',
padding: 8,
},
label: {
marginRight: 32,
flex: 1,
},
button: {
display: 'flex',
},
});