{"version":3,"sources":["../../../../src/sensors/usePointerSwipe/core.ts"],"sourcesContent":["import { batch, observable } from \"@legendapp/state\";\nimport {\n  type DeepMaybeObservable,\n  type ReadonlyObservable,\n  get,\n  createObserve,\n  onUnmount,\n} from \"@usels/core\";\nimport type { MaybeEventTarget } from \"../../types\";\nimport { createEventListener } from \"../../browser/useEventListener/core\";\n\nexport type UseSwipeDirection = \"up\" | \"down\" | \"left\" | \"right\" | \"none\";\nexport type PointerType = \"mouse\" | \"touch\" | \"pen\";\n\nexport interface UsePointerSwipeOptions {\n  /** Minimum distance in px to trigger swipe. Default: 50 */\n  threshold?: number;\n  /** Callback on swipe start */\n  onSwipeStart?: (e: PointerEvent) => void;\n  /** Callback on swipe move */\n  onSwipe?: (e: PointerEvent) => void;\n  /** Callback on swipe end */\n  onSwipeEnd?: (e: PointerEvent, direction: UseSwipeDirection) => void;\n  /** Pointer types to listen to. Default: ['mouse', 'touch', 'pen'] */\n  pointerTypes?: PointerType[];\n  /** Disable text selection during swipe. Default: false */\n  disableTextSelect?: boolean;\n}\n\nexport interface UsePointerSwipeReturn {\n  /** Whether swiping is in progress */\n  isSwiping$: ReadonlyObservable<boolean>;\n  /** Swipe direction */\n  direction$: ReadonlyObservable<UseSwipeDirection>;\n  /** Distance swiped on X axis */\n  distanceX$: ReadonlyObservable<number>;\n  /** Distance swiped on Y axis */\n  distanceY$: ReadonlyObservable<number>;\n  /** Start position */\n  posStart$: ReadonlyObservable<{ x: number; y: number }>;\n  /** End position */\n  posEnd$: ReadonlyObservable<{ x: number; y: number }>;\n  /** Stop listening */\n  stop: () => void;\n}\n\n/**\n * Framework-agnostic reactive pointer-swipe detector.\n *\n * Listens to `pointerdown` / `pointermove` / `pointerup` on the target and\n * tracks swipe direction, distance, and start/end positions. `options` fields\n * are read reactively via `opts$`, so threshold / pointerTypes / callbacks\n * may be swapped at runtime.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function createPointerSwipe(\n  target: MaybeEventTarget,\n  options?: DeepMaybeObservable<UsePointerSwipeOptions>\n): UsePointerSwipeReturn {\n  const opts$ = observable(options);\n\n  const isSwiping$ = observable(false);\n  const direction$ = observable<UseSwipeDirection>(\"none\");\n  const distanceX$ = observable(0);\n  const distanceY$ = observable(0);\n  const posStart$ = observable({ x: 0, y: 0 });\n  const posEnd$ = observable({ x: 0, y: 0 });\n\n  const state = {\n    isPointerDown: false,\n    isSwiping: false,\n  };\n\n  const eventIsAllowed = (e: PointerEvent): boolean => {\n    const types = opts$.peek()?.pointerTypes;\n    if (types) return types.includes(e.pointerType as PointerType);\n    return true;\n  };\n\n  const computeDirection = (dx: number, dy: number): UseSwipeDirection => {\n    const t = opts$.peek()?.threshold ?? 50;\n    if (Math.max(Math.abs(dx), Math.abs(dy)) < t) return \"none\";\n    if (Math.abs(dx) > Math.abs(dy)) {\n      return dx > 0 ? \"left\" : \"right\";\n    }\n    return dy > 0 ? \"up\" : \"down\";\n  };\n\n  const onPointerDown = (e: PointerEvent) => {\n    if (!eventIsAllowed(e)) return;\n    state.isPointerDown = true;\n    const eventTarget = e.target as HTMLElement | undefined;\n    if (eventTarget && typeof eventTarget.setPointerCapture === \"function\") {\n      try {\n        eventTarget.setPointerCapture(e.pointerId);\n      } catch {\n        // setPointerCapture may throw if the pointer is no longer active\n      }\n    }\n    batch(() => {\n      posStart$.set({ x: e.clientX, y: e.clientY });\n      posEnd$.set({ x: e.clientX, y: e.clientY });\n      distanceX$.set(0);\n      distanceY$.set(0);\n      direction$.set(\"none\");\n    });\n    opts$.peek()?.onSwipeStart?.(e);\n  };\n\n  const onPointerMove = (e: PointerEvent) => {\n    if (!eventIsAllowed(e) || !state.isPointerDown) return;\n    const start = posStart$.peek();\n    const dx = start.x - e.clientX;\n    const dy = start.y - e.clientY;\n    const dir = computeDirection(dx, dy);\n    const becameSwiping = !state.isSwiping && dir !== \"none\";\n    if (becameSwiping) state.isSwiping = true;\n    batch(() => {\n      posEnd$.set({ x: e.clientX, y: e.clientY });\n      distanceX$.set(dx);\n      distanceY$.set(dy);\n      direction$.set(dir);\n      if (becameSwiping) isSwiping$.set(true);\n    });\n    if (state.isSwiping) {\n      opts$.peek()?.onSwipe?.(e);\n    }\n  };\n\n  const onPointerUp = (e: PointerEvent) => {\n    if (!eventIsAllowed(e)) return;\n    if (state.isSwiping) {\n      opts$.peek()?.onSwipeEnd?.(e, direction$.peek());\n    }\n    state.isPointerDown = false;\n    state.isSwiping = false;\n    isSwiping$.set(false);\n  };\n\n  // Apply `touch-action: pan-y` and user-select toggling reactively while the\n  // target is mounted; restore on unmount or target change.\n  createObserve(() => {\n    const el = get(target) as HTMLElement | null;\n    if (!el) return;\n    const prevTouchAction = el.style.getPropertyValue(\"touch-action\");\n    el.style.setProperty(\"touch-action\", \"pan-y\");\n    const disableTextSelect = opts$.get()?.disableTextSelect;\n    if (disableTextSelect) {\n      el.style.setProperty(\"-webkit-user-select\", \"none\");\n      el.style.setProperty(\"user-select\", \"none\");\n    } else {\n      el.style.removeProperty(\"-webkit-user-select\");\n      el.style.removeProperty(\"user-select\");\n    }\n    return () => {\n      if (prevTouchAction) {\n        el.style.setProperty(\"touch-action\", prevTouchAction);\n      } else {\n        el.style.removeProperty(\"touch-action\");\n      }\n      el.style.removeProperty(\"-webkit-user-select\");\n      el.style.removeProperty(\"user-select\");\n    };\n  });\n\n  const stopDown = createEventListener(target, \"pointerdown\", onPointerDown, { passive: true });\n  const stopMove = createEventListener(target, \"pointermove\", onPointerMove, { passive: true });\n  const stopUp = createEventListener(target, \"pointerup\", onPointerUp, { passive: true });\n\n  const stop = () => {\n    stopDown();\n    stopMove();\n    stopUp();\n  };\n\n  onUnmount(stop);\n\n  return {\n    isSwiping$: isSwiping$ as ReadonlyObservable<boolean>,\n    direction$: direction$ as ReadonlyObservable<UseSwipeDirection>,\n    distanceX$: distanceX$ as ReadonlyObservable<number>,\n    distanceY$: distanceY$ as ReadonlyObservable<number>,\n    posStart$: posStart$ as ReadonlyObservable<{ x: number; y: number }>,\n    posEnd$: posEnd$ as ReadonlyObservable<{ x: number; y: number }>,\n    stop,\n  };\n}\n"],"mappings":"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","names":[]}