{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react/input", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "input.tsx",
      "content": "\"use client\";\n\nimport type { InputChangeEventDetails } from \"@base-ui/react/input\";\nimport { Input as InputPrimitive } from \"@base-ui/react/input\";\nimport {\n  motion,\n  useMotionValue,\n  useReducedMotion,\n  useSpring,\n} from \"motion/react\";\nimport {\n  forwardRef,\n  type ReactNode,\n  useCallback,\n  useEffect,\n  useId,\n  useRef,\n  useState,\n} from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst DEFAULT_SPRING = {\n  stiffness: 500,\n  damping: 30,\n  mass: 0.5,\n} as const;\n\nconst OPEN_EYELID_PATH =\n  \"M15.008,6.083l.881-1.441c.216-.354,.105-.815-.248-1.031-.354-.215-.815-.105-1.031,.248l-.907,1.482c-.678-.331-1.388-.588-2.124-.769l.333-1.655c.082-.406-.182-.802-.587-.883-.405-.078-.802,.181-.883,.587l-.339,1.685c-.364-.037-.732-.057-1.103-.057s-.739,.02-1.103,.057l-.339-1.685c-.082-.406-.48-.666-.883-.587-.406,.082-.669,.477-.587,.883l.333,1.655c-.736,.181-1.446,.438-2.124,.769l-.907-1.482c-.215-.353-.677-.463-1.031-.248-.353,.216-.464,.678-.248,1.031l.881,1.441c-.594,.402-1.154,.867-1.668,1.392-.29,.295-.285,.771,.011,1.061,.295,.29,.77,.285,1.061-.011,1.754-1.789,4.1-2.774,6.605-2.774s4.851,.985,6.605,2.774c.147,.15,.341,.225,.536,.225,.189,0,.379-.071,.525-.214,.296-.29,.301-.765,.011-1.061-.515-.525-1.074-.99-1.668-1.392Z\";\n\nconst CLOSED_EYELID_PATH =\n  \"M16.666,6.734c-.295-.29-.77-.285-1.061,.011-1.754,1.789-4.1,2.774-6.605,2.774s-4.851-.985-6.605-2.774c-.29-.295-.765-.3-1.061-.011-.296,.29-.301,.765-.011,1.061,.515,.525,1.074,.99,1.669,1.393l-.881,1.441c-.216,.353-.105,.815,.249,1.031,.122,.075,.257,.11,.391,.11,.252,0,.499-.127,.64-.359l.906-1.482c.678,.331,1.388,.588,2.124,.769l-.333,1.655c-.082,.406,.182,.802,.587,.883,.05,.01,.1,.015,.149,.015,.35,0,.663-.246,.734-.602l.339-1.685c.364,.037,.732,.057,1.103,.057s.739-.02,1.103-.057l.339,1.685c.072,.356,.385,.602,.734,.602,.049,0,.099-.005,.149-.015,.406-.082,.669-.477,.587-.883l-.333-1.655c.736-.181,1.446-.438,2.124-.769l.906,1.482c.141,.231,.388,.359,.64,.359,.134,0,.269-.036,.391-.11,.354-.216,.465-.678,.249-1.031l-.881-1.441c.594-.403,1.154-.867,1.669-1.393,.29-.295,.285-.771-.011-1.061Z\";\n\nconst EYE_MORPH_ORIGIN = \"9px 10.5px\";\n\nconst EYE_BLINK_TRANSITION = {\n  type: \"spring\",\n  stiffness: 640,\n  damping: 34,\n  mass: 0.38,\n} as const;\n\nconst INSTANT_TRANSITION = { duration: 0 } as const;\n\nconst FIREFOX_USER_AGENT_PATTERN = /firefox|fxios/i;\nconst CHROME_USER_AGENT_PATTERN = /chrome|chromium|crios/i;\n\nconst SMOOTH_CARET_TYPES = new Set([\n  \"text\",\n  \"search\",\n  \"url\",\n  \"email\",\n  \"password\",\n  \"tel\",\n]);\n\nconst controlCornerClassName =\n  \"rounded-lg supports-[corner-shape:squircle]:corner-squircle supports-[corner-shape:squircle]:rounded-[11px]\";\n\nconst componentThemeClassName =\n  \"[--ic-background:#ffffff] [--ic-foreground:#111111] [--ic-primary:#111111] [--ic-secondary:#646b75] [--ic-surface-border:#e9edf2] [--ic-border:#e3e7ec] [--ic-card:#ffffff] [--ic-card-foreground:#111111] [--ic-muted:#f5f7fa] [--ic-muted-foreground:#6d7480] [--ic-accent:#f3f5f8] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--ic-accent-foreground:#111111] [--ic-input:#e3e7ec] [--ic-ring:rgba(17,17,17,0.16)] [--ic-destructive:#dc2626] [--ic-paper:#fcfcfd] [--ic-popover-foreground:#111111] [--ic-brand:#0ea5e9] [--ic-brand-soft:#bae6fd] [--ic-shadow-soft:0_18px_38px_-24px_rgba(15,23,42,0.35)] [--ic-chart-1:oklch(0.52_0.19_254)] [--ic-chart-2:oklch(0.74_0.11_232)] [--ic-chart-3:oklch(0.42_0.16_262)] [--ic-chart-4:oklch(0.84_0.07_228)] [--ic-chart-5:oklch(0.62_0.14_240)] [--color-background:var(--ic-background)] [--color-foreground:var(--ic-foreground)] [--color-primary:var(--ic-primary)] [--color-secondary:var(--ic-secondary)] [--color-border:var(--ic-border)] [--color-card:var(--ic-card)] [--color-card-foreground:var(--ic-card-foreground)] [--color-muted:var(--ic-muted)] [--color-muted-foreground:var(--ic-muted-foreground)] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--color-input:var(--ic-input)] [--color-ring:var(--ic-ring)] [--color-destructive:var(--ic-destructive)] [--color-paper:var(--ic-paper)] [--color-popover-foreground:var(--ic-popover-foreground)] [--color-brand:var(--ic-brand)] [--color-brand-soft:var(--ic-brand-soft)] [--color-chart-1:var(--ic-chart-1)] [--color-chart-2:var(--ic-chart-2)] [--color-chart-3:var(--ic-chart-3)] [--color-chart-4:var(--ic-chart-4)] [--color-chart-5:var(--ic-chart-5)] dark:[--ic-background:#111111] dark:[--ic-foreground:#f6f3ec] dark:[--ic-primary:#f6f3ec] dark:[--ic-secondary:#cbc6bb] dark:[--ic-surface-border:#2a2a25] dark:[--ic-border:#2b2a25] dark:[--ic-card:#111111] dark:[--ic-card-foreground:#f6f3ec] dark:[--ic-muted:#171716] dark:[--ic-muted-foreground:#9a958a] dark:[--ic-accent:#1a1a18] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] dark:[--ic-accent-foreground:#f6f3ec] dark:[--ic-input:#2b2a25] dark:[--ic-ring:rgba(246,243,236,0.18)] dark:[--ic-destructive:#f87171] dark:[--ic-paper:#171716] dark:[--ic-popover-foreground:#f6f3ec] dark:[--ic-brand:#38bdf8] dark:[--ic-brand-soft:#0c4a6e] dark:[--ic-shadow-soft:0_20px_44px_-28px_rgba(0,0,0,0.6)] dark:[--ic-chart-1:oklch(0.68_0.17_250)] dark:[--ic-chart-2:oklch(0.82_0.09_225)] dark:[--ic-chart-3:oklch(0.58_0.15_260)] dark:[--ic-chart-4:oklch(0.75_0.12_235)] dark:[--ic-chart-5:oklch(0.88_0.06_220)]\";\n\nconst inputShellClassName = cn(\n  \"group flex w-full items-center gap-1 border border-input bg-background pr-1 pl-3.5 text-base transition-[color,box-shadow,border-color] sm:text-sm\",\n  \"hover:border-ring/40\",\n  \"focus-within:border-ring focus-within:ring-1 focus-within:ring-ring/25\",\n  \"[&:has(input:disabled)]:cursor-not-allowed [&:has(input:disabled)]:opacity-50\",\n  \"[&:has(input:read-only)]:bg-muted/30\",\n  \"[&:has(input[aria-invalid=true])]:border-destructive [&:has(input[aria-invalid=true])]:ring-1 [&:has(input[aria-invalid=true])]:ring-destructive/20 dark:[&:has(input[aria-invalid=true])]:border-destructive/50 dark:[&:has(input[aria-invalid=true])]:ring-destructive/40\"\n);\n\nconst inputControlClassName = cn(\n  \"h-full w-full min-w-0 flex-1 bg-transparent text-[16px] text-foreground outline-none placeholder:text-muted-foreground sm:text-sm\",\n  \"[-webkit-tap-highlight-color:transparent] read-only:cursor-default disabled:cursor-not-allowed\"\n);\n\nconst inputSizeClassNames = {\n  default: \"h-11\",\n  sm: \"h-9\",\n} as const;\n\nconst inputActionClassName =\n  \"flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-md text-muted-foreground/70 transition-colors hover:bg-accent/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50\";\n\ntype InputSize = keyof typeof inputSizeClassNames;\n\ntype SpringConfig = {\n  stiffness?: number;\n  damping?: number;\n  mass?: number;\n};\n\ntype InputRenderProps = React.InputHTMLAttributes<HTMLInputElement> & {\n  className?: string;\n  ref?: React.Ref<HTMLInputElement>;\n  style?: React.CSSProperties;\n};\n\ntype InputProps = Omit<InputPrimitive.Props, \"size\" | \"type\"> & {\n  children?: ReactNode;\n  description?: ReactNode;\n  descriptionClassName?: string;\n  endAdornment?: ReactNode;\n  errorMessage?: ReactNode;\n  errorMessageClassName?: string;\n  fontSize?: number;\n  invalid?: boolean;\n  label?: ReactNode;\n  labelClassName?: string;\n  required?: boolean;\n  shellClassName?: string;\n  showClear?: boolean;\n  showPasswordToggle?: boolean;\n  size?: InputSize;\n  spring?: SpringConfig;\n  startAdornment?: ReactNode;\n  type?: React.HTMLInputTypeAttribute;\n  wrapperClassName?: string;\n};\n\nfunction setRef<T>(ref: React.Ref<T> | undefined, value: T | null) {\n  if (typeof ref === \"function\") {\n    ref(value);\n    return;\n  }\n\n  if (ref) {\n    (ref as React.MutableRefObject<T | null>).current = value;\n  }\n}\n\nfunction resolveInputRenderProps(inputProps: InputRenderProps) {\n  const {\n    className: primitiveClassName,\n    ref: primitiveRef,\n    style: primitiveStyle,\n    ...resolvedInputProps\n  } = inputProps;\n\n  return {\n    primitiveClassName,\n    primitiveRef,\n    primitiveStyle,\n    resolvedInputProps,\n  };\n}\n\nfunction getPasswordChar() {\n  if (typeof navigator === \"undefined\") {\n    return \"\\u2022\";\n  }\n\n  return navigator.userAgent.match(FIREFOX_USER_AGENT_PATTERN)\n    ? \"\\u25CF\"\n    : \"\\u2022\";\n}\n\nfunction isChromeBrowser() {\n  if (typeof navigator === \"undefined\") {\n    return false;\n  }\n\n  return Boolean(navigator.userAgent.match(CHROME_USER_AGENT_PATTERN));\n}\n\nlet webKitPasswordMeasureSupported: boolean | null = null;\n\nfunction canMeasurePasswordWithTextSecurity() {\n  if (webKitPasswordMeasureSupported !== null) {\n    return webKitPasswordMeasureSupported;\n  }\n\n  if (typeof document === \"undefined\") {\n    webKitPasswordMeasureSupported = false;\n    return false;\n  }\n\n  const probe = document.createElement(\"span\");\n  probe.style.setProperty(\"-webkit-text-security\", \"disc\");\n  webKitPasswordMeasureSupported =\n    probe.style.getPropertyValue(\"-webkit-text-security\") === \"disc\";\n\n  return webKitPasswordMeasureSupported;\n}\n\nfunction getCaretIndex(target: HTMLInputElement) {\n  const selectionStart = target.selectionStart ?? 0;\n  const selectionEnd = target.selectionEnd ?? 0;\n\n  if (selectionStart === selectionEnd) {\n    return selectionStart;\n  }\n\n  return target.selectionDirection === \"backward\"\n    ? selectionStart\n    : selectionEnd;\n}\n\nfunction scrollCaretIntoView(\n  target: HTMLInputElement,\n  absoluteWidth: number,\n  isRtl: boolean\n) {\n  const styles = window.getComputedStyle(target);\n  const paddingLeft = Number.parseFloat(styles.paddingLeft) || 0;\n  const paddingRight = Number.parseFloat(styles.paddingRight) || 0;\n  const maxScroll = Math.max(0, target.scrollWidth - target.clientWidth);\n  const visibleRight = target.scrollLeft + target.clientWidth - paddingRight;\n  const visibleLeft = target.scrollLeft + paddingLeft;\n\n  if (isRtl) {\n    const scrollTarget = Math.max(\n      0,\n      target.scrollWidth - target.clientWidth - absoluteWidth + paddingLeft\n    );\n\n    if (target.scrollLeft > scrollTarget) {\n      target.scrollLeft = scrollTarget;\n    } else if (target.scrollLeft < scrollTarget - paddingRight) {\n      target.scrollLeft = Math.min(scrollTarget, maxScroll);\n    }\n\n    return;\n  }\n\n  if (absoluteWidth > visibleRight) {\n    target.scrollLeft = Math.min(\n      absoluteWidth - target.clientWidth + paddingRight,\n      maxScroll\n    );\n    return;\n  }\n\n  if (absoluteWidth < visibleLeft) {\n    target.scrollLeft = Math.max(0, absoluteWidth - paddingLeft);\n  }\n}\n\nfunction supportsSmoothCaret(\n  type?: string,\n  hasCustomRender?: boolean,\n  passwordVisible?: boolean\n) {\n  if (hasCustomRender) {\n    return false;\n  }\n\n  if (type === \"file\" || type === \"hidden\") {\n    return false;\n  }\n\n  if (type === \"password\" && passwordVisible) {\n    return true;\n  }\n\n  return SMOOTH_CARET_TYPES.has(type ?? \"text\");\n}\n\nfunction clearNativeInputValue(input: HTMLInputElement) {\n  const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n    window.HTMLInputElement.prototype,\n    \"value\"\n  )?.set;\n\n  nativeInputValueSetter?.call(input, \"\");\n  input.dispatchEvent(new Event(\"input\", { bubbles: true }));\n  input.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n\nfunction IconClear({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden\n      className={className}\n      fill=\"none\"\n      height=\"14\"\n      viewBox=\"0 0 14 14\"\n      width=\"14\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M3.5 3.5l7 7M10.5 3.5l-7 7\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"1.5\"\n      />\n    </svg>\n  );\n}\n\nfunction PasswordVisibilityIcon({\n  prefersReducedMotion,\n  visible,\n}: {\n  prefersReducedMotion: boolean;\n  visible: boolean;\n}) {\n  const blinkTransition = prefersReducedMotion\n    ? INSTANT_TRANSITION\n    : EYE_BLINK_TRANSITION;\n  const lidTransition = prefersReducedMotion\n    ? INSTANT_TRANSITION\n    : {\n        ...EYE_BLINK_TRANSITION,\n        delay: visible ? 0.02 : 0,\n      };\n  const pupilTransition = prefersReducedMotion\n    ? INSTANT_TRANSITION\n    : {\n        ...EYE_BLINK_TRANSITION,\n        delay: visible ? 0 : 0.05,\n      };\n\n  return (\n    <motion.svg\n      animate={{ rotate: 0, scale: 1 }}\n      aria-hidden\n      className=\"size-3.5 shrink-0\"\n      fill=\"currentColor\"\n      initial={false}\n      transition={blinkTransition}\n      viewBox=\"0 0 18 18\"\n    >\n      <motion.g\n        animate={{\n          opacity: visible ? 1 : 0,\n          scaleY: visible ? 1 : 0.42,\n          y: visible ? 0 : 1.35,\n        }}\n        initial={false}\n        style={{\n          transformBox: \"fill-box\",\n          transformOrigin: EYE_MORPH_ORIGIN,\n        }}\n        transition={lidTransition}\n      >\n        <path d={OPEN_EYELID_PATH} />\n      </motion.g>\n      <motion.g\n        animate={{\n          opacity: visible ? 0 : 1,\n          scaleY: visible ? 0.42 : 1,\n          y: visible ? -1.35 : 0,\n        }}\n        initial={false}\n        style={{\n          transformBox: \"fill-box\",\n          transformOrigin: EYE_MORPH_ORIGIN,\n        }}\n        transition={lidTransition}\n      >\n        <path d={CLOSED_EYELID_PATH} />\n      </motion.g>\n      <motion.circle\n        animate={{\n          opacity: visible ? 1 : 0,\n          r: visible ? 3.5 : 0,\n        }}\n        cx={9}\n        cy={10.5}\n        initial={false}\n        transition={pupilTransition}\n      />\n    </motion.svg>\n  );\n}\n\nfunction getFilledState(\n  value: InputPrimitive.Props[\"value\"] | undefined,\n  defaultValue: InputPrimitive.Props[\"defaultValue\"] | undefined\n) {\n  const resolved = value ?? defaultValue;\n  if (resolved === undefined || resolved === null) {\n    return false;\n  }\n\n  return String(resolved).length > 0;\n}\n\nfunction shouldIgnoreShellActivation(target: EventTarget | null) {\n  const element = target as HTMLElement | null;\n  if (!element) {\n    return true;\n  }\n\n  if (element.closest('[data-slot=\"input-action\"]')) {\n    return true;\n  }\n\n  if (element.closest('[data-slot=\"input\"]')) {\n    return true;\n  }\n\n  return false;\n}\n\nfunction getInputFieldMeta({\n  ariaDescribedByProp,\n  ariaInvalidProp,\n  description,\n  errorMessage,\n  generatedId,\n  id,\n  invalid,\n  label,\n}: {\n  ariaDescribedByProp?: string;\n  ariaInvalidProp?: boolean | \"false\" | \"true\" | \"grammar\" | \"spelling\";\n  description?: ReactNode;\n  errorMessage?: ReactNode;\n  generatedId: string;\n  id?: string;\n  invalid: boolean;\n  label?: ReactNode;\n}) {\n  const inputId = id ?? generatedId;\n  const descriptionId = description ? `${inputId}-description` : undefined;\n  const errorId = errorMessage ? `${inputId}-error` : undefined;\n  const showInvalid =\n    invalid || Boolean(errorMessage) || ariaInvalidProp === true;\n  const hasFieldChrome = Boolean(label || description || errorMessage);\n\n  return {\n    describedBy: [ariaDescribedByProp, descriptionId, errorId]\n      .filter(Boolean)\n      .join(\" \"),\n    descriptionId,\n    errorId,\n    hasFieldChrome,\n    inputId,\n    showInvalid,\n  };\n}\n\nfunction useShellFocus(\n  disabled: boolean | undefined,\n  inputRef: React.RefObject<HTMLInputElement | null>\n) {\n  const focusInputFromShell = useCallback(() => {\n    if (disabled) {\n      return;\n    }\n\n    inputRef.current?.focus();\n  }, [disabled, inputRef]);\n\n  const handleShellMouseDown = useCallback(\n    (event: React.MouseEvent<HTMLDivElement>) => {\n      if (disabled || shouldIgnoreShellActivation(event.target)) {\n        return;\n      }\n\n      event.preventDefault();\n      focusInputFromShell();\n    },\n    [disabled, focusInputFromShell]\n  );\n\n  return { handleShellMouseDown };\n}\n\ntype UseSmoothCaretOptions = {\n  passwordVisible: boolean;\n  prefersReducedMotion: boolean | null;\n  spring: SpringConfig;\n  userRender: InputProps[\"render\"];\n};\n\nfunction useSmoothCaret({\n  passwordVisible,\n  prefersReducedMotion,\n  spring,\n  userRender,\n}: UseSmoothCaretOptions) {\n  const caretX = useMotionValue(0);\n  const caretOpacity = useMotionValue(0);\n  const containerRef = useRef<HTMLDivElement>(null);\n  const inputRef = useRef<HTMLInputElement>(null);\n  const measureRef = useRef<HTMLSpanElement>(null);\n  const caretXRef = useRef(caretX);\n  caretXRef.current = caretX;\n  const caretOpacityMotionRef = useRef(caretOpacity);\n  caretOpacityMotionRef.current = caretOpacity;\n\n  const springCaretX = useSpring(\n    caretX,\n    prefersReducedMotion\n      ? { stiffness: 10_000, damping: 100, mass: 0.1 }\n      : spring\n  );\n\n  const syncMeasureSpan = useCallback((isPassword = false) => {\n    const input = inputRef.current;\n    const measureSpan = measureRef.current;\n    if (!(input && measureSpan)) {\n      return;\n    }\n\n    const styles = window.getComputedStyle(input);\n    const passwordChar = getPasswordChar();\n    const useWebKitPasswordMeasure =\n      isPassword && canMeasurePasswordWithTextSecurity();\n\n    let resolvedFontSize = styles.fontSize;\n    if (\n      passwordChar === \"\\u2022\" &&\n      isPassword &&\n      !isChromeBrowser() &&\n      !useWebKitPasswordMeasure\n    ) {\n      resolvedFontSize = `${Number.parseFloat(resolvedFontSize) + 6.25}px`;\n    }\n\n    measureSpan.style.font = `${styles.fontStyle} ${styles.fontWeight} ${resolvedFontSize} ${styles.fontFamily}`;\n    measureSpan.style.letterSpacing = styles.letterSpacing;\n    measureSpan.style.lineHeight = styles.lineHeight;\n    measureSpan.style.fontFeatureSettings = styles.fontFeatureSettings;\n    measureSpan.style.fontVariationSettings = styles.fontVariationSettings;\n    measureSpan.style.direction = styles.direction;\n  }, []);\n\n  const measurePrefixWidth = useCallback(\n    (text: string, isPassword = false) => {\n      const input = inputRef.current;\n      const measureSpan = measureRef.current;\n      if (!(input && measureSpan)) {\n        return null;\n      }\n\n      syncMeasureSpan(isPassword);\n\n      if (isPassword && canMeasurePasswordWithTextSecurity()) {\n        measureSpan.style.setProperty(\"-webkit-text-security\", \"disc\");\n        measureSpan.textContent = text;\n      } else if (isPassword) {\n        measureSpan.style.removeProperty(\"-webkit-text-security\");\n        measureSpan.textContent = getPasswordChar().repeat(text.length);\n      } else {\n        measureSpan.style.removeProperty(\"-webkit-text-security\");\n        measureSpan.textContent = text;\n      }\n\n      const paddingLeft =\n        Number.parseFloat(window.getComputedStyle(input).paddingLeft) || 0;\n\n      return text.length > 0\n        ? measureSpan.offsetWidth + paddingLeft\n        : paddingLeft - 1;\n    },\n    [syncMeasureSpan]\n  );\n\n  const updateCaretFromInput = useCallback(\n    (target: HTMLInputElement) => {\n      if (\n        !supportsSmoothCaret(target.type, Boolean(userRender), passwordVisible)\n      ) {\n        return;\n      }\n\n      const selectionStart = target.selectionStart ?? 0;\n      const selectionEnd = target.selectionEnd ?? 0;\n      const hasSelection = selectionStart !== selectionEnd;\n      const caretIndex = getCaretIndex(target);\n      const isPassword = target.type === \"password\";\n      const textBeforeCaret = target.value.slice(0, caretIndex);\n\n      const absoluteWidth = measurePrefixWidth(textBeforeCaret, isPassword);\n      if (absoluteWidth === null) {\n        return;\n      }\n\n      const styles = window.getComputedStyle(target);\n      const isRtl = styles.direction === \"rtl\";\n      const paddingLeft = Number.parseFloat(styles.paddingLeft) || 0;\n      const paddingRight = Number.parseFloat(styles.paddingRight) || 0;\n\n      scrollCaretIntoView(target, absoluteWidth, isRtl);\n\n      let caretPosition = absoluteWidth - target.scrollLeft;\n\n      if (isRtl) {\n        const fullWidth =\n          measurePrefixWidth(target.value, isPassword) ?? paddingLeft;\n        caretPosition =\n          target.clientWidth - paddingRight - (fullWidth - absoluteWidth);\n      }\n\n      const minX = paddingLeft - 1;\n      const maxX = target.clientWidth - paddingRight;\n      const isCaretVisible = caretPosition >= minX && caretPosition <= maxX + 1;\n\n      caretXRef.current.set(Math.min(Math.max(caretPosition, minX), maxX));\n\n      if (!isCaretVisible || hasSelection) {\n        caretOpacityMotionRef.current.set(0);\n        return;\n      }\n\n      caretOpacityMotionRef.current.set(1);\n    },\n    [measurePrefixWidth, passwordVisible, userRender]\n  );\n\n  const updateCaretRef = useRef(updateCaretFromInput);\n  updateCaretRef.current = updateCaretFromInput;\n  const caretFrameRef = useRef<number | null>(null);\n\n  const scheduleCaretUpdate = useCallback((target: HTMLInputElement) => {\n    if (caretFrameRef.current !== null) {\n      cancelAnimationFrame(caretFrameRef.current);\n    }\n\n    caretFrameRef.current = requestAnimationFrame(() => {\n      caretFrameRef.current = null;\n      updateCaretRef.current(target);\n    });\n  }, []);\n\n  useEffect(\n    () => () => {\n      if (caretFrameRef.current !== null) {\n        cancelAnimationFrame(caretFrameRef.current);\n      }\n    },\n    []\n  );\n\n  return {\n    caretOpacity,\n    caretOpacityRef: caretOpacityMotionRef,\n    containerRef,\n    inputRef,\n    measureRef,\n    scheduleCaretUpdate,\n    springCaretX,\n    updateCaretRef,\n  };\n}\n\nfunction useSmoothCaretListeners(\n  smoothCaret: boolean,\n  containerRef: React.RefObject<HTMLDivElement | null>,\n  inputRef: React.RefObject<HTMLInputElement | null>,\n  updateCaretRef: React.MutableRefObject<(target: HTMLInputElement) => void>\n) {\n  useEffect(() => {\n    const input = inputRef.current;\n    const container = containerRef.current;\n    if (!(input && container && smoothCaret)) {\n      return;\n    }\n\n    const updateCaretIfFocused = () => {\n      if (document.activeElement === input) {\n        updateCaretRef.current(input);\n      }\n    };\n\n    const handleSelectionChange = () => {\n      if (document.activeElement !== input) {\n        return;\n      }\n\n      requestAnimationFrame(() => {\n        if (document.activeElement === input) {\n          updateCaretRef.current(input);\n        }\n      });\n    };\n\n    document.addEventListener(\"selectionchange\", handleSelectionChange);\n    if (typeof document !== \"undefined\" && \"fonts\" in document) {\n      document.fonts.addEventListener(\"loadingdone\", updateCaretIfFocused);\n      document.fonts.ready.then(updateCaretIfFocused).catch(() => undefined);\n    }\n    input.addEventListener(\"scroll\", updateCaretIfFocused);\n\n    const resizeObserver = new ResizeObserver(updateCaretIfFocused);\n    resizeObserver.observe(container);\n\n    updateCaretIfFocused();\n\n    return () => {\n      document.removeEventListener(\"selectionchange\", handleSelectionChange);\n      if (typeof document !== \"undefined\" && \"fonts\" in document) {\n        document.fonts.removeEventListener(\"loadingdone\", updateCaretIfFocused);\n      }\n      input.removeEventListener(\"scroll\", updateCaretIfFocused);\n      resizeObserver.disconnect();\n    };\n  }, [containerRef, inputRef, smoothCaret, updateCaretRef]);\n}\n\ntype InputShellProps = {\n  ariaInvalidProp?: boolean | \"false\" | \"true\" | \"grammar\" | \"spelling\";\n  caretOpacity: ReturnType<typeof useMotionValue<number>>;\n  caretOpacityRef: React.MutableRefObject<\n    ReturnType<typeof useMotionValue<number>>\n  >;\n  children?: ReactNode;\n  className?: InputPrimitive.Props[\"className\"];\n  containerRef: React.RefObject<HTMLDivElement | null>;\n  defaultValue?: InputPrimitive.Props[\"defaultValue\"];\n  describedBy?: string;\n  disabled?: boolean;\n  endAdornment?: ReactNode;\n  filled: boolean;\n  fontSize?: number;\n  forwardRef: React.Ref<HTMLInputElement>;\n  handleClear: (event: React.MouseEvent<HTMLButtonElement>) => void;\n  handleShellMouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;\n  handleValueChange: NonNullable<InputPrimitive.Props[\"onValueChange\"]>;\n  hasFieldChrome: boolean;\n  inputId: string;\n  inputProps: Omit<\n    InputPrimitive.Props,\n    | \"className\"\n    | \"defaultValue\"\n    | \"disabled\"\n    | \"id\"\n    | \"onBlur\"\n    | \"onChange\"\n    | \"onFocus\"\n    | \"onKeyDown\"\n    | \"onValueChange\"\n    | \"placeholder\"\n    | \"readOnly\"\n    | \"render\"\n    | \"required\"\n    | \"type\"\n    | \"value\"\n  >;\n  inputRef: React.RefObject<HTMLInputElement | null>;\n  measureRef: React.RefObject<HTMLSpanElement | null>;\n  onBlur?: InputPrimitive.Props[\"onBlur\"];\n  onChange?: InputPrimitive.Props[\"onChange\"];\n  onFocus?: InputPrimitive.Props[\"onFocus\"];\n  onKeyDown?: InputPrimitive.Props[\"onKeyDown\"];\n  passwordVisible: boolean;\n  placeholder?: string;\n  prefersReducedMotion: boolean | null;\n  readOnly?: boolean;\n  required?: boolean;\n  resolvedType: React.HTMLInputTypeAttribute;\n  scheduleCaretUpdate: (target: HTMLInputElement) => void;\n  setPasswordVisible: React.Dispatch<React.SetStateAction<boolean>>;\n  setFilled: React.Dispatch<React.SetStateAction<boolean>>;\n  shellClassName?: string;\n  showClear: boolean;\n  showInvalid: boolean;\n  showPasswordToggle: boolean;\n  size: InputSize;\n  smoothCaret: boolean;\n  springCaretX: ReturnType<typeof useSpring>;\n  startAdornment?: ReactNode;\n  style?: InputPrimitive.Props[\"style\"];\n  type: React.HTMLInputTypeAttribute;\n  userRender: InputProps[\"render\"];\n  value?: InputPrimitive.Props[\"value\"];\n  wrapperClassName?: string;\n};\n\nfunction InputShell({\n  ariaInvalidProp,\n  caretOpacity,\n  caretOpacityRef,\n  children,\n  className,\n  containerRef,\n  defaultValue,\n  describedBy,\n  disabled,\n  endAdornment,\n  filled,\n  fontSize,\n  forwardRef,\n  handleClear,\n  handleShellMouseDown,\n  handleValueChange,\n  hasFieldChrome,\n  inputId,\n  inputProps,\n  inputRef,\n  measureRef,\n  onBlur,\n  onChange,\n  onFocus,\n  onKeyDown,\n  passwordVisible,\n  placeholder,\n  prefersReducedMotion,\n  readOnly,\n  required,\n  resolvedType,\n  scheduleCaretUpdate,\n  setFilled,\n  setPasswordVisible,\n  shellClassName,\n  showClear,\n  showInvalid,\n  showPasswordToggle,\n  size,\n  smoothCaret,\n  springCaretX,\n  startAdornment,\n  style,\n  type,\n  userRender,\n  value,\n  wrapperClassName,\n}: InputShellProps) {\n  return (\n    <div\n      className={cn(\n        componentThemeClassName,\n        controlCornerClassName,\n        inputShellClassName,\n        inputSizeClassNames[size],\n        shellClassName,\n        hasFieldChrome ? undefined : wrapperClassName\n      )}\n      data-size={size}\n      data-slot=\"input-shell\"\n      onMouseDown={handleShellMouseDown}\n    >\n      {startAdornment ? (\n        <span className=\"flex shrink-0 items-center text-muted-foreground\">\n          {startAdornment}\n        </span>\n      ) : null}\n      <div\n        className=\"relative grid min-w-0 flex-1 grid-cols-1\"\n        ref={containerRef}\n        style={{\n          ...(smoothCaret ? { caretColor: \"transparent\" } : undefined),\n          ...(fontSize === undefined ? undefined : { fontSize }),\n        }}\n      >\n        <InputPrimitive\n          {...inputProps}\n          aria-describedby={describedBy || undefined}\n          aria-invalid={showInvalid ? true : ariaInvalidProp}\n          className={cn(\n            inputControlClassName,\n            \"col-start-1 col-end-2 row-start-1 row-end-2 text-inherit leading-none\",\n            className\n          )}\n          data-slot=\"input\"\n          defaultValue={defaultValue}\n          disabled={disabled}\n          id={inputId}\n          onBlur={(event) => {\n            if (smoothCaret) {\n              caretOpacityRef.current.set(0);\n            }\n            onBlur?.(event);\n          }}\n          onChange={(event) => {\n            setFilled(event.currentTarget.value.length > 0);\n            onChange?.(event);\n            scheduleCaretUpdate(event.currentTarget);\n          }}\n          onFocus={(event) => {\n            onFocus?.(event);\n            scheduleCaretUpdate(event.currentTarget);\n          }}\n          onKeyDown={(event) => {\n            onKeyDown?.(event);\n            scheduleCaretUpdate(event.currentTarget);\n          }}\n          onValueChange={handleValueChange}\n          placeholder={placeholder}\n          readOnly={readOnly}\n          render={(inputProps, state) => {\n            if (userRender) {\n              return typeof userRender === \"function\"\n                ? userRender(inputProps, state)\n                : userRender;\n            }\n\n            const {\n              primitiveClassName,\n              primitiveRef,\n              primitiveStyle,\n              resolvedInputProps,\n            } = resolveInputRenderProps(inputProps);\n\n            return (\n              <input\n                {...resolvedInputProps}\n                className={primitiveClassName}\n                ref={(node) => {\n                  inputRef.current = node;\n                  setRef(primitiveRef, node);\n                  setRef(forwardRef, node);\n                }}\n                style={{ ...primitiveStyle, ...style }}\n              />\n            );\n          }}\n          required={required}\n          type={resolvedType}\n          {...(value !== undefined ? { value } : { defaultValue })}\n        />\n        {smoothCaret ? (\n          <>\n            <span\n              aria-hidden\n              className=\"pointer-events-none invisible absolute top-0 left-0 whitespace-pre\"\n              ref={measureRef}\n            />\n            <motion.div\n              className=\"pointer-events-none col-start-1 col-end-2 row-start-1 row-end-2 h-[1em] w-px self-center bg-foreground\"\n              style={{ opacity: caretOpacity, x: springCaretX }}\n            />\n          </>\n        ) : null}\n      </div>\n      {showClear && filled && !disabled && !readOnly ? (\n        <button\n          aria-label=\"Clear input\"\n          className={inputActionClassName}\n          data-slot=\"input-action\"\n          onClick={handleClear}\n          onMouseDown={(event) => {\n            event.preventDefault();\n          }}\n          type=\"button\"\n        >\n          <IconClear />\n        </button>\n      ) : null}\n      {showPasswordToggle && type === \"password\" && !disabled && !readOnly ? (\n        <button\n          aria-label={passwordVisible ? \"Hide password\" : \"Show password\"}\n          aria-pressed={passwordVisible}\n          className={inputActionClassName}\n          data-slot=\"input-action\"\n          onClick={(event) => {\n            event.preventDefault();\n            event.stopPropagation();\n            const input = inputRef.current;\n            const selectionStart = input?.selectionStart ?? null;\n            const selectionEnd = input?.selectionEnd ?? null;\n            setPasswordVisible((current) => !current);\n            if (input) {\n              requestAnimationFrame(() => {\n                input.focus();\n                if (selectionStart !== null && selectionEnd !== null) {\n                  input.setSelectionRange(selectionStart, selectionEnd);\n                }\n                scheduleCaretUpdate(input);\n              });\n            }\n          }}\n          onMouseDown={(event) => {\n            event.preventDefault();\n          }}\n          type=\"button\"\n        >\n          <PasswordVisibilityIcon\n            prefersReducedMotion={prefersReducedMotion === true}\n            visible={passwordVisible}\n          />\n        </button>\n      ) : null}\n      {endAdornment ? (\n        <span className=\"flex shrink-0 items-center text-muted-foreground\">\n          {endAdornment}\n        </span>\n      ) : null}\n      {children}\n    </div>\n  );\n}\n\nfunction InputFieldWrapper({\n  children,\n  description,\n  descriptionClassName,\n  descriptionId,\n  errorMessage,\n  errorMessageClassName,\n  errorId,\n  label,\n  inputId,\n  labelClassName,\n  required,\n  wrapperClassName,\n}: {\n  children: ReactNode;\n  description?: ReactNode;\n  descriptionClassName?: string;\n  descriptionId?: string;\n  errorMessage?: ReactNode;\n  errorMessageClassName?: string;\n  errorId?: string;\n  inputId: string;\n  label?: ReactNode;\n  labelClassName?: string;\n  required: boolean;\n  wrapperClassName?: string;\n}) {\n  return (\n    <div className={cn(\"flex w-full flex-col gap-2\", wrapperClassName)}>\n      {label ? (\n        <label\n          className={cn(\"font-medium text-foreground text-sm\", labelClassName)}\n          htmlFor={inputId}\n        >\n          {label}\n          {required ? (\n            <span aria-hidden className=\"text-destructive\">\n              {\" \"}\n              *\n            </span>\n          ) : null}\n        </label>\n      ) : null}\n      {children}\n      {description ? (\n        <p\n          className={cn(\n            \"text-muted-foreground text-xs leading-snug\",\n            descriptionClassName\n          )}\n          id={descriptionId}\n        >\n          {description}\n        </p>\n      ) : null}\n      {errorMessage ? (\n        <p\n          aria-live=\"polite\"\n          className={cn(\n            \"text-destructive text-xs leading-snug\",\n            errorMessageClassName\n          )}\n          id={errorId}\n          role=\"alert\"\n        >\n          {errorMessage}\n        </p>\n      ) : null}\n    </div>\n  );\n}\n\nconst Input = forwardRef<HTMLInputElement, InputProps>(\n  (\n    {\n      \"aria-describedby\": ariaDescribedByProp,\n      \"aria-invalid\": ariaInvalidProp,\n      children,\n      className,\n      defaultValue,\n      description,\n      descriptionClassName,\n      disabled,\n      endAdornment,\n      errorMessage,\n      errorMessageClassName,\n      fontSize,\n      id,\n      invalid = false,\n      label,\n      labelClassName,\n      onBlur,\n      onChange,\n      onFocus,\n      onKeyDown,\n      onValueChange,\n      placeholder,\n      readOnly,\n      render: userRender,\n      required = false,\n      shellClassName,\n      showClear = false,\n      showPasswordToggle: showPasswordToggleProp,\n      size = \"default\",\n      spring = DEFAULT_SPRING,\n      startAdornment,\n      style,\n      type = \"text\",\n      value,\n      wrapperClassName,\n      ...props\n    },\n    ref\n  ) => {\n    const generatedId = useId();\n    const fieldMeta = getInputFieldMeta({\n      ariaDescribedByProp,\n      ariaInvalidProp,\n      description,\n      errorMessage,\n      generatedId,\n      id,\n      invalid,\n      label,\n    });\n\n    const [passwordVisible, setPasswordVisible] = useState(false);\n    const [filled, setFilled] = useState(() =>\n      getFilledState(value, defaultValue)\n    );\n    const prefersReducedMotion = useReducedMotion();\n    const showPasswordToggle =\n      showPasswordToggleProp ?? (type === \"password\" && !readOnly && !disabled);\n    const resolvedType = type === \"password\" && passwordVisible ? \"text\" : type;\n    const smoothCaret = supportsSmoothCaret(\n      resolvedType,\n      Boolean(userRender),\n      passwordVisible\n    );\n\n    const {\n      caretOpacity,\n      caretOpacityRef,\n      containerRef,\n      inputRef,\n      measureRef,\n      scheduleCaretUpdate,\n      springCaretX,\n      updateCaretRef,\n    } = useSmoothCaret({\n      passwordVisible,\n      prefersReducedMotion,\n      spring,\n      userRender,\n    });\n\n    useSmoothCaretListeners(\n      smoothCaret,\n      containerRef,\n      inputRef,\n      updateCaretRef\n    );\n\n    const { handleShellMouseDown } = useShellFocus(disabled, inputRef);\n\n    const handleValueChange = useCallback<\n      NonNullable<InputPrimitive.Props[\"onValueChange\"]>\n    >(\n      (nextValue, eventDetails) => {\n        setFilled(nextValue.length > 0);\n        onValueChange?.(nextValue, eventDetails);\n\n        const target = inputRef.current;\n        if (target) {\n          scheduleCaretUpdate(target);\n        }\n      },\n      [inputRef, onValueChange, scheduleCaretUpdate]\n    );\n\n    const handleClear = useCallback(\n      (event: React.MouseEvent<HTMLButtonElement>) => {\n        event.preventDefault();\n        event.stopPropagation();\n\n        const input = inputRef.current;\n        if (!input || disabled || readOnly) {\n          return;\n        }\n\n        clearNativeInputValue(input);\n        setFilled(false);\n        onValueChange?.(\"\", {\n          event: event.nativeEvent,\n          reason: \"none\",\n        } as unknown as InputChangeEventDetails);\n        input.focus();\n        scheduleCaretUpdate(input);\n      },\n      [disabled, inputRef, onValueChange, readOnly, scheduleCaretUpdate]\n    );\n\n    useEffect(() => {\n      if (value !== undefined) {\n        setFilled(getFilledState(value, defaultValue));\n      }\n    }, [defaultValue, value]);\n\n    useEffect(() => {\n      if (type !== \"password\") {\n        setPasswordVisible(false);\n      }\n    }, [type]);\n\n    const shell = (\n      <InputShell\n        ariaInvalidProp={ariaInvalidProp}\n        caretOpacity={caretOpacity}\n        caretOpacityRef={caretOpacityRef}\n        className={className}\n        containerRef={containerRef}\n        defaultValue={defaultValue}\n        describedBy={fieldMeta.describedBy}\n        disabled={disabled}\n        endAdornment={endAdornment}\n        filled={filled}\n        fontSize={fontSize}\n        forwardRef={ref}\n        handleClear={handleClear}\n        handleShellMouseDown={handleShellMouseDown}\n        handleValueChange={handleValueChange}\n        hasFieldChrome={fieldMeta.hasFieldChrome}\n        inputId={fieldMeta.inputId}\n        inputProps={props}\n        inputRef={inputRef}\n        measureRef={measureRef}\n        onBlur={onBlur}\n        onChange={onChange}\n        onFocus={onFocus}\n        onKeyDown={onKeyDown}\n        passwordVisible={passwordVisible}\n        placeholder={placeholder}\n        prefersReducedMotion={prefersReducedMotion}\n        readOnly={readOnly}\n        required={required}\n        resolvedType={resolvedType}\n        scheduleCaretUpdate={scheduleCaretUpdate}\n        setFilled={setFilled}\n        setPasswordVisible={setPasswordVisible}\n        shellClassName={shellClassName}\n        showClear={showClear}\n        showInvalid={fieldMeta.showInvalid}\n        showPasswordToggle={showPasswordToggle}\n        size={size}\n        smoothCaret={smoothCaret}\n        springCaretX={springCaretX}\n        startAdornment={startAdornment}\n        style={style}\n        type={type}\n        userRender={userRender}\n        value={value}\n        wrapperClassName={wrapperClassName}\n      >\n        {children}\n      </InputShell>\n    );\n\n    if (!fieldMeta.hasFieldChrome) {\n      return shell;\n    }\n\n    return (\n      <InputFieldWrapper\n        description={description}\n        descriptionClassName={descriptionClassName}\n        descriptionId={fieldMeta.descriptionId}\n        errorId={fieldMeta.errorId}\n        errorMessage={errorMessage}\n        errorMessageClassName={errorMessageClassName}\n        inputId={fieldMeta.inputId}\n        label={label}\n        labelClassName={labelClassName}\n        required={required}\n        wrapperClassName={wrapperClassName}\n      >\n        {shell}\n      </InputFieldWrapper>\n    );\n  }\n);\nInput.displayName = \"Input\";\n\nexport { Input };\nexport type { InputChangeEventDetails } from \"@base-ui/react/input\";\nexport type { InputProps, InputSize };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Input",
  "description": "Input with a spring-animated caret."
}
