{"version":3,"sources":["../../../../src/sensors/useSwipe/core.ts"],"sourcesContent":["import { batch, observable } from \"@legendapp/state\";\nimport { type DeepMaybeObservable, type ReadonlyObservable, onUnmount } from \"@usels/core\";\nimport type { MaybeEventTarget } from \"../../types\";\nimport { createEventListener } from \"../../browser/useEventListener/core\";\nimport type { UseSwipeDirection } from \"../usePointerSwipe/core\";\n\nexport type { UseSwipeDirection };\n\nexport interface UseSwipeOptions {\n  /** Minimum distance in px to trigger swipe. Default: 50 */\n  threshold?: number;\n  /** Callback on swipe start */\n  onSwipeStart?: (e: TouchEvent) => void;\n  /** Callback on swipe move */\n  onSwipe?: (e: TouchEvent) => void;\n  /** Callback on swipe end */\n  onSwipeEnd?: (e: TouchEvent, direction: UseSwipeDirection) => void;\n  /** Use passive event listeners. Default: true */\n  passive?: boolean;\n}\n\nexport interface UseSwipeReturn {\n  /** Whether swiping is in progress */\n  isSwiping$: ReadonlyObservable<boolean>;\n  /** Swipe direction */\n  direction$: ReadonlyObservable<UseSwipeDirection>;\n  /** Horizontal swipe length (startX - currentX) */\n  lengthX$: ReadonlyObservable<number>;\n  /** Vertical swipe length (startY - currentY) */\n  lengthY$: ReadonlyObservable<number>;\n  /** Touch start coordinates */\n  coordsStart$: ReadonlyObservable<{ x: number; y: number }>;\n  /** Current/end touch coordinates */\n  coordsEnd$: ReadonlyObservable<{ x: number; y: number }>;\n  /** Stop listening */\n  stop: () => void;\n}\n\n/**\n * Framework-agnostic reactive swipe detector based on TouchEvents.\n *\n * Listens to `touchstart` / `touchmove` / `touchend` on the target and tracks\n * swipe direction, length, and start/end coordinates. `threshold` and callback\n * fields are read reactively via `opts$`, so they may be swapped at runtime.\n * `passive` is consumed once at mount — AddEventListenerOptions cannot change\n * after the listener is registered.\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function createSwipe(\n  target: MaybeEventTarget,\n  options?: DeepMaybeObservable<UseSwipeOptions>\n): UseSwipeReturn {\n  const opts$ = observable(options);\n\n  // passive is mount-time only (AddEventListenerOptions is fixed at attach time)\n  const passive = opts$.peek()?.passive ?? true;\n\n  const isSwiping$ = observable(false);\n  const direction$ = observable<UseSwipeDirection>(\"none\");\n  const lengthX$ = observable(0);\n  const lengthY$ = observable(0);\n  const coordsStart$ = observable({ x: 0, y: 0 });\n  const coordsEnd$ = observable({ x: 0, y: 0 });\n\n  const state = {\n    isTracking: false,\n    isSwiping: false,\n    startX: 0,\n    startY: 0,\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 onTouchStart = (e: TouchEvent) => {\n    const touch = e.touches[0];\n    if (!touch) return;\n    state.isTracking = true;\n    state.startX = touch.clientX;\n    state.startY = touch.clientY;\n    batch(() => {\n      coordsStart$.set({ x: touch.clientX, y: touch.clientY });\n      coordsEnd$.set({ x: touch.clientX, y: touch.clientY });\n      lengthX$.set(0);\n      lengthY$.set(0);\n      direction$.set(\"none\");\n    });\n    opts$.peek()?.onSwipeStart?.(e);\n  };\n\n  const onTouchMove = (e: TouchEvent) => {\n    if (!state.isTracking) return;\n    const touch = e.touches[0];\n    if (!touch) return;\n    const dx = state.startX - touch.clientX;\n    const dy = state.startY - touch.clientY;\n    const dir = computeDirection(dx, dy);\n    const becameSwiping = !state.isSwiping && dir !== \"none\";\n    if (becameSwiping) state.isSwiping = true;\n    batch(() => {\n      coordsEnd$.set({ x: touch.clientX, y: touch.clientY });\n      lengthX$.set(dx);\n      lengthY$.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 onTouchEnd = (e: TouchEvent) => {\n    if (state.isSwiping) {\n      opts$.peek()?.onSwipeEnd?.(e, direction$.peek());\n    }\n    state.isTracking = false;\n    state.isSwiping = false;\n    isSwiping$.set(false);\n  };\n\n  const listenerOpts: AddEventListenerOptions | undefined = passive ? { passive: true } : undefined;\n  const stopStart = createEventListener(target, \"touchstart\", onTouchStart, listenerOpts);\n  const stopMove = createEventListener(target, \"touchmove\", onTouchMove, listenerOpts);\n  const stopEnd = createEventListener(target, \"touchend\", onTouchEnd, listenerOpts);\n\n  const stop = () => {\n    stopStart();\n    stopMove();\n    stopEnd();\n  };\n\n  onUnmount(stop);\n\n  return {\n    isSwiping$: isSwiping$ as ReadonlyObservable<boolean>,\n    direction$: direction$ as ReadonlyObservable<UseSwipeDirection>,\n    lengthX$: lengthX$ as ReadonlyObservable<number>,\n    lengthY$: lengthY$ as ReadonlyObservable<number>,\n    coordsStart$: coordsStart$ as ReadonlyObservable<{ x: number; y: number }>,\n    coordsEnd$: coordsEnd$ as ReadonlyObservable<{ x: number; y: number }>,\n    stop,\n  };\n}\n"],"mappings":"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","names":[]}