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

update element position automatically when bounds is updated

Open o02112 opened this issue 7 years ago • 7 comments
trafficstars

when bounds changed

For example "bounds" area becomes smaller. Update draggable element position automatically, in case of element getting out of the visible area. Or just trigger the onDrag event once.

o02112 avatar Sep 14 '18 10:09 o02112

@o02112 I have the same problem. Did you find solution for this bug?

JSerhii avatar Nov 22 '18 15:11 JSerhii

@o02112 I have the same problem. Did you find solution for this bug?

Not yet, I just let the element be there where it is, even it out of the visible area.

o02112 avatar Nov 28 '18 10:11 o02112

I've solved this problem by essentially faking a mouse down event. You'll have to decide for yourself when to call this fakeDrag function obviously, but I've included a window resize example. Tested on Chrome 72 and version 3.2.1 of react-draggable.

class SomeReactComponent extends React.Component<Props> {

    private childRef: React.RefObject<HTMLDivElement>;

    constructor(props) {
        super(props);
        this.childRef = React.createRef();
    }

    componentDidMount() {
        window.addEventListener('resize', this.onWindowResize);
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.onWindowResize);
    }

    render() {
        const props = this.props;

        return (
            <Draggable
                axis={"x"}
                bounds={"parent"}
            >
                <div
                    ref={this.childRef}
                >
                    {props.children}
                </div>
            </Draggable>
        )
    }

    private onWindowResize = () => {
        this.fakeDrag();
    };

    private fakeDrag = () => {
        const clickEvent = document.createEvent ('MouseEvents');
        clickEvent.initEvent ("mousedown", true, true);
        this.childRef.current.dispatchEvent(clickEvent);
    }
}

EitanElbaz avatar Mar 19 '19 14:03 EitanElbaz

I wasn't able to get @EitanElbaz's solution to work for me but I made some modifications and now it is working. The key was triggering a mousemove on the document:


  onWindowResize = () => {
    //trigger a fake drag event here so that the dialog will stay in the window on window resize
    const targetNode = document.querySelector(`.${Classes.DIALOG_HEADER}`);
    if (targetNode) {
      //--- Simulate a natural mouse-click sequence.
      triggerMouseEvent(targetNode, "mouseover");
      triggerMouseEvent(targetNode, "mousedown");
      triggerMouseEvent(document, "mousemove");
      triggerMouseEvent(targetNode, "mouseup");
      triggerMouseEvent(targetNode, "click");
    } else console.log("*** Target node not found!");

    function triggerMouseEvent(node, eventType) {
      const clickEvent = document.createEvent("MouseEvents");
      clickEvent.initEvent(eventType, true, true);
      node.dispatchEvent(clickEvent);
    }
  };

tnrich avatar May 20 '20 21:05 tnrich

Is there any progress?

shenenya avatar Feb 03 '21 03:02 shenenya

Here's a functional component that wraps react-draggable and applies the (second) fix above. You should be able to use it as a drop in replacement for react-draggable.

import { useRef, useEffect } from "react";
import ReactDraggable from "react-draggable";

const Draggable = ({ children, ...props }) => {
  const ref = useRef();

  useEffect(() => {
    const listener = () => {
      triggerMouseEvent(ref.current, "mouseover");
      triggerMouseEvent(ref.current, "mousedown");
      triggerMouseEvent(document, "mousemove");
      triggerMouseEvent(ref.current, "mouseup");
      triggerMouseEvent(ref.current, "click");
    };

    addEventListener("resize", listener);
    return () => removeEventListener("resize", listener);
  }, []);

  const triggerMouseEvent = (element, eventType) => {
    const mouseEvent = document.createEvent("MouseEvents");

    mouseEvent.initEvent(eventType, true, true);
    element.dispatchEvent(mouseEvent);
  };

  return (
    <ReactDraggable {...props}>
      <div style={{ display: "inline-block" }} ref={ref}>
        {children}
      </div>
    </ReactDraggable>
  )
};

export default Draggable;

For some reason, I also needed display: "inline-block" but you might be able to remove that. I hope this helps someone.

tuzz avatar Oct 20 '21 14:10 tuzz

I slightly modified @tuzz's solution to move away from deprecated Event.initEvent:

// ...
const triggerMouseEvent = (element, eventType) => {
    const mouseEvent = new Event(eventType, {
      bubbles: true,
      cancelable: true,
    });
    element.dispatchEvent(mouseEvent);
 };
// ...

lytovka avatar Oct 15 '22 02:10 lytovka