import { fromEvent , Observable } from 'rxjs' ; import { filter , map , switchMap } from 'rxjs/operators' ; import { RotationNumber } from '../types' ; import { rotationOfElement } from '../utils/dom' ; import { angleBetweenPoints , round } from '../utils/misc' ; import { documentMouseMove$ , documentMouseUp$ , requestAnimationFramesUntil , } from './misc' ; interface RotateObservableOptions { element : HTMLElement ; handle : HTMLElement ; onComplete : ( ) => void ; } param element param handle export const createRotateObservable = ( { element , handle , onComplete , } ) : Observable < RotationNumber > => { const mouseDown$ = fromEvent < MouseEvent > ( handle , 'mousedown' ) ; return mouseDown$ . pipe ( filter ( ( e ) => e . which === 1 ) , switchMap ( ( e ) => { e . preventDefault ( ) ; e . stopPropagation ( ) ; const position = element . getBoundingClientRect ( ) ; const axisX = ( element . offsetWidth + position . left * 2 ) / 2 ; const axisY = ( element . offsetHeight + position . top * 2 ) / 2 ; const angleFromAxis = angleBetweenPoints ( axisX , axisY ) ; const currentRotation = rotationOfElement ( element ) ; const initialAngle = angleFromAxis ( e . clientX , e . clientY ) - currentRotation ; const rotate$ = documentMouseMove$ . pipe ( map ( translateRotation ( angleFromAxis , initialAngle ) ) ) ; return requestAnimationFramesUntil ( rotate$ , documentMouseUp$ , onComplete ) ; } ) ) ; } ; /**
 * A function that calculates the angle from a non-exposed point
 * to the point provided as parameters when calling the function.
 */ type AngleFromPointCalculator = ( x : number , y : number ) => number ; /**
 * Calculate the final rotation based on the mouse's angle from
 * the center axis and original rotation of the html element.
 */ const translateRotation = ( angleCalculator , initialAngle ) => ( e ) : RotationNumber => { const interval = e . shiftKey ? 15 : 0.1 ; const angle = round ( angleCalculator ( e . clientX , e . clientY ) - initialAngle , interval ) ; return { rotation : `${ angle }deg` , } ; } ;