-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[docs] add avatar upload example #45131
Comments
Hey @Demianeen, thanks for reaching out! This would be useful. I added the Just a note for anyone working on this: I think we need to find a more straightforward way to achieve the demo, as the code shared in the description is a bit intricate for a demo. |
I never added stuff like this to the docs, but I am happy to try! The only thing is that I agree that code is a bit too complicated for a demo. Does mui has some helper functions that I can look into that can help to remove useTab or useFocus? It would remove almost half of the code Also, there is an @ts-expect-error in the component itself, I am not sure if I would be able to fix that types issue. Should we do anything about it? |
@Demianeen I would suggest using
About the Let me know if it works 👍🏼 |
Thanks for pointers! I managed to remove dependency on additional hooks: import type { ElementType } from 'react'
import { forwardRef, memo, useImperativeHandle, useRef, useState } from 'react'
import type {
AvatarProps,
ButtonBaseProps,
FormHelperTextProps,
} from '@mui/material'
import { Avatar, ButtonBase, FormHelperText, styled } from '@mui/material'
import PersonIcon from '@mui/icons-material/Person'
const VisuallyHiddenInput = styled('input')({
display: 'none',
})
const ClickableAvatarWrapper = styled(ButtonBase)<
ButtonBaseProps & { component: ElementType }
>(({ theme }) => ({
width: 100,
height: 100,
cursor: 'pointer',
transition: 'all .1s',
borderRadius: '50%',
'&.Mui-focusVisible': {
outline: `4px solid ${theme.palette.primary.main}`,
outlineOffset: '4px',
},
}))
const ClickableAvatar = styled(Avatar)(() => ({
width: 100,
height: 100,
'&:hover': {
filter: 'brightness(90%)',
},
}))
interface AvatarUploadProps {
avatarProps?: AvatarProps
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
helperTextProps?: FormHelperTextProps
}
export const AvatarUpload = memo(
forwardRef<HTMLInputElement, AvatarUploadProps>(function AvatarUpload(
{ avatarProps, inputProps, helperTextProps },
ref,
) {
const [imageSrc, setImageSrc] = useState<string>()
const inputRef = useRef<HTMLInputElement>(null)
useImperativeHandle(ref, () => inputRef.current!)
const handleImageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (file) {
// Read the file as a data URL
const reader = new FileReader()
reader.onload = () => {
setImageSrc(reader.result as string)
}
reader.readAsDataURL(file)
}
inputProps?.onChange?.(event)
}
// https://stackoverflow.com/questions/75121073/unable-to-trigger-input-event-from-label-element-with-keyboard
const handleKeyDown = (event: React.KeyboardEvent<HTMLSpanElement>) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
inputRef.current?.click()
}
}
return (
<ClickableAvatarWrapper
component='label'
tabIndex={0}
role='button'
onKeyDown={handleKeyDown}
>
<ClickableAvatar
variant='circular'
src={imageSrc}
{...avatarProps}
>
<PersonIcon
sx={{
fontSize: '40px',
}}
/>
</ClickableAvatar>
<VisuallyHiddenInput
type='file'
accept={'image/png, image/jpg, image/jpeg'}
multiple={false}
{...inputProps}
// those props couldn't be overridden directly, but you still can use on change or ref
onChange={handleImageChange}
ref={inputRef}
/>
<FormHelperText {...helperTextProps} />
</ClickableAvatarWrapper>
)
}),
) The only other thing I am unsure about is form helper text, because I use label here for focus and not the input itself, user wouldn't be able to focus on hidden input. I am not sure if FormHelperText will be accessible with this approach. What do you think? |
Related page
https://mui.com/material-ui/react-avatar/
Kind of issue
Missing information
Issue description
I recently needed to implement avatar upload inside form during sign up flow, I ended up with a bit hacky solution where I need to have visually hidden input nearby the clickable avatar, and that avatar will be a label, so that click on label will cause click on input. I also needed to put another hack in place so that input navigation with tab would work. It seems like quite popular usecase. Potentially adding section in the docs on how to implement it could save a lot of time and hacks in code to other people like me.
Just as the reference, this is what I end up with:
useFocus.ts (just tracks focus of the elem):
useTab(tracks if the tab key is the last pressed):
Context
No response
Search keywords: mui avatar profile upload input hidden
The text was updated successfully, but these errors were encountered: