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
[Button] Upload file input type #9716
Comments
So far, we have a demo: https://v4.mui.com/components/buttons/#upload-button material-ui/docs/src/pages/components/buttons/UploadButtons.tsx Lines 25 to 36 in 15108a5
However, mind this issue: #22141. |
...so I guess the answer is "no" |
Dropzone is in the roadmap: https://github.com/mui/material-ui/issues/22434. |
There's a 3rd party component: https://github.com/Yuvaleros/material-ui-dropzone. |
Make sure Button has |
Can anyone explains why we should set |
@andykao1213 I imagine there are two issues with |
Hello, I think I came to a satisfying solution 👍 When designing forms, the TextField component is used a lot because it has a label, an error helper text which is very useful for validation, it can be multiline, it's compatible with Why not using it for file input ? Therefore, here is an example component that retains all the benefits, at least for me. import { useRef, useState } from "react";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import Box from "@material-ui/core/Box";
import TextField from "@material-ui/core/TextField";
import ButtonBase from "@material-ui/core/ButtonBase";
const FileInput = ({ label, onChange, error }) => {
const ref = useRef();
const theme = useTheme();
const classes = useStyles();
const [attachment, setAttachment] = useState();
const handleChange = (event) => {
const files = Array.from(event.target.files);
const [file] = files;
setAttachment(file);
if (!!onChange) onChange({ target: { value: file } });
};
return (
<Box
position="relative"
height={98}
color={
!!error ? theme.palette.error.main : theme.palette.background.paper
}
borderBottom={4}
>
<Box position="absolute" top={0} bottom={0} left={0} right={0} mx={2}>
<TextField
className={classes.field}
InputProps={{ disableUnderline: true }}
margin="normal"
fullWidth
disabled
label={label}
value={attachment?.name || ""}
error={!!error}
helperText={error?.message || " "}
/>
</Box>
<ButtonBase
className={classes.button}
component="label"
onKeyDown={(e) => e.keyCode === 32 && ref.current?.click()}
>
<input
ref={ref}
type="file"
accept="image/*"
hidden
onChange={handleChange}
/>
</ButtonBase>
</Box>
);
};
const useStyles = makeStyles((theme) => ({
field: {
"& .MuiFormLabel-root.Mui-disabled": {
color: theme.palette.text.secondary,
},
},
button: {
width: "100%",
height: "100%",
overflow: "hidden",
},
}));
export default FileInput; The TextField component is used for datas display only (disabled & to be styled as needed), a ButtonBase component is used for input and triggering. It would be easy to adapt it for multiple files with a multiline TextField... IMO this approach allows for a good separation of concerns and makes me think of TextField as a good direction for a file input component... |
Here is codesandbox to see the result : https://codesandbox.io/s/react-mui-file-input-cfsno |
Thank you @narkai |
It fails with
|
if we set the |
I'm posting the solution I came up with after a bit of research: import Button from '@mui/material/Button';
import InputAdornment from '@mui/material/InputAdornment';
import TextField from '@mui/material/TextField';
import { useRef } from 'react';
export default function FileInlnput() {
const inputRef = useRef<HTMLInputElement>(null);
return (
<TextField
type="file"
inputRef={inputRef}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Button
variant="outlined"
size="small"
onClick={(e) =>
inputRef.current?.dispatchEvent(
new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
buttons: 1,
}),
)
}
>
Choose File
</Button>
</InputAdornment>
),
}}
sx={{
'& input::file-selector-button': {
display: 'none',
},
}}
/>
);
} This solution retains the ability to drag an drop the files and uses TextField as the input. |
Or there is a library for this : https://viclafouch.github.io/mui-file-input/ (React 18 / MUI V5) import React from 'react'
import { MuiFileInput } from 'mui-file-input'
const MyComponent = () => {
const [value, setValue] = React.useState(null)
const handleChange = (newValue) => {
setValue(newValue)
}
return <MuiFileInput value={value} onChange={handleChange} />
} |
Doesn't seem usable in current state with react hook form (which is quite an issue). It's lacking a reference forward (to the inner input I'd assume?). I don't see either a way to restrict the file types, which is kinda a big deal (maybe I'm blind though). |
The support for multiple files selection is not implemented very well... there is no |
As of end of 2022 I've made a Multiple File Input based on @narkai 's example in TypeScript. import React, { useRef } from 'react';
import { Box, Button, InputAdornment, TextField } from '@mui/material';
import { AttachFile } from '@mui/icons-material';
interface spreadSheetSelectProps {
files: File[] | null;
setFiles: React.Dispatch<React.SetStateAction<File[] | null>>;
}
export const SpreadsheetSelect: React.FC<spreadSheetSelectProps> = (
{ files, setFiles }
) => {
// const [files, setFiles] = useState<File[] | null>(null);
const ref = useRef<HTMLInputElement>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) return;
const files = Array.from(e.target.files);
setFiles(files);
};
const getFileNames = () => (
files?.reduce(
(fileNames, file) => `${fileNames} ${fileNames !== '' ? ',' : ''} ${file.name}`, ''
)
|| ''
);
return (
<Box
position="relative"
height={57}
width="100%"
>
<Box position="absolute" width="100%">
<TextField
fullWidth
label="Select a spreadsheet"
value={getFileNames()}
required
sx={{ pointerEvents: 'none' }}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<AttachFile />
</InputAdornment>
)
}}
/>
</Box>
<Button
component="label"
onKeyDown={(e) => e.key === '32' && ref.current?.click()}
fullWidth
sx={{ height: '100%' }}
>
<input
ref={ref}
type="file"
onChange={handleChange}
accept="application/vnd.ms-excel, .xlsx, .xls, .csv"
hidden
multiple
/>
</Button>
</Box>
);
};
export default SpreadsheetSelect; |
Hi! This is my example const [file, setFile] = useState();
const [fileName, setFileName] = useState('');
const changeFile = (e) => {
const file = e.target.files[0];
setFileName(file.name);
setFile(file);
}
<TextField value={fileName} label="File Name" />
<Button variant="contained" component="label">
Upload File
<input type="file" name="file" onChange={changeFile} hidden />
</Button> |
Joy UI has a demo with it: https://mui.com/joy-ui/react-button/#file-upload. I created #38766, I think we can add the exact same one in Material UI. I don't see why it should be any different. |
Is there a component for file input type which allows us to upload images?
The text was updated successfully, but these errors were encountered: