{"version":3,"file":"use-move.mjs","names":[],"sources":["../../src/use-move/use-move.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { clamp } from '../utils';\n\nexport interface UseMovePosition {\n  x: number;\n  y: number;\n}\n\nexport function clampUseMovePosition(position: UseMovePosition) {\n  return {\n    x: clamp(position.x, 0, 1),\n    y: clamp(position.y, 0, 1),\n  };\n}\n\nexport interface UseMoveHandlers {\n  onScrubStart?: () => void;\n  onScrubEnd?: () => void;\n}\n\nexport interface UseMoveReturnValue<T extends HTMLElement = any> {\n  ref: React.RefCallback<T | null>;\n  active: boolean;\n}\n\nexport function useMove<T extends HTMLElement = any>(\n  onChange: (value: UseMovePosition) => void,\n  handlers?: UseMoveHandlers,\n  dir: 'ltr' | 'rtl' = 'ltr'\n): UseMoveReturnValue<T> {\n  const mounted = useRef<boolean>(false);\n  const isSliding = useRef(false);\n  const frame = useRef(0);\n  const [active, setActive] = useState(false);\n\n  useEffect(() => {\n    mounted.current = true;\n  }, []);\n\n  const refCallback: React.RefCallback<T | null> = useCallback(\n    (node) => {\n      const onScrub = ({ x, y }: UseMovePosition) => {\n        cancelAnimationFrame(frame.current);\n\n        frame.current = requestAnimationFrame(() => {\n          if (mounted.current && node) {\n            node.style.userSelect = 'none';\n            const rect = node.getBoundingClientRect();\n\n            if (rect.width && rect.height) {\n              const _x = clamp((x - rect.left) / rect.width, 0, 1);\n              onChange({\n                x: dir === 'ltr' ? _x : 1 - _x,\n                y: clamp((y - rect.top) / rect.height, 0, 1),\n              });\n            }\n          }\n        });\n      };\n\n      const bindEvents = () => {\n        document.addEventListener('mousemove', onMouseMove);\n        document.addEventListener('mouseup', stopScrubbing);\n        document.addEventListener('touchmove', onTouchMove, { passive: false });\n        document.addEventListener('touchend', stopScrubbing);\n      };\n\n      const unbindEvents = () => {\n        document.removeEventListener('mousemove', onMouseMove);\n        document.removeEventListener('mouseup', stopScrubbing);\n        document.removeEventListener('touchmove', onTouchMove);\n        document.removeEventListener('touchend', stopScrubbing);\n      };\n\n      const startScrubbing = () => {\n        if (!isSliding.current && mounted.current) {\n          isSliding.current = true;\n          typeof handlers?.onScrubStart === 'function' && handlers.onScrubStart();\n          setActive(true);\n          bindEvents();\n        }\n      };\n\n      const stopScrubbing = () => {\n        if (isSliding.current && mounted.current) {\n          isSliding.current = false;\n          setActive(false);\n          unbindEvents();\n          setTimeout(() => {\n            typeof handlers?.onScrubEnd === 'function' && handlers.onScrubEnd();\n          }, 0);\n        }\n      };\n\n      const onMouseDown = (event: MouseEvent) => {\n        startScrubbing();\n        event.preventDefault();\n        onMouseMove(event);\n      };\n\n      const onMouseMove = (event: MouseEvent) => onScrub({ x: event.clientX, y: event.clientY });\n\n      const onTouchStart = (event: TouchEvent) => {\n        if (event.cancelable) {\n          event.preventDefault();\n        }\n\n        startScrubbing();\n        onTouchMove(event);\n      };\n\n      const onTouchMove = (event: TouchEvent) => {\n        if (event.cancelable) {\n          event.preventDefault();\n        }\n\n        onScrub({ x: event.changedTouches[0].clientX, y: event.changedTouches[0].clientY });\n      };\n\n      node?.addEventListener('mousedown', onMouseDown);\n      node?.addEventListener('touchstart', onTouchStart, { passive: false });\n\n      return () => {\n        if (node) {\n          node.removeEventListener('mousedown', onMouseDown);\n          node.removeEventListener('touchstart', onTouchStart);\n        }\n      };\n    },\n    [dir, onChange]\n  );\n\n  return { ref: refCallback, active };\n}\n\nexport namespace useMove {\n  export type Handlers = UseMoveHandlers;\n  export type ReturnValue<T extends HTMLElement> = UseMoveReturnValue<T>;\n}\n"],"mappings":";;;;AAQA,SAAgB,qBAAqB,UAA2B;AAC9D,QAAO;EACL,GAAG,MAAM,SAAS,GAAG,GAAG,EAAE;EAC1B,GAAG,MAAM,SAAS,GAAG,GAAG,EAAE;EAC3B;;AAaH,SAAgB,QACd,UACA,UACA,MAAqB,OACE;CACvB,MAAM,UAAU,OAAgB,MAAM;CACtC,MAAM,YAAY,OAAO,MAAM;CAC/B,MAAM,QAAQ,OAAO,EAAE;CACvB,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;AAE3C,iBAAgB;AACd,UAAQ,UAAU;IACjB,EAAE,CAAC;AA+FN,QAAO;EAAE,KA7FwC,aAC9C,SAAS;GACR,MAAM,WAAW,EAAE,GAAG,QAAyB;AAC7C,yBAAqB,MAAM,QAAQ;AAEnC,UAAM,UAAU,4BAA4B;AAC1C,SAAI,QAAQ,WAAW,MAAM;AAC3B,WAAK,MAAM,aAAa;MACxB,MAAM,OAAO,KAAK,uBAAuB;AAEzC,UAAI,KAAK,SAAS,KAAK,QAAQ;OAC7B,MAAM,KAAK,OAAO,IAAI,KAAK,QAAQ,KAAK,OAAO,GAAG,EAAE;AACpD,gBAAS;QACP,GAAG,QAAQ,QAAQ,KAAK,IAAI;QAC5B,GAAG,OAAO,IAAI,KAAK,OAAO,KAAK,QAAQ,GAAG,EAAE;QAC7C,CAAC;;;MAGN;;GAGJ,MAAM,mBAAmB;AACvB,aAAS,iBAAiB,aAAa,YAAY;AACnD,aAAS,iBAAiB,WAAW,cAAc;AACnD,aAAS,iBAAiB,aAAa,aAAa,EAAE,SAAS,OAAO,CAAC;AACvE,aAAS,iBAAiB,YAAY,cAAc;;GAGtD,MAAM,qBAAqB;AACzB,aAAS,oBAAoB,aAAa,YAAY;AACtD,aAAS,oBAAoB,WAAW,cAAc;AACtD,aAAS,oBAAoB,aAAa,YAAY;AACtD,aAAS,oBAAoB,YAAY,cAAc;;GAGzD,MAAM,uBAAuB;AAC3B,QAAI,CAAC,UAAU,WAAW,QAAQ,SAAS;AACzC,eAAU,UAAU;AACpB,YAAO,UAAU,iBAAiB,cAAc,SAAS,cAAc;AACvE,eAAU,KAAK;AACf,iBAAY;;;GAIhB,MAAM,sBAAsB;AAC1B,QAAI,UAAU,WAAW,QAAQ,SAAS;AACxC,eAAU,UAAU;AACpB,eAAU,MAAM;AAChB,mBAAc;AACd,sBAAiB;AACf,aAAO,UAAU,eAAe,cAAc,SAAS,YAAY;QAClE,EAAE;;;GAIT,MAAM,eAAe,UAAsB;AACzC,oBAAgB;AAChB,UAAM,gBAAgB;AACtB,gBAAY,MAAM;;GAGpB,MAAM,eAAe,UAAsB,QAAQ;IAAE,GAAG,MAAM;IAAS,GAAG,MAAM;IAAS,CAAC;GAE1F,MAAM,gBAAgB,UAAsB;AAC1C,QAAI,MAAM,WACR,OAAM,gBAAgB;AAGxB,oBAAgB;AAChB,gBAAY,MAAM;;GAGpB,MAAM,eAAe,UAAsB;AACzC,QAAI,MAAM,WACR,OAAM,gBAAgB;AAGxB,YAAQ;KAAE,GAAG,MAAM,eAAe,GAAG;KAAS,GAAG,MAAM,eAAe,GAAG;KAAS,CAAC;;AAGrF,SAAM,iBAAiB,aAAa,YAAY;AAChD,SAAM,iBAAiB,cAAc,cAAc,EAAE,SAAS,OAAO,CAAC;AAEtE,gBAAa;AACX,QAAI,MAAM;AACR,UAAK,oBAAoB,aAAa,YAAY;AAClD,UAAK,oBAAoB,cAAc,aAAa;;;KAI1D,CAAC,KAAK,SAAS,CAChB;EAE0B;EAAQ"}