How do I use the SpeedDial to upload a file?

Viewed 321

(this seem to have been asked previously but I couldn't find any hint on if it was actually answered)

MUI has a good demo for creating upload buttons which boils down to:

<input accept="image/*" className={classes.input} id="icon-button-file" type="file" />
<label htmlFor="icon-button-file">
  <IconButton color="primary" aria-label="upload picture" component="span">
    <PhotoCamera />
  </IconButton>
</label>

What I wonder is how to implement the same using the Speed Dial. Inherently the SpeedDialAction seems to materialize as a <button/>, but it's not possible to e.g. wrap the SpeedDialAction in a <label htmlFor /> as its parent will try to set some props on it and will fail.

So how do I initiate the file selection from within the Speed Dial or a FAB in general?

2 Answers

You can create a wrapper component that forwards props to SpeedDialAction.

function UploadSpeedDialAction(props) {
  return (
    <React.Fragment>
      <input
        accept="image/*"
        style={{ display: "none" }}
        id="icon-button-file"
        type="file"
      />
      <label htmlFor="icon-button-file">
        <SpeedDialAction
          icon={<CloudUploadIcon />}
          tooltipTitle="upload"
          component="span"
          {...props}
        ></SpeedDialAction>
      </label>
    </React.Fragment>
  );
}

https://codesandbox.io/s/material-demo-forked-h6s4l

(Note to future readers: For v5, time allowing, we hope to rationalise where props rather than context are used to control children, in order to solve exactly this kind of issue. So check whether this solution is still needed.)

It is - in my knowledge - not possible to add the htmlFor in any way. So what I would do is to add a hidden input type file and then add a ref to it. Then in the onclick of the SpeedDialAction button I would call a handler function that clicks on the input ref. Like this:

const inputRef = useRef();
const handleFileUploadClick = () => {
  inputRef.current.click();
};

Then your SpeedDialAction:

<SpeedDialAction
  onClick={handleFileUploadClick}
  ... the rest of your props
/>

And then finnaly your actual input:

<input
  style={{ display: "none" }}
  ref={inputRef}
  accept="image/*"
  id="contained-button-file"
  multiple
  type="file"
/>

Working demo: https://codesandbox.io/s/material-demo-forked-f9i6q?file=/demo.tsx:1691-1868

Related