Is there the way to change white color of a main page mask of "react-native-drawer" component? - react-native

As I see in android applications almost all drawers in open state makes a black "mask" above main content.
white mask in "react-native-drawer:
black color example:
It is possible to change the color of this "mask" in "react-native-drawer" component to black?

Was looking for the same thing and found a solution at GitHub.
tweenHandler={ratio => ({
main: {
opacity: 1,
},
mainOverlay: {
opacity: ratio / 2,
backgroundColor: 'black',
},
})}
Tested it out and here's what I got (made it pink for visibility):
<Drawer
tweenHandler={ratio => ({
main: {
opacity: 1,
},
mainOverlay: {
opacity: ratio / 2,
backgroundColor: 'pink',
},
})}
ref={(ref) => { this._drawer = ref; }}
content={ navigationView }
side="right"
panOpenMask={.25}
>
Screenshot:

Related

Style Drawer menu toggle button in react native

I'm trying to add some background shade to the toggle button and also increase the size a little more, but I'm unable to find the right prop that targets the button.
Here's my code.
<Drawer.Navigator screenOptions={(navigation) => ({
drawerStyle:{width:280},
drawerItemStyle: {
borderRadius: 0,
width: '100%',
marginVertical: 0,
marginLeft: 0,
},
drawerLabelStyle: {
fontFamily: 'sans-serif',
fontWeight:'100'
},
drawerActiveTintColor: '#288df9',
drawerInactiveTintColor: '#444'
})}>
...
</Drawer.Navigator>
Any help on how to style the toggle button will be rightly appreciated.
If you check the implementation of this Drawer you can find that this button is an Image hardcoded inside: https://github.com/react-navigation/react-navigation/blob/b91c9b05ff96727f5fa6ef0bec51b5d7eac06600/packages/drawer/src/views/DrawerToggleButton.tsx#L37
export default function DrawerToggleButton({ tintColor, ...rest }: Props) {
const navigation = useNavigation<DrawerNavigationProp<ParamListBase>>();
return (
<PlatformPressable
{...rest}
accessible
accessibilityRole="button"
android_ripple={{ borderless: true }}
onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}
style={styles.touchable}
hitSlop={Platform.select({
ios: undefined,
default: { top: 16, right: 16, bottom: 16, left: 16 },
})}
>
<Image
style={[styles.icon, tintColor ? { tintColor } : null]}
source={require('./assets/toggle-drawer-icon.png')}
fadeDuration={0}
/>
</PlatformPressable>
);
}
I think what you can do is replace the image in your node_modules and using patch-package save it as a patch in your local repository.
Another way is to implement your own Button and use openDrawer/closeDrawer methods to control the drawer.

React native Flatlist items with negative margin

I have a simple vertical Flatlist on Android and I would like to render some of its items with a negative margin. The goal is for those items to appear wider than the Flatlist.
Something like this with red being an item of the flatlist:
Unfortunately, the edges of the items are cut by the edge of the Flatlist.
Is there a way to display items that are wider than the Flatlist that renders them?
EDIT:
I know I can achieve the visual on my illustration by adding a margin/padding to every item on the list except the red one. What I would like to know is if it is possible to make a specific item wider than the Flatlist itself (not just wider than the other items)
I would rather stylize the one item that needs to be wider, rather than all the others.
I myself actually never make an item in FlatList with wider scale of the whole flatList cause I know it's a bad idea and it tends to be ugly looking, just imagine an item with an overlapped verticalScrollIndicator. But the better way is that you can add a View like this.
...FlatList tag.....
renderItem={({ item, index }) => {
return (
<View style={{ paddingHorizontal:item.isWider ? 0 : '5%' }}>
....Children
</View>
)
}}
You can also write the statement to work only for some particular item index.
By the way, the scroll indicator will not overlap with any item and it has a controlled and better looking UI.
Whenever you want to render specific items without margin value, the only thing you have to do is override style.
I create a sample application according to your requirements.
import React, { Component } from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text } from 'react-native';
const DATA = [
{
id: 1,
title: 'First Item',
},
{
id: 2,
title: 'Second Item',
},
{
id: 3,
title: 'Third Item',
},
{
id: 4,
title: 'Forth Item',
},
{
id: 5,
title: 'Fifth Item',
},
{
id: 6,
title: 'Sixth Item',
},
];
export default class App extends Component {
renderItems = ({ item }) => (
// Suppose if you want to change margin value & background color of id == 3
<View style={item.id !== 3 ? styles.item : [styles.item, { backgroundColor: 'red', margin: 0 }]}>
<Text>{item.title}</Text>
</View>
)
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={this.renderItems}
keyExtractor={item => item.id}
/>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
},
item: {
backgroundColor: 'green',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
});
Hope this helps you. Feel free for doubts.
According to the image you provided, and only style the specific list item, I could change align-self, width, and negative margin to achieve what you want.
Apply the specific item
<FlatList
contentContainerStyle={styles.list}
data={data}
renderItem={({ item, index }) => (
<View style={[styles.item, index === 3 && styles.oversize]} />
)}
/>
styles:
oversize: {
backgroundColor: 'orange',
alignSelf: 'stretch',
width: 'auto',
marginHorizontal: -16,
},
Here is working example

Make a chart clickable when placed under another SVG <View>

I am having trouble working with some SVG in React Native.
I have a chart that is clickable and works well, I then needed to place another SVG on top of the chart in order to draw a line that would represent a limit score value. The problem that I am now facing is that I cannot click on the chart anymore since the view of the SVG is placed on top of it.
I made the background color of the SVG to be transparent so that I can at least see the chart behind it but, I do not know how to make it clickable again.
Is there any work around where I can maybe make the chart clickable trough a transparent view that is place on top?
It might be a stupid question but I am pretty new to both react and JS in general, so I could really use any type of help. :)
Here is the picture of the chart:
Polar Chart and Svg circle
And here the same Svg with a non-transparent background that, as you can see, covers almost the hole chart.
Svg covering the chart
Here's the Svg code:
export class NutritionChartSvg extends Component {
render() {
return (
<View style={styles.container} >
<Svg height={height * 0.5} width={width * 0.5} viewBox="0 0 100 100">
<Svg.Circle
id="circle"
cx="50"
cy="13"
r="40"
stroke="gray"
strokeWidth="0.6"
fill="none"
/>
<Text fill="black" fontSize="8" dy="-2">
<TextPath href="#circle" startOffset='181'>
100
</TextPath>
</Text>
</Svg>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignContent: 'center',
justifyContent: 'center' ,
position: 'absolute',
left: '25%',
height: 'auto',
width: 'auto',
},
});'
This is the chart form chartjs:
export const NutritionChart = (props) => {
return (
<Chart
chartConfiguration={
{
type: 'polarArea',
data: {
labels: ['Fiber', 'Protein', 'Healthy Oil', 'Good Energy', 'Immune
Defense'],
datasets: [
{
label: '# of Votes',
data: [
50,
140,
90,
120,
100,
],
backgroundColor: backgroundColor,
borderColor: borderColor,
borderWidth: datasets.border,
hoverBackgroundColor: hoverBackgroundColor,
},
],
},
options: {
layout: {
padding: {
top: options.layout.padding.top,
bottom: options.layout.padding.bottom,
}
},
legend: {
display: false,
fullWidth: false,
labels: {
fontSize: options.legend.labels.fontSize,
boxWidth: options.legend.labels.boxWidth,
padding: options.legend.labels.padding,
}
},
scale: {
display: false,
ticks: {
min:0,
max:200,
}
},
responsive: true,
maintainAspectRatio: false,
},
}
}
defaultFontSize={10}
/>
);
};
and they are together in a view :
<View style={styles.nutritionChart} key={3}>
<NutritionChart/>
<NutritionChartSvg/>
</View>
Either:
Move the limit line into the chart SVG, instead of laying it separately on top, or
Set pointer-events: none on the top SVG. This will make clicks pass right through it.

React Native TabNavigator change TabStyle to follow according to the text

I am using expo v27.0, react native 0.55 and I as you can see in the picture that the tab have somewhat a fixed width like a default width from the tab navigation, and the text wrap into three lines, I want the text to be in 1 line and nowrap, and i have tried styling (flexWrap:
'nowrap', flex: 1) in TabStyle, LabelStyle in TabBarOptions, but still can't get the tab to have the width according to the text inside the tab.
I populate the text for the tabs dynamically from json using fetch, therefore all tabs will have different width according to the text. How to I make the tab to follow the width of the text ?
All answers are greatly welcomed.
Thank you in advance.
Solved, turns out just need to set the width to auto as follows:
tabBarOptions: {
tabStyle: {
width: 'auto'
}
}
You can use render label in render header and in that you can return your Text component and Text is having numberOfLines props that will be 1 and it will add ... at end of the text after one line.
Check example snippet:
_renderLabel = props => {
let preparedProps = {
style: {
fontFamily: fonts.Regular,
marginVertical: 8
},
fontType: props.focused ? "Medium" : "Light"
};
return (
<Text
{...preparedProps}
numberOfLines={1}
ref={ref => {
ref && this.props.addAppTourTarget(ref, props.route.key);
}}
>
{props.route.type === "free" && this.state.is_premium_member
? this.labels.premium
: props.route.title}
</Text>
);
};
_renderHeader = props => (
<TabBar
{...props}
bounces={true}
style={{
backgroundColor: colors.cardBlue
}}
indicatorStyle={{
backgroundColor: colors.radicalRed,
height: 1,
borderRightWidth: initialLayout.width * 0.1,
borderLeftWidth: initialLayout.width * 0.1,
borderColor: colors.cardBlue
}}
tabStyle={{
padding: 0,
borderTopColor: "transparent",
borderWidth: 0
}}
renderLabel={this._renderLabel}
/>
);
_handleIndexChange = index => this.setState({ index });
_renderScene = ({ route, focused }) => {
switch (route.key) {
case "a":
return <One {...this.props} route={route} focused={focused} />;
case "b":
return (
<Two {...this.props} isSeries={true} focused={focused} />
);
case "c":
return <Three {...this.props} route={route} focused={focused} />;
default:
return null;
}
};

How to set background color of view transparent in React Native

This is the style of the view that i have used
backCover: {
position: 'absolute',
marginTop: 20,
top: 0,
bottom: 0,
left: 0,
right: 0,
}
Currently it has a white background. I can change the backgroundColor as i want like '#343434' but it accepts only max 6 hexvalue for color so I cannot give opacity on that like '#00ffffff'. I tried using opacity like this
backCover: {
position: 'absolute',
marginTop: 20,
top: 0,
bottom: 0,
left: 0,
right: 0,
opacity: 0.5,
}
but it reduces visibility of view's content.
So any answers?
Use rgba value for the backgroundColor.
For example,
backgroundColor: 'rgba(52, 52, 52, 0.8)'
This sets it to a grey color with 80% opacity, which is derived from the opacity decimal, 0.8. This value can be anything from 0.0 to 1.0.
The following works fine:
backgroundColor: 'rgba(52, 52, 52, alpha)'
You could also try:
backgroundColor: 'transparent'
Try this backgroundColor: '#00000000'
it will set background color to transparent, it follows #rrggbbaa hex codes
Surprisingly no one told about this, which provides some !clarity:
style={{
backgroundColor: 'white',
opacity: 0.7
}}
Try to use transparent attribute value for making transparent background color.
backgroundColor: 'transparent'
You should be aware of the current conflicts that exists with iOS and RGBA backgrounds.
Summary: public React Native currently exposes the iOS layer shadow
properties more-or-less directly, however there are a number of
problems with this:
1) Performance when using these properties is poor by default. That's
because iOS calculates the shadow by getting the exact pixel mask of
the view, including any tranlucent content, and all of its subviews,
which is very CPU and GPU-intensive. 2) The iOS shadow properties do
not match the syntax or semantics of the CSS box-shadow standard, and
are unlikely to be possible to implement on Android. 3) We don't
expose the layer.shadowPath property, which is crucial to getting
good performance out of layer shadows.
This diff solves problem number 1) by implementing a default
shadowPath that matches the view border for views with an opaque
background. This improves the performance of shadows by optimizing for
the common usage case. I've also reinstated background color
propagation for views which have shadow props - this should help
ensure that this best-case scenario occurs more often.
For views with an explicit transparent background, the shadow will
continue to work as it did before ( shadowPath will be left unset,
and the shadow will be derived exactly from the pixels of the view and
its subviews). This is the worst-case path for performance, however,
so you should avoid it unless absolutely necessary. Support for this
may be disabled by default in future, or dropped altogether.
For translucent images, it is suggested that you bake the shadow into
the image itself, or use another mechanism to pre-generate the shadow.
For text shadows, you should use the textShadow properties, which work
cross-platform and have much better performance.
Problem number 2) will be solved in a future diff, possibly by
renaming the iOS shadowXXX properties to boxShadowXXX, and changing
the syntax and semantics to match the CSS standards.
Problem number 3) is now mostly moot, since we generate the shadowPath
automatically. In future, we may provide an iOS-specific prop to set
the path explicitly if there's a demand for more precise control of
the shadow.
Reviewed By: weicool
Commit: https://github.com/facebook/react-native/commit/e4c53c28aea7e067e48f5c8c0100c7cafc031b06
Adding reference of React-Native Version 0.64
Named colors
Named Colors: DOCS
In React Native you can also use color name strings as values.
Note: React Native only supports lowercase color names. Uppercase color names are not supported.
transparent#
This is a shortcut for rgba(0,0,0,0), same like in CSS3.
Hence you can do this:
background: {
backgroundColor: 'transparent'
},
Which is a shortcut of :
background: {
backgroundColor: 'rgba(0,0,0,0)'
},
In case you have hex color, you can convert it to rgba and set the opacity there:
const hexToRgbA = (hex, opacity) => {
let c;
if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
c = hex.substring(1).split('');
if (c.length === 3) {
c = [c[0], c[0], c[1], c[1], c[2], c[2]];
}
c = `0x${c.join('')}`;
return `rgba(${[(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',')},${opacity})`;
}
throw new Error('Bad Hex');
};
const color = '#1f8b7f'; // could be a variable
return (
<View style={{ backgroundColor: hexToRgbA(color, 0.1) }} />
)
source that helped me
This will do the trick help you,
Add one View element and add style as below to that view
.opaque{
position:'absolute',
backgroundColor: 'black',
opacity: 0.7,
zIndex:0
}
The best way to use background is hex code #rrggbbaa but it should be in hex.
Eg: 50% opacity means 256/2 =128, then convert that value(128) in HEX that will be 80,use #00000080 80 here means 50% transparent.
Here is my solution to a modal that can be rendered on any screen and initialized in App.tsx
ModalComponent.tsx
import React, { Component } from 'react';
import { Modal, Text, TouchableHighlight, View, StyleSheet, Platform } from 'react-native';
import EventEmitter from 'events';
// I keep localization files for strings and device metrics like height and width which are used for styling
import strings from '../../config/strings';
import metrics from '../../config/metrics';
const emitter = new EventEmitter();
export const _modalEmitter = emitter
export class ModalView extends Component {
state: {
modalVisible: boolean,
text: string,
callbackSubmit: any,
callbackCancel: any,
animation: any
}
constructor(props) {
super(props)
this.state = {
modalVisible: false,
text: "",
callbackSubmit: (() => {}),
callbackCancel: (() => {}),
animation: new Animated.Value(0)
}
}
componentDidMount() {
_modalEmitter.addListener(strings.modalOpen, (event) => {
var state = {
modalVisible: true,
text: event.text,
callbackSubmit: event.onSubmit,
callbackCancel: event.onClose,
animation: new Animated.Value(0)
}
this.setState(state)
})
_modalEmitter.addListener(strings.modalClose, (event) => {
var state = {
modalVisible: false,
text: "",
callbackSubmit: (() => {}),
callbackCancel: (() => {}),
animation: new Animated.Value(0)
}
this.setState(state)
})
}
componentWillUnmount() {
var state = {
modalVisible: false,
text: "",
callbackSubmit: (() => {}),
callbackCancel: (() => {})
}
this.setState(state)
}
closeModal = () => {
_modalEmitter.emit(strings.modalClose)
}
startAnimation=()=>{
Animated.timing(this.state.animation, {
toValue : 0.5,
duration : 500
}).start()
}
body = () => {
const animatedOpacity ={
opacity : this.state.animation
}
this.startAnimation()
return (
<View style={{ height: 0 }}>
<Modal
animationType="fade"
transparent={true}
visible={this.state.modalVisible}>
// render a transparent gray background over the whole screen and animate it to fade in, touchable opacity to close modal on click out
<Animated.View style={[styles.modalBackground, animatedOpacity]} >
<TouchableOpacity onPress={() => this.closeModal()} activeOpacity={1} style={[styles.modalBackground, {opacity: 1} ]} >
</TouchableOpacity>
</Animated.View>
// render an absolutely positioned modal component over that background
<View style={styles.modalContent}>
<View key="text_container">
<Text>{this.state.text}?</Text>
</View>
<View key="options_container">
// keep in mind the content styling is very minimal for this example, you can put in your own component here or style and make it behave as you wish
<TouchableOpacity
onPress={() => {
this.state.callbackSubmit();
}}>
<Text>Confirm</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
this.state.callbackCancel();
}}>
<Text>Cancel</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
);
}
render() {
return this.body()
}
}
// to center the modal on your screen
// top: metrics.DEVICE_HEIGHT/2 positions the top of the modal at the center of your screen
// however you wanna consider your modal's height and subtract half of that so that the
// center of the modal is centered not the top, additionally for 'ios' taking into consideration
// the 20px top bunny ears offset hence - (Platform.OS == 'ios'? 120 : 100)
// where 100 is half of the modal's height of 200
const styles = StyleSheet.create({
modalBackground: {
height: '100%',
width: '100%',
backgroundColor: 'gray',
zIndex: -1
},
modalContent: {
position: 'absolute',
alignSelf: 'center',
zIndex: 1,
top: metrics.DEVICE_HEIGHT/2 - (Platform.OS == 'ios'? 120 : 100),
justifyContent: 'center',
alignItems: 'center',
display: 'flex',
height: 200,
width: '80%',
borderRadius: 27,
backgroundColor: 'white',
opacity: 1
},
})
App.tsx render and import
import { ModalView } from './{your_path}/ModalComponent';
render() {
return (
<React.Fragment>
<StatusBar barStyle={'dark-content'} />
<AppRouter />
<ModalView />
</React.Fragment>
)
}
and to use it from any component
SomeComponent.tsx
import { _modalEmitter } from './{your_path}/ModalComponent'
// Some functions within your component
showModal(modalText, callbackOnSubmit, callbackOnClose) {
_modalEmitter.emit(strings.modalOpen, { text: modalText, onSubmit: callbackOnSubmit.bind(this), onClose: callbackOnClose.bind(this) })
}
closeModal() {
_modalEmitter.emit(strings.modalClose)
}
Hope I was able to help some of you, I used a very similar structure for in-app notifications
Happy coding