How to a full-width text input in header in React Navigation 6? - css

In my React Native (Expo) application, I wanted to upgrade React Navigation from V5 to V6. However, I could not make TextInput in stack navigator header full-width. I tried 'auto' and '100%' for the width value in styling, however neither helped with a real wide textbox.
Here is the link for Expo snack for reproduction: https://snack.expo.io/#vahdet/reactnavigation6-headerbar and the App.js content from it is below. I guess I am short of some flexbox knowledge in headerSearchBarStyle:
import React, { useLayoutEffect, useState } from 'react';
import { Text, TextInput, View, StyleSheet } from 'react-native';
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { enableScreens } from 'react-native-screens';
import { AppearanceProvider } from 'react-native-appearance';
import { StatusBar } from 'expo-status-bar';
import { createStackNavigator } from '#react-navigation/stack';
enableScreens();
const HomeStack = createStackNavigator();
const Search = () => {
const navigation = useNavigation();
const [searchText, setSearchText] = useState('');
// Customize header
useLayoutEffect(() => {
navigation.setOptions({
headerTitle: () => (
<TextInput
style={styles.headerSearchBarStyle}
value={searchText}
onChangeText={(val) => setSearchText(val)}
containerStyle={styles.searchBarContainerStyle}
placeholder="Search..."
returnKeyType="search"
textContentType="none"
cancelButtonTitle="Cancel"
/>
)
})
}, [navigation, searchText]);
return (
<View style={styles.view}>
{!searchText ? (
<Text>Search results go here</Text>
) : (
<Text>Initial (no search) content goes here</Text>
)}
</View>
)
}
const App = () => {
return (
<AppearanceProvider>
<StatusBar style="auto" />
<NavigationContainer>
<HomeStack.Navigator initialRouteName="Search">
<HomeStack.Screen name="Search" component={Search} />
</HomeStack.Navigator>
</NavigationContainer>
</AppearanceProvider>
);
}
const styles = StyleSheet.create({
headerSearchBarStyle: {
width: 'auto', // also tried '100%'
borderColor: 'black',
borderWidth: 1,
backgroundColor: 'transparent'
},
});
export default App;
EDIT: After Kartikey's approach I want to elaborate that by full-width, I do not necessarily mean the full screen width: There may be scenarios with headerLeft (e.g. back button) or headerRight components at the same time.

Use Device Width
import { Dimensions } from "react-native";
const ScreenWidth = Dimensions.get('window').width;
and
headerSearchBarStyle: {
width: ScreenWidth,
borderColor: 'black',
borderWidth: 1,
backgroundColor: 'transparent',
margin: 10,
},
You can also set it to width: ScreenWidth - 30, just to give some margin
Working Example

Related

MUI alert component conflicts in css

I'm using Material UI 5.10.11 and the MuiAlert component sometimes goes wrong.
This is what it should looks like when severity='error'
However in some pages, it looks like this
Below are my codes. Can anyone have a look at it and try to figure out what's wrong with my work?
import React, { useState, useEffect } from 'react';
import { Snackbar } from '#mui/material';
import MuiAlert from '#mui/material/Alert';
const Alert = React.forwardRef(function Alert(props, ref) {
return <MuiAlert elevation={24} ref={ref} variant="filled" {...props} />;
});
export default function MessageDialog(props) {
const defaultPosition = {
vertical: 'top',
horizontal: 'center'
};
const autoHideDuration = 5000;
const [open, setOpen] = useState(false);
useEffect(() => {
let timeoutId;
if (props.open) {
setOpen(true);
timeoutId = setTimeout(() => {
setOpen(false);
}, autoHideDuration);
}
return () => {
clearTimeout(timeoutId);
};
}, [props.open]);
return (
<>
<Snackbar
anchorOrigin={{
vertical: defaultPosition.vertical,
horizontal: defaultPosition.horizontal
}}
open={open}
>
<Alert severity={props.type} sx={{ width: '100%' }}>
{props.children}
</Alert>
</Snackbar>
</>
);
}
Many thanks in advance!
The fault is due to the css that I spotted out in the screenshots.
It looks like problem comes from Mui component class name which is MuiPaper-root. In page with white background it overrites because probably some other Mui components have same class name and you are using white background with this component or you have a css file with white background assigned to MuiPaper-root class in that page

Overriding a Single Style Prop from Stylesheet Using React Native

With React Native, I'm looking to use StyleSheet to define a style and then use that style in numerous components, but I would like to change or override individual props for a few components. For example, a stack of 10 views with 5 different colors but all other props the same. Is this possible? What does the syntax look like?
I can't imagine I have to define 5 different styles or use in-line styling. Thanks very much for your help.
You can export some globally used styles from a single module, and import them wherever you need. Then to combine styles you can use the array syntax like [ styleA, styleB ].
So in a simple example you could do something like:
// ./styles.js
import { StyleSheet } from 'react-native';
export default StyleSheet.create({
containerDefault: {
height: 100,
width: 300,
backgroundColor: 'black'
},
backgroundBlue: {
backgroundColor: 'blue'
},
backgroundGreen: {
backgroundColor: 'green'
}
});
And then...
// ./SomeComponent.js
import React from 'react';
import { View, Text } from 'react-native';
import styles from './styles';
const ComponentBlack = () => {
return (
<View style={styles.containerDefault}>
<Text>I should be black</Text>
</View>
);
};
const ComponentBlue = () => {
return (
<View style={[styles.containerDefault, styles.backgroundBlue]}>
<Text>I should be blue</Text>
</View>
);
};
const ComponentGreen = () => {
return (
<View style={[styles.containerDefault, styles.backgroundGreen]}>
<Text>I should be green</Text>
</View>
);
};
export default () => {
return (
<View>
<ComponentBlack />
<ComponentBlue />
<ComponentGreen />
</View>
);
};

How to allow customization of a React component's style via props, when withStyles api is used?

I'm writing some simple reusable component for our React(with MaterialUI) application.
The problem is, that i want to allow different styles of this same reusable component, to be customized via props, by the consuming component.
This is some of the code:
import { withStyles } from '#material-ui/core';
const styles = theme => ({
image: {
maxHeight: '200px'
}
});
render() {
const classes = this.props.classes
return (
<div>
...
<img className={classes.image} src={this.state.filePreviewSrc} alt="" />
...
</div>
);
}
Let's say, i want to allow the programmer to customize the appearance of classes.image. Can the hard-coded image class be overwritten somehow?
Is using withStyles api is even the correct approach, for creating components whose appearance can be customized by the consuming component/programmer?
There are three main approaches available for how to support customization of styles:
Leverage props within your styles
Leverage props to determine whether or not certain classes should be applied
Do customization via withStyles
For option 3, the styles of the wrapping component will be merged with the original, but the CSS classes of the wrapping component will occur later in the <head> and will win over the original.
Below is an example showing all three approaches:
ReusableComponent.js
import React from "react";
import { withStyles } from "#material-ui/core/styles";
const styles = {
root: props => ({
backgroundColor: props.rootBackgroundColor
? props.rootBackgroundColor
: "green"
}),
inner: props => ({
backgroundColor: props.innerBackgroundColor
? props.innerBackgroundColor
: "red"
})
};
const ReusableComponent = ({ classes, children, suppressInnerDiv = false }) => {
return (
<div className={classes.root}>
Outer div
{suppressInnerDiv && <div>{children}</div>}
{!suppressInnerDiv && (
<div className={classes.inner}>
Inner div
<div>{children}</div>
</div>
)}
</div>
);
};
export default withStyles(styles)(ReusableComponent);
index.js
import React from "react";
import ReactDOM from "react-dom";
import { withStyles } from "#material-ui/core/styles";
import ReusableComponent from "./ReusableComponent";
const styles1 = theme => ({
root: {
backgroundColor: "lightblue",
margin: theme.spacing(2)
},
inner: {
minHeight: 100,
backgroundColor: "yellow"
}
});
const Customization1 = withStyles(styles1)(ReusableComponent);
const styles2 = {
inner: {
backgroundColor: "purple",
color: "white"
}
};
const Customization2 = withStyles(styles2)(ReusableComponent);
function App() {
return (
<div className="App">
<ReusableComponent>Not customized</ReusableComponent>
<Customization1>Customization 1 via withStyles</Customization1>
<Customization2>Customization 2 via withStyles</Customization2>
<ReusableComponent rootBackgroundColor="lightgrey" suppressInnerDiv>
Customization via props
</ReusableComponent>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Reddit text field implementation Material-UI

So I'm trying to immitate the reddit text field implementation from material-ui, I've gone ahead and setup this custom component, but I'm getting a invalid hook call error everytime I run on the const classes=... Line
Here's the code:
import React, { Component } from "react";
import { TextField } from "#material-ui/core";
import { fade, makeStyles } from "#material-ui/core/styles";
import styles from "./LNTextField.module.css";
const useStylesReddit = makeStyles(theme => ({
root: {
border: "1px solid #e2e2e1",
overflow: "hidden",
borderRadius: 4,
backgroundColor: "#fcfcfb",
transition: theme.transitions.create(["border-color", "box-shadow"]),
"&:hover": {
backgroundColor: "#fff"
},
"&$focused": {
backgroundColor: "#fff",
boxShadow: `${fade(theme.palette.primary.main, 0.25)} 0 0 0 2px`,
borderColor: theme.palette.primary.main
}
},
focused: {}
}));
class LNTextField extends Component {
render() {
var classNames = require("classnames");
const classes = useStylesReddit();
return (
<TextField
InputProps={{ classes, disableUnderline: true }}
{...this.props}
/>
);
}
}
export default LNTextField;
Also since I just copied it I'm not sure how I can type this code in a seperate css files and refer to the hover and focused bits appropriately, so If you could also tell me how to do that that'd be great. Thanks!
According to React, you are getting this error because:
You can’t use Hooks inside of a class component
Convert your class component to functional component:
const LNTextField = props => {
var classNames = require("classnames");
const classes = useStylesReddit();
return (
<TextField
InputProps={{ classes, disableUnderline: true }}
{...props}
/>
);
}

Changing styles during clicking

I have ReactJS project and I want to change colour of button during clicking. I know that it is a Ripple API but it's very incomprehensible to use it. Could someone advise me how can I do that?
I've tried to create two elements - parent and child - and changed background of child to transparent while clicking. Unfortunately I have also 'classes' object responsible for changing class if button is active and it is just not working.
My code below:
import React, { Component } from 'react';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import PropTypes from 'prop-types';
import styles from './MydButton.style';
class MyButton extends Component {
constructor(props) {
super(props);
this.state = {
isClicked: false
};
}
handleClick = () => {
this.setState({ isClicked: !this.state.isClicked });
}
render() {
const {
classes,
children,
color,
disabled,
className,
onClick,
type,
border,
...props
} = this.props;
const myClass = this.state.isClicked ? 'auxClass' : 'buttonDefaultRoot';
return (
<div className={classes.parentRoot} >
<Button
classes={{
root: disabled
? classes.buttonDisabledRoot
: classes.buttonRoot,
label: disabled
? classes.buttonLabelDisabled
: classes.buttonLabel,
}}
{...props}
onClick={this.handleClick}
className={myClass}
disabled={disabled}
type={type === undefined ? 'button' : type}
>
{children}
</Button>
</div>
)
}
};
MyButton.propTypes = {
children: PropTypes.string.isRequired,
disabled: PropTypes.bool,
classes: PropTypes.object.isRequired,
};
MyButton.defaultProps = {
disabled: false,
};
export default withStyles(styles)(MyButton);
and styles:
const buttonRoot = {
border: 0,
height: 48,
width: '100%',
}
export default theme => ({
buttonDefaultRoot: {
...buttonRoot,
transition: 'all 1s ease-in-out',
backgroundImage: 'linear-gradient(to right, #F59C81, #E65DA2, #E65DA2, #B13A97, #881E8E)',
boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.16)',
backgroundSize: '300% 100%',
marginTop: 0,
'&:hover': {
backgroundPosition: '100% 0%',
transition: 'all 1s ease-in-out',
}
},
parentRoot: {
...buttonRoot,
backgroundColor: 'red',
backgroundSize: '300% 100%',
marginTop: 36,
},
auxClass: {
backgroundImage: 'none',
},
Material UI Core for ReactJS
The documentation is very good. I have updated my answer to accomodate the specific needs of this question. I have also included two general solutions for anyone who stumbles upon this question.
Tailored Solution:
Changes background color of button from classes.buttonDefaultRoot (a color defined by owner of question) to the gradient defined by the owner of this question.
First step, have a variable stored in state. You can call it whatever you want, but I'm calling bgButton. Set this to this.props.classes.buttonDefaultRoot like so:
state = {
bgButton: this.props.classes.buttonDefaultRoot,
}
Next, you want to define your function that will handle the click. Again, call it what you want. I will call it handleClick.
handleClick = () => {
const { classes } = this.props; //this grabs your css style theme
this.setState({ bgButton: classes.parentRoot.auxClass }); //accessing styles
};
A couple of things are happening here. First, I am destructuring props. So, I am creating a new const variable called classes that has the same value as this.props.classes. The classes contains a set of objects that defines your css styles for your buttons, margins, etc. You can access those styles just like you would if you were trying to get the value of a prop in an obj.
In this case you can access your button style by doing, classes.buttonDefaultRoot. That takes care of your handle click function.
Last step: render the button. In your render method you want to grab your bgButton from state like so:
render() {
const { bgButton } = this.state;
Then you want to assign your className of your button to bgButton and add the onClick functionality like this (this follows the Material UI Core documentation):
<Button variant="contained" color="primary" className={classNames(bgButton)} onClick={this.handleClick}>Button Name</Button>
Putting it all together you get this:
import React, { Component } from "react";
import Button from "#material-ui/core/Button";
import PropTypes from "prop-types";
import classNames from "classnames";
import { withStyles } from "#material-ui/core/styles";
export default theme => ({ ... }) //not going to copy all of this
class MyButton extends Component {
state = {
bgButton: null
};
handleClick = () => {
const { classes } = this.props;
this.setState({ bgButton: classes.parentRoot.auxClass });
};
render() {
const { bgButton } = this.state;
return (
<div className={classes.container}>
<Button
variant="contained"
color="primary"
className={classNames(bgButton)}
onClick={this.handleClick}
>
Custom CSS
</Button>
</div>
);
}
}
MyButton.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(MyButton);
General Solution
This solution is for those who want to use the predefined colors, i.e. default, primary, secondary, inherit. This implementation does not need the PropTypes or className imports. This will change the color from the predefined blue to the predefined pink. That's it.
state = {
bgButton: "primary",
}
handleClick = () => {
this.setState({ bgButton: "secondary" });
}
render() {
const { bgButton } = this.state;
return(
...
<Button
onClick = {this.handleClick}
variant = "contained" //checked Material UI documentation
color={bgButton}
> ..etc.
General Solution 2
To accommodate your custom styles to the button, you would have to import PropTypes and classNames and take a similar approach as the tailored solution above. The only difference here will be my syntax and class name. I am closely following the documentation here so you can easily follow along and readjust where necessary.
import React, { Component } from "react";
import Button from "#material-ui/core/Button";
import PropTypes from "prop-types";
import classNames from "classnames";
import { withStyles } from "#material-ui/core/styles";
import purple from "#material-ui/core/colors/purple";
const styles = theme => ({
container: {
display: "flex",
flexWrap: "wrap"
},
margin: {
margin: theme.spacing.unit
},
cssRoot: {
color: theme.palette.getContrastText(purple[500]),
backgroundColor: purple[500],
"&:hover": {
backgroundColor: purple[700]
}
},
bootstrapRoot: {
boxShadow: "none",
textTransform: "none",
fontSize: 16,
padding: "6px 12px",
border: "1px solid",
backgroundColor: "#007bff",
borderColor: "#007bff",
fontFamily: [
"-apple-system",
"BlinkMacSystemFont",
'"Segoe UI"',
"Roboto",
'"Helvetica Neue"',
"Arial",
"sans-serif",
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"'
].join(","),
"&:hover": {
backgroundColor: "#0069d9",
borderColor: "#0062cc"
},
"&:active": {
boxShadow: "none",
backgroundColor: "#0062cc",
borderColor: "#005cbf"
},
"&:focus": {
boxShadow: "0 0 0 0.2rem rgba(0,123,255,.5)"
}
}
});
class MyButton extends Component {
state = {
bgButton: null
};
handleClick = () => {
const { classes } = this.props;
this.setState({ bgButton: classes.cssRoot });
};
render() {
const { classes } = this.props; //this gives you access to all styles defined above, so in your className prop for your HTML tags you can put classes.container, classes.margin, classes.cssRoot, or classes.bootstrapRoot in this example.
const { bgButton } = this.state;
return (
<div className={classes.container}>
<Button
variant="contained"
color="primary"
className={classNames(bgButton)}
onClick={this.handleClick}
>
Custom CSS
</Button>
</div>
);
}
}
MyButton.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(MyButton);
A tip. You no longer need a constructor or to bind methods.
Hope this helps.

Resources