react-draggable icon indicating copy to clipboard operation
react-draggable copied to clipboard

Does not respect bounds on new props

Open travisoneill opened this issue 6 years ago • 3 comments

Bounds seem to be calculated only on drag which presents a couple of issues:

  1. If bounds are changed and passed down from a parent component they will not be respected until the element is dragged

  2. Scaling operations on children can cause them to be outside of draggable element bounds and will not be recoverable since not able to click on the now invisible element

travisoneill avatar Jun 20 '18 21:06 travisoneill

I think you are expected to change position manually if bounds are changing over time, here's example:

  const right = document.body.clientWidth / 2;
  const [position, setPosition] = React.useState({ x: 0, y: 0 });
  React.useEffect(() => {
    const listen = () => {
      const right = document.body.clientWidth / 2; // notice shadowed variable
      if (position.x > right) {
        setPosition({ x: right, y: position.y });
      }
    };
    window.addEventListener('resize', listen);
    return () => window.removeEventListener('resize', listen);
  });
  return (
      <ReactDraggable
        position={position}
        onDrag={(e, { x, y }) => {
          setPosition({ x, y });
        }}
        bounds={{ left: 0, right }}
      >
          Stuff to drag
      </ReactDraggable>

so when bounds change and your new x doesn't fit inside right you have to manually change position.x and it will cause the component to move

JLarky avatar Aug 06 '20 19:08 JLarky

That doesn't feel like a great solution to me. It forces you to roundtrip through Redux state during a drag. I'm currently needing to do an elaborate workaround, something like:

  const windowSize = useWindowSize()
  const actual = useRef({ x: 0, y: 0 })
  const [position, setPosition] = useState(defaultPosition)

  const bounds = useMemo(() => {
    return constraintsBasedOnWindowSize(windowSize)
  }, [windowSize, isExpanded])

  const setBoundedPosition = useCallback(position => {
    const constrained = constrain(position, bounds)
    actual.current = constrained
    if (!areEqual(position, constrained)) {
      setPosition(constrained)
    }
  }, [bounds, position])

  return (
    <Draggable
      onDrag={(_, position) => { setBoundedPosition(position) }}
      onStop={() => { setPosition(actual.current) }}
      position={position}
    >
    ...
    </Draggable>
  )

which is very much a workaround for behaviour I would hope the library to take care of.

alecmce avatar Apr 03 '21 11:04 alecmce

Related issue: https://github.com/react-grid-layout/react-draggable/issues/363

There's an interesting workaround suggested there: https://github.com/react-grid-layout/react-draggable/issues/363#issuecomment-631750224. Simulates a mouse down event to trigger the internal calculations related to bounds.

klimeryk avatar Nov 15 '21 12:11 klimeryk