Skip to content

Instantly share code, notes, and snippets.

@DimitryDushkin
Created June 5, 2019 10:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DimitryDushkin/6fb3fd1b7d73de4b39ca6fbf77a2adf3 to your computer and use it in GitHub Desktop.
Save DimitryDushkin/6fb3fd1b7d73de4b39ca6fbf77a2adf3 to your computer and use it in GitHub Desktop.
useDraggable for React based on RxJS
import React, { useRef, useLayoutEffect } from "react";
import { fromEvent, Observable } from "rxjs";
import { takeUntil, mergeMap, map } from "rxjs/operators";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
<h1>useDraggable based on RxJS</h1>
<DraggableComponent />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
type DragEvent = { x: number; y: number };
function createDragObservable<T extends PointerEvent>(
up$: Observable<T>,
down$: Observable<T>,
move$: Observable<T>
): Observable<DragEvent> {
let startPosition: DragEvent;
return down$.pipe(
mergeMap(e => {
startPosition = startPosition || { x: e.pageX, y: e.pageY };
return move$.pipe(
takeUntil(up$),
map(e => ({
x: e.pageX - startPosition.x,
y: e.pageY - startPosition.y
}))
);
})
);
}
function useDraggable(draggableRef: React.RefObject<HTMLElement>) {
const drag$ = useRef<Observable<DragEvent>>();
useLayoutEffect(() => {
if (!draggableRef.current) {
return () => {};
}
const down$ = fromEvent<PointerEvent>(draggableRef.current, "pointerdown");
const move$ = fromEvent<PointerEvent>(document, "pointermove");
const up$ = fromEvent<PointerEvent>(document, "pointerup");
drag$.current = createDragObservable(up$, down$, move$);
}, [draggableRef]);
return drag$;
}
function DraggableComponent() {
const draggableDivRef = useRef<HTMLDivElement>();
const drag$ = useDraggable(draggableDivRef);
useLayoutEffect(() => {
if (!drag$.current) {
return () => {};
}
const dragSubscription = drag$.current.subscribe(e => {
if (!draggableDivRef.current) {
return;
}
draggableDivRef.current.style.transform = `translateY(${e.y}px)`;
});
return () => {
dragSubscription.unsubscribe();
};
}, [drag$]);
return (
<div
ref={draggableDivRef}
style={{ userSelect: "none", padding: "8px", backgroundColor: "#eee" }}
>
drag me
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment