I wanted to create the same and nearly never styled components before. So here we go, feel free to add things to it and vice versa.
So for the start I went with Mui's complex buttons you can find here:
https://mui.com/material-ui/react-button/#complex-button
They already got me the base for the overlay styling.
From there I put those in an avatar component, because profile picture and so on. Since I wanted to use that as an onClick file upload, I implemented the
<input type="file />
. This seemed to only work when inside a button when prop component="label"
is set.
see here: How to enable file upload on React's Material UI simple input?
Unfortunately it looks like styled components, like <ImageButton>
can not carry this prop.
see here: https://github.com/mui/material-ui/issues/35966
Thats why the workaround with the <ImageInputButton>
is/was needed.
I also tried wrapping the <CameraAltIcon>
into an <IconButton>
which can carry the component prop, but then only the icon is clickable and not the whole avatar, which was my goal here. Wrapping the whole avatar didn't work for me.
Since I have no idea how to codesandbox and co, where you can try it live, I will post the two pictures of behaviour here, as well as the code below.
Styled hoverable avatar, left: normal, right: on hover
import {
Avatar,
Button,
ButtonProps
} from '@mui/material';
import CameraAltIcon from '@mui/icons-material/CameraAlt';
import { styled } from '@mui/material/styles';
//This button is needed so "component='label'" can be set for the styled component ImageButton.
//This way the whole overlay works as an input and is clickable.
const ImageInputButton = (props: ButtonProps<'label'>) => {
return <Button {...props} component="label" />;
};
//start of styling for logo and overlay
const ImageButton = styled(ImageInputButton)(({ 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.4,
},
'& .MuiImageMarked-root': {
opacity: 0.8,
},
},
}));
//styling for image/logo/profilepicture
const ImageSrc = styled('span')({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundPosition: 'center',
});
//styling for opacity effect
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'),
}));
//styling for overlay logo (here the cameraIcon)
const Image = styled('span')(({ theme }) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
opactiy: 0,
color: theme.palette.common.white,
}));
//end of styling
return (
<Avatar
sx={{
height: 150,
width: 150,
backgroundColor: 'transparent',
}}
>
<ImageButton
focusRipple
key={'random'}
style={{
width: '100%',
}}
>
<ImageSrc style={{backgroundImage: `url(${image})`}} />
<ImageBackdrop className='MuiImageBackdrop-root' />
<Image>
<CameraAltIcon
className='MuiImageMarked-root'
fontSize='large'
sx={{
color: 'white',
opacity: 0,
}}
/>
</Image>
<input type='file' accept='image/*' hidden onChange={(e) => submitLogo(e.target.files)} />
</ImageButton>
</Avatar>
);