I am working with 1.0.0-rc.
In drag-around-naive
:
const boxTarget = {
drop(props, monitor, component) {
const item = monitor.getItem();
const delta = monitor.getDifferenceFromInitialOffset();
const left = Math.round(item.left + delta.x);
const top = Math.round(item.top + delta.y);
component.moveBox(item.id, left, top);
}
};
At present it looks like I need to get the offset from the page of the original item, then I can find the offset from the page of where I dropped, then I need to find the offset of the drop container from the page, then do that calculation and I have the relative position in my drop target container.
This seems like a lot of work for something pretty common...
Seems like getClientOffset
returns me the position relative to the viewport. So then all I have to do is find my offset in my drop target. Do you think its still worth having a convenience method relative to drop target?
Yeah I guess we could have this for symmetry. For now, you can just use findDOMNode(component).getBoundingClientRect()
, but I wouldn't mind adding this if more people voice support. Let's keep this open and see what others say.
I would like something like this as well. I need to get the bounding rect/coordinates of the drop target, so when I hover something on it I can calculate if it's on the left, right, top or bottom side inside the drop target.
The relative coordinates can be useful for other use cases. For a Trello-like app, when you drag a card over a list, the list scrolls to bottom when hovering near bottom of drop area, and scrolls up when hovering near the top.
Trivial to implement on user space tho.
Also, I vaguely remember reading that accessing bounding rectangle triggers reflow in browsers and somewhat costly. Not sure if this is still the case.
I don't think there's a way to do this without actually accessing getBoundingClientRect
because you never know if the item moved.
For a Trello-like app, when you drag a card over a list, the list scrolls to bottom when hovering near bottom of drop area, and scrolls up when hovering near the top.
That's a great use case.
The perf penalty is indeed, significant, and will be wasted if it's added to React DnD but your app doesn't use it. Therefore, I don't want to add this to React DnD.
It is easy to implement in userland, and you will be able to do things like throttle getBoundingClientRect
calls, or put the related scrolling code into an animation frame.
I'm open to adding “scroll on hover at the bottom” _as an example_ to the sortable stress test. This would involve adding another drop target at the Container level that only listens to hover
and scrolls inside an animation frame.
However I won't add the DOM coordinate methods to React DnD for the reasons described above.
@gaearon I'm interested in generalized solution. How can we use react-dnd-html5-backend
instead of getBoundingClientRect
directly?
I solved this by passing the DropTargetMonitor
functions getInitialSourceClientOffset
and getSourceClientOffset
to a callback function inside my onDrop
event. This way, my callback function can calculate the correct coordinates related to the view port.
// some code before...
const [, dropRef] = useDrop({
accept: 'STICKY',
drop(item, monitor) {
move(
item.id,
monitor.getInitialSourceClientOffset(),
monitor.getSourceClientOffset()
);
},
});
// I'm updating my state with npm immer (immutable helper) throuthg the produce function...
const move = (id, initialPosition, finalPosition) => {
setList(
produce(list, draft => {
list.map(item => {
if (item.id === id) {
item.position = getCorrectDroppedOffsetValue(
initialPosition,
finalPosition
);
}
return item;
});
})
);
};
// and finally, my getCorrectDroppedOffsetValue function
const getCorrectDroppedOffsetValue = (initialPosition, finalPosition) => {
// get the container (view port) position by react ref...
const dropTargetPosition = ref.current.getBoundingClientRect();
const { y: finalY, x: finalX } = finalPosition;
const { y: initialY, x: initialX } = initialPosition;
// calculate the correct position removing the viewport position.
// finalY > initialY, I'm dragging down, otherwise, dragging up
const newYposition =
finalY > initialY
? initialY + (finalY - initialY) - dropTargetPosition.top
: initialY - (initialY - finalY) - dropTargetPosition.top;
const newXposition =
finalX > initialX
? initialX + (finalX - initialX) - dropTargetPosition.left
: initialX - (initialX - finalX) - dropTargetPosition.left;
return {
x: newXposition,
y: newYposition,
};
};
findDOMNode
does not work with functional component, and useDrop#drop
does not pass any component
in. Now if I want to get the ref
of my component I have to store it separately and manually call the drop connector to pass it. Is there any more concise way to do this?
function useCustomDrop(onDrop: (info: unknown, xy: Point) => void) {
const ref = useRef<Element>();
const [, dropTarget] = useDrop<any, void, unknown>({
accept: 'item-type',
drop(item, monitor) {
const offset = monitor.getSourceClientOffset();
if (offset && ref.current) {
const dropTargetXy = ref.current.getBoundingClientRect();
onDrop(item.data, {
x: offset.x - dropTargetXy.left,
y: offset.y - dropTargetXy.top,
});
}
}
});
return (elem) => {
ref.current = elem;
dropTarget(ref);
};
}
@quanganhtran do you have any complete code samples you'd be willing share, please?
@dendrochronology
A contrived example: https://codesandbox.io/s/elastic-liskov-00ldf
The API is nicer when we don't have to store the ref
ourselves (useGlobalDrop
vs useLocalDrop
). Now if only the ref
, which I assume react-dnd
already obtains, is attached ~to the monitor
object~ somewhere (accessible in drop
, I have no opinion where), it would resolves this.
this solution isn't reliable especially when you zoom in the browser or device resolution change. any idea?
i have the same question @sepehr09
Just passing by after a few google searches... Any update on the original question? (obtaining the x/y of a dropped item relative to the drop target)
I guess I'll join the list of those, who needs this. Any help please?
I think this feature very necessary, need to be add in library. Right now, Anyone worked with it, please help us !
Most helpful comment
Yeah I guess we could have this for symmetry. For now, you can just use
findDOMNode(component).getBoundingClientRect()
, but I wouldn't mind adding this if more people voice support. Let's keep this open and see what others say.