{
  "mappings": "AACA,cAAc,oBAAoB;AAkDlC,OAAO,iBAAS,+BACd,sBACA;AAwEF,iBAAiB,OAAO;CACtB;CACA;CACA;CACA;;AAGF,YAAY,qBAAqB;CAC/B;CACA,aAAa;CACb,cAAc;CACd,WAAW;CACX,eAAe;CACf;CACA,mBAAmB;;AAGrB,iBAAiB,uBAAuB;UAC7B;UACA,OAAO;CAChB,IAAI,SAAS,QAAQ;CACrB;CACA,mBAAmB,QAAQ;;AAG7B,YAAY;AAgBZ,OAAO,iBAAS,4BACd,4BACC;AAeH,OAAO,iBAAS,8BACd,OAAO,gDACP;AAQF,OAAO,iBAAS,qBAAqB",
  "names": [],
  "sources": [
    "src/gestureState.ts"
  ],
  "version": 3,
  "sourcesContent": [
    "import { createGlobalState } from './globalState'\nimport type { GestureState } from './types'\n\nconst state = createGlobalState<GestureState>(`gesture`, {\n  enabled: false,\n  Gesture: null,\n  GestureDetector: null,\n  ScrollView: null,\n  RootView: null,\n})\n\ntype GestureEnabledFreezeState = {\n  frozen: boolean\n  enabled: boolean\n  warned: boolean\n}\n\nconst GESTURE_ENABLED_FREEZE_KEY = '__tamagui_gesture_enabled_freeze__'\n\nfunction getGestureEnabledFreezeState(): GestureEnabledFreezeState {\n  const g = globalThis as typeof globalThis & {\n    [GESTURE_ENABLED_FREEZE_KEY]?: GestureEnabledFreezeState\n  }\n\n  if (!g[GESTURE_ENABLED_FREEZE_KEY]) {\n    g[GESTURE_ENABLED_FREEZE_KEY] = {\n      frozen: false,\n      enabled: false,\n      warned: false,\n    }\n  }\n\n  return g[GESTURE_ENABLED_FREEZE_KEY]!\n}\n\nfunction warnGestureEnabledMutationIgnored(source: string) {\n  const freezeState = getGestureEnabledFreezeState()\n\n  if (freezeState.warned || process.env.NODE_ENV === 'production') {\n    return\n  }\n\n  freezeState.warned = true\n\n  console.warn(\n    `[Tamagui] Ignored ${source} because gesture handler press events were already ` +\n      `${freezeState.enabled ? 'enabled' : 'disabled'} when TamaguiProvider mounted. ` +\n      `Configure gesture handler mode before the first render.`\n  )\n}\n\nexport function canChangeGestureHandlerEnabled(\n  nextEnabled: boolean,\n  source: string\n): boolean {\n  const freezeState = getGestureEnabledFreezeState()\n\n  if (!freezeState.frozen || freezeState.enabled === nextEnabled) {\n    return true\n  }\n\n  warnGestureEnabledMutationIgnored(source)\n  return false\n}\n\nlet pressGestureDebugId = 0\nlet externalPressDebugId = 0\n\n// distance in dp the finger may travel before a press is treated as a\n// scroll/drag and cancelled. tracked in absolute (screen) coordinates by the\n// Tap's onTouchesMove (see createPressGesture) so a view translating under a\n// stationary finger - a sheet sliding on keyboard open/close, a sheet open\n// animation - does not cancel the press; only genuine finger travel does.\n// this also makes scroll-cancels-press work for any scroll container (a plain\n// RN ScrollView/FlatList or an RNGH one), since the press cancels itself\n// rather than relying on a parent RNGH gesture to steal it.\nconst PRESS_MOVE_CANCEL_DISTANCE = 12\nconst PRESS_MOVE_CANCEL_DISTANCE_SQ =\n  PRESS_MOVE_CANCEL_DISTANCE * PRESS_MOVE_CANCEL_DISTANCE\n\n// minimal shapes of the RNGH gesture events createPressGesture reads. RNGH is\n// injected at runtime (GestureState.Gesture is untyped), so this module keeps\n// no type dependency on react-native-gesture-handler - these name just the\n// fields the press gesture touches.\ntype GesturePoint = {\n  absoluteX: number\n  absoluteY: number\n}\ntype GestureBeginEvent = Partial<GesturePoint>\ntype GestureTouchEvent = {\n  changedTouches?: GesturePoint[]\n  allTouches?: GesturePoint[]\n}\n\ntype PressOwnerSource = 'internal' | 'external' | null\n\nfunction getEventPointerId(e: any): number | null {\n  const pointerId =\n    e?.pointerId ??\n    e?.pointer?.id ??\n    e?.event?.pointerId ??\n    e?.event?.pointer?.id ??\n    e?.nativeEvent?.pointerId ??\n    e?.nativeEvent?.id ??\n    e?.event?.nativeEvent?.pointerId ??\n    e?.event?.nativeEvent?.id ??\n    null\n\n  return pointerId == null || Number.isNaN(pointerId) ? null : Number(pointerId)\n}\n\n/**\n * Global press coordination - ensures only innermost pressable fires press events,\n * matching RN Pressable/responder system semantics where deepest component wins.\n * Uses a grace period to allow child gestures to steal ownership from parent,\n * since RNGH fires parent gestures before child gestures.\n */\nconst pressState = {\n  owner: null as object | null,\n  ownerId: null as number | null,\n  ownerSource: null as PressOwnerSource,\n  ownerPointerId: null as number | null,\n  timestamp: 0,\n}\n\nexport interface Insets {\n  top?: number\n  left?: number\n  bottom?: number\n  right?: number\n}\n\nexport type PressGestureConfig = {\n  debugName?: string | null\n  onPressIn?: (e: any) => void\n  onPressOut?: (e: any) => void\n  onPress?: (e: any) => void\n  onLongPress?: (e: any) => void\n  delayLongPress?: number\n  hitSlop?: number | Insets | null\n}\n\nexport interface GestureHandlerAccessor {\n  readonly isEnabled: boolean\n  readonly state: GestureState\n  set(updates: Partial<GestureState>): void\n  disable(): void\n  createPressGesture(config: PressGestureConfig): any\n}\n\nexport type ExternalPressOwnershipToken = object\n\nfunction resetPressOwner() {\n  pressState.owner = null\n  pressState.ownerId = null\n  pressState.ownerSource = null\n  pressState.ownerPointerId = null\n  pressState.timestamp = 0\n}\n\nfunction resetStaleOwner(now: number, debugName?: string | null) {\n  if (now - pressState.timestamp > 2000) {\n    resetPressOwner()\n  }\n}\n\nexport function claimExternalPressOwnership(\n  debugName?: string | null\n): ExternalPressOwnershipToken {\n  const now = Date.now()\n  resetStaleOwner(now, debugName)\n\n  const token = {}\n  const ownerId = ++externalPressDebugId\n\n  pressState.owner = token\n  pressState.ownerId = ownerId\n  pressState.ownerSource = 'external'\n  pressState.timestamp = now\n\n  return token\n}\n\nexport function releaseExternalPressOwnership(\n  token: ExternalPressOwnershipToken | null | undefined,\n  debugName?: string | null\n): void {\n  if (!token || pressState.owner !== token) {\n    return\n  }\n  resetPressOwner()\n}\n\nexport function getGestureHandler(): GestureHandlerAccessor {\n  return {\n    get isEnabled(): boolean {\n      return state.get().enabled\n    },\n    get state(): GestureState {\n      return state.get()\n    },\n    set(updates: Partial<GestureState>): void {\n      if (\n        updates.enabled !== undefined &&\n        !canChangeGestureHandlerEnabled(updates.enabled, 'getGestureHandler().set()')\n      ) {\n        return\n      }\n\n      Object.assign(state.get(), updates)\n    },\n\n    disable(): void {\n      if (!canChangeGestureHandlerEnabled(false, 'getGestureHandler().disable()')) {\n        return\n      }\n\n      state.get().enabled = false\n    },\n\n    createPressGesture(config: PressGestureConfig): any {\n      const { Gesture } = state.get()\n      if (!Gesture) return null\n\n      const longPressDuration = config.delayLongPress ?? 500\n\n      // unique token for this gesture instance - used to track ownership\n      const myToken = {}\n      const myDebugId = ++pressGestureDebugId\n      // mutable gesture state kept on an object so handler bodies that get\n      // workletized by react-native-worklets (>=0.7.4) see live values.\n      // primitive `let`s get frozen into the worklet's serialized __closure\n      // at factory time, so reassignments from workletized handlers never\n      // propagate out. object property mutation goes through the captured\n      // reference and is always observed.\n      const flags = {\n        didLongPress: false,\n        didPressIn: false,\n        pressInTimer: null as ReturnType<typeof setTimeout> | null,\n        // absolute (screen) coords of the press start, and whether the finger\n        // has since travelled far enough to be treated as a scroll/drag.\n        moveStartX: null as number | null,\n        moveStartY: 0,\n        cancelledByMove: false,\n      }\n\n      // Grace period for child gestures to steal ownership from parent.\n      // RNGH fires parent before child, but we want innermost to win.\n      // Claims typically span a few ms, 24ms gives enough room to observe\n      // ordering while debugging slower frames.\n      const GRACE_PERIOD_MS = process.env.TAMAGUI_RNGH_PRESS_DELAY\n        ? +process.env.TAMAGUI_RNGH_PRESS_DELAY\n        : 24\n\n      const tryClaimOwnership = (e: any) => {\n        const now = Date.now()\n        resetStaleOwner(now, config.debugName)\n\n        const currentPointerId = getEventPointerId(e)\n        const isSameTouchPointer =\n          currentPointerId == null ||\n          pressState.ownerPointerId == null ||\n          pressState.ownerPointerId === currentPointerId\n\n        if (\n          pressState.owner === null ||\n          (pressState.ownerSource === 'internal' && isSameTouchPointer)\n        ) {\n          pressState.owner = myToken\n          pressState.ownerId = myDebugId\n          pressState.ownerSource = 'internal'\n          pressState.ownerPointerId = currentPointerId\n          pressState.timestamp = now\n        }\n        return pressState.owner === myToken\n      }\n\n      const isOwner = () => pressState.owner === myToken\n\n      const releaseOwnership = () => {\n        if (flags.pressInTimer) {\n          clearTimeout(flags.pressInTimer)\n          flags.pressInTimer = null\n        }\n        if (pressState.owner === myToken) {\n          resetPressOwner()\n        }\n      }\n\n      const firePressIn = (e: any) => {\n        if (!flags.didPressIn && isOwner()) {\n          flags.didPressIn = true\n          config.onPressIn?.(e)\n        }\n      }\n\n      const schedulePressIn = (e: any) => {\n        if (flags.pressInTimer) {\n          clearTimeout(flags.pressInTimer)\n        }\n        flags.pressInTimer = setTimeout(() => {\n          flags.pressInTimer = null\n          if (isOwner()) {\n            firePressIn(e)\n          }\n        }, GRACE_PERIOD_MS + 1)\n      }\n\n      // Tap gesture for regular presses.\n      //\n      // maxDuration is long so long-presses aren't cut off. We deliberately do\n      // not use RNGH's .maxDistance(): it measures finger travel in the gesture\n      // view's own coordinate space, so a view that translates under a\n      // stationary finger (a sheet sliding on keyboard open/close, a sheet open\n      // animation) reads as finger movement and silently cancels the press -\n      // onPress never fires. Instead onTouchesMove tracks the finger in\n      // absolute (screen) coordinates and cancels only on genuine finger\n      // travel. That also makes scroll-cancels-press work for any scroll\n      // container, since the press cancels itself instead of depending on a\n      // parent RNGH gesture to steal the touch.\n      const tap = Gesture.Tap()\n        .runOnJS(true)\n        .maxDuration(10000) // allow very long presses\n        .onBegin((e: GestureBeginEvent) => {\n          flags.didLongPress = false\n          flags.didPressIn = false\n          flags.cancelledByMove = false\n          flags.moveStartX = typeof e.absoluteX === 'number' ? e.absoluteX : null\n          flags.moveStartY = typeof e.absoluteY === 'number' ? e.absoluteY : 0\n          tryClaimOwnership(e)\n          // Defer onPressIn until after the grace window so child pressables\n          // can steal ownership, but flush it on tap end for very fast taps.\n          schedulePressIn(e)\n        })\n        .onTouchesMove((e: GestureTouchEvent) => {\n          if (flags.cancelledByMove || flags.moveStartX === null) return\n          const touch = e.changedTouches?.[0] ?? e.allTouches?.[0]\n          if (!touch) return\n          const dx = touch.absoluteX - flags.moveStartX\n          const dy = touch.absoluteY - flags.moveStartY\n          if (dx * dx + dy * dy <= PRESS_MOVE_CANCEL_DISTANCE_SQ) return\n          // finger has travelled far enough to be a scroll/drag, not a tap.\n          // release the pressStyle now (mid-scroll, so it doesn't stay stuck)\n          // and make onEnd skip onPress on finger lift.\n          flags.cancelledByMove = true\n          if (flags.pressInTimer) {\n            clearTimeout(flags.pressInTimer)\n            flags.pressInTimer = null\n          }\n          if (flags.didPressIn) {\n            flags.didPressIn = false\n            config.onPressOut?.(e)\n          }\n          releaseOwnership()\n        })\n        .onEnd((e: unknown) => {\n          if (isOwner() && !flags.didLongPress && !flags.cancelledByMove) {\n            firePressIn(e)\n            config.onPress?.(e)\n          }\n        })\n        .onFinalize((e: unknown) => {\n          if (isOwner()) {\n            config.onPressOut?.(e)\n            releaseOwnership()\n          } else if (flags.didPressIn) {\n            // we already fired onPressIn but lost ownership before finalize\n            // (e.g. finger dragged onto a sibling pressable and that one\n            // claimed ownership). fire onPressOut so callers can clear their\n            // press state - otherwise pressStyle stays stuck on this view.\n            flags.didPressIn = false\n            config.onPressOut?.(e)\n          }\n        })\n\n      if (config.hitSlop) tap.hitSlop(config.hitSlop)\n\n      // if no long press handler, just use tap\n      if (!config.onLongPress) return tap\n\n      // LongPress gesture for long press handling\n      const longPress = Gesture.LongPress()\n        .runOnJS(true)\n        .minDuration(longPressDuration)\n        .onStart((e: any) => {\n          flags.didLongPress = true\n          if (isOwner()) {\n            firePressIn(e)\n            config.onLongPress?.(e)\n          }\n        })\n\n      if (config.hitSlop) longPress.hitSlop(config.hitSlop)\n\n      // exclusive: longPress has priority, tap is fallback for quick presses\n      return Gesture.Exclusive(longPress, tap)\n    },\n  }\n}\n"
  ]
}