{
  "mappings": "AAGA,cAEE,iBAEA,yBACA,2BACA,8BACK;AAGP,SAAS,eAAe,WAAW,YAAY;KAe1C,iBAAiB,6BAA6B,aAAa,KAAI;KAE/D,eAAe;CAAE,OAAO;IAAa,QACxC,KACE,SAAS,uBACP,UACA,eACA,YACA,aACA,SACA,sBACA,UACA,cACA,YACA;KAID,eAAe;CAAE,MAAM;IAAa,QAAQ,SAAS;KAErD,kBAAkB,eAAe;KAgCjC,0BAA0B;CAE7B;;AAGF,OAAO,cAAM,cAAc,SAAS,yBAAyB;AAC7D,OAAO,cAAM,cAAc,SAAS,yBAAyB;AAE7D,OAAO,iBAAS,kBACd,kBACC,wBAAwB,SAAS;KA2D/B,gBAAgB,wBAAwB,SAAS;AAEtD,OAAO,cAAM,2BAA2B,0BAA0B;AAgBlE,OAAO,cAAM,wBAAwB,uBAAuB;AAO5D,OAAO,cAAM,0BACX,MAAM,iBACN,WAAW,GAAG;AAKhB,OAAO,iBAAS,iBAAiB,UAAU,kBACzC,YAAY,GACZ,UAAU,0BACT,gBAAgB",
  "names": [],
  "sources": [
    "src/createAnimations.tsx"
  ],
  "version": 3,
  "sourcesContent": [
    "import { getEffectiveAnimation, normalizeTransition } from '@tamagui/animation-helpers'\nimport { isWeb, useIsomorphicLayoutEffect } from '@tamagui/constants'\nimport { ResetPresence, usePresence } from '@tamagui/use-presence'\nimport type {\n  AnimatedNumberStrategy,\n  AnimationDriver,\n  TransitionProp,\n  UniversalAnimatedNumber,\n  UseAnimatedNumberReaction,\n  UseAnimatedNumberStyle,\n} from '@tamagui/web'\nimport { useEvent, useThemeWithState } from '@tamagui/web'\nimport React from 'react'\nimport { Animated, type Text, type View } from 'react-native'\n\n// detect Fabric (New Architecture) — Paper doesn't support native driver for all style keys\nconst isFabric =\n  !isWeb && typeof global !== 'undefined' && !!global.__nativeFabricUIManager\n\n// Helper to resolve dynamic theme values like {dynamic: {dark: \"value\", light: undefined}}\nconst resolveDynamicValue = (value: any, isDark: boolean): any => {\n  if (value && typeof value === 'object' && 'dynamic' in value) {\n    const dynamicValue = isDark ? value.dynamic.dark : value.dynamic.light\n    return dynamicValue\n  }\n  return value\n}\n\ntype AnimationsConfig<A extends object = any> = { [Key in keyof A]: AnimationConfig }\n\ntype SpringConfig = { type?: 'spring' } & Partial<\n  Pick<\n    Animated.SpringAnimationConfig,\n    | 'delay'\n    | 'bounciness'\n    | 'damping'\n    | 'friction'\n    | 'mass'\n    | 'overshootClamping'\n    | 'speed'\n    | 'stiffness'\n    | 'tension'\n    | 'velocity'\n  >\n>\n\ntype TimingConfig = { type: 'timing' } & Partial<Animated.TimingAnimationConfig>\n\ntype AnimationConfig = SpringConfig | TimingConfig\n\nconst animatedStyleKey = {\n  transform: true,\n  opacity: true,\n}\n\nconst colorStyleKey = {\n  backgroundColor: true,\n  color: true,\n  borderColor: true,\n  borderLeftColor: true,\n  borderRightColor: true,\n  borderTopColor: true,\n  borderBottomColor: true,\n}\n\n// these style keys are costly to animate and only work with native driver on Fabric\nconst costlyToAnimateStyleKey = {\n  borderRadius: true,\n  borderTopLeftRadius: true,\n  borderTopRightRadius: true,\n  borderBottomLeftRadius: true,\n  borderBottomRightRadius: true,\n  borderWidth: true,\n  borderLeftWidth: true,\n  borderRightWidth: true,\n  borderTopWidth: true,\n  borderBottomWidth: true,\n  ...colorStyleKey,\n}\n\ntype CreateAnimationsOptions = {\n  // override native driver detection (default: auto-detect Fabric)\n  useNativeDriver?: boolean\n}\n\nexport const AnimatedView: Animated.AnimatedComponent<typeof View> = Animated.View\nexport const AnimatedText: Animated.AnimatedComponent<typeof Text> = Animated.Text\n\nexport function useAnimatedNumber(\n  initial: number\n): UniversalAnimatedNumber<Animated.Value> {\n  const state = React.useRef(\n    null as any as {\n      val: Animated.Value\n      composite: Animated.CompositeAnimation | null\n      strategy: AnimatedNumberStrategy\n    }\n  )\n  if (!state.current) {\n    state.current = {\n      composite: null,\n      val: new Animated.Value(initial),\n      strategy: { type: 'spring' },\n    }\n  }\n\n  return {\n    getInstance() {\n      return state.current.val\n    },\n    getValue() {\n      return state.current.val['_value']\n    },\n    stop() {\n      state.current.composite?.stop()\n      state.current.composite = null\n    },\n    setValue(next: number, { type, ...config } = { type: 'spring' }, onFinish) {\n      const val = state.current.val\n\n      const handleFinish = onFinish\n        ? ({ finished }) => (finished ? onFinish() : null)\n        : undefined\n\n      if (type === 'direct') {\n        val.setValue(next)\n      } else if (type === 'spring') {\n        state.current.composite?.stop()\n        const composite = Animated.spring(val, {\n          ...config,\n          toValue: next,\n          useNativeDriver: isFabric,\n        })\n        composite.start(handleFinish)\n        state.current.composite = composite\n      } else {\n        state.current.composite?.stop()\n        const composite = Animated.timing(val, {\n          ...config,\n          toValue: next,\n          useNativeDriver: isFabric,\n        })\n        composite.start(handleFinish)\n        state.current.composite = composite\n      }\n    },\n  }\n}\n\ntype RNAnimatedNum = UniversalAnimatedNumber<Animated.Value>\n\nexport const useAnimatedNumberReaction: UseAnimatedNumberReaction<RNAnimatedNum> = (\n  { value },\n  onValue\n) => {\n  const onChange = useEvent((current) => {\n    onValue(current.value)\n  })\n\n  React.useEffect(() => {\n    const id = value.getInstance().addListener(onChange)\n    return () => {\n      value.getInstance().removeListener(id)\n    }\n  }, [value, onChange])\n}\n\nexport const useAnimatedNumberStyle: UseAnimatedNumberStyle<RNAnimatedNum> = (\n  value,\n  getStyle\n) => {\n  return getStyle(value.getInstance())\n}\n\nexport const useAnimatedNumbersStyle = (\n  vals: RNAnimatedNum[],\n  getStyle: (...currentValues: any[]) => any\n): any => {\n  return getStyle(...vals.map((v) => v.getInstance()))\n}\n\nexport function createAnimations<A extends AnimationsConfig>(\n  animations: A,\n  options?: CreateAnimationsOptions\n): AnimationDriver<A> {\n  const nativeDriver = options?.useNativeDriver ?? isFabric\n\n  return {\n    isReactNative: true,\n    inputStyle: 'value',\n    outputStyle: 'inline',\n    avoidReRenders: true,\n    animations,\n    needsCustomComponent: true,\n    View: AnimatedView,\n    Text: AnimatedText,\n    useAnimatedNumber,\n    useAnimatedNumberReaction,\n    useAnimatedNumberStyle,\n    useAnimatedNumbersStyle,\n    usePresence,\n    ResetPresence,\n    useAnimations: ({\n      props,\n      onDidAnimate,\n      style,\n      componentState,\n      presence,\n      useStyleEmitter,\n    }) => {\n      const isDisabled = isWeb && componentState.unmounted === true\n      const isExiting = presence?.[0] === false\n      const sendExitComplete = presence?.[1]\n      const [, themeState] = useThemeWithState({})\n      // Check scheme first, then fall back to checking theme name for 'dark'\n      const isDark = themeState?.scheme === 'dark' || themeState?.name?.startsWith('dark')\n\n      /** store Animated value of each key e.g: color: AnimatedValue */\n      const animateStyles = React.useRef<Record<string, Animated.Value>>({})\n      const animatedTranforms = React.useRef<{ [key: string]: Animated.Value }[]>([])\n      const animationsState = React.useRef(\n        new WeakMap<\n          Animated.Value,\n          {\n            interpolation: Animated.AnimatedInterpolation<any>\n            current?: number | string | undefined\n            // only for colors\n            animateToValue?: number\n          }\n        >()\n      )\n\n      // exit cycle guards to prevent stale/duplicate completion\n      const exitCycleIdRef = React.useRef(0)\n      const exitCompletedRef = React.useRef(false)\n      const wasExitingRef = React.useRef(false)\n\n      // detect transition into/out of exiting state\n      const justStartedExiting = isExiting && !wasExitingRef.current\n      const justStoppedExiting = !isExiting && wasExitingRef.current\n\n      // start new exit cycle only on transition INTO exiting\n      if (justStartedExiting) {\n        exitCycleIdRef.current++\n        exitCompletedRef.current = false\n      }\n      // invalidate pending callbacks when exit is canceled/interrupted\n      if (justStoppedExiting) {\n        exitCycleIdRef.current++\n      }\n\n      const animateOnly = (props.animateOnly as string[]) || []\n      const hasTransitionOnly = !!props.animateOnly\n\n      // Track if we just finished entering (transition from entering to not entering)\n      // must be declared before args array that uses justFinishedEntering\n      const isEntering = !!componentState.unmounted\n      const wasEnteringRef = React.useRef(isEntering)\n      const justFinishedEntering = wasEnteringRef.current && !isEntering\n      React.useEffect(() => {\n        wasEnteringRef.current = isEntering\n      })\n\n      const args = [\n        JSON.stringify(style),\n        componentState,\n        isExiting,\n        !!onDidAnimate,\n        isDark,\n        justFinishedEntering,\n        hasTransitionOnly,\n      ]\n\n      const res = React.useMemo(() => {\n        const runners: Function[] = []\n        const completions: Promise<void>[] = []\n\n        // Determine animation state for enter/exit transitions\n        // Use 'enter' if we're entering OR if we just finished entering\n        const animationState: 'enter' | 'exit' | 'default' = isExiting\n          ? 'exit'\n          : isEntering || justFinishedEntering\n            ? 'enter'\n            : 'default'\n\n        const nonAnimatedStyle = {}\n\n        for (const key in style) {\n          const rawVal = style[key]\n          // Resolve dynamic theme values (like $theme-dark)\n          const val = resolveDynamicValue(rawVal, isDark)\n          if (val === undefined) continue\n\n          if (isDisabled) {\n            continue\n          }\n\n          if (animatedStyleKey[key] == null && !costlyToAnimateStyleKey[key]) {\n            nonAnimatedStyle[key] = val\n            continue\n          }\n\n          if (hasTransitionOnly && !animateOnly.includes(key)) {\n            nonAnimatedStyle[key] = val\n            continue\n          }\n\n          if (key !== 'transform') {\n            animateStyles.current[key] = update(key, animateStyles.current[key], val)\n            continue\n          }\n          // key: 'transform'\n          // for now just support one transform key\n          if (!val) continue\n          if (typeof val === 'string') {\n            console.warn(`Warning: Tamagui can't animate string transforms yet!`)\n            continue\n          }\n\n          for (const [index, transform] of val.entries()) {\n            if (!transform) continue\n            // tkey: e.g: 'translateX'\n            const tkey = Object.keys(transform)[0]\n            const currentTransform = animatedTranforms.current[index]?.[tkey]\n            animatedTranforms.current[index] = {\n              [tkey]: update(tkey, currentTransform, transform[tkey]),\n            }\n            animatedTranforms.current = [...animatedTranforms.current]\n          }\n        }\n\n        const animatedTransformStyle =\n          animatedTranforms.current.length > 0\n            ? {\n                transform: animatedTranforms.current.map((r) => {\n                  const key = Object.keys(r)[0]\n                  const val =\n                    animationsState.current!.get(r[key])?.interpolation || r[key]\n                  return { [key]: val }\n                }),\n              }\n            : {}\n\n        const animatedStyle = {\n          ...Object.fromEntries(\n            Object.entries(animateStyles.current).map(([k, v]) => [\n              k,\n              animationsState.current!.get(v)?.interpolation || v,\n            ])\n          ),\n          ...animatedTransformStyle,\n        }\n\n        return {\n          runners,\n          completions,\n          style: [nonAnimatedStyle, animatedStyle],\n        }\n\n        function update(\n          key: string,\n          animated: Animated.Value | undefined,\n          valIn: string | number\n        ) {\n          const isColorStyleKey = colorStyleKey[key]\n          const [val, type] = isColorStyleKey ? [0, undefined] : getValue(valIn)\n          let animateToValue = val\n          const value = animated || new Animated.Value(val)\n          const curInterpolation = animationsState.current.get(value)\n\n          let interpolateArgs: any\n          if (type) {\n            interpolateArgs = getInterpolated(\n              curInterpolation?.current ?? value['_value'],\n              val,\n              type\n            )\n            animationsState.current!.set(value, {\n              interpolation: value.interpolate(interpolateArgs),\n              current: val,\n            })\n          }\n\n          if (isColorStyleKey) {\n            animateToValue = curInterpolation?.animateToValue ? 0 : 1\n            interpolateArgs = getColorInterpolated(\n              curInterpolation?.current as string,\n              // valIn is the next color\n              valIn as string,\n              animateToValue\n            )\n            animationsState.current!.set(value, {\n              current: valIn,\n              interpolation: value.interpolate(interpolateArgs),\n              animateToValue: curInterpolation?.animateToValue ? 0 : 1,\n            })\n          }\n\n          if (value) {\n            const animationConfig = getAnimationConfig(\n              key,\n              animations,\n              props.transition,\n              animationState\n            )\n\n            let resolve\n            const promise = new Promise<void>((res) => {\n              resolve = res\n            })\n            completions.push(promise)\n\n            runners.push(() => {\n              value.stopAnimation()\n\n              function getAnimation() {\n                return Animated[animationConfig.type || 'spring'](value, {\n                  toValue: animateToValue,\n                  useNativeDriver: nativeDriver,\n                  ...animationConfig,\n                })\n              }\n\n              const animation = animationConfig.delay\n                ? Animated.sequence([\n                    Animated.delay(animationConfig.delay),\n                    getAnimation(),\n                  ])\n                : getAnimation()\n\n              animation.start(({ finished }) => {\n                // always resolve during exit (element is leaving anyway)\n                // for non-exit, only resolve on successful completion\n                if (finished || isExiting) {\n                  resolve()\n                }\n              })\n            })\n          }\n\n          if (process.env.NODE_ENV === 'development') {\n            if (props['debug'] === 'verbose') {\n              // prettier-ignore\n              console.info(\n                ' 💠 animate',\n                key,\n                `from (${value['_value']}) to`,\n                valIn,\n                `(${val})`,\n                'type',\n                type,\n                'interpolate',\n                interpolateArgs\n              )\n            }\n          }\n          return value\n        }\n      }, args)\n\n      // track previous exiting state\n      React.useEffect(() => {\n        wasExitingRef.current = isExiting\n      })\n\n      useIsomorphicLayoutEffect(() => {\n        res.runners.forEach((r) => r())\n\n        // capture current cycle id\n        const cycleId = exitCycleIdRef.current\n\n        // handle zero-completion case immediately\n        if (res.completions.length === 0) {\n          onDidAnimate?.()\n          if (isExiting && !exitCompletedRef.current) {\n            exitCompletedRef.current = true\n            sendExitComplete?.()\n          }\n          return\n        }\n\n        let cancel = false\n        Promise.all(res.completions).then(() => {\n          if (cancel) return\n          // guard against stale cycle completion\n          if (isExiting && cycleId !== exitCycleIdRef.current) return\n          if (isExiting && exitCompletedRef.current) return\n\n          onDidAnimate?.()\n          if (isExiting) {\n            exitCompletedRef.current = true\n            sendExitComplete?.()\n          }\n        })\n        return () => {\n          cancel = true\n        }\n      }, args)\n\n      // avoidReRenders: receive style changes imperatively from tamagui\n      // and update Animated.Values directly without React re-renders\n      // reuses the same update() + runner pattern as the useMemo path\n      useStyleEmitter?.((nextStyle) => {\n        for (const key in nextStyle) {\n          const rawVal = nextStyle[key]\n          const val = resolveDynamicValue(rawVal, isDark)\n          if (val === undefined) continue\n\n          if (key === 'transform' && Array.isArray(val)) {\n            for (const [index, transform] of val.entries()) {\n              if (!transform) continue\n              const tkey = Object.keys(transform)[0]\n              const currentTransform = animatedTranforms.current[index]?.[tkey]\n              animatedTranforms.current[index] = {\n                [tkey]: update(tkey, currentTransform, transform[tkey]),\n              }\n            }\n          } else if (animatedStyleKey[key] != null || costlyToAnimateStyleKey[key]) {\n            animateStyles.current[key] = update(key, animateStyles.current[key], val)\n          }\n        }\n\n        // run the queued animations immediately\n        res.runners.forEach((r) => r())\n\n        function update(\n          key: string,\n          animated: Animated.Value | undefined,\n          valIn: string | number\n        ) {\n          const isColor = colorStyleKey[key]\n          const [numVal, type] = isColor ? [0, undefined] : getValue(valIn)\n          let animateToValue = numVal\n          const value = animated || new Animated.Value(numVal)\n          const curInterpolation = animationsState.current.get(value)\n\n          if (type) {\n            animationsState.current.set(value, {\n              interpolation: value.interpolate(\n                getInterpolated(\n                  curInterpolation?.current ?? value['_value'],\n                  numVal,\n                  type\n                )\n              ),\n              current: numVal,\n            })\n          }\n\n          if (isColor) {\n            animateToValue = curInterpolation?.animateToValue ? 0 : 1\n            animationsState.current.set(value, {\n              current: valIn,\n              interpolation: value.interpolate(\n                getColorInterpolated(\n                  curInterpolation?.current as string,\n                  valIn as string,\n                  animateToValue\n                )\n              ),\n              animateToValue: curInterpolation?.animateToValue ? 0 : 1,\n            })\n          }\n\n          const animationConfig = getAnimationConfig(\n            key,\n            animations,\n            props.transition,\n            'default'\n          )\n          res.runners.push(() => {\n            value.stopAnimation()\n            const anim = Animated[animationConfig.type || 'spring'](value, {\n              toValue: animateToValue,\n              useNativeDriver: nativeDriver,\n              ...animationConfig,\n            })\n            ;(animationConfig.delay\n              ? Animated.sequence([Animated.delay(animationConfig.delay), anim])\n              : anim\n            ).start()\n          })\n\n          return value\n        }\n      })\n\n      if (process.env.NODE_ENV === 'development') {\n        if (props['debug'] === 'verbose') {\n          console.info(`Animated`, { response: res, inputStyle: style, isExiting })\n        }\n      }\n\n      return res\n    },\n  }\n}\n\nfunction getColorInterpolated(\n  currentColor: string | undefined,\n  nextColor: string,\n  animateToValue: number\n) {\n  const inputRange = [0, 1]\n  const outputRange = [currentColor ? currentColor : nextColor, nextColor]\n  if (animateToValue === 0) {\n    // because we are animating from value 1 to 0, we need to put target color at the beginning\n    outputRange.reverse()\n  }\n  return {\n    inputRange,\n    outputRange,\n  }\n}\n\nfunction getInterpolated(current: number, next: number, postfix = 'deg') {\n  if (next === current) {\n    current = next - 0.000000001\n  }\n  const inputRange = [current, next]\n  const outputRange = [`${current}${postfix}`, `${next}${postfix}`]\n  if (next < current) {\n    inputRange.reverse()\n    outputRange.reverse()\n  }\n  return {\n    inputRange,\n    outputRange,\n  }\n}\n\nfunction getAnimationConfig(\n  key: string,\n  animations: AnimationsConfig,\n  transition?: TransitionProp,\n  animationState: 'enter' | 'exit' | 'default' = 'default'\n): AnimationConfig {\n  const normalized = normalizeTransition(transition)\n  const shortKey = transformShorthands[key]\n\n  // Check for property-specific animation\n  const propAnimation = normalized.properties[key] ?? normalized.properties[shortKey]\n\n  let animationType: string | null = null\n  let extraConf: any = {}\n\n  if (typeof propAnimation === 'string') {\n    // Direct animation name: { x: 'quick' }\n    animationType = propAnimation\n  } else if (propAnimation && typeof propAnimation === 'object') {\n    // Config object: { x: { type: 'quick', delay: 100 } }\n    // Use effective animation based on state if no explicit type in config\n    animationType =\n      propAnimation.type || getEffectiveAnimation(normalized, animationState)\n    extraConf = propAnimation\n  } else {\n    // Fall back to effective animation based on state (enter/exit/default)\n    animationType = getEffectiveAnimation(normalized, animationState)\n  }\n\n  // Apply global delay if no property-specific delay\n  if (normalized.delay && !extraConf.delay) {\n    extraConf = { ...extraConf, delay: normalized.delay }\n  }\n\n  const found = animationType ? animations[animationType] : {}\n  return {\n    ...found,\n    // Apply global spring config overrides (from transition={['bouncy', { stiffness: 1000 }]})\n    ...normalized.config,\n    // Property-specific config takes highest precedence\n    ...extraConf,\n  }\n}\n\n// try both combos\nconst transformShorthands = {\n  x: 'translateX',\n  y: 'translateY',\n  translateX: 'x',\n  translateY: 'y',\n}\n\nfunction getValue(input: number | string, isColor = false) {\n  if (typeof input !== 'string') {\n    return [input] as const\n  }\n  const [_, number, after] = input.match(/([-0-9]+)(deg|%|px)/) ?? []\n  return [+number, after] as const\n}\n"
  ]
}