9

I have been working on react-dnd (which is drag and drop component). So, far drop target get identified based on mouse pointer, I am wondering is there any option to change it like, which the drop target needs to get identified based on the drag object intersects over 50% of drop target.

which is similar to jQuery UI drag and drop feature which contains 'tolerance: intersect' in droppable elements.

veerasuthan V
  • 330
  • 5
  • 17

2 Answers2

5

Check out the sortable example of React-DnD, specifically the hover function within cardTarget:

const cardTarget = {
  hover(props, monitor, component) {
    const dragIndex = monitor.getItem().index;
    const hoverIndex = props.index;

    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return;
    }

    // Determine rectangle on screen
    const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;

    // Determine mouse position
    const clientOffset = monitor.getClientOffset();

    // Get pixels to the top
    const hoverClientY = clientOffset.y - hoverBoundingRect.top;

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return;
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return;
    }

    // Time to actually perform the action
    props.moveCard(dragIndex, hoverIndex);

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    monitor.getItem().index = hoverIndex;
  }
};

These two lines I think is what you are looking for:

// Dragging downwards
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
  return;
}

// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
  return;
}

It checks when hovering, if the item you are hovering has crossed the 50% threshold for moving the item, and then it will perform the reorder action.

Wolfie
  • 1,369
  • 8
  • 16
0

You can do something like this.

useEffect(
    () =>
        monitor.subscribeToOffsetChange(() => {
            if (!ref.current) {
                return;
            }

            const offset = monitor.getClientOffset();
            if (offset === null) {
                if (
                    !monitor.isDragging() &&
                    backgroundColor !== "#fff" &&
                    item.current &&
                    item.current?.position == -1
                ) {
                    if (
                        blank.answer.filter((a) => a.value == item.value)
                            .length > 0
                    ) {
                        return;
                    }
                    onDrop(blankIndex, item.current);
                    setBackgroundColor("#fff");
                } else {
                    setBackgroundColor("#fff");
                }
                return;
            }
            const left =
                offset.x - ref.current?.getBoundingClientRect().width;
            const right =
                offset.x + ref.current?.getBoundingClientRect().width;
            const top =
                offset.y - ref.current?.getBoundingClientRect().height;
            const bottom =
                offset.y + ref.current?.getBoundingClientRect().height;
            const dragRect = {
                left,
                right,
                top,
                bottom,
            };
            const targetRect = ref.current?.getBoundingClientRect();
            // Calculate the intersection area
            const intersectionLeft = Math.max(
                dragRect.left,
                targetRect.left,
            );
            const intersectionTop = Math.max(dragRect.top, targetRect.top);
            const intersectionRight = Math.min(
                dragRect.right,
                targetRect.right,
            );
            const intersectionBottom = Math.min(
                dragRect.bottom,
                targetRect.bottom,
            );

            const intersectionWidth = Math.max(
                0,
                intersectionRight - intersectionLeft,
            );
            const intersectionHeight = Math.max(
                0,
                intersectionBottom - intersectionTop,
            );
            const intersectionArea = intersectionWidth * intersectionHeight;

            // Calculate the total area of the drop target
            const targetWidth = targetRect.right - targetRect.left;
            const targetHeight = targetRect.bottom - targetRect.top;
            const targetArea = targetWidth * targetHeight;

            // Determine the percentage of intersection
            const intersectionPercentage =
                (intersectionArea / targetArea) * 100;
            // Allow the drop only if the intersection percentage is over 50%
            if (intersectionPercentage > 20) {
                setItem(monitor.getItem());
                setBackgroundColor("#FFDE49");
                // Allow the drop
            } else {
                setBackgroundColor("#fff");
                // Do not allow the drop
            }
        }),
    [monitor],
);
ISHAN AJMERI
  • 23
  • 2
  • 6