Created
June 5, 2019 10:49
-
-
Save DimitryDushkin/6fb3fd1b7d73de4b39ca6fbf77a2adf3 to your computer and use it in GitHub Desktop.
useDraggable for React based on RxJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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