pass material ui styles to component - react - reactjs

I am using a functional component with React, I need to show SVG Icon based on state and I want to load the relevant icon
so the parent will show only call :
<icon classes:... , state..></icon>
1- how can I pass style and if it does not exist and use a default style in the child?
now I have smth like in the parent :
... createStyle
IconSuccess: {
fontSize: 20,
width: 20,
},
IconWarning: {
fontSize: 20,
width: 20,
},
but i want smth like :
icon:{
width:..
font ..
warning: { color}
success: { color}
}
then
<IconChild state={state} classes={{ icon: itemStyle.icon}} />
this is work only if I pass specific style like:
<IconChild state={state} classes={{ iconWarning: itemStyle.iconWarning}} />
then in the childCOmponent I am doing smth like:
const classes = useStyles(props);
if( props.state == 1){
return <className={`${classes.iconWarning}`} />
}
else{
return <className={`${classes.iconSuccess}`} />
}
so basically I am trying to understand how to create a really generic component that I can use and pass and that need a state to choose the specific icon and also from specific class
do I need HOC ? or different approach

As I understand, you want to:
Reuse some common properties like width and fontSize.
Custom render other properties like color.
Then this is my approach:
First, make new style for commonly used properties.
Secondly, create new styles for conditional use of each state.
Last, use something like classnames to combine all classes.
So the main idea here is: instead of using one class for each item, now using two classes for each one. That's it!
const useStyles = withStyles({
commonProperty: {
fontSize: '20px',
width: '20px',
},
successOnlyProperty: {
color: 'green'
},
warningOnlyProperty: {
color: 'orange'
},
});

Related

How to pass props to StyleSheet on custom view in React Native?

So, I have a custom view. On that view, besides taking the children components, I also wanna take backgroundColor and some other StyleSheet property so I can style it depends on the screens.
This is the App.tsx.
export const MainScreen = ({}: Props) => {
return (
<CustomView backgroundColor={"#000"}>
<Text>Example</Text>
</CustomView>
);
};
And this is the CustomView.tsx
type Props = {
children: React.ReactNode;
backgroundColor: string;
};
export const CustomView = ({ children, backgroundColor }: Props) => {
return <View style={styles.container}>{children}</View>;
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
});
Say, I wanna change the background color for this screen to #000 like on the codes above. But, I don't know how to handle the props on the CustomView so it can change the backgroundColor.
I tried writing this code
return <View style={styles.container, {backgroundColor: backgroundColor}}>{children}</View>;
But it overwrites the styles.container. On the other hand, if I write this
return <View style={{backgroundColor: backgroundColor}, styles.container}>{children}</View>;
It always throws the error that the left side of comma (the backgroundColor itself) is unused and has no side effects.
So, what should I do? How can I pass the props to handle the StyleSheet?
You can pass an array to the style prop.
According to the docs:
The style prop can be a plain old JavaScript object. That's what we usually use for example code. You can also pass an array of styles - the last style in the array has precedence, so you can use this to inherit styles.
return <View style={[styles.container, { backgroundColor: backgroundColor }]}>{children}</View>;

Trying to style an item with a specific custom class with material UI

I am trying to manage a music playlist with a material ui table. And I would like to change the appearance of the item I am currently playing. At first I was juste setting it as "selected" but now, I have custom theme, and I don't know what I should overwrite to have the "selected" appearances coherent with the theme of my playlist. So, instead, I would prefer to add a class to my row "playing", and then, style my item differently regarding the class it has.
I did not find anything working to do so. When I tried to add my custom class into my theme, I got this error :
mergeClasses.js:25 Material-UI: The key `&.playing` provided to the classes prop is not implemented in ForwardRef(TableCell).
You can only override one of the following: root,head,body,footer,sizeSmall,paddingCheckbox,paddingNone,alignLeft,alignCenter,alignRight,alignJustify,stickyHeader.
So, I guess what I was trying to do must not be done like I tried to. I tried this :
const StyledTableCell = withStyles((theme) => ({
root: {
borderColor: theme.root.borderColor,
},
head: {
backgroundColor: theme.palette.background.default,
color: theme.palette.common.white,
},
body: {
fontSize: 14,
},
"&.playing": { backgroundColor: "rgba(0, 0, 0, 0.16)" },
}))(TableCell);
How can I add specific rendering rules to a custom classe in material ui ?
&.playing should be inside of root to be applied to the root element.
const StyledTableCell = withStyles((theme) => ({
root: {
'&.playing': {
backgroundColor: '#ccc',
}
},
}))(TableCell);

Arbitrarily injecting styles with styled-components

I'm populating a grid with various controls (in this example: up-down counter and a text box).
Currently, I'm injecting styles in the cls member (in this example can be e.g. wide-input and narrow-input):
render(): ReactNode {
const input: CellItem[] = [
{ isUpdown: false, cls: 'wide-input' },
{ isUpdown: true, cls: 'narrow-input' },
];
return (
<GridContainer>
input.map(content, index): ReactNode => {
return (
content.isUpdown ?
<StyledUpdownCell className={content.cls} /> :
<StyledTextBoxCell className={content.cls} /> :
)
}
</GridContainer>
);
}
My question is what is the proper way to do it using styled-components?
Is there a way to inject any arbitrary style (content.cls in this example, but tomorrow it could be also setting custom border color for instance)
Using styled components, you can have access to props passed to your custom styled component.
So, you could create different 'themes' props which you pass to your StyledUpdownCell and then access those inside the component styles. For example
const StyledUpdownCell = styled.div`
border-color: ${props => props.warningTheme ? 'red' : 'black'};
`
in use:
<StyledUpdownCell warningTheme />
You could also pass props directly but with a default e.g.
const StyledUpdownCell = styled.div`
border-color: ${props => props.borderColor || 'black'};
`
in use:
<StyledUpdownCell borderColor="violet" />
It's really up to you and how you want to design your component's API.
Side note: I've found this little library helpful for when creating components which have a lot of different props: https://github.com/yldio/styled-is

Apply radiobutton color using styled-components in Material UI?

In the Material UI documents, they provided sample code to show how one can change the color of a Radiobutton.
const GreenRadio = withStyles({
root: {
color: green[400],
'&$checked': {
color: green[600],
},
},
checked: {},
})(props => <Radio color="default" {...props} />);
I would like to replicate this with styled-component instead i.e. const StyledRadio = styled(Radio) but I am not too familiar with the syntax such as the ampersand and the dollar sign - how can I do this?
When using styled components with MUI, the CSS is applied to the root class of the component. If you need to apply a more specific style, then you'll need to target the relevant class. In this case, you'll need to target the .Mui-checked class:
const StyledRadio = styled(Radio)`
color: ${green[400]};
&.Mui-checked {
color: ${green[600]};
}
`;
The MUI docs are really good in that they list the CSS classnames for each component. If you visit the API docs for the Radio component, you'll see the .Mui-checked class listed there (under the 'Global Styles' column).
Here's a working example in Code Sandbox: https://codesandbox.io/embed/styled-components-9pewl
Here's the appropriate styled-components syntax:
const GreenRadio = styled(Radio)`
color: ${green[400]};
&.Mui-checked {
color: ${green[600]};
}
`;
Related documentation: https://material-ui.com/customization/components/#pseudo-classes

material-ui: AppBar: strategy for restricting an image height to AppBar height?

can anyone provide guidance around an idiomatic way to place an image in an AppBar and have it be restricted to the standard material height (e.g. 64px for a desktop)?
i'm currently using material-ui#next (1.0.0-beta.2 currently).
i have found that something like:
<AppBar>
<Toolbar>
<IconButton color="contrast" aria-label="Menu">
<MenuIcon />
</IconButton>
<img src={logo} style={{height: 64}}/>
<Typography type="title" color="inherit">
React Scratch
</Typography>
</Toolbar>
</AppBar>
works well.
the actual logo is a png file with a height greater than 64, so if i don't ratchet it down, it expands the height of the AppBar out of Material spec.
in the current master branch version of src/styles there is a getMuiTheme.js which seems to deliver this height readily, but in the #next version i am looking at, that file doesn't even exist and tbh, i can't easily determine how that height is being set anymore.
i found that the AppBar is currently being renovated for composability, so that churn might make it challenging to answer this question, but just in case anyone has a good handle on this, i figured i would toss the question out there.
thanks!
In all cases I've seen, an AppBar is implemented with a Toolbar as it's first child. The Toolbar's stylesheet dictates it's height based on the breakpoints defined in the theme.
Take a look here: https://github.com/callemall/material-ui/blob/v1-beta/src/Toolbar/Toolbar.js
You can use a similar approach to define a stylesheet with a class for your AppBar images that varies the height for the applicable breakpoints. Then when rendering the component, apply the class to your image.
Note: if you use the withStyles HOC, as is done in the Toolbar, AppBar etc, the classes defined in that stylesheet will be available through a prop named classes.
You are right about the AppBar's need for composability, but that issue has not been solved yet, and this is the beta branch anyway. When it is solved, there should be a better solution that would be worth migrating towards.
I hope this answer helps. I would have added code samples but I am answering from my phone while waiting in a grocery store parking lot. If I get a chance I will update this answer.
Here's one approach, duplicating the styles in a new reusable component:
import createStyleSheet from 'material-ui/styles/createStyleSheet';
import withStyles from 'material-ui/styles/withStyles';
// define these styles once, if changes are needed because of a change
// to the material-ui beta branch, the impact is minimal
const styleSheet = createStyleSheet('ToolbarImage', theme => ({
root: {
height: 56,
[`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
height: 48,
},
[theme.breakpoints.up('sm')]: {
height: 64,
},
},
}));
// a reusable component for any image you'd need in a toolbar/appbar
const ToolbarImage = (props) => {
const { src, classes } = this.props;
return (
<img src={src} className={classes.root} />
);
};
// this higher order component uses styleSheet to add
// a classes prop that contains the name of the classes
export default withStyles(styleSheet)(ToolbarImage);
Another approach is to add the standard toolbar heights to the theme as business variables, override the root class for all Toolbars so that it makes use of them, and use the theme whenever you need to reference them again:
// define the standard heights in one place
const toolbarHeights = {
mobilePortrait: 56,
mobileLandscape: 48,
tabletDesktop: 64,
};
// create the theme as you normally would, but add the heights
let theme = createMuiTheme({
palette: createPalette({
primary: blue,
accent: pink,
}),
standards: {
toolbar: {
heights: toolbarHeights,
},
},
});
// recreate the theme, overriding the toolbar's root class
theme = createMuiTheme({
...theme,
overrides: {
MuiToolbar: {
// Name of the styleSheet
root: {
position: 'relative',
display: 'flex',
alignItems: 'center',
minHeight: theme.standards.toolbar.heights.mobilePortrait,
[`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: {
minHeight: theme.standards.toolbar.heights.mobileLandscape,
},
[theme.breakpoints.up('sm')]: {
minHeight: theme.standards.toolbar.heights.tabletDesktop,
},
},
},
},
});
Then you can reference these heights in any stylesheet you create because they're part of the theme.
UPDATED FOLLOWING THE RELEASE OF 1.0.0-beta.11:
There is now a toolbar mixin available on the theme that provides the toolbar minHeight for each breakpoint. If you need to style an element relative to the standard height of the AppBar component, you can use this object to build your own styles:
const toolbarRelativeProperties = (property, modifier = value => value) => theme =>
Object.keys(theme.mixins.toolbar).reduce((style, key) => {
const value = theme.mixins.toolbar[key];
if (key === 'minHeight') {
return { ...style, [property]: modifier(value) };
}
if (value.minHeight !== undefined) {
return { ...style, [key]: { [property]: modifier(value.minHeight) } };
}
return style;
}, {});
In this example, toolbarRelativeProperties returns a function that will return an object that can be spread into your style object. It addresses the simple case of setting a specified property to a value that is based on the AppBar height.
A simple usage example would be the generation of a dynamic CSS expression for height calculation, which is depending on the standard height of the AppBar:
const componentStyle = theme => ({
root: {
height: toolbarRelativeProperties('height', value => `calc(100% - ${value}px)`)(theme)
}
});
The generated style definition might look like this:
{
height: 'calc(100% - 56px)',
'#media (min-width:0px) and (orientation: landscape)': {
height: 'calc(100% - 48px)'
},
'#media (min-width:600px)': {
height: 'calc(100% - 64px)'
}
}

Resources