{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "liquid-glass-searchbox",
  "title": "Liquid Glass Searchbox",
  "author": "jyotirmoydas <jyotirmoydas12345@gmail.com>",
  "description": "An iOS 26-inspired liquid glass searchbox with focus animations and SVG refraction effects.",
  "dependencies": ["cn", "motion", "lucide-react"],
  "registryDependencies": ["https://jyotimoydas.in/r/liquid-glass-lib.json"],
  "files": [
    {
      "path": "src/registry/liquid-glass-searchbox/liquid-glass-searchbox.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"cn\";\nimport { SearchIcon, XIcon } from \"lucide-react\";\nimport { motion, useMotionValue, useSpring, useTransform } from \"motion/react\";\nimport type React from \"react\";\nimport { forwardRef, useEffect, useId, useRef } from \"react\";\n\nimport { RefractionFilter } from \"@/registry/liquid-glass-lib/refraction-filter\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface LiquidGlassSearchboxProps\n  extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"onChange\"> {\n  // ─────────────────────────────────────────────────────────────────────────\n  // Input Control\n  // ─────────────────────────────────────────────────────────────────────────\n\n  /**\n   * Controlled value of the searchbox.\n   */\n  value?: string;\n\n  /**\n   * Callback triggered when the input value changes.\n   */\n  onChange?: (value: string) => void;\n\n  /**\n   * Callback triggered when the clear button is clicked.\n   */\n  onClear?: () => void;\n\n  /**\n   * Whether to show the clear button when there's text.\n   * @default true\n   */\n  showClearButton?: boolean;\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Appearance\n  // ─────────────────────────────────────────────────────────────────────────\n\n  /**\n   * Additional CSS classes.\n   */\n  className?: string;\n\n  /**\n   * Width of the searchbox in pixels.\n   * @default 420\n   */\n  width?: number;\n\n  /**\n   * Height of the searchbox in pixels.\n   * @default 56\n   */\n  height?: number;\n\n  /**\n   * Background color opacity when idle.\n   * @default 0.05\n   */\n  idleOpacity?: number;\n\n  /**\n   * Background color opacity when focused.\n   * @default 0.2\n   */\n  focusOpacity?: number;\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Optical Parameters (Advanced)\n  // ─────────────────────────────────────────────────────────────────────────\n\n  /**\n   * Blur level for the glass refraction effect.\n   * @default 1\n   */\n  blur?: number;\n\n  /**\n   * Opacity of the specular highlight.\n   * @default 0.2\n   */\n  specularOpacity?: number;\n\n  /**\n   * Saturation multiplier for the specular highlight.\n   * @default 4\n   */\n  specularSaturation?: number;\n\n  /**\n   * Base refraction level (controls magnification intensity).\n   * @default 0.7\n   */\n  refractionLevel?: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A sleek liquid glass searchbox with iOS 26-inspired refraction effects.\n *\n * @example\n * ```tsx\n * <LiquidGlassSearchbox\n *   placeholder=\"Search...\"\n *   onChange={(value) => console.log(\"Search:\", value)}\n * />\n * ```\n *\n * @example With controlled value\n * ```tsx\n * const [query, setQuery] = useState(\"\");\n * <LiquidGlassSearchbox\n *   value={query}\n *   onChange={setQuery}\n *   onClear={() => setQuery(\"\")}\n * />\n * ```\n */\nexport const LiquidGlassSearchbox = forwardRef<\n  HTMLInputElement,\n  LiquidGlassSearchboxProps\n>(\n  (\n    {\n      // Input Control\n      value,\n      onChange,\n      onClear,\n      showClearButton = true,\n      placeholder = \"Search\",\n      disabled = false,\n      // Appearance\n      className,\n      width = 420,\n      height = 56,\n      idleOpacity = 0.05,\n      focusOpacity = 0.2,\n      // Optical Parameters\n      blur = 1,\n      specularOpacity = 0.2,\n      specularSaturation = 4,\n      refractionLevel = 0.7,\n      // Rest of input props\n      ...inputProps\n    },\n    ref\n  ) => {\n    const filterId = useId();\n    const internalRef = useRef<HTMLInputElement>(null);\n    const inputRef = (ref as React.RefObject<HTMLInputElement>) || internalRef;\n    const radius = height / 2;\n\n    // ─────────────────────────────────────────────────────────────────────────\n    // Motion Values\n    // ─────────────────────────────────────────────────────────────────────────\n    const focused = useMotionValue(0);\n    const pointerDown = useMotionValue(0);\n\n    // Background opacity based on focus and press\n    const backgroundFromPointer = useTransform(\n      pointerDown,\n      [0, 1],\n      [idleOpacity, 0.3]\n    );\n    const backgroundFromFocus = useTransform(\n      focused,\n      [0, 1],\n      [idleOpacity, focusOpacity]\n    );\n    const backgroundOpacity = useSpring(\n      useTransform([backgroundFromPointer, backgroundFromFocus], (vals) =>\n        Math.max(vals[0] as number, vals[1] as number)\n      ),\n      { damping: 80, stiffness: 2000 }\n    );\n\n    // Scale animation on focus/press\n    const scale = useSpring(\n      useTransform(() => {\n        const pd = pointerDown.get();\n        const f = focused.get();\n        return (f ? 1 : 0.98) * (pd ? 0.99 : 1);\n      }),\n      { damping: 40, stiffness: 800 }\n    );\n\n    // Refraction scale ratio\n    const scaleRatio = useMotionValue(refractionLevel);\n\n    // ─────────────────────────────────────────────────────────────────────────\n    // Pointer Release Handler\n    // ─────────────────────────────────────────────────────────────────────────\n    useEffect(() => {\n      const onPointerUp = () => pointerDown.set(0);\n      window.addEventListener(\"pointerup\", onPointerUp);\n      window.addEventListener(\"mouseup\", onPointerUp);\n      window.addEventListener(\"touchend\", onPointerUp);\n      return () => {\n        window.removeEventListener(\"pointerup\", onPointerUp);\n        window.removeEventListener(\"mouseup\", onPointerUp);\n        window.removeEventListener(\"touchend\", onPointerUp);\n      };\n    }, [pointerDown]);\n\n    // ─────────────────────────────────────────────────────────────────────────\n    // Handlers\n    // ─────────────────────────────────────────────────────────────────────────\n    const handleFocus = () => focused.set(1);\n    const handleBlur = () => focused.set(0);\n    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n      onChange?.(e.target.value);\n    };\n    const handleClear = () => {\n      onClear?.();\n      inputRef.current?.focus();\n    };\n    const handleContainerClick = () => {\n      inputRef.current?.focus();\n    };\n\n    const hasValue = value && value.length > 0;\n\n    // ─────────────────────────────────────────────────────────────────────────\n    // Render\n    // ─────────────────────────────────────────────────────────────────────────\n    return (\n      <motion.div\n        className={cn(\n          \"relative select-none\",\n          disabled && \"pointer-events-none opacity-50\",\n          className\n        )}\n        style={{ width, height, borderRadius: radius, scale }}\n        onMouseDown={() => !disabled && pointerDown.set(1)}\n        onMouseUp={() => pointerDown.set(0)}\n        onClick={handleContainerClick}\n      >\n        {/* Refraction Filter */}\n        <RefractionFilter\n          id={filterId}\n          width={width}\n          height={height}\n          radius={radius}\n          bezelWidth={27}\n          glassThickness={70}\n          bezelType=\"convex_squircle\"\n          refractiveIndex={1.5}\n          blur={blur}\n          scaleRatio={scaleRatio}\n          specularOpacity={specularOpacity}\n          specularSaturation={specularSaturation}\n        />\n\n        {/* Glass Layer */}\n        <motion.div\n          className=\"absolute inset-0\"\n          style={{\n            borderRadius: radius,\n            backdropFilter: `url(#${filterId})`,\n            backgroundColor: useTransform(\n              backgroundOpacity,\n              (op) => `rgba(255, 255, 255, ${op})`\n            ),\n            boxShadow: \"0 4px 16px rgba(0, 0, 0, 0.16)\",\n            transform: \"translateZ(0)\",\n          }}\n        />\n\n        {/* Content */}\n        <div\n          className=\"absolute inset-0 flex items-center gap-3 px-5 text-foreground/90\"\n          style={{ borderRadius: radius, zIndex: 1 }}\n        >\n          <SearchIcon\n            className=\"shrink-0 opacity-70\"\n            size={20}\n            aria-hidden=\"true\"\n          />\n\n          <input\n            ref={inputRef}\n            type=\"search\"\n            placeholder={placeholder}\n            aria-label={placeholder}\n            value={value}\n            onChange={handleChange}\n            onFocus={handleFocus}\n            onBlur={handleBlur}\n            disabled={disabled}\n            className={cn(\n              \"min-w-0 flex-1 border-0 bg-transparent outline-none\",\n              \"text-[15px] leading-none text-foreground/70\",\n              \"placeholder:text-foreground/50\",\n              \"select-text selection:bg-blue-500/30 selection:text-inherit\"\n            )}\n            style={{ padding: 0 }}\n            {...inputProps}\n          />\n\n          {/* Clear Button */}\n          {showClearButton && hasValue && (\n            <button\n              type=\"button\"\n              onClick={handleClear}\n              className=\"shrink-0 rounded-full p-1 opacity-50 transition-opacity hover:opacity-100\"\n              aria-label=\"Clear search\"\n            >\n              <XIcon size={16} />\n            </button>\n          )}\n        </div>\n      </motion.div>\n    );\n  }\n);\n\nLiquidGlassSearchbox.displayName = \"LiquidGlassSearchbox\";\n\nexport default LiquidGlassSearchbox;\n",
      "type": "registry:component"
    }
  ],
  "docs": "https://jyotimoydas.in/components/liquid-glass-searchbox",
  "type": "registry:component"
}
