atlassian / react-beautiful-dnd

Beautiful and accessible drag and drop for lists with React
https://react-beautiful-dnd.netlify.app
Other
33.4k stars 2.57k forks source link

Conditional prevent drag before drag start #1722

Open chuckWu1989 opened 4 years ago

chuckWu1989 commented 4 years ago

Hi forks, thanks for wonderful dnd lib first and Appreciate contributors.

I have a feature requirement. My scenario is: I need to judge if the element could be dragged before I started to drag (I used the virtual mode and react-virtualized list). I had tried to use the isDragDisabled props to implement this feature but I found it's hard to achieve. Here is a pseudo code to explain what I want:

async function beforeDrag(setIsdraggable) {
  if (...) {
    return setIsdraggable(true);
  }
 return setIsdraggable(false);
}

const App = (props) => {
  const [isDragDisabled, setIsdraggable] = React.useState(true);
  return (
    <DragDropContext beforeDrag={() => beforeDrag(setIsdraggable)}>
      <Droppable mode="virtual">
       {(...) => (
         <List
           rowRenderer={(renderProps) => (
             <DraggableRow 
                isDragDisabled={isDragDisabled} 
                {...renderProps} 
              />}
         />
       )}
      </Droppable>
    </DragDropContext>

const DraggableRow = (props) => {
  return (
    <Draggable isDragDisabled={props.isDragDisabled}>
      {...}
    </Draggable>
  );
};

I think the reason is because that the rbd listened the mouse-down event to start whole process.

The earliest entry I can control the isDragDisabled props is mouse-down and I need to support asynchronous judgement =(. When I set isDragDisabled as true into Draggable is too late in this stage because the dragging process is started by startPendingDrag method.

chuckWu1989 commented 4 years ago

I have an idea to support this feature. We can add a function (named shouldDragStart or somewhat) into DragDropContext. This lifecycle method would be called when mouse-down event was triggered. It could be an asynchronous or synchronous function and it would return a boolean value. If the result is true, the startPendingDrag method would be called, or the cancel method would be called. Here is a pseudo code to show how to use the shouldDragStart props:

function shouldDragStart(before) {
  const { draggableId } = before;
  if (...) return true;
  return false;
}

<DragDropContext shouldDragStart={shouldDragStart}>
  {...}
</DragDropContext>

Here is a quick brain storm, it might have something insufficient. Please help me to review this idea. Furthermore, I can contribute this function if you think it's a good idea, thanks.

chuckWu1989 commented 4 years ago

I created a PR, there is an example. https://github.com/atlassian/react-beautiful-dnd/pull/1726

chuckWu1989 commented 4 years ago

Hi, forks. I had a few updating about my ideas. First, I renamed the function as shouldStartCapture because I want to follow the lifecycle order and coding style in rbd. I think the name of shouldStartCapture is suitable. It means that it would run the judgement before beforeCapture lifecycle. Another updating is significant, I gave up to support asynchronous judgement =(. there are two reasons I convinced myself:

  1. The asynchronous judgement is dangerous in this stage. If the promise took a trip too long time. It would cause many uncontrolled conditions.
  2. It's would be a major updating... The whole behavior of sensors would be changed because of asynchronous. It should be discussed more carefully before we decided to support this feature.

I still believed that asynchronous judgement is possible and had its scenario, however, it should be discussed more. At this time, I work around to solve the asynchronous condition in my business scenario.

The whole updating ideas already committed to PR #1726. Review required =(.

clairefro commented 4 years ago

The whole updating ideas already committed to PR #1726. Review required =(.

Where can we find documentation on how to use shouldStartCapture? It appears it is available on DragDropContext as a prop but i can't figure out how to use it to exempt certain 'draggables' from being draggable (i.e a child modal)

chuckWu1989 commented 4 years ago

Hi, I re-created the PR in #1764 and it required for review in a long while. In shouldStartCapture method, the draggableId was given as a input param so that you can get the certain draggables by draggableId. This method required a boolean value to check if the dragging action should be started. The typing definition is:

shouldStartCapture?: (draggableId: string) => boolean
kambleaa007 commented 2 years ago

Not working as expected and shown in example, stories/22-disabled-before-drag-start.stories.js here is codesandbox of non working code, i dont want to drag 0th element, very first element, still getting dragged

https://codesandbox.io/s/using-react-beautiful-dnd-with-hooks-forked-69txec?file=/src/index.js

finally solved using this,

<Draggable
      draggableId={quote.id}
      index={index}
      isDragDisabled={index == 0 ? true : false}
    >