How to set width and height dynamically based on images that got passed with materal-ui Modal component?

Viewed 112

Here is a sample code sandbox for this issue.

Things I want to do is

1, Click open modal button to open the modal.

2, This opened modal shows a whole image. (No margin, padding just want to show the whole image itself)

So I need to get image width and height that is different based on which images get passed.

Right now, I can get width and height like this.

  function getMeta(url) {
    const img = new Image();
    img.addEventListener("load", function () {
      console.log(this.naturalWidth + " " + this.naturalHeight);
      setWidth(this.naturalWidth);
      setHeight(this.naturalHeight);
    });
    img.src = url;
  }

To execute this function like this

   getMeta("https://cdn.pixabay.com/photo/2022/03/08/07/08/water-7055153_1280.jpg");

But I don't know how to set width and height to Box component dynamically.

const style = {
  position: "absolute",
  top: "50%",
  left: "50%",
  transform: "translate(-50%, -50%)",
  width: 500,  // this needs dynamic
  height: 500, // this needs dynamic 
  bgcolor: "background.paper",
  boxShadow: 24,
  p: 4
};

export default function BasicModal() {
  const [open, setOpen] = React.useState(false);

  const handleOpen = () => {
    setOpen(true);
    getMeta(
      "https://cdn.pixabay.com/photo/2022/03/08/07/08/water-7055153_1280.jpg"
    );
  };
  const handleClose = () => setOpen(false);
  const [width, setWidth] = React.useState(0);
  console.log("width", width);
  const [height, setHeight] = React.useState(0);
  console.log("height", height);

  function getMeta(url) {
    const img = new Image();
    img.addEventListener("load", function () {
      console.log(this.naturalWidth + " " + this.naturalHeight);
      setWidth(this.naturalWidth);
      setHeight(this.naturalHeight);
    });
    img.src = url;
  }


  return (
    <div>
      <Button onClick={handleOpen}>Open modal</Button>
      <Modal
        open={open}
        onClose={handleClose}
        aria-labelledby="modal-modal-title"
        aria-describedby="modal-modal-description"
      >
        <Box sx={style}>
         <div>This is an image</div>
        </Box>
      </Modal>
    </div>
  );
}

Attempts

I thought I could simply use setState for width and height

const style = {
  position: "absolute",
  top: "50%",
  left: "50%",
  transform: "translate(-50%, -50%)",
  width: width,  // changed
  height: height, // changed
  bgcolor: "background.paper",
  boxShadow: 24,
  p: 4
};

export default function BasicModal() {
  const [open, setOpen] = React.useState(false);

  const handleOpen = () => {
    setOpen(true);
    getMeta(
      "https://cdn.pixabay.com/photo/2022/03/08/07/08/water-7055153_1280.jpg"
    );
  };

  const [width, setWidth] = React.useState(0);
  const [height, setHeight] = React.useState(0);

  function getMeta(url) {
    const img = new Image();
    img.addEventListener("load", function () {
      console.log(this.naturalWidth + " " + this.naturalHeight);
      setWidth(this.naturalWidth); // set width
      setHeight(this.naturalHeight); // set height
    });
    img.src = url;
  }

...

But this didn't work.

Attempt2

This kind works. I use makeStyles but I'm just wondering why I need two variables for styling.

const useStyles = makeStyles({
  root: (props) => ({
    position: "absolute",
    top: "50%",
    left: "50%",
    transform: "translate(-50%, -50%)",
    width: props.width, // this needs dynamic
    height: props.height, // this needs dynamic
    bgcolor: "background.paper",
    boxShadow: 24
  })
});

const style = {
  position: "absolute",
  top: "50%",
  left: "50%",
  transform: "translate(-50%, -50%)",
  width: 500, // this needs dynamic
  height: 500, // this needs dynamic
  bgcolor: "background.paper",
  boxShadow: 24
};

.
.
.
  return (
    <div>
      <Button onClick={handleOpen}>Open modal</Button>
      <Modal
        open={open}
        onClose={handleClose}
        aria-labelledby="modal-modal-title"
        aria-describedby="modal-modal-description"
      >
        <Box
          sx={style} // here
          className={classes.root} // here
          component="img"
          src="https://cdn.pixabay.com/photo/2022/06/13/15/36/grain-7260250__340.jpg"
        ></Box>
      </Modal>
    </div>
  );

It seems like I need both useStyles and style. If I remove one of them, it starts broking the style.

Here is the code for this at the moment.

1 Answers

You can pass the width and height as a parameter to the sx prop in Box.

<Box 
  sx={{ 
    ...style,
    width: width, 
    height: height
  }}
>
  <div>This is an image</div>
</Box>

This can be shortened even further to:

<Box sx={{ ...style, width, height }}>
  <div>This is an image</div>
</Box>

By spreading style, you are able to update width and height while maintaining the rest of the styling.

You can view this in action in a reworked version of your sandbox example: https://codesandbox.io/s/basicmodal-demo-material-ui-forked-pqtdz0?file=/demo.js

Related