React / Material-UI Novice here. I am trying to configure a set of buttons inside my app Bar that have a background picture to make it look a little cleaner. I have used a lot of code from examples online (shock) and got it to a place where i am happy with how it has formatted on a full size view (md++). However, when i downsize it to a small breakpoint though, the button image then stack instead (which is what i want) but i lose my text to the left. I have tried shifting to the right in many different ways but i dont think thats the right way to do it, is there something i am missing in making the text flex, i want the text to be in the middle?
import React from 'react'
import { AppBar, Toolbar } from "#mui/material";
import { makeStyles } from '#mui/styles'
import Button from '#mui/material/Button'
import Stack from '#mui/material/Stack'
import ButtonBase from '#mui/material/ButtonBase';
import { styled } from '#mui/material/styles';
import Typography from '#mui/material/Typography';
import Box from '#mui/material/Box';
const useStyles = makeStyles(theme => ({
button: {
...theme.typography.mainmenu,
borderRadius: "40px",
marginLeft: "1px",
height: "45px",
"&:hover": {
backgroundColor: theme.palette.secondary
}
},
}))
const images = [
{
url: '/assets/breakfastMenu.jpg',
title: 'Breakfast',
width: '33.33%',
},
{
url: '/assets/steak.jpg',
title: 'Mains',
width: '33.33%',
},
{
url: '/assets/desserts.jpg',
title: 'Desserts',
width: '33.33%',
},
];
const Image = styled('span')(({ theme }) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
color: theme.palette.common.primary,
}));
const ImageButton = styled(ButtonBase)(({ theme }) => ({
position: 'relative',
height: 150,
[theme.breakpoints.down('sm')]: {
width: '100% !important', // Overrides inline-style
height: 100,
},
'&:hover, &.Mui-focusVisible': {
zIndex: 1,
'& .MuiImageBackdrop-root': {
opacity: 0.15,
},
'& .MuiImageMarked-root': {
opacity: 0,
},
'& .MuiTypography-root': {
border: '4px solid currentColor',
},
},
}));
const ImageSrc = styled('span')({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundPosition: 'center 40%',
});
const ImageBackdrop = styled('span')(({ theme }) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundColor: theme.palette.common.black,
opacity: 0.4,
transition: theme.transitions.create('opacity'),
}));
const ImageMarked = styled('span')(({ theme }) => ({
height: 3,
width: 18,
backgroundColor: theme.palette.common.white,
position: 'absolute',
bottom: -2,
left: 'calc(50% - 9px)',
transition: theme.transitions.create('opacity'),
}));
const Header = () => {
const classes = useStyles();
return (<React.Fragment><AppBar position="sticky" className={classes.appBar}>
<Toolbar disableGutters className={classes.mainToolbar} sx={{ justifyContent: "center" }}>
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={10}>
{/* <Button variant="contained" color="secondary" className={classes.button}>Breakfast</Button>
<Button variant="contained" color="secondary" className={classes.button}>Mains</Button>
<Button variant="contained" color="secondary" className={classes.button}>Desserts</Button> */}
<Box sx={{ display: 'flex', flexWrap: 'wrap', minWidth: 900, width: '100%' }}>
{images.map((image) => (
<ImageButton
focusRipple
key={image.title}
style={{
width: image.width,
}}
>
<ImageSrc style={{
backgroundImage: `url(${image.url})`
}} />
<ImageBackdrop className="MuiImageBackdrop-root" />
<Image>
<Typography
component="span"
variant="subtitle1"
color="white"
fontWeight="bold"
sx={{
position: 'relative',
p: "7em",
pt: "2em",
pb: (theme) => `calc(${theme.spacing(1)} + 6px)`,
}}
>
{image.title}
<ImageMarked className="MuiImageMarked-root" />
</Typography>
</Image>
</ImageButton>
))}
</Box>
</Stack>
</Toolbar>
</AppBar>
</React.Fragment >
)
}
export default Header
I have moved on from this, but in case anybody finds it, using MUI i completely adapted my application by using:
import useMediaQuery from '#mui/material/useMediaQuery'
and some example like:
const matches = useMediaQuery(theme.breakpoints.down("md"))
to control when the application changes its styles
Related
Hi I have this component in my app, Right now the height of this ButonBase is hard coded.
I get an image from backend and I set that image as a backgroundImage for the ImageImagine component.
I need to make height of that ButonBase to bee dinamic so the background image in imageImagine can mentain original aspect ratio.
import React, { useState } from 'react';
import {
Box,
ButtonBase as MuiButtonBase,
IconButton,
Typography,
styled,
} from '#mui/material';
import Label from './Label';
import Favorite from './Favorite';
import MintingContextButton from './MintingContextButton';
import StatusLabel from 'src/components/StatusLabel';
import PublicLabel from 'src/components/PublicLabel';
import { IMAGINE_STATUS } from 'src/constants';
import { authDomain } from 'src/config';
const ButtonBase = styled(MuiButtonBase)(({ theme }) => ({
position: 'relative',
height: 342,
width: '100%',
borderRadius: 32,
[theme.breakpoints.down('xs')]: {
width: '100% !important', // Overrides inline-style
height: 326,
},
'&:hover': {
'& .imageTitle': {
opacity: 1,
transition: theme.transitions.create('opacity'),
zIndex: 1,
},
'& .imageBackdrop': {
opacity: 0.7,
},
'& .MuiIconButton-favorite': {
opacity: 1,
transition: theme.transitions.create('opacity'),
zIndex: 12,
},
'& .MuiButton-minting': {
opacity: 1,
transition: theme.transitions.create('opacity'),
zIndex: 12,
},
},
}));
const ContextContainer = styled('span')(() => ({
position: 'absolute',
width: '100%', // should on if real mint button
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
padding: 16,
zIndex: 1, // Remove if real mint button
}));
const StyledStatusLabel = styled(StatusLabel)(() => ({
position: 'absolute',
left: 16,
top: 16,
zIndex: 1,
}));
const MintIconButton = styled(IconButton)(() => ({
position: 'absolute',
left: 16,
top: 16,
zIndex: 1,
background: 'rgba(0, 0, 0, 0.7)',
'&:hover': {
background: 'rgba(128, 58, 254, 0.9)',
},
}));
const ImageTitle = styled('span')(({ theme }) => ({
opacity: 0,
textAlign: 'left',
// [theme.breakpoints.down('sm')]: {
// opacity: 1,
// zIndex: 1
// },
}));
const ImageBackdrop = styled('span')(({ theme }) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundColor: theme.palette.common.black,
opacity: 0,
transition: theme.transitions.create('opacity'),
borderRadius: 32,
// [theme.breakpoints.down('sm')]: {
// opacity: 0.7,
// },
}));
const ImageImagine = styled('span')(() => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundPosition: 'center 40%',
borderRadius: 32,
}));
const GalleryItem = ({
imagine,
imagine: {
id,
title,
status,
isMyFavorite,
public: publicStatus,
context: imgContext,
thumb_url: imgUrl,
count_favorites: countFavorites,
},
onOpen,
onClose,
...props
}) => {
const [isHoverFavorite, setHoverFavorite] = useState(false);
return (
<>
<ButtonBase
className="MuiButtonBase-gallery"
disableRipple
focusRipple
onClick={(event) => onOpen(event, id)}
>
<ContextContainer>
<Box display="flex" justifyContent="space-between">
<Box
display="flex"
visibility={status === IMAGINE_STATUS.COMPLETE && 'hidden'}
>
<StyledStatusLabel status={status} />
</Box>
{status === IMAGINE_STATUS.COMPLETE && (
<React.Fragment>
<Favorite
imagineId={id}
isMyFavorite={isMyFavorite}
isHoverFavorite={isHoverFavorite}
isHidden
onHoverFavorite={setHoverFavorite}
count={countFavorites}
/>
{/* <MintingContextButton imagine={imagine} /> */}
</React.Fragment>
)}
</Box>
<ImageTitle className="imageTitle">
<Typography color="textPrimary" variant="subtitle1" noWrap>
{title}
</Typography>
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
>
{imgContext && <Label>{imgContext}</Label>}
<PublicLabel publicStatus={publicStatus} />
</Box>
</ImageTitle>
</ContextContainer>
<ImageImagine
style={{
backgroundImage: `url(${
imgUrl ?? `${authDomain}/images/glitchcat.gif`
})`,
}}
/>
<ImageBackdrop className="imageBackdrop" />
</ButtonBase>
</>
);
};
export default GalleryItem;
Had you tried to set the height of the ButtonBase into height: auto and the min-height into min-height: fit-content?
I'm using MUI v5 to build linear progress bar.I have a scenario where if value in progress bar is 100%,I need to show tick icon at the end.The width of the bar with/without tick icon should be same,meaning icon shouldn't be placed after the bar.It should be at the end of bar.I tried with stylings and able to place tick icon at the end.But I'm unable to show the icon clearly as the bar overlaps with tick icon.
<div style={{ display: "flex", flexDirection: "row", position: "relative", alignItems: "center" }}>
<LinearProgress
variant="determinate"
sx={{
width: "100%",
borderRadius: "4px"
}}
value={50}
/>
<CheckCircleIcon sx={{ color: "blue" }} style={{ position: "absolute", width: "20px", display: "flex", justifyContent: "flex-end", right: "-2px", color: "#fff", fontWeight: "bold" }} />
</div>
Current Design
Expected Design
Here is a live demo where I've customized an MUI Slider with a checkmark SliderThumb.
The demo includes the foundation for using this as a progress bar:
Disable the slider to ignore user input. Keep in mind that disabling will change the color to gray. You can override disabled behavior through .Mui-disabled
Set the slider's value using a state variable that corresponds to your current progress
You may also choose to customize a LinearProgress component in the same way I've customized the Slider above. See the docs for LinearProgress customization.
Full slider code:
import * as React from 'react'
import Slider, { SliderThumb } from '#mui/material/Slider'
import { styled } from '#mui/material/styles'
import Box from '#mui/material/Box'
import CheckCircleIcon from '#mui/icons-material/CheckCircle'
const CheckMarkSlider = styled(Slider)(({ theme }) =>
({
color: '#3a8589',
height: 3,
padding: '13px 0',
'& .MuiSlider-thumb':
{
height: 20,
width: 20,
backgroundColor: '#fff',
border: '1px solid currentColor',
'&:hover': {
boxShadow: '0 0 0 8px rgba(58, 133, 137, 0.16)',
},
'& .checkmark-bar':
{
height: 9,
width: 1,
backgroundColor: 'currentColor',
marginLeft: 1,
marginRight: 1,
},
},
'& .MuiSlider-track':
{
height: 3,
},
'& .MuiSlider-rail':
{
color: theme.palette.mode === 'dark' ? '#bfbfbf' : '#d8d8d8',
opacity: theme.palette.mode === 'dark' ? undefined : 1,
height: 3,
},
}))
const CheckMarkThumbComponent = (props) =>
{
const { children, ...other } = props
return (
<SliderThumb {...other}>
{children}
<CheckCircleIcon />
</SliderThumb>
)
}
const CustomizedSlider = () =>
{
const [value, setValue] = React.useState(20)
React.useEffect(() =>
{
const intervalId = setInterval(() => setValue(Math.random() * 100), 500)
return () => clearInterval(intervalId)
}, [value])
return (
<Box sx={{ width: 320 }}>
<CheckMarkSlider
value = {value}
disabled
components={{ Thumb: CheckMarkThumbComponent }} />
</Box>
)
}
export default CustomizedSlider
I have a MUI Modal set up, it's currently working, however the backdrop is being displayed as black. I have styling in place, but for some reason the backdrop is not being updated and remains pitch black.
My code is below, a screenshot is also below of what I see when the Modal is opened. Please let me know how I can fix this issue.
import { Box, Modal, Typography } from "#material-ui/core";
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
modalStyle: {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 300,
bgcolor: "background.paper",
boxShadow: 24,
p: 4,
backgroundColor: "white",
borderRadius: "10px",
},
}));
const PopupModal = ({ postModalOpen, handleClose, children }) => {
const classes = useStyles();
return (
<Modal open={postModalOpen} onClose={handleClose}>
<Box className={classes.modalStyle}>
ModalText
<Typography
id="modal-modal-title"
variant="h6"
component="h2"
style={{
fontSize: "14px",
marginLeft: "5px",
}}
>
{children}
</Typography>
</Box>
</Modal>
);
};
export default PopupModal;
If you'd like to style the backdrop, your styles must be passed to the Modal component directly:
For example:
const useStyles = makeStyles((theme) => ({
/** Changed modalStyle */
modalStyle: { backgroundColor: "rgba(255, 0, 0, 0.5)" },
/** Moved content styling to new class */
contentStyle: {
/** Also removed some invalid attributes **/
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "300px",
backgroundColor: "white",
borderRadius: "10px"
}
}));
const PopupModal = ({ postModalOpen, handleClose, children }) => {
const classes = useStyles();
return (
// Note the changed className props with your sample code
<Modal
open={postModalOpen}
onClose={handleClose}
className={classes.modalStyle}
>
<Box className={classes.contentStyle}>
<Typography
id="modal-modal-title"
variant="h6"
component="h2"
style={{
fontSize: "14px",
marginLeft: "5px"
}}
>
{children}
</Typography>
</Box>
</Modal>
);
};
Working Example: https://codesandbox.io/s/material-demo-forked-edhqx?file=/demo.js
FYI: You can also override all backdrops in your application, on a global scale, by passing a MuiBackdrop override object to createMuiTheme().
This is how I customized my MUI Backdrop (MUI v5)
<Backdrop open={isOpen} sx={{ backgroundColor: 'rgba(0, 0, 0, 0.25)', zIndex: 1 }} onClick={handleCloseMenu} />
I'm a newbie to MUI/react and I've been trying to place an Icon button beside my input base form, however I'm struggling to make it so that the input form consumes all the available space in my search div. Instead my search icon wrapper is the one that takes majority of the space. I'm really confused what I'm doing wrong, Can someone please shed some light on me?
Here's my code:
import * as React from "react";
import { styled } from "#mui/material/styles";
import Box from "#mui/material/Box";
import InputBase from "#mui/material/InputBase";
import SearchIcon from "#mui/icons-material/Search";
import IconButton from "#mui/material/IconButton";
const Search = styled("div")(({ theme }) => ({
display: "flex",
position: "relative",
borderRadius: 30,
backgroundColor: "#ffffff",
border: "1px",
borderStyle: "solid",
borderColor: "#55597d",
// marginLeft: 10,
width: "auto",
".MuiInputBase-root": {
width: "100%",
},
}));
const SearchIconWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
// position: 'absolute',
// pointerEvents: 'none',
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
// backgroundColor: 'black',
width: "100%",
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: "inherit",
"& .MuiInputBase-input": {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(0)})`,
paddingRight: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create("width"),
width: "100%",
},
}));
export default function SearchAppBar({
searchQuery,
setSearchQuery,
clearGenre,
onDropDownChange,
}) {
return (
<Box sx={{ flexGrow: 1 }}>
<Search
sx={{
width: { xs: "90vw", md: "50vw", lg: "30vw" },
margin: "auto",
marginBottom: "20px",
}}
>
<form action="/" method="get">
<StyledInputBase
defaultValue={searchQuery}
// placeholder="Search All Games…"
inputProps={{ "aria-label": "search" }}
type="search"
name="s"
id="site-search"
/>
</form>
<SearchIconWrapper>
<IconButton>
<SearchIcon style={{ color: "#55597d" }} />
</IconButton>
</SearchIconWrapper>
</Search>
</Box>
);
}
You need to remove the width: 100% in the SearchIconWrapper on the right first. And because the Search component is a flex container, you also need to add flexGrow: 1 to the first child (the form) so it can expand to fit the parent and push the icon to the far right:
const Search = styled("div")(({ theme }) => ({
display: "flex",
position: "relative",
borderRadius: 30,
backgroundColor: "#ffffff",
border: "1px",
borderStyle: "solid",
borderColor: "#55597d",
// marginLeft: 10,
// ---------------------------------- add the following styles
"& :first-child": {
flexGrow: 1
}
width: "auto",
".MuiInputBase-root": {
width: "100%"
}
}));
const SearchIconWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
// position: 'absolute',
// pointerEvents: 'none',
display: "flex",
alignItems: "center",
justifyContent: "flex-end"
// backgroundColor: 'black',
// -----------------------------------> comment this line: width: "100%"
}));
I am trying to use material ui's appbar and code in some custom responsiveness to it. I am creating a personal project that will be accessed from a Galaxy s8 smartphone. Ultimately I would like the nav-links and logout button on the navbar to consolidate into a hamburger button when the viewport is small. I am not quite sure how to achieve this however as I am using flexbox on the webpage when it is at full size and it keeps messing me up.
Here is the appbar at full size:
Here is the appbar at phone size with the hamburger button:
Here is the desired effect I would like with items, posts, groups, and the log out button being pushed to an accordion like menu on mobile:
Here is my navbar component code:
import { withStyles } from "#material-ui/core/styles";
import styles from "./styles/NavBarStyles";
import { LoggedInContext } from "./contexts/LoggedIn";
import { ThemeContext } from "./contexts/ThemeContext";
function Navbar(props) {
const { isDarkMode, toggleTheme } = useContext(ThemeContext);
const { loggedIn, changeLogIn, token, setToken } = useContext(
LoggedInContext
);
const { classes } = props;
const [mobileHamburgerOpen, setMobileHamburger] = useState(false);
const handleMobileClick = () => {
setMobileHamburger(!mobileHamburgerOpen);
console.log(mobileHamburgerOpen);
};
return (
<div className={classes.root}>
<AppBar
position="static"
style={{ background: isDarkMode ? "#2E3B55" : "#715AFF" }}
>
<Toolbar>
<Typography className={classes.title} variant="h6" color="inherit">
{isDarkMode ? "🌚" : "🌞"}
</Typography>
<Switch onChange={toggleTheme} />
<div className={classes.grow} />
{loggedIn && (
<div className={classes.loggedIn}>
<div className={classes.navlinks}>
<NavLink
to="/items"
activeStyle={{ background: "rgba(0, 0, 0, 0.5)" }}
>
Items
</NavLink>
<NavLink
to="/facebookitems"
activeStyle={{ background: "rgba(0, 0, 0, 0.5)" }}
>
<i className="fab fa-facebook-square"></i> Posts
</NavLink>
<NavLink
to="/groups"
activeStyle={{ background: "rgba(0, 0, 0, 0.5)" }}
>
<i className="fab fa-facebook-square"></i> Groups
</NavLink>
</div>
<Button
className={classes.logOutButton}
variant="contained"
color="secondary"
onClick={handleClick}
>
Log Out
</Button>
<Button
className={classes.hamburgerMenu}
variant="contained"
color="primary"
onClick={handleMobileClick}
>
<i className="fas fa-bars"></i>
</Button>
</div>
)}
</Toolbar>
</AppBar>
</div>
);
}
export default withStyles(styles)(Navbar);
Here is my style sheet code:
import { fade } from "#material-ui/core/styles/colorManipulator";
const styles = theme => ({
root: {
width: "100%",
marginBottom: 0
},
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
title: {
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)
},
marginLeft: 0,
width: "100%",
[theme.breakpoints.up("sm")]: {
marginLeft: theme.spacing(1),
width: "auto"
}
},
searchIcon: {
width: theme.spacing(9),
height: "100%",
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
inputRoot: {
color: "inherit",
width: "100%"
},
inputInput: {
paddingTop: theme.spacing(1),
paddingRight: theme.spacing(1),
paddingBottom: theme.spacing(1),
paddingLeft: theme.spacing(10),
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: 120,
"&:focus": {
width: 200
}
}
},
loggedIn: {
width: "100%",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
paddingLeft: "2rem"
},
navlinks: {
// [theme.breakpoints.down("sm")]: {
// position: 'absolute',
// backgroundColor: "rgba(0,0,0,.7)",
// top: '0%',
// right: '0%',
// width: '100vw',
// height: '100vh'
// },
"& a": {
color: "white",
fontWeight: "bold",
fontSize: "1.5rem",
textDecoration: "none",
marginRight: '1rem',
padding: ".5rem",
borderRadius: "15px"
},
"& a:active": {
backgroundColor: "rgba(0, 0, 0, 0.5)"
}
},
hamburgerMenu: {
display: 'none',
[theme.breakpoints.down("sm")]: {
display: 'block',
marginLeft: "auto",
backgroundColor: "white",
color: "#715AFF"
}
},
logOutButton: {
// [theme.breakpoints.down("sm")]: {
// display: 'none'
// }
}
});
export default styles;