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}
/>
);
}
Related
Currently, I am trying to modify KeyboardDatePicker board color, size, font, padding, but unfortunately, all approaches don’t work. I tried so far:
1 . useStyles :
const useStyles = (params: any) =>
makeStyles(() =>
createStyles({
componentStyle: {
width: params.width ? params.width : 'auto',
color: params.color ? params.color : 'inherit',
verticalAlign: 'middle',
fontSize: '12px',
border: 'solid 2px #0070D8',
},
})
);
Doesn’t override and a border appears on current KeyboardDatePicker border, size doesn’t change as well.
2 . Theme provide, it overrides calendar theme, but not KeyboardDatePicker date box.
<ThemeProvider theme={theme}>
3 . Add styles into KeyboardDatePicker, it is the only working approach
style={{width:"246px",height:"44px"}}
How would you suggest modifying styles of KeyboardDatePicker, and yes style={} approach it's not the correct way to changes styles. p.s I am using Material-UI 4
My KeyboardDatesPicker:
<KeyboardDatePicker
format="MM/dd/yyyy"
margin="normal"
id="date-picker-inline"
defaultValue={props.value}
value={selectedDate}
required={props.required}
showTodayButton={true}
disableToolbar
inputVariant="outlined"
variant="inline"
onChange={(selectedDate) => setSelectedDate(selectedDate)}
KeyboardButtonProps={{
"aria-label": "change date",
}}
keyboardIcon={<Icon icon={ICONS.Cool_icon} />}
className={classes.componentStyle} // do not overide , but puts on top
/>
makeStyles is a hook factory that returns a style hook (usually called useStyles), this is how it's used:
const useStyles = makeStyles(...);
In your code, you define useStyles as a function that return makeStyles instead of telling makeStyles to create a new hook which doesn't make sense here, so change your code to the above. I also fixed the styles for you. The text color styles should be placed in InputBase component:
const useStyles = makeStyles(() =>
createStyles({
componentStyle: {
verticalAlign: "middle",
fontSize: "12px",
width: (params) => (params.width ? params.width : "auto"),
"& fieldset": {
border: "solid 2px #0070D8"
},
"& .MuiInputBase-root": {
height: (params) => (params.height ? params.height : "auto"),
color: (params) => (params.color ? params.color : "inherit")
}
}
})
);
const classes = useStyles({
color: "red",
width: 400,
height: 80,
});
<KeyboardDatePicker
onChange={() => {}}
inputVariant="outlined"
InputProps={{
className: classes.componentStyle
}}
/>
If you want to style via createMuiTheme, here is the equivalent code. Note that you can't pass the component props to create dynamic styles unlike the useStyles approach above:
const theme = createMuiTheme({
overrides: {
MuiTextField: {
root: {
verticalAlign: "middle",
fontSize: "12px",
width: 150,
"& fieldset": {
border: "solid 2px #0070D8"
}
}
}
}
});
And it should work again. For reference, see this section to know how you can use makeStyles with component props.
It seems you don't need to write a custom hook like this useStyles = (params: any) => ..., the hook returned by makeStyles already accepts a props param.
When styling MUI components you need to check the API for each component to define the object you pass to makeStyles, in this case, the date picker component is a group of other MUI components, if you go to the API you'll see different props to pass to each individual component. To style the input you pass the classes returned by the useStyle hook in InputProps, with root rule as it is in the Input API, apply other rules if you need more specific styles.
const useInputStyles = makeStyles({
root: {
width: (props) => (props.width ? props.width : "auto"),
color: (props) => (props.color ? props.color : "inherit"),
verticalAlign: "middle",
fontSize: "12px",
border: "solid 2px #0070D8"
}
});
...
const inputClasses = useInputStyles()
...
<KeyboardDatePicker
...
InputProps={{ classes: inputClasses }}
/>
and to style the "board", not sure if you mean the popover, since you use the inline variant, you pass the styles in the PopoverProps, defining the styles in the paper rule as described in the Popover API
const usePopoverStyles = makeStyles({
paper: {
backgroundColor: "green"
}
});
...
const popoverClasses = usePopoverStyles();
...
<KeyboardDatePicker
...
PopoverProps={{ classes: popoverClasses }}
/>
you can see it working here https://codesandbox.io/s/mui-keyboarddatepicker-styles-sueqd?file=/src/App.tsx
I have implemented it like this in TS, not a finished component, but I hope that this assists people. I am about to add MuiFormLabel-root etc to add more specific styling to the label.
const useDatePickerStyles = makeStyles<ITheme, ITextFieldStyleProps>((theme) =>
createStyles({
datePickerContainer: ({ isValid, isError }) => ({
border: 'solid',
borderRadius: 4,
borderWidth: theme.mvf.border.width.thin,
borderColor: theme.mvf.palette.border,
...(!isError && {
'&:hover': {
boxShadow: theme.mvf.boxShadow.primary,
borderColor: theme.mvf.palette.primary.main,
},
...(isValid && {
color: theme.mvf.palette.primary.main,
boxShadow: theme.mvf.boxShadow.primary,
borderColor: theme.mvf.palette.primary.main,
}),
}),
...(isError && {
color: theme.mvf.palette.error,
boxShadow: theme.mvf.boxShadow.error,
borderColor: theme.mvf.palette.error,
}),
}),
datePicker: () => ({
margin: theme.mvf.spacing.small,
}),
}),
);
export default useDatePickerStyles;
And get access to classes like so
const DatePicker: DatePickerType = ({
id,
onChange,
format,
value,
label,
errorMessage,
placeholder,
isVerticallyCentered,
...props
}: IDatePickerProps) => {
const isValid = !errorMessage && !!value;
const classes = useDatePickerStyles({
isError: !!errorMessage,
isVerticallyCentered,
isValid,
});
return (
<div className={classes.datePickerContainer}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
id={id}
fullWidth
maxDateMessage={''}
minDateMessage={''}
invalidDateMessage={''}
className={classes.datePicker}
label={isVerticallyCentered ? undefined : label} // don't show as label will be outside
placeholder={placeholder}
format={format} // of the displayed date
emptyLabel={placeholder} // displayed value if empty
name="datePicker"
InputLabelProps={{
className: classes.inputLabel,
}}
margin="normal"
value={value}
onChange={onChange}
InputProps={{
className: classes.inputPropsClasses,
inputProps: { className: classes.textInput },
disableUnderline: true,
}}
inputVariant="standard"
KeyboardButtonProps={{
className: classes.calendarButton,
}}
{...props}
/>
</MuiPickersUtilsProvider>
</div>
);
};
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
I'd like to modify MuiIconButton-root class when I use MuiSvgIcon with fontSizeSmall.
import React from 'react'
import { createMuiTheme, ThemeProvider } from '#material-ui/core/styles';
const theme = createMuiTheme({
overrides: {
MuiSvgIcon: {
fontSizeSmall: {
padding: "5px"
}
}
}
});
export default function Root(props) {
return (
<ThemeProvider theme={theme}>
<MyApp />
</ThemeProvider>
)
}
I've modified MuiSvgIcon, but it can't modify MuiButtonBase-root.
Is there any way to override the padding value of MuiIconButton-root when I use small MuiSvgIcon?
Following the picture:
yes you can override the style of root like this, I am not creating your exact scenario but I am going to show you the same functionality with a button from Material UI, first get all the files needed:
npm install #material-ui/styles
npm install #material-ui/core
and then:
//other react imports
import { makeStyles, withStyles } from '#material-ui/core/styles'; //you need to include this to change the style
import Button from '#material-ui/core/Button'; //this is just the button
const useStyles = theme => ({
root: {
'& > *': {
margin: theme.spacing(1),
},
},
button: {
fontSize: "16px",
fontWeight: "600",
textAlign: "center",
border: "none",
cursor: "pointer",
color: "#fff",
backgroundColor: "#C8385E"
}
});
class Welcome extends React.Component {
render() {
return (<div>
<Button
className={classes.button}
variant="contained" component="span">
BUTTON TEXT
</Button>
</div>);
}
}
export default withStyles(useStyles)(Welcome);
you need to export the class like shown above, include the "withStyles" and pass in the styles that you have, here its called "useStyles" and then pass the class name. this way you can edit the styles in the UseStyles and it will actually show those changes on the screen.
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.
I'm pretty new to css and i'm a little confused here. I'm using material ui with react and redux. I want somehow to edit some properties of a specific component. For example suppose we use TextField with disabled property. As i can see the disabled property contains these properties(i saw that from the material ui node modules in textfield).
var styles = {
root: {
borderTop: 'none',
borderLeft: 'none',
borderRight: 'none',
borderBottomStyle: 'solid',
borderBottomWidth: 1,
borderColor: borderColor,
bottom: 8,
boxSizing: 'content-box',
margin: 0,
position: 'absolute',
width: '100%'
},
disabled: {
borderBottomStyle: 'dotted',
borderBottomWidth: 2,
borderColor: disabledTextColor
},
But i dont want when it's disable for the borderBottomLine to be dotted. I want to change it to hidden. How to do such an action without affecting the frameworks code?
You can override some default styles of material-ui components. Look at this section of docs. Pay attention to this example:
import React from 'react';
import {cyan500} from 'material-ui/styles/colors';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import AppBar from 'material-ui/AppBar';
// This replaces the textColor value on the palette
// and then update the keys for each component that depends on it.
// More on Colors: http://www.material-ui.com/#/customization/colors
const muiTheme = getMuiTheme({
textField: {
backgroundColor: 'yellow',
},
datePicker: {
color: 'yellow',
},
});
// MuiThemeProvider takes the theme as a property and passed it down the hierarchy.
const Main = () => (
<MuiThemeProvider muiTheme={muiTheme}>
<AppBar title="My AppBar" />
</MuiThemeProvider>
);
export default Main;
Here, we override background-color for TextField component and color for DatePicker. You should import getMuiTheme function, pass to its object with properties which you want to override. Unfortunately, for disabled TextField you can override only text color. You can check all properties which you can override from source of default theme - https://github.com/callemall/material-ui/blob/master/src/styles/getMuiTheme.js
const muiTheme = getMuiTheme({
textField: {
backgroundColor: 'yellow',
},
datePicker: {
color: 'yellow',
},
});
After that, you should pass muiTheme to the eponymous property
of MuiThemeProvider component. This component should wrap root-component of your application.
const Main = () => (
<MuiThemeProvider muiTheme={muiTheme}>
<AppBar title="My AppBar" />
</MuiThemeProvider>
);
Here's sample code. Use style in your preferred jsx tag and edit it normally like CSS, but the properties & values must be inside quotation marks("").
import React from "react";
import AppBar from "#mui/material/AppBar";
import Toolbar from "#mui/material/Toolbar";
const index = () => {
return (
<AppBar style={{ backgroundColor: "black", height: "65px" }}>
<Toolbar></Toolbar>
</AppBar>
);
};
export default index;