Is there a way to "inject" pure CSS into MUI v5 - material-ui

I have a custom spinner that is currently using keyframes like so:
import { keyframes } from "#mui/system";
...
const keyframeSpinner = keyframes`
0%{transform:rotate(0deg);}
100%{transform:rotate(360deg);}
`;
...
<Box
sx={{
animation: `${keyframeSpinner} 1s linear infinite`,
}}
/>
...
I don't want to import #mui/system and I don't want to use styled components.
So, I'm trying to find a solution where I can uses pure css or another solution that I'm unaware of.

You can easily apply in-line CSS styles to components using emotion, which is also used by MUI.
For example, here is the css prop from emotion being used to customize background-color and hover on a div. The code you write in the css prop can be pure CSS.
import { css, jsx } from '#emotion/react'
const color = 'darkgreen'
const customCss = css`
background-color: hotpink
&:hover { color: ${color} }
`
render(
<div css = {customCss}>
This div has a hotpink background.
</div>
)

Related

Can't figure out how to style material ui datagrid

I'm trying to style material-ui DataGrid component to justify the content in the cells. I am reading the material ui docs about styling but I don't seem to doing it correct and frankly find the docs on styling very confusing.
The doc here: https://material-ui.com/customization/components/#overriding-styles-with-classes implies I should be able to do something like this:
const StyledDataGrid = withStyles({
cellCenter: {
justifyContent: "center",
},
})(DataGrid);
<div style={{ height: 300, width: '100%' }}>
<StyledDataGrid rows={rows} columns={columns} />
</div>
However, when I do this, I don't see the style being added to the MuiDataGrid-cellCenter DOM element. Attaching a screenshot which shows the element classes. In the inspector I see that the style isn't being added (and if I add it manually I get the desired results). Am I not using the withStyles function correctly?
So after a bit more messing around, I believe the issue is that the DataGrid component does not support the classes property (which it seems most of the material ui components do). I believe the withStyles usage about is shorthand for passing the classes via the classes prop. Since the prop isn't listed in the API https://material-ui.com/api/data-grid/ I'm assuming this is why it isn't working. I confirmed that I can get the styles working by using a combination of the className parameter with descendant selection.
If someone determines I'm wrong and there is a way to get withStyles working on this component please comment.
const useStyles = makeStyles({
root: {
"& .MuiDataGrid-cellCenter": {
justifyContent: "center"
}
}
});
...
export default function X() {
const classes = useStyles();
return (
...
<DataGrid className={classes.root} checkboxSelection={true} rows={rows} columns={columns} />
...
)
}
ALTERNATIVE SOLUTION: (for others with similar issues)
If you are working within a class and cannot use hooks...
<div>
<DataGrid
rows={rows}
columns={columns}
sx={{
'&.MuiDataGrid-root .MuiDataGrid-cell:focus': {
outline: 'none',
},
}}
/>
</div>

Modal for fullsize image with gatsby-image - limit height and width

What I want to achive
I am using gatsby and want to design an image gallery. Clicking on one of the images shall open a modal, which: (1) is showing the image in maximum possible size, so that it still fits into the screen and (2) is centered in the screen.
My Code
/* imagemodal.js */
import React from 'react'
import * as ImagemodalStyles from './imagemodal.module.css'
import { Modal } from 'react-bootstrap'
import Img from 'gatsby-image'
import { useStaticQuery, graphql } from 'gatsby'
export default function Imagemodal() {
const data = useStaticQuery(graphql`
query {
file(relativePath: { eq: "images/mytestimage.jpg" }) {
childImageSharp {
fluid(maxWidth: 1200) {
...GatsbyImageSharpFluid
}
}
}
}
`)
return (
<div>
<Modal
show={true}
centered
className={ImagemodalStyles.imageModal}
dialogClassName={ImagemodalStyles.imageModalDialog}
onHide={(e) => console.log(e)}
>
<Modal.Header closeButton />
<Modal.Body className={ImagemodalStyles.imageModalBody}>
<h1>TestInhalt</h1>
<Img fluid={data.file.childImageSharp.fluid} />
</Modal.Body>
</Modal>
</div>
)
}
/* imagemodal.module.scss */
.imageModalDialog {
display: inline-block;
width: auto;
}
.imageModal {
text-align: center;
}
.imageModalBody img {
max-height: calc(100vh - 225px);
}
The Problem
The image does not scale to the screen size. The image is either too big - so it flows over the vieport - or it is too small. Secondly, the modal size does not respond to the image size correctly and / or is not centered.
What I tried
I used this suggestion for the CSS: How to limit the height of the modal?
I tried as well dozens of other CSS parameter combinations. But I could not find a working solution.
I tried to format the gatsby-image directly with a style-tag.
I tried as well react-modal but had similar problems.
Does anyone have a good solution to show a gatsby-image in full screen size in a responsive modal? For me it is okay to use either the bootstrap-modal or react-modal - or any other suitable solution.
Edit
In the end I ended up with a workaround. I used react-image-lightbox and took the Image-Source from gatsby-image as the input for lightbox. My component gets the data from the graphQL query in the props via props.imageData.
This works quite well for me:
import Lightbox from 'react-image-lightbox';
...
export default function Imagegallery(props) {
...
const allImages = props.imageData.edges
const [indexImageToShow, setIndexImageToShow] = useState()
...
return(
<Lightbox
mainSrc={allImages[indexImageToShow].node.childrenImageSharp[0].fluid.src}
...
/>
Special thanks to #FerranBuireu to point me to the right direction
Assuming that the functionality works as expected, as it seems, it's a matter of CSS rules, not React/Gatsby issue. The following rule:
.imageModalBody img {
max-height: calc(100vh - 225px);
}
It Will never be applied properly, since gatsby-image creates an output of HTML structure of nested <div>, <picture> and <img> so your rule will be affected by the inherited and relativity of the HTML structure. In other words, you are not pointing to the image itself with that rule because of the result HTML structure.
You should point to the <Img>, which indeed, it's a wrapper, not an <img>.
return (
<div>
<Modal show={true} onHide={handleClose} centered className={ImagemodalStyles.imageModal} dialogClassName={ImagemodalStyles.imageModalDialog}>
<Modal.Header closeButton />
<Modal.Body>
<Img className={ImagemodalStyles.imageModalBody} fluid={props.data.file.childImageSharp.fluid} />
</Modal.Body>
</Modal>
</div>
)
The snippet above will add the (spot the difference, without img):
.imageModalBody {
max-height: calc(100vh - 225px);
}
To the wrapper, which may or may not fix the issue, but at least will apply the rule correctly. It's difficult to know what's wrong without a CodeSandbox but you will apply the styles correctly with this workaround.
Keep always in mind that when using gatsby-image, the <img> it's profound in the resultant HTML structure so your styles should apply to the outer wrapper of it.

How to extend material-ui component style to another react component from differently library?

For example:
import { CssBaseline, ThemeProvider, Typography } from "#material-ui/core"; // MUI **Typography** component
import { Text, RichText } from "#some-library/core"; // some-library's **Text** component
const theme = createMuiTheme({
overrides: {
typography: {
h1: {
fontFamily: Garamond,
fontSize: pxToRem(56),
lineHeight: pxToRem(64),
letterSpacing: pxToRem(-0.5),
[breakpoints.up("sm")]: {
fontSize: pxToRem(72),
lineHeight: pxToRem(76)
},
[breakpoints.up("md")]: {
fontSize: pxToRem(104),
lineHeight: pxToRem(112)
},
[breakpoints.up("lg")]: {
fontSize: pxToRem(120),
lineHeight: pxToRem(128),
letterSpacing: pxToRem(-0.75)
}
}
}
}
});
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Typography variant="h1">MUI Typography Heading</Typography>
<Text tag="h1">Library B text component</Text>
</ThemeProvider>
);
}
All I want is to apply the same styles of the <Typography> for the <Text> component.
I know can I can add className to the other component like <Text className={"MuiTypography-root MuiTypography-h1"} tag="h1">. Is that the right approach? And if I do like this would the props of components also be used in the component? Like <Text variant="h2"> and would it take all the responsiveness of Typography too?
Can the styles of MUI component be extended to another react component with all the other functionality (props etc) using any other way?
I am trying to create a shared (MUI components + some-library components) component library and using MUI as the base library for components and styles. And I have created custom theme for the MUI components but not able to extend these styles to the components of another library which too has few basic components (text field, buttons, form elements etc).
Please let know if the question makes sense as I am very new to react and MUI. Thanks much.
Ciao, for what I know, the answer should be no: you cannot extend material-ui component style to another component from different library. Because a component from another library would not accepts the same Material UI props (in your example, variant="h1" would be not recognized as valid props in other component except Material UI components).
Of course, as you said, you could assign the same Material UI class to another component and that component will follow the same style:
<Text className={"MuiTypography-root MuiTypography-h1"} tag="h1"> // this work
Or you could extend a Material UI component. Suppose that you want to make your own Text, you could extend Typography component using withStyles like:
import { withStyles } from "#material-ui/core/styles";
const MyText = withStyles((theme) => ({
root: {
color: "green" // in this case, MyText will have a green text color
}
}))((props) => <Typography {...props} />);
But, as you can see, I extended a Typography component, not a component coming from another library. Here a codesandbox example.
As I said, this is what I know. Maybe there is a way to achieve what you asked (even if I have some doubts because how to map Material UI props to be accepted by another component that has different props?)

How to change expansion panel icon position to the left?

In my app, the expansion arrow has to be in the left side of the panel.
But, by default it's displaying in the right side.
This :
<ExpansionPanelSummary
className={classes.panelSummary}
expandIcon={<ExpandMoreIcon />}
IconButtonProps={{edge: 'start'}}
aria-controls='panel1a-content'
id='panel1a-header'
>
Doesn't made it.
Granted, you can't (easily) change the order in which the components appear in the HTML. However, there is a way using only CSS. ExpansionPanelSummary uses display: flex; you can therefore set the order property on the icon to make it appear to the left of the content.
This can be achieved with either useStyles or withStyles (Or possibly using plain CSS, but I haven't tried it); here's how you'd go about using the latter:
import withStyles from "#material-ui/core/styles/withStyles";
const IconLeftExpansionPanelSummary = withStyles({
expandIcon: {
order: -1
}
})(ExpansionPanelSummary);
You can then write the rest of your code using IconLeftExpansionPanelSummary instead of ExpansionPanelSummary when you want the icon to appear to the left. Don't forget to set IconButtonProps={{edge: 'start'}} on the component for proper spacing.
<AccordionSummary
className={classes.accordionSummary}
classes={{
expandIcon: classes.expandIcon,
expanded: classes.expanded
}}
IconButtonProps={{
disableRipple: true
}}
></AccordionSummary>
You can add class and use flex-direction
accordionSummary: {
flexDirection: 'row-reverse'
}
It's simple
add class on <ExpansionPanelSummary> like this
<ExpansionPanelSummary className={classes.panelSummary}>
add css against this class in jss like this
panelSummary:{flexDirection: "row-reverse"},
In case using css
add class on <ExpansionPanelSummary> like this
<ExpansionPanelSummary className="panelSummary">
add css against this class in jss like this
.panelSummary{flex-direction: row-reverse;}
you can get the expansion panel icon on left by removing it from expandIcon and add it as a children in Summary something like this
<ExpansionPanel defaultExpanded={true}>
<ExpansionPanelSummary aria-controls="panel1a-content">
{this.state.expanded ? <RemoveIcon/> : <ExpandIcon />}
<Typography component='h4' variant='h4'>My Expansion Panel</Typography>
</ExpansionPanelSummary>
<ExpansionPanelsDetails />
</ExpansionPanel>
The challenge is that the order is hardcoded into the codebase and you will not be able to use the ExpansionPanel as is.
If you look at the implementation, you will find the code as below
<div className={clsx(classes.content, { [classes.expanded]: expanded })}>{children}</div>
{expandIcon && (
<IconButton
disabled={disabled}
className={clsx(classes.expandIcon, {
[classes.expanded]: expanded,
})}
edge="end"
component="div"
tabIndex={-1}
aria-hidden
{...IconButtonProps}
>
{expandIcon}
</IconButton>
)}
As you see the <div> contains the text and then the IconButton is displayed.
So, you may have to work with what's provided out of the box or create your own Component based on what material-UI provides.
Hope that helps.
You can modify the CSS class like this:
notice the absolute position, in this way you can move the div that contains the icon whatever position you want with 'left' or 'right' properties
const useStyles = makeStyles((theme) => ({
ExpansionPanelSummaryExpandedIcon: {
'& div.MuiExpansionPanelSummary-expandIcon': {
position: 'absolute',
right: '5%',
},
}
}));
and then use in the ExpansionPanelSummary
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1-content"
id="panel1bh-header"
className={classes.ExpansionPanelSummaryExpandedIcon}
>
references:
https://cssinjs.org/?v=v10.3.0
https://v4-8-3.material-ui.com/customization/components/#overriding-styles-with-classes

How do I globally override variant, color, style, etc. for Material-UI components?

Instead of doing this everywhere:
<Button variant="contained" color="primary"
style={{textTransform: "none"}}
>
Description
</Button>
I just want to write:
<Button>
Description
</Button>
Can I use theme overrides to do this and what would that look like?
Note that I'm trying to override both Material-UI properties and CSS styles. I want to do this globally (i.e. not using withStyles() stuff everywhere).
Or can this only be done by defining some kind of new AppButton component?
Currently using material-ui 3.2.2
You can do this with global overrides for your theme.
Documentation is here https://material-ui.com/customization/themes/#customizing-all-instances-of-a-component-type
Doing it this way will still allow you override the variant on a per component basis as well.
const theme = createMuiTheme({
props: {
// Name of the component ⚛️
MuiButton: {
// The properties to apply
variant: 'contained'
},
},
});
Here's an alternate way to do this, without defining a new component.
Custom components can be awkward when used with Material-UI's JSS styling solution with Typescript. I've found it difficult to define WithStyle types when combining style types from the shared component and the thing using it.
Instead of defining components, it's possible to define sets of default properties that you then apply with the spread operator.
Define and export a standard set of shared props somewhere out in your app:
import {LinearProgressProps} from "#material-ui/core/LinearProgress";
export const linearProps: LinearProgressProps = {
variant:"indeterminate",
color:"primary",
style:{height:"2px"},
};
Then use those props in your app:
<LinearProgress {...linearProps} />
This is then easy to override with custom properties, custom inline styles or JSS generated styles:
<LinearProgress {...linearProps} className={classes.customProgress}
color="secondary" style={{...linearProps.style, width: "100%"}} />
For anyone finding this question, assuming there is no Material-UI way to do this, here's my custom button component.
import * as React from "react";
import {Button} from "#material-ui/core";
import {ButtonProps} from "#material-ui/core/Button";
export class AppButton extends React.Component<ButtonProps, {}>{
render(){
let {style, ...props} = this.props;
return <Button {...props} variant="contained" color="primary"
style={{...style, textTransform: "none"}}
>
{this.props.children}
</Button>
}
}
AppButton.muiName = 'Button';