How to implement responsive width for Drawer MUI v5 component? - css

I am using Material UI v5, and am trying to make a responsive drawer where for smaller devices it will take up 100% of screen width while for larger devices it should only take 1/3 of screen width. But I have no idea how to access Paper property to modify the actual width and make it responsive.
My code:
import { Drawer, styled } from "#mui/material";
const ResponsiveDrawer = styled(Drawer)(({ theme }) => ({
[theme.breakpoints.up("md")]: {
width: "33%", // THIS ONLY CHANGES DRAWER WIDTH NOT PAPER WIDTH INSIDE THE DRAWER
},
[theme.breakpoints.down("md")]: {
width: "100%",
},
}));
export { ResponsiveDrawer };
How I use it:
import { ResponsiveDrawer } from "./Style";
<ResponsiveDrawer
anchor="right"
open={drawer.state}
onClose={() => drawer.onClick(false)}
>
...
</ResponsiveDrawer>

I figured it out shortly after posting the question. This involves inline styling using useMediaQuery.
const largeScreen = useMediaQuery(theme.breakpoints.up("sm"))
<Drawer
anchor="right"
open={drawer.state}
onClose={() => drawer.onClick(false)}
PaperProps={largeScreen ? {
sx: {
width: 450,
}
} : {
sx: {
width: "100%",
}
}
}
>
<CartContent cart={cart} drawer={drawer}/>
</Drawer>

You can add a div inside the <Drawer> or <SwipeableDrawer> component like so and control the width of the div through CSS (or emotion/styled, if you prefer).
<Drawer ...>
<div className="container">...</div>
</Drawer>
.container {
width: 95vw; // for mobile
... add media queries for rest of the screen sizes here
}

In their docs (expand the code blow) they give an example how to access the paper CSS, where you could add your width settings:
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
({ theme, open }) => ({
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
...(open && {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
}),
...(!open && {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
}),
}),
);
They add the same mixin to '& .MuiDrawer-paper' in the main drawer css.
So for your responsive drawer you should add this paper selector to your styled CSS (maybe check with the inspector, if its the right one):
const ResponsiveDrawer = styled(Drawer)(({ theme }) => ({
[theme.breakpoints.up("md")]: {
width: "33%",
'& .MuiDrawer-paper': {
width: "33%",
},
},
[theme.breakpoints.down("md")]: {
width: "100%",
'& .MuiDrawer-paper': {
width: "100%",
},
},
}));
More information about customizing nested elements can be found on https://mui.com/material-ui/customization/how-to-customize/#the-sx-prop.

Related

Griffel - CSS in JS - Cannot overwrite child class rule on parent element hover

I'm using Griffel https://griffel.js.org/ for the first time and I am struggling to achieve this simple thing. By default this button is hidden and when the user hover's over the parent I want to make the child visible.
I am using React so I have this button component inside the parent div.
const styles = useStyles();
<div className={styles.userContainer}>
<Button className={styles.removeUserButton} />
</div>
CSS:
export const useStyles = makeStyles({
removeUserButton: {
display: 'none'
},
userContainer: {
backgroundColor: '#fefefe',
'&:hover': {
'& removeUserButton': {
display: 'inline-block'
}
}
}
})
I was not sure if it will know what the removeUserButton class is so I also tried by making it variable. So I added:
const removeUserClassName = 'removeUserButton';
on top of the file and then in CSS:
[removeUserClassName]: {
display: 'none'
},
userContainer: {
backgroundColor: '#fefefe',
'&:hover': {
[`& ${removeUserClassName}`]: {
display: 'inline-block'
}
}
}
But this still does not work.
And in case you are wondering I have also tried the first piece of code by adding . before removeUserButton.
Has anyone used Griffel and knows how to handle this?
Appreciate any guidance. Thank you!
Here is a sandbox I created:
https://codesandbox.io/s/griffel-hover-issue-gwx1sj
The issue is that you are attempting to use a classname that is in your configuration (removeUserClassName). Yet, Griffel uses dynamic class names. So you'll need to use a different selector.
You can see the nature of the dynamic class names in this playground example.
Here is a working StackBlitz that uses the button as selector rather than class name and it works fine.
Here is the code for reference:
import { makeStyles } from '#griffel/react';
export const useStyles = makeStyles({
removeUserButton: {
display: 'none',
},
userContainer: {
width: '150px',
height: '50px',
backgroundColor: 'black',
':hover': {
'& button': { display: 'inline-block' },
backgroundColor: 'blue',
},
},
});

material UI popover - position on small screen

I have been trying for some time to figure out a way to position material ui popover under my anchor, and leave it like that always even on smaller screens.
Here is a sandbox example: https://codesandbox.io/s/material-demo-yvcqu?file=/demo.js
This is the best I got, but the scroll is not really on the body at this point its on the popover container div, and that does not help me.
Just to explain I know I can use AnchorElement with position but on smaller screens, the popover will just hide the Anchor, I would like the popover to always be under it, and just make the body scroll, so I can see the full popover content when i scroll down.
import React from "react";
import {makeStyles,MuiThemeProvider,createMuiTheme} from "#material-ui/core/styles";
import Popover from "#material-ui/core/Popover";
import Button from "#material-ui/core/Button";
export default function SimplePopover() {
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const theme2 = createMuiTheme({
overrides: {
MuiButton: {
root: {
top: 400
}
},
MuiPopover: {
root: {
},
paper: {
height: 500
}
}
}
});
return (
<div>
<MuiThemeProvider theme={theme2}>
<Button
variant="contained"
color="primary"
onClick={handleClick}
>
Open Popover with anchor
</Button>
<Popover
id="popover-with-anchor"
open={Boolean(anchorEl)}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
>
Popover content.
</Popover>
</MuiThemeProvider>
</div>
);
}
Images for example. When popover is bigger than the screen it fits itself in the screen and go overs the anchor
instead of being under the anchor
It's interesting the Popover component doesn't have a property to handle this situation. I ran into a similar issue on a smaller device when I had a long list of data in the popover. To fix this, I just set top myself on the PaperProps property of the Popover component. See below:
<Popover PaperProps={{ style: { top: myAnchor.current ? myAnchor.current.getBoundingClientRect().bottom : 0 } }}></Popover>
Don't do overflow: scroll on root. Instead, do overflowY: auto on paper.
See codesandbox and play around.
Try this:
overrides: {
MuiPopover: {
root: {
// overflow: "scroll"
},
paper: {
left: 50,
top: "500px !important",
height: 50,
overflowY: "auto"
}
}
}

How to make Material-UI Snackbar not take up the whole screen width using anchorOrigin?

I have a class in React which uses an input field which is part of the website header:
If the input is invalid then I want to display a snackbar. I'm using Material-UI components.
The problem is I defined anchorOrigin to be center and top as per Material-UI API. However the snackbar takes up the whole screen width while I want it to only take up the top center location of the screen. My message is quite short, for example "Value invalid" but if it's longer then I should be able to use newlines. I'm not sure if there's some setting in Material-UI API to alter this (I couldn't find one) or I need to use CSS.
This is my code:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import InputBase from '#material-ui/core/InputBase';
import Snackbar from '#material-ui/core/Snackbar';
import SnackbarMessage from './SnackbarMessage.js';
const classes = theme => ({
inputRoot: {
color: 'inherit',
width: '100%',
},
inputInput: {
paddingTop: theme.spacing.unit,
paddingRight: theme.spacing.unit,
paddingBottom: theme.spacing.unit,
paddingLeft: theme.spacing.unit * 10,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('sm')]: {
width: 120,
'&:focus': {
width: 200,
},
},
}
});
class Test extends Component {
state = {
appId: '',
snackBarOpen: false
}
render() {
return (
<div>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
value={'test'} />
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center'
}}
open={true}
autoHideDuration={5000}
>
<SnackbarMessage
variant="warning"
message={"test message"}
/>
</Snackbar>
</div>
)
}
}
Material-UI set Snackbars to full viewport-width below the breakpoint "md" (600px).
You can use overrides (https://material-ui.com/customization/overrides/) and set new values to the default CSS classes of the component described in the components API (i.e. https://material-ui.com/api/snackbar/). So you can override the class anchorOriginTopCenter as follows:
const styles = theme => ({
anchorOriginTopCenter: {
[theme.breakpoints.down('md')]: {
top: "your value/function here",
justifyContent: 'center',
},
},
root: {
[theme.breakpoints.down('md')]: {
borderRadius: 4,
minWidth: "your value / function here",
},
},
});
The first objects overrides the default class {anchorOriginTopCenter}, the second 'root' is applied to first element in your snackbar (probably a 'div').
I do not know if we can add some style to the component anchor origin field. I think the div needs to be managed using CSS. It's an anchor, not style.
<Snakbar
className = "my-snakbar"
{/*All your other stuff*/}
>
{//Stuff}
</Snakbar>
CSS
.my-snakbar {
width: 200px;
//Maybe use flexbox for positioning then
}
Let me know your thoughts
Daniel
Improved Answer
Code copied from origional question and modified
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Snackbar from '#material-ui/core/Snackbar';
const classes = theme => ({
inputRoot: {
color: 'inherit',
width: '100%',
},
inputInput: {
paddingTop: theme.spacing.unit,
paddingRight: theme.spacing.unit,
paddingBottom: theme.spacing.unit,
paddingLeft: theme.spacing.unit * 10,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('sm')]: {
width: 120,
'&:focus': {
width: 200,
},
},
}
});
class ComingSoon extends Component {
render() {
const styles = {
container: {
position: "fixed",
top: "0px",
width: "100%",
height: "30px"
},
snakbar: {
background: "black",
color: "white",
width: "100px",
height: "100%",
display: "flex",
justifyContent: "center",
alignContent: "center",
margin: "0 auto"
}
};
return (
<div className = "snakbar-container" style = {styles.container}>
<Snackbar
className = "my-snakbar"
style = {styles.snakbar}
anchorOrigin={{
vertical: 'top',
horizontal: 'center'
}}
open={true}
autoHideDuration={5000}
>
<span>My Message</span>
</Snackbar>
</div>
)
}
}
export default ComingSoon;
Screen shot:
Let me know if this helped
Daniel

How to removing the padding for card in and design?

I am using ant design to react UI components. I need to remove the padding given for the ant design card.
So I need to remove the padding given for the classes .ant-card-wider-padding and .ant-card-body.I am using JSS for styling the UI components.
cardStyle: {
marginTop: '30px',
boxShadow: '0px 1px 10px rgba(0,1,1,0.15)',
backgroundColor: '#ffffff',
borderStyle: 'solid',
outline: 'none',
width: '100%',
},
i am using cardStyle class to styling ant design card.Now i need to remove the padding in that card.
From the documentation of Ant Design
You need to override the style in bodyStyle not cardStyle
bodyStyle: Inline style to apply to the card content
<Card title="Card title" bodyStyle={{padding: "0"}}>Card content</Card>
use fullWidth props for removing padding..,
<Card.Section fullWidth>
<ResourceList
items={[
{
id: 341,
url: 'customers/341',
name: 'Mae Jemison',
location: 'Decatur, USA',
}
]}
renderItem={
(item) => {
const {id, url, name, location} = item;
const defaultImage = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNLS4wMi0uMDFoMTAwdjEwMGgtMTAweiIgZmlsbD0iI2ZmZTBjMyIvPjxwYXRoIGZpbGw9IiNmZjk2N2QiIGQ9Ik0wIDBoNjkuNDF2MTAwSDB6Ii8+PHBhdGggZD0iTTY5LjkyIDB2NDQuMzJMNTEuMzQgNTV2NDVIMTAwVjB6IiBmaWxsPSIjZmZlMGMzIi8+PHBhdGggZmlsbD0iIzMyY2FjNiIgZD0iTTM5LjMyIDc2YTExLjg1IDExLjg1IDAgMCAwIDEyIDExLjYyVjc2Ii8+PHBhdGggZmlsbD0iIzAwOTc5NiIgZD0iTTM5LjMyIDc2YTEyIDEyIDAgMCAxIDEyLTExLjgyVjc2Ii8+PHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSI1IiBkPSJNNDMuNzQgMTkuODNhMTIuODIgMTIuODIgMCAxIDEtMjUuNjQgMCIvPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iNCIgZD0iTTI3LjM5IDMxLjZsLTEuNTggNS45Nm05LjM3LTUuNzJsMi41NSA1LjQ3bTQuMjYtOS44NWwzLjUzIDQuNW0tMjUuNDMtNC41bC0zLjUzIDQuNSIvPjwvc3ZnPgo=" ;
const media = <Thumbnail source={defaultImage} size="small" name={name} />;
return (
<ResourceList.Item id={id} url={url} media={media}>
<Stack alignment="center">
<Stack.Item fill>
<TextStyle>{name}</TextStyle>
</Stack.Item>
<Stack.Item>
<TextStyle>Last changed</TextStyle>
</Stack.Item>
<Stack.Item>
<Button>Edit Giffy</Button>
</Stack.Item>
</Stack>
</ResourceList.Item>
);
}
}
/>
</Card.Section>
very simple just add bodyStyle in Card Component
<Card bodyStyle={{ padding: "0"}}>
You can use this:
.cardStyle {
padding: 0;
}
If didn't work, use this:
.cardStyle {
padding: 0 !important;
}
I'm not too familiar with JSS but if your other styles are being applied then I assume the issue is not with that.
I was able to remove the padding from the card with the following code.
//style.less
.panelcard { ... }
.panelcard .ant-card-body {
padding: 0;
}
// panelCard.js
import { Card } from 'antd';
require('./style.less');
const PanelCard = ({ children }) => {
return (
<Card className='panelcard'>
{children} // <p>Some Child Component(s)</p>
</Card>
);
}
// invocation
<PanelCard label='Panel Title'>
<p>Some Child Component(s)</p>
</PanelCard>
This gave me the following output (card is the white box):
I am not sure if this is the preferred way of customizing antd's components but I didn't really find too much on antd's website about overriding styles, only on extending components.
Try using :global in you scss/less
div { // or any parent element/class
:global {
.ant-card-body {
passing: <number>px; // number can be 0 onwards
}
}
}

material-ui-next: Setting image size to fit a container

I've started out with Material-ui-next and have some problems with displaying images that they use the entire size of a container.
E.g. I use:
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: 550
}
});
In render:
<Card className={classes.Card}>
<CardMedia
className={classes.Media}
image={ImgPomodoR}
title="a pomodoro tomatoe timer in material design"
/>
<CardContent>
<Typography gutterBottom variant="headline" component="h2">
...
The documentation says I have to specify a height for the image to get displayed. The 'media' example gives the image a height of 0, however, if I apply that my image is not getting displayed - mentioned example.
Right now, for me it's a trial and error of the Media-height, that it fits the Card container without being cropped.
Is there no 'auto' way of doing this?
Any help is highly appreciated,
cheers mates,
Tobias
Edit: I should mention that height: "100%" // maxHeight: "100%" does also not work for me.
I was having the same issue. Setting both width and height to '100%' worked for me.
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: '100%',
width: '100%'
}
});
However, if your images have different heights, this will cause cards to be different heights and most of the time that is not what you want. For that, you can specify a height and keep the width at '100%'.
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: 550,
width: '100%'
}
});
This will stretch the images to fit the container. For my case, I wanted part of the image to be shown without stretching the images. To achieve this, simply set the objectFit property to cover. This worked nicely for me.
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: 550,
width: '100%',
objectFit: 'cover'
}
});
Hope this helps someone,
I think it would work
const styles = theme => ({
card:{
backgroundColor: 'white',
marginBottom: 40,
},
media: {
height: 0,
// paddingTop: '56.25%', // 16:9,
paddingTop: '100%', // 1:1,
},
});

Resources