Write JS inside material ui styles - css

I wanted to put some conditional logic inside mui styles
Currently using inline style, which works
<div className={classes.form} style={{alignItems: ((Code.length>10 || Description.length>100) ? 'start' : 'center')}}>
...
</div>
But want to do the same thing inside mui styles, which gives error
// Code and Description state
...
const useStyles = makeStyles(theme => ({
form: {
alignItems: {(Code.length>10 || Description.length>100) ? 'start' : 'center'}, //Code not defined
}

You can pass a props the the useStyles hook and use it like this:
const classes = useStyles(props);
const useStyles = makeStyles(theme => ({
form: {
alignItems: props => props.Code.length>10 || props.Description.length>100 ? 'start' : 'center',
}

You never mentioned how Code and Description are defined, but you can pass props to makeStyles.
const useStyles = makeStyles(theme => ({
form: {
alignItems: props => (props.Code.length > 10 || props.Description.length > 100) ? 'start' : 'center'
}
}));
// Usage
const classes = useStyles(props);

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

Rerender Tooltip when Scrolling React

Thanks for your help in advance, I have an issue with a tooltip, it is supposed that I should show the tooltip when a condition is given, but due to the scroll when rerendering the list the validation fails.
Here is working right, the complete list shows the tooltips where it is supposed to be. enter image description here
But then, when I scroll down the view is re-render and the tooltip fails. enter image description here
The idea is that the tooltip (and the underline) should be shown when I have group names too long using this boolean disableHoverListener={textDoesOverflow}, and it is working at the beginning but then ... fails.
Here's the code and the styles.
Please help!!
export const BaseFilteredUsersGroups: React.FC<IFilteredUsersGroups> = (props) => {
const {
userId,
filteredGroupIds = [],
localize,
} = props;
const sizeGroupsRef = React.useRef(null);
const sizeTitleRef = React.useRef(null);
const styles = useStyles();
const usersGroups = useSelector((state: IDuxStore) => {
const groups = filteredGroupIds.map(groupId => select.group.getGroupByGroupId(state, groupId));
return groups.filter(group => group?.memberships?.some(user => user.userId === userId));
});
const labelTitle = localize.formatItems(usersGroups.map(group => group.title));
const textDoesOverflow = sizeGroupsRef?.current?.getBoundingClientRect()?.width >= sizeTitleRef?.current?.getBoundingClientRect()?.width;
const finalStyle = textDoesOverflow ? styles.groupTitle : styles.groupTitleOverflow;
return (<div className={styles.usersGroups} ref={sizeGroupsRef}>
{<Tooltip title={labelTitle} disableHoverListener={textDoesOverflow} placement="top" onScrollCapture={}>
{<span className={finalStyle} ref={sizeTitleRef}>
{labelTitle}
</span>}
</Tooltip>}
</div >);
};
Here the styles:
export const useStyles = makeStyles(theme => {
return createStyles({
usersGroups:{
textOverflow: 'ellipsis',
overflow: 'hidden',
},
groupTitle: {
whiteSpace: 'nowrap',
fontWeight: theme.typography.fontWeightMedium,
color: theme.palette.text.secondary,
},
groupTitleOverflow: {
whiteSpace: 'nowrap',
fontWeight: theme.typography.fontWeightMedium,
color: theme.palette.text.secondary,
textDecorationLine: 'underline',
}
});
});
const textDoesOverflow =
sizeGroupsRef?.current?.getBoundingClientRect()?.width
>= sizeTitleRef?.current?.getBoundingClientRect()?.width;
const finalStyle = textDoesOverflow ? styles.groupTitle : styles.groupTitleOverflow;
The conditional logic here is reversed. Right now if the text width is greater than the sizeTitleRef width it will return groupTitle not groupTitleOverflow. So instead you may want to switch up the ternary operator to this:
const finalStyle = textDoesOverflow ? styles.groupTitleOverflow : styles.groupTitle;

passing mixins to styled-component

I am trying to pass a mixin from Material UI to a styled-component. The issue is that I can't figure out a way to pass the mixin value to the styled-component without assigning it to to a css property. For example, this is not possible:
const Example = styled.div`
${p => p.theme.mixins.toolbar};
`;
Edit: The issue ended up being the semi-colon next to the closing '}'. I believe adding a semi colon makes the styled-component think that you are adding a normal property, not a mixin.
You need to spread the mixins not calling it, like so:
const Example = styled.div`
${props => ({ ...props.theme.mixins.toolbar })}
`;
Yet that will return style object, you might wanna convert the resulting object into css-compatible syntax as follows:
const Example = styled.div`
${props => (Object.entries({ ...props.theme.mixins.toolbar }).reduce((styleString, [propName, propValue]) => {
if (propName.indexOf('#') !== -1) {
// iterate over media queries
return `${styleString}${propName} { ${Object.entries(propValue).reduce((ss, [pn, pv]) => {
pn = pn.replace(/([A-Z])/g, m => `-${m[0].toLowerCase()}`);
return `${ss}${pn}:${pv+(Number.isInteger(pv) ? 'px' : '')};`;
}, '')}; }`;
}
propName = propName.replace(/([A-Z])/g, matches => `-${matches[0].toLowerCase()}`); // convert camel-case properties into dash-splitted attributes
return `${styleString}${propName}:${propValue+(Number.isInteger(propValue) ? 'px' : '')};`; // append css pixel unit to integer values
}, ''))}
`;
I read your edit but deleting the semicolon didn't work for me. This worked:
import React from "react";
import { createMuiTheme } from "#material-ui/core/styles";
import styled, { ThemeProvider } from "styled-components";
const Content = styled.div`
toolbar: ${props => props.theme.mixins.toolbar};
`;
const theme = createMuiTheme();
const Wrapper = props => {
return (
<ThemeProvider theme={theme}>
<Content children={props.children} />
</ThemeProvider>
);
};
export default Wrapper;
I read this documentation, and very simple code:
const Offset = styled('div')(({ theme }) => theme.mixins.toolbar);
function App() {
return (
<React.Fragment>
<AppBar position="fixed">
<Toolbar>{/* content */}</Toolbar>
</AppBar>
<Offset />
</React.Fragment>
);
}

How to style a functional stateless component in Reactjs using classes object

I want to write and style a functional stateless component in ReactJs as described here.
const MyBlueButton = props => {
const styles = { background: 'blue', color: 'white' };
return <button {...props} style={styles} />;
};
The problem is that I want to add in some styles from stateful components as described here.
const styles = theme => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper,
},
});
The problem is that when I try to do something like this:
<div className={classes.root}>
I get the error:
'classes' is not defined no-undef
How do I access the withStyles classes object to style root the way I want?
If I understood right here is how you can do this with a functional component.
const styles = theme => ( {
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper,
},
} );
const App = ( props ) => {
const { classes } = props;
return <div className={classes.root}>Foo</div>;
};
export default withStyles( styles )( App );

How to add multiple classes in Material UI using the classes props?

Using the css-in-js method to add classes to a react component, how do I add multiple components?
Here is the classes variable:
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap'
},
spacious: {
padding: 10
},
});
Here is how I used it:
return (<div className={ this.props.classes.container }>)
The above works, but is there a way to add both classes, without using the classNames npm package? Something like:
<div className={ this.props.classes.container + this.props.classes.spacious}>
you can use string interpolation:
<div className={`${this.props.classes.container} ${this.props.classes.spacious}`}>
You could use clsx. I noticed it used in the MUI buttons examples
First install it:
npm install --save clsx
Then import it in your component file:
import clsx from 'clsx';
Then use the imported function in your component:
<div className={ clsx(classes.container, classes.spacious)}>
you can install this package
https://github.com/JedWatson/classnames
and then use it like this
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
To have multiple classes applied to a component, wrap the classes you would like to apply within classNames.
For example, in your situation, your code should look like this,
import classNames from 'classnames';
const styles = theme => ({
container: {
display: "flex",
flexWrap: "wrap"
},
spacious: {
padding: 10
}
});
<div className={classNames(classes.container, classes.spacious)} />
Make sure that you import classNames!!!
Have a look at material ui documentation where they use multiple classes in one component to create a customized button
You can also use the extend property (the jss-extend plugin is enabled by default):
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap'
},
spaciousContainer: {
extend: 'container',
padding: 10
},
});
// ...
<div className={ this.props.classes.spaciousContainer }>
I think this will solve your problem:
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap'
},
spacious: {
padding: 10
},
});
and in react component:
<div className={`${classes.container} ${classes.spacious}`}>
You can add multiple string classes and variable classes or props classes at same time in this way
className={`${classes.myClass} ${this.props.classes.myClass2} MyStringClass`}
three classes at same time
Yes, jss-composes provides you this:
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap'
},
spacious: {
composes: '$container',
padding: 10
},
});
And then you just use classes.spacious.
classNames package can also be used as advanced as:
import classNames from 'classnames';
var arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'
let buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true }); // => 'btn-primary'
You can use this method below:
import clsx from 'clsx';
return <div className={clsx(classes.container, 'spacious')} />
This link helps.
You can apply two classes in material UI like that
import classNames from "classnames";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
container: {
display: 'flex',
flexWrap: 'wrap',
},
spacious: {
padding: 10,
},
});
some code
<div className={classNames(classes.container, classes.spacious)}>
Try this one!
</div>
How to add two classes using the comparison operator in Material UI.
If you want to use the comparison operator to define one or two classes, you need to apply
import classNames from "classnames";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
maineButton: {
borderRadius: "10px",
minWidth: "80px",
border: "1x solid #dcdcdc",
},
selectedButton: {
border: "1x solid #3f51b5",
},
});
some code
const [selected, setSelected] = useState(0);
some code
{data?.map((el, index) => (
<ButtonBase
className={classNames(
classes.maineButton,
index === selected && classes.selectedButton
)}
onClick{()=> setSelected(index)}
>
{el.text}
</ButtonBase>
))}
if selected it will give you two classes
className={classNames(classes.maineButton, classes.selectedButton)}
if not, it will be only one
className={classNames(classes.maineButton)}
If you want to assign multiple class names to your element, you can use arrays.
So in your code above, if this.props.classes resolves to something like ['container', 'spacious'], i.e. if
this.props.classes = ['container', 'spacious'];
you can simply assign it to div as
<div className = { this.props.classes.join(' ') }></div>
and result will be
<div class='container spacious'></div>
It can be done painlessly with descructuring, after all, these are JavaScript objects:
const truncate = {
width: '100px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
};
email: {
color: '#747474',
...truncate,
},

Resources