I'm using material-ui's JSS implementation to styling classes.
I have a lot of duplicated code when it comes to the components' styles since I have separated my components.
For example, I have cards which all use common styling:
const styles = theme => ({
cardContainer: {
position: 'relative',
width: '50%',
padding: theme.spacing.unit / 2,
},
cardOuter: {
height: '100%',
width: '100%',
textAlign: 'start',
},
card: {
width: '100%',
background: theme.palette.backgrounds.card.off,
},
cardOn: {
background: theme.palette.backgrounds.card.on,
},
cardUnavailable: {
background: theme.palette.backgrounds.card.disabled,
},
cardContent: {
display: 'flex',
flexWrap: 'wrap',
minHeight: 98,
height: 98,
[theme.breakpoints.down('sm')]: {
minHeight: 74,
height: 74,
},
padding: `${theme.spacing.unit * 1.5}px !important`,
},
});
which I would only rarely want to extend upon the styles inside the component, but would like to import these objects into an existing styles function so I do not have to duplicate these objects.
Has anyone or does anyone know how to do this?
Thanks
Figured it out. For future viewers:
const styles = theme => ({
...card(theme),
grid: {
height: '100%',
width: 'fit-content',
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 2,
flexWrap: 'nowrap',
overflowY: 'hidden',
},
name: {
overflow: 'hidden',
textOverflow: 'ellipsis',
fontSize: '1.12rem',
fontColor: theme.palette.text.main,
[theme.breakpoints.down('sm')]: {
fontSize: '0.9rem',
}
},
state: {
textOverflow: 'ellipsis',
margin: '0 auto',
marginTop: theme.spacing.unit / 2,
fontSize: '1.0rem',
fontColor: theme.palette.text.light,
[theme.breakpoints.down('sm')]: {
fontSize: '0.8rem',
}
},
alarmArmedHome: {
background: theme.palette.backgrounds.card.alarm.home,
},
alarmArmedAway: {
background: theme.palette.backgrounds.card.alarm.away,
},
alarmTriggered: {
background: theme.palette.backgrounds.card.alarm.triggered,
},
icon: {
margin: '0 auto',
color: theme.palette.text.icon,
fontSize: '2.7rem',
[theme.breakpoints.down('sm')]: {
fontSize: '1.7rem',
}
},
});
card.js
const styles = (theme) => ({
cardContainer: {
position: 'relative',
width: '50%',
padding: theme.spacing.unit / 2,
},
cardOuter: {
height: '100%',
width: '100%',
textAlign: 'start',
},
card: {
width: '100%',
background: theme.palette.backgrounds.card.off,
},
cardOn: {
background: theme.palette.backgrounds.card.on,
},
cardUnavailable: {
background: theme.palette.backgrounds.card.disabled,
},
cardContent: {
display: 'flex',
flexWrap: 'wrap',
minHeight: 98,
height: 98,
[theme.breakpoints.down('sm')]: {
minHeight: 74,
height: 74,
},
padding: `${theme.spacing.unit * 1.5}px !important`,
},
});
export default styles;
So you pretty much have to join the objects passing in the theme if required:
...card(theme),
Related
I have a MUI styled component that renders a green circular badge.
const StyledGreenBadge = styled(Badge)(({ theme }) => ({
'& .MuiBadge-badge': {
backgroundColor: '#44b700',
color: '#44b700',
width: '15px',
height: '15px',
borderRadius: '100%',
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
'&::after': {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: '50%',
animation: 'ripple 1.2s infinite ease-in-out',
border: '1px solid currentColor',
content: '""',
},
},
'#keyframes ripple': {
'0%': {
transform: 'scale(.8)',
opacity: 1,
},
'100%': {
transform: 'scale(2.4)',
opacity: 0,
},
},
}));
Now, I want my code to be DRY, so I want to create a StyledYellowBadge.
All I have to do is somehow just change the color property of StyledGreenBadge.
Yet, I could not figure out how for 3 hours.
I have tried something like this:
color: { desiredColor === 'yellow' ? 'yellow' : #44b700'},
where desiredColor is a second argument, after
{ theme }
How can I make achieve this?
You can add custom properties to your styled MUI component by describing the type:
const StyledGreenBadge = styled(Badge)<{ badgeColor?: string }>(
Then, you can pass described property (badgeColor in this case) to your styled Badge component:
<StyledGreenBadge badgeColor="red" badgeContent={4} color="primary">
and assign it to the property you want:
backgroundColor: props.badgeColor ?? "#44b700",
Full code:
const StyledGreenBadge = styled(Badge)<{ badgeColor: string }>(
({ theme, ...props }) => {
console.log(props);
return {
"& .MuiBadge-badge": {
backgroundColor: props.badgeColor ?? "#44b700",
color: "#44b700",
width: "15px",
height: "15px",
borderRadius: "100%",
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
"&::after": {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
borderRadius: "50%",
animation: "ripple 1.2s infinite ease-in-out",
border: "1px solid currentColor",
content: '""'
}
},
"#keyframes ripple": {
"0%": {
transform: "scale(.8)",
opacity: 1
},
"100%": {
transform: "scale(2.4)",
opacity: 0
}
}
};
}
);
export default function SimpleBadge() {
return (
<StyledGreenBadge badgeColor="red" badgeContent={4} color="primary">
<MailIcon color="action" />
</StyledGreenBadge>
);
}
Demo
I'm struggling to make a perfect triangle button instead of a pizza slice button on a slider in Material UI and ReactJS, this is the task I want to complet exemple bellow:
And this is where I'm currently at:
//App
const CustumSlider = withStyles({
root: {
color: "transparent",
height: 2,
padding: '15px 0',
},
thumb: {
boxShadow: iOSBoxShadow,
marginLeft: -16,
width: 0,
height: 0,
borderLeft: "16px solid transparent",
borderRight: "16px solid transparent",
borderBottom: "16px solid #f39200",
cursor: "pointer",
marginTop: "9px",
'&:focus, &:hover, &$active': {
color: "transparent",
boxShadow: iOSBoxShadow,
'#media (hover: none)': {
boxShadow: iOSBoxShadow,
},
},
},
valueLabel: {
left: '-16px',
top: -25,
fontSize: 16,
fontFamily: "Nunito",
fontWeight: 700,
'& *': {
background: 'transparent',
color: '#f39200',
},
},
track: {
height: 15,
borderTopLeftRadius: "5px",
borderBottomLeftRadius: "5px",
color: "#003865",
},
rail: {
height: 15,
opacity: 0.5,
borderTopLeftRadius: "5px",
borderBottomLeftRadius: "5px",
borderTopRightRadius: "5px",
borderBottomRightRadius: "5px",
backgroundColor: "rgba(0, 0, 0, 0.4)",
},
mark: {
backgroundColor: '#bfbfbf',
height: 8,
width: 1,
marginTop: -3,
},
markActive: {
opacity: 1,
backgroundColor: 'currentColor',
},
})(Slider);
Is there any solution to getting a triangle button in the slider?
Just add
borderRadius: "0px",
to the thumb style
sandbox
I am struggling to get the material-ui app bar example to work as I would like to. Codesandbox (from Material-UI website).
What I Am Trying To Achieve:
What I am trying to achieve is to get the search field to grow all the way to the right (effectively taking the majority of the app bar no matter what screen size).
What I Have Tried:
I have tried using flexGrow: 1 as follows which only grows the search bar slightly (not till the end):
search: {
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginRight: theme.spacing(2),
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(3),
width: 'auto',
flexGrow:1
},}
I have also tried setting width: '100%' instead of 'width: auto' but that makes the Material-UI logo disappear.
Image of desired result:
Remove the width: 'auto' from your current code,
Add minWidth with title
title: {
display: 'none',
minWidth: '120px', // Add
[theme.breakpoints.up('sm')]: {
display: 'block',
},
},
search: {
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: fade(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: fade(theme.palette.common.white, 0.25),
},
marginRight: theme.spacing(2),
marginLeft: 0,
width: '100%', // Keep
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(3),
// width: 'auto', // Remove
},
},
import React from 'react';
import PropTypes from 'prop-types';
import SearchIcon from '#material-ui/icons/Search';
import InputBase from '#material-ui/core/InputBase';
import { AccountCircle } from '#material-ui/icons';
import { withStyles, AppBar, Toolbar, Paper, Typography, IconButton } from '#material-ui/core';
const styles = (theme) => ({
root: {
borderRadius: theme.shape.borderRadius * 0.5,
backgroundColor: theme.palette.primary.main,
display: 'flex',
},
logo: {
borderRadius: theme.shape.borderRadius * 0.5,
backgroundColor: theme.palette.secondary.main,
marginTop: theme.spacing.unit * 2,
marginBottom: theme.spacing.unit * 2,
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 5,
flex: 0.5,
},
logoFont: {
color: theme.palette.primary.main,
fontWeight: 'bolder',
paddingTop: theme.spacing.unit * 0.5,
paddingBottom: theme.spacing.unit * 0.5,
paddingLeft: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 2,
},
headerPads: {
paddingTop: theme.spacing.unit * 3,
paddingBottom: theme.spacing.unit * 2,
paddingRight: theme.spacing.unit * 10,
paddingLeft: theme.spacing.unit * 10,
},
containerHorizontalAlignment: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'end',
paddingTop: theme.spacing.unit,
paddingBottom: theme.spacing.unit,
flex: 10,
},
searchBar: {
marginTop: theme.spacing.unit,
marginBottom: theme.spacing.unit,
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit * 5,
borderRadius: theme.shape.borderRadius * 0.5,
backgroundColor: theme.palette.secondary.main,
width: 'auto',
/* [theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing.unit,
width: 'auto',
}, */
display: 'flex',
flexDirection: 'row',
},
searchIcon: {
color: theme.palette.primary.main,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginLeft: 20,
height: '100%',
width: theme.spacing.unit * 7,
},
inputRoot: {
color: theme.palette.primary.main,
width: '100%',
},
inputInput: {
transition: theme.transitions.create('width'),
width: 'auto',
[theme.breakpoints.up('sm')]: {
width: '25%', //change it to 250(number)
'&:focus': {
width: '100%', //change it to 1000(number), it will work fine, but I need percentages.
},
},
},
loginButtonContainer: {
flex: 1,
},
loginButton: {
justifyContent: 'right',
marginTop: theme.spacing.unit * 0.5,
color: theme.palette.secondary.main,
},
});
function ExpandingSearchBar(props) {
const { classes} = props;
return (
<div className={classes.headerPads}>
<Paper square className={ classes.root }>
<div className={classes.logo}>
<Typography variant="h5" className={classes.logoFont}>
PlaceHolder
</Typography>
</div>
//Problematic div: the search bar
<div style={{border: '1px solid white'}} className={ classes.containerHorizontalAlignment }>
<div className={classes.searchBar}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder='Search'
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
/>
</div>
</div>
<div className={classes.loginButton}>
<IconButton className={classes.loginButton}>
<AccountCircle fontSize='large'/>
</IconButton>
</div>
</Paper>
</div>
);
}
ExpandingSearchBar.propTypes = {
classes: PropTypes.object.isRequired,
}
export default withStyles(styles)(ExpandingSearchBar);
Problems:
in styles.inputInput I have create a transition using material-ui's theme.transition.create, the problem is, if the widths are fixed numbers it works fine, if I pass the percentages to it the component just has a fixed width no expansion or transitions at all.
One more problem is if the width searchBar it more than it's parent can accommodate it will no push the 'PlaceHolder' out of Paper but will push the button at the end out of the Paper element and itself will push the itself past the right boundary.
I have used theme.create.transitions() from material-ui but please suggest any solutions using only CSS as well.
Try it here in the sandbox, and switch the browser window of sandbox to full screen.
Thanks
The primary issue was that your "searchBar" div was constraining the meaning of "100%" for your input. The key is to use InputBase for the entire search input which means using a startAdornment for the search icon:
<div className={classes.searchBar}>
<InputBase
startAdornment={<SearchIcon />}
placeholder="Search"
classes={{
root: classes.inputRoot,
focused: classes.inputFocused
}}
/>
</div>
Then there were some adjustments necessary for the styling (moving some styling from searchBar to inputRoot and from inputInput to inputRoot):
searchBar: {
width: "100%",
display: "flex",
flexDirection: "row"
},
inputRoot: {
marginTop: theme.spacing.unit,
marginBottom: theme.spacing.unit,
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit * 5,
borderRadius: theme.shape.borderRadius * 0.5,
backgroundColor: theme.palette.secondary.main,
color: theme.palette.primary.main,
transition: theme.transitions.create("width"),
width: "auto",
[theme.breakpoints.up("sm")]: {
width: "25%" //change it to 250(number)
},
"&$inputFocused": {
[theme.breakpoints.up("sm")]: {
width: "100%" //change it to 1000(number), it will work fine, but I need percentages.
}
}
},
inputFocused: {},
Here's a working modification of your sandbox:
I’m having trouble aligning two components within another div in React. I’m using relative positioning for the parent div (snippetButtonHolder) and absolute positioning for its children (snippet and button). I want snippet to be centered in the parent and button to be under snippet and to the right, but for some reason when I use these attributes they are positioned relative to the entire page, not to the parent div. Does anyone have a suggestion as to what I should do differently?
const styles = {
module: {
marginTop: '30px',
padding: '20px',
},
snippet: {
backgroundColor: '#f2f2f2',
border: 'solid 1px #ccc',
borderRadius: '4px',
margin: '0 auto',
padding: '10px',
width: '100%',
position: 'absolute',
left: '50%',
},
snippetButtonHolder: {
width: '95%',
position: 'relative',
},
button: {
float: 'right',
marginTop: '5px',
position: 'absolute',
left: '94%',
},
};
export default class CodeSnippet extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div style={styles.module}>
<div style={styles.snippetButtonHolder}>
<div style={styles.snippet}>
<div
{'text will go here'}
</div>
{this.state.showButton ?
<button
style={styles.button}>
Press me
</button>
: null}
</div>
</div>
</div>
);
}
}
Give this a try:
const styles = {
module: {
marginTop: '30px',
padding: '20px',
},
snippet: {
backgroundColor: '#f2f2f2',
border: 'solid 1px #ccc',
borderRadius: '4px',
display: 'inline-block',
overflow: 'hidden',
padding: '10px',
},
snippetButtonHolder: {
textAlign: 'center',
width: '95%',
},
button: {
float: 'right',
marginTop: '5px',
},
};