How can I get combining from react beautiful dnd to work on my items?

3.5k views Asked by At

I am using react beautiful dnd and have created 3 columns with list-items. I want to add the feature to combine items. I have read the documentation, but still can't seem to figure out why it's not working.

The problem seems to be in onDragEnd, where it can find result.combine (combine) like in the documentation, but it doesn't seem to be true when it gets to the if statement. Am I overlooking something? Can someone please explain to me what is happening, why it's not working?

Thank you in advance!

resources I've used:

my data:

const initialData = {
  tasks: {
    'task-1': { id: 'task-1', content: 'task-1' },
    'task-2': { id: 'task-2', content: 'task-2' },
    'task-3': { id: 'task-3', content: 'task-3' },
  },
  columns: {
    'column-1': {
      id: 'column-1',
      title: 'column-1',
      taskIds: ['task-1', 'task-2'],
    },
    'column-2': {
      id: 'column-2',
      title: 'column-2',
      taskIds: [],
    },
    'column-3': {
      id: 'column-3',
      title: 'column-3',
      taskIds: ['task-3'],
    },
  },
  columnOrder: ['column-1', 'column-2', 'column-3'],
};

export default initialData;

index file with onDragEnd

const onDragEnd = result => {
    const { destination, source, draggableId, combine } = result;

    //console.log(`drag: ${combine.draggableId} drop: ${combine.droppableId}`);

    if (!destination) {
      return; // not dropped in a known destination
    }
    if (destination.draggableId === source.droppableId && destination.index === source.index) {
      return; // dropped in same location
    }

    const start = state.columns[source.droppableId]; //get selected column
    const finish = state.columns[destination.droppableId]; //get new selected column

    if (combine) { 

      //console.log(`drag: ${combine.draggableId} drop: ${combine.droppableId}`); 

      const combineTaskIds = Array.from(start.taskIds); 
      combineTaskIds.splice(source.index, 1); 
      const newColumn = {
        ...start,
        taskIds: combineTaskIds,
      }; 

      setState(prevState => ({ ...prevState, columns: { ...prevState.columns, [newColumn.id]: newColumn } }));
    }

    if (start === finish) { //move in same column
      const newTaskIds = Array.from(start.taskIds);
      newTaskIds.splice(source.index, 1); 
      newTaskIds.splice(destination.index, 0, draggableId); 

      const newColumn = {
        ...start,
        taskIds: newTaskIds,
      }; // create new column with new tasks

      setState(prevState => ({ ...prevState, columns: { ...prevState.columns, [newColumn.id]: newColumn } }));
    }

    if (start !== finish) { 
      const startTaskIds = Array.from(start.taskIds);
      startTaskIds.splice(source.index, 1); 
      const newStart = {
        ...start,
        taskIds: startTaskIds,
      };

      const finishTaskIds = Array.from(finish.taskIds);
      finishTaskIds.splice(destination.index, 0, draggableId); 
      const newFinish = {
        ...finish,
        taskIds: finishTaskIds,
      };

      setState(prevState => ({ ...prevState, columns: { ...prevState.columns, [newStart.id]: newStart, [newFinish.id]: newFinish } }));
    }
  }

return <DragDropContext onDragEnd={onDragEnd} >
    <Container>
      {
        state.columnOrder.map(columnId => {
          const column = state.columns[columnId];
          const tasks = column.taskIds.map(taskId => state.tasks[taskId]);

          return <Column key={column.id} column={column} tasks={tasks} />;
        })
      }
    </Container>
  </DragDropContext >

Droppable Columns (with isCombineEnabled as true)

  <Container>
      <List dense>
        <ListItemText primary={props.column.title} />
        <Droppable droppableId={props.column.id} isCombineEnabled>
          {(provided, snapshot) => (
            <ListItem
              {...provided.droppableProps}
              innerRef={provided.innerRef}
              isDraggingOver={snapshot.isDraggingOver}
              button>
              {props.tasks.map((task, index) => <Task key={task.id} task={task} index={index} />)}
              {provided.placeholder}
            </ListItem>
          )}
        </Droppable>
      </List>
    </Container>

Draggable Task Items

<Draggable draggableId={props.task.id} index={props.index}>
      {(provided, snapshot) => (
        <Container
          {...provided.draggableProps}
          {...provided.dragHandleProps}
          innerRef={provided.innerRef}
          isDragging={snapshot.isDragging}
        >
          {props.task.content}
        </Container>
      )}
    </Draggable>
1

There are 1 answers

0
DCCW On BEST ANSWER

Finally I have solved the issue and wanted to share this in case someone might end up with the same problem.

The problem was indeed in onDragEnd before getting to the conditional for combine. const finish needs to change depending on the usage, for it will have either one or the other. Combine or Destination.

    const onDragEnd = result => {
    const { destination, source, draggableId, combine } = result;

  if (!combine && !destination) {
      return; // not dropped in a known destination
    }
    if (!combine && destination.draggableId === source.droppableId && destination.index === source.index) {
      return; // dropped in same location
    }

    const start = state.columns[source.droppableId]; //get selected column
    const finish = combine ? state.columns[combine.droppableId] : state.columns[destination.droppableId]; //get new selected column

    if (combine) {
      //just removing the dragging item
      const combineTaskIds = Array.from(start.taskIds); //create new array with current columns taskids 
      combineTaskIds.splice(source.index, 1); // from this index we want to remove 1 item
      const newColumn = {
        ...start,
        taskIds: combineTaskIds,
      }; // create new column with new tasks

      setState(prevState => ({ ...prevState, columns: { ...prevState.columns, [newColumn.id]: newColumn } }));
    }

    if (start === finish) { //move in same column
      const newTaskIds = Array.from(start.taskIds);
      newTaskIds.splice(source.index, 1); // from this index we want to remove 1 item
      newTaskIds.splice(destination.index, 0, draggableId); // from this index we want to add the draggable item

      const newColumn = {
        ...start,
        taskIds: newTaskIds,
      }; // create new column with new tasks

      setState(prevState => ({ ...prevState, columns: { ...prevState.columns, [newColumn.id]: newColumn } }));
    }

    if (start !== finish) { //move to different column
      const startTaskIds = Array.from(start.taskIds);
      startTaskIds.splice(source.index, 1); //remove item from index
      const newStart = {
        ...start,
        taskIds: startTaskIds,
      };

      const finishTaskIds = Array.from(finish.taskIds);
      finishTaskIds.splice(destination.index, 0, draggableId); // add draggable to index
      const newFinish = {
        ...finish,
        taskIds: finishTaskIds,
      };

      setState(prevState => ({ ...prevState, columns: { ...prevState.columns, [newStart.id]: newStart, [newFinish.id]: newFinish } }));
    }
  }

Any additions or corrections to my answer are always welcome to learn.