2

I'm using Material UI with react-beautiful-dnd to create a pretty simple Grid column layout with elements that can be reordered. However, I'm having this jank issue with the spacing between the elements that's hard to explain, so I'll just show you a gif of it:

Reordering

I'm not sure where this issue could be coming from or even what to call it. Internally, the spacing property of Grid is implemented using padding, and I can't even find any information on why that wouldn't work with the drag and drop system. Here's the main part of the code:

import { makeStyles } from "@material-ui/core";
import { Grid, Paper } from "@material-ui/core";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import { produce } from "immer";

function ReorderQuestion(props) {
  const [items, setItems] = useState(props.items);
  const classes = useStyles();

  function onDragEnd({ source, destination }) {
    if (!destination) {
      return;
    }

    if (
      destination.droppableId === source.droppableId &&
      destination.index === source.index
    ) {
      return;
    }

    setItems(
      produce(draft => {
        draft.splice(destination.index, 0, draft.splice(source.index, 1)[0]);
      })
    );
  }

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Droppable droppableId="reorderQuestion" direction="horizontal">
        {provided => (
          <div {...provided.droppableProps} ref={provided.innerRef}>
            <Grid spacing={3} container direction="row">
              {items.map((imageSrc, index) => (
                <Grid item key={imageSrc}>
                  <Draggable draggableId={imageSrc} index={index}>
                    {(provided, snapshot) => (
                      <Paper
                        innerRef={provided.innerRef}
                        {...provided.draggableProps}
                        {...provided.dragHandleProps}
                        elevation={
                          snapshot.isDragging && !snapshot.isDropAnimating
                            ? 5
                            : 2
                        }
                        className={classes.option}
                      >
                        <img src={imageSrc} alt="Hiking" />
                      </Paper>
                    )}
                  </Draggable>
                </Grid>
              ))}
              {provided.placeholder}
            </Grid>
          </div>
        )}
      </Droppable>
    </DragDropContext>
  );
}

const useStyles = makeStyles(theme => ({
  option: {
    padding: theme.spacing(2)
  }
}));
naiveai
  • 590
  • 2
  • 14
  • 42

2 Answers2

4

Figured out the solution, thanks to @mal#8537 on the Reactiflux Discord.

The issue is that the innerRef is being provided to the Paper component, which doesn't actually contain the spacing - ie, it doesn't use margins. The Grid item, its parent component, uses padding to achieve the spacing, therefore it must be moved inside the Draggable and provided with the innerRef (and the draggableProps) for the spacing to be dragged along correctly.

I simply replaced the inside of items.map with this:

<Draggable draggableId={imageSrc} index={index} key={imageSrc}>
    {(provided, snapshot) => (
        <Grid item
            innerRef={provided.innerRef} // PROVIDE REF HERE, TO GRID
            {...provided.draggableProps}>
          <Paper
            {...provided.dragHandleProps}
            elevation={
              snapshot.isDragging && !snapshot.isDropAnimating
                ? 5
                : 2
            }
            className={classes.option}
          >
            <img src={imageSrc} alt="Hiking" />
          </Paper>
        </Grid>
    )}
</Draggable>
naiveai
  • 590
  • 2
  • 14
  • 42
0

I attempted the solution proposed by @naiveal, but unfortunately it did not work for me. To address the issue, I added an extra div with a height equal to the margin height in the draggable element, which resolved the problem. While it may not be the most elegant solution, it did the job for me. I hope this workaround proves helpful to anyone else encountering a similar issue.

   <Draggable draggableId={id} index={index}>
      {(provided) => (
        <div
          ref={provided.innerRef}
          {...provided.draggableProps}
          {...provided.dragHandleProps}
        >
          {/* The extra div */}
          <div style={{ height: "1rem" }} /> 
           .....
        </div>
      )}
   </Draggable>
SAHIL
  • 64
  • 2