Ad

How Do I Prevent Drop In Originating Container In React Js And React-beautiful-dnd?

- 1 answer

I am using React and react-beautiful-dnd.

This has had me stumped

My use case is to drag items from container1 to container2.

  1. Items in Container1 cannot be dropped in Container 1 only in Container 2.
  2. If an item in container 1 is dragging the remaining items in Container 1 should not move to allow a drop.

I created a sample fiddle to isolate the use case - https://codesandbox.io/s/34z92ny69p

Any help is appreciated

G

Ad

Answer

If I'm understanding what you want:

  • Allow Container 1 items to be moved into Container 2.
  • Do not allow Container 1 to be empty.
  • Do not allow any items to be moved back into Container 1.

Optional: I also set it up so that you can disable dragging by passing down an isDragDisabled prop in the DroppableContainer, for example:

<DroppableContainer
  droppableId="Container2"
  title="Container 2"
  data={this.state.container2Data}
  isDragDisabled
/>

Working example: https://codesandbox.io/s/moy02o60yx

components/Workspace.js

import React, { Component } from "react";
import { DragDropContext } from "react-beautiful-dnd";
import { Grid, Row } from "react-bootstrap";
import DroppableContainer from "./DroppableContainer";

const testData = {
  container1Data: [
    { id: 1, name: "item 1" },
    { id: 2, name: "item 2" },
    { id: 3, name: "item 3" },
    { id: 4, name: "item 4" },
    { id: 5, name: "item 5" },
    { id: 6, name: "item 6" }
  ],
  container2Data: [
    { id: 101, name: "other item 1" },
    { id: 102, name: "other item 2" }
  ]
};

export default class Workspace extends Component {
  state = {
    container1Data: testData.container1Data,
    container2Data: testData.container2Data
  };

  onDragEnd = ({ destination, source }) => {
    if (
      !destination ||
      destination.droppableId !== "Container2" ||
      (destination.droppableId === source.droppableId &&
        destination.index === source.index)
    ) {
      return;
    }

    this.setState(prevState => {
      const addItem = prevState.container1Data.splice(source.index, 1);
      prevState.container2Data.splice(destination.index, 0, ...addItem);

      return {
        container1Data: [...prevState.container1Data],
        container2Data: [...prevState.container2Data]
      };
    });
  };

  render = () => (
    <DragDropContext onDragEnd={this.onDragEnd}>
      <Grid bsClass="box-container">
        <Row>
          <DroppableContainer
            droppableId="Container1"
            title="Container 1"
            data={this.state.container1Data}
            dropDisabled
          />
          <DroppableContainer
            droppableId="Container2"
            title="Container 2"
            data={this.state.container2Data}
          />
        </Row>
      </Grid>
    </DragDropContext>
  );
}

components/DroppableContainer.js

import React, { PureComponent } from "react";
import { Droppable } from "react-beautiful-dnd";
import { Col } from "react-bootstrap";
import styled from "styled-components";
import DraggableItem from "./DraggableItem";

const StyledDiv = styled.div`
  border: 1px solid #000080;
  padding: 15px;
`;

export default class DroppableContainer extends PureComponent {
  renderDraggableItems = () =>
    this.props.data.map((item, i) => (
      <DraggableItem
        key={i}
        item={item}
        index={i}
        isDragDisabled={
          this.props.isDragDisabled || this.props.data.length === 1
        }
      />
    ));

  render = () => (
    <Col sm={4}>
      <Droppable
        droppableId={this.props.droppableId}
        isDropDisabled={this.props.dropDisabled || false}
      >
        {provided => (
          <StyledDiv
            className={`container ${this.props.data.length === 1 ? "disabled" : null }`}
            ref={provided.innerRef}
            {...provided.droppableProps}
          >
            <div className="row">
              <div className="col">{this.props.title}</div>
            </div>
            {this.renderDraggableItems()}
            {provided.placeholder}
          </StyledDiv>
        )}
      </Droppable>
    </Col>
  );
}

components/DraggableItem.js

import React from "react";
import { Draggable } from "react-beautiful-dnd";
import { Col } from "react-bootstrap";
import styled from "styled-components";

const DragItemStyled = styled.span`
  text-transform: uppercase;
  outline: none;
  border: 0;
  background-color: ${props => (props.isDragDisabled ? "#d8d8d8" : "#bec7bd")};
  line-height: 32px;
  color: ${props => (props.isDragDisabled ? "#a9a9a9" : "#000080")};
  display: inline-block;
  font-family: Karla, Verdana, sans-serif;
  font-size: 14px;
  padding-left: 15px;
  padding-right: 10px;
  cursor: ${props => (props.isDragDisabled ? "no-drop" : "grab")};
  border-radius: 5px;
  margin-bottom: 5px;
  width: 150px;
`;

const DraggableItem = ({ item, index, isDragDisabled }) => (
  <Draggable
    key={item.id}
    draggableId={JSON.stringify({
      nodeId: item.id,
      type: "DragItem"
    })}
    index={index}
    isDragDisabled={isDragDisabled}
  >
    {provided => (
      <div
        className="row"
        {...provided.draggableProps}
        {...provided.dragHandleProps}
        ref={provided.innerRef}
      >
        <Col md={4}>
          <DragItemStyled isDragDisabled={isDragDisabled}>
            {item.name}
          </DragItemStyled>
        </Col>
      </div>
    )}
  </Draggable>
);

export default DraggableItem;
Ad
source: stackoverflow.com
Ad