{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input",
  "title": "Input",
  "description": "A core component that allows users to enter text.",
  "dependencies": [
    "react-native-reanimated"
  ],
  "registryDependencies": [
    "@tetra-ui/button"
  ],
  "files": [
    {
      "path": "ui/input.tsx",
      "content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n  Children,\n  cloneElement,\n  createContext,\n  isValidElement,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport {\n  type BlurEvent,\n  type FocusEvent,\n  Platform,\n  Pressable,\n  TextInput as RNTextInput,\n  View,\n} from \"react-native\";\nimport Animated, {\n  interpolateColor,\n  useAnimatedStyle,\n  useSharedValue,\n  withTiming,\n} from \"react-native-reanimated\";\nimport { useCSSVariable } from \"uniwind\";\nimport { cn } from \"../lib/utils\";\nimport { Button, ButtonIcon, type ButtonProps, ButtonText } from \"./button\";\n\n// Constants\nconst ANIMATION_DURATION = 120;\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\n// Types\ntype InternalInputAddonButtonContextType = VariantProps<\n  typeof inputAddonButtonVariants\n> &\n  Pick<ButtonProps, \"variant\">;\n\nexport type InputProps = Omit<\n  React.ComponentPropsWithRef<typeof RNTextInput>,\n  \"editable\"\n> & {\n  disabled?: boolean;\n};\n\nexport type InputPressableProps = React.ComponentProps<typeof Pressable> & {\n  disabled?: boolean;\n  invalid?: boolean;\n  focused?: boolean;\n};\n\nexport type InputAddonProps = React.ComponentProps<typeof View> &\n  VariantProps<typeof inputAddonVariants> & {\n    children: React.ReactNode;\n  };\n\nexport type InputAddonChild =\n  | React.ReactElement<InputAddonProps>\n  | null\n  | false;\nexport type InputAddonChildren = InputAddonChild | InputAddonChild[];\n\ntype InputAddonIconProps = {\n  children: React.ReactNode;\n  className?: string;\n};\n\ntype InputAddonButtonProps = Omit<React.ComponentProps<typeof Button>, \"size\"> &\n  VariantProps<typeof inputAddonButtonVariants>;\n\ntype UseInputFocusStateProps = {\n  onFocus?: (e: FocusEvent) => void;\n  onBlur?: (e: BlurEvent) => void;\n};\n\n// Context\nconst InputAddonButtonContext =\n  createContext<InternalInputAddonButtonContextType | null>(null);\n\nconst useInputAddonButtonContext = () => {\n  const context = useContext(InputAddonButtonContext);\n  if (!context) {\n    throw new Error(\n      \"useInputAddonButtonContext must be used within a Button component\"\n    );\n  }\n  return context;\n};\n\n// Components\nexport const Input = ({ className, disabled, ...props }: InputProps) => {\n  return (\n    <RNTextInput\n      className={cn(\n        \"grow font-normal text-base text-foreground leading-tight\",\n        className\n      )}\n      editable={!disabled}\n      placeholderTextColorClassName=\"accent-muted-foreground\"\n      {...props}\n    />\n  );\n};\n\nexport const InputPressable = ({\n  children,\n  disabled,\n  invalid,\n  focused,\n  onPress,\n  className,\n  ...props\n}: InputPressableProps) => {\n  const [inputColor, ringColor, destructiveColor] = useCSSVariable([\n    \"--color-input\",\n    \"--color-ring\",\n    \"--color-destructive\",\n  ]) as [string, string, string];\n\n  const outlineWidth = useSharedValue(1);\n  const outlineColorProgress = useSharedValue(0);\n\n  useEffect(() => {\n    if (invalid) {\n      outlineWidth.value = withTiming(2, { duration: ANIMATION_DURATION });\n      outlineColorProgress.value = withTiming(2, {\n        duration: ANIMATION_DURATION,\n      });\n    } else if (focused) {\n      outlineWidth.value = withTiming(2, { duration: ANIMATION_DURATION });\n      outlineColorProgress.value = withTiming(1, {\n        duration: ANIMATION_DURATION,\n      });\n    } else {\n      outlineWidth.value = withTiming(1, { duration: ANIMATION_DURATION });\n      outlineColorProgress.value = withTiming(0, {\n        duration: ANIMATION_DURATION,\n      });\n    }\n  }, [focused, invalid, outlineWidth, outlineColorProgress]);\n\n  const animatedStyle = useAnimatedStyle(() => {\n    const color = interpolateColor(\n      outlineColorProgress.value,\n      [0, 1, 2],\n      [inputColor, ringColor, destructiveColor]\n    );\n\n    // Android outline ignores borderRadius and shows in the corners.\n    if (Platform.OS === \"android\") {\n      return {\n        borderColor: color,\n        borderWidth: outlineWidth.value,\n      };\n    }\n\n    return {\n      outlineColor: color,\n      outlineWidth: outlineWidth.value,\n    };\n  });\n\n  return (\n    <AnimatedPressable\n      {...props}\n      accessibilityState={{ disabled }}\n      className={cn(\n        \"flex min-h-12 w-full flex-row items-center gap-2 rounded-lg bg-background px-3 py-2 active:bg-accent/90 disabled:opacity-50 dark:active:bg-accent/50\",\n        className\n      )}\n      disabled={disabled}\n      onPress={onPress}\n      style={animatedStyle}\n    >\n      {children}\n    </AnimatedPressable>\n  );\n};\n\nexport const InputAddon = ({ align, className, ...props }: InputAddonProps) => {\n  return (\n    <View className={cn(inputAddonVariants({ align }), className)} {...props} />\n  );\n};\n\nexport const InputAddonIcon = ({\n  children,\n  ...props\n}: InputAddonIconProps): React.ReactElement | null => {\n  const child = Children.only(children);\n\n  if (!child) {\n    if (__DEV__) {\n      throw new Error(\n        \"InputAddonIcon expects a single React element as children\"\n      );\n    }\n    return null;\n  }\n\n  return cloneElement(child as React.ReactElement<InputAddonIconProps>, {\n    ...props,\n    className: cn(\"size-6 text-muted-foreground\", props.className),\n  });\n};\n\nexport const InputAddonButton = ({\n  className,\n  variant = \"ghost\",\n  size = \"sm\",\n  disabled,\n  busy,\n  ...props\n}: InputAddonButtonProps) => {\n  const ctx = useMemo(() => ({ size, variant }), [size, variant]);\n  return (\n    <InputAddonButtonContext.Provider value={ctx}>\n      <Button\n        {...props}\n        busy={busy}\n        className={cn(inputAddonButtonVariants({ size }), className)}\n        disabled={disabled}\n        size={size}\n        variant={variant}\n      />\n    </InputAddonButtonContext.Provider>\n  );\n};\n\nexport const InputAddonButtonText = (\n  props: React.ComponentProps<typeof ButtonText>\n) => {\n  const ctx = useInputAddonButtonContext();\n  return (\n    <ButtonText\n      {...props}\n      className={cn(inputAddonButtonTextVariants(ctx), props.className)}\n    />\n  );\n};\n\nexport const InputAddonButtonIcon = (\n  props: React.ComponentProps<typeof ButtonIcon>\n) => {\n  const ctx = useInputAddonButtonContext();\n\n  return (\n    <ButtonIcon\n      {...props}\n      className={cn(inputAddonButtonIconVariants(ctx), props.className)}\n    />\n  );\n};\n\n// Hooks\nexport const useInputFocusState = ({\n  onFocus,\n  onBlur,\n}: UseInputFocusStateProps) => {\n  const [isFocused, setIsFocused] = useState(false);\n\n  const internalRef = useRef<RNTextInput>(null);\n\n  const handleFocus = useCallback(\n    (e: FocusEvent) => {\n      onFocus?.(e);\n\n      setIsFocused(true);\n    },\n    [onFocus]\n  );\n\n  const handleBlur = useCallback(\n    (e: BlurEvent) => {\n      onBlur?.(e);\n\n      setIsFocused(false);\n    },\n    [onBlur]\n  );\n\n  const handlePress = useCallback(() => internalRef.current?.focus(), []);\n\n  return {\n    handleBlur,\n    handleFocus,\n    handlePress,\n    internalRef,\n    isFocused,\n  };\n};\n\nexport const useInputAddons = (\n  children?: InputAddonChildren | React.ReactElement | null\n) => {\n  const startAddons: InputAddonChild[] = [];\n  const endAddons: InputAddonChild[] = [];\n\n  Children.forEach(children, (child) => {\n    if (isValidElement(child) && child.type === InputAddon) {\n      const typedChild = child as InputAddonChild;\n\n      if (typedChild) {\n        if (\n          typeof typedChild.props.align === \"undefined\" ||\n          typedChild.props.align === \"inline-start\"\n        ) {\n          startAddons.push(typedChild);\n        } else {\n          endAddons.push(typedChild);\n        }\n      }\n    }\n  });\n\n  return {\n    endAddons,\n    pressableClassName: cn(\n      startAddons.length && \"pl-0\",\n      endAddons.length && \"pr-0\"\n    ),\n    startAddons,\n  };\n};\n\n// Styles\nconst inputAddonVariants = cva(\"flex items-center justify-center\", {\n  defaultVariants: {\n    align: \"inline-start\",\n  },\n  variants: {\n    align: {\n      \"inline-end\": \"pr-3\",\n      \"inline-start\": \"pl-3\",\n    },\n  },\n});\n\nconst inputAddonButtonVariants = cva(\"w-fit gap-1 shadow-none\", {\n  defaultVariants: {\n    size: \"sm\",\n  },\n  variants: {\n    size: { icon: \"size-7\", sm: \"h-8 px-2\" },\n  },\n});\n\nconst inputAddonButtonTextVariants = cva(\"text-sm\", {\n  defaultVariants: {\n    variant: \"default\",\n  },\n  variants: {\n    variant: {\n      default: \"text-primary-foreground\",\n      destructive: \"text-white\",\n      ghost: \"text-muted-foreground\",\n      link: \"text-muted-foreground\",\n      outline: \"text-muted-foreground\",\n      secondary: \"text-secondary-foreground\",\n    },\n  },\n});\n\nconst inputAddonButtonIconVariants = cva(\"\", {\n  defaultVariants: {\n    size: \"sm\",\n  },\n  variants: {\n    size: {\n      icon: \"size-5\",\n      sm: \"size-4\",\n    },\n    variant: {\n      default: \"text-primary-foreground\",\n      destructive: \"text-white\",\n      ghost: \"text-muted-foreground\",\n      link: \"text-muted-foreground\",\n      outline: \"text-muted-foreground\",\n      secondary: \"text-secondary-foreground\",\n    },\n  },\n});\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
