{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "liquid-glass-slider",
  "title": "Liquid Glass Slider",
  "author": "jyotirmoydas <jyotirmoydas12345@gmail.com>",
  "description": "An iOS 26-inspired liquid glass slider with draggable thumb and SVG refraction effects.",
  "dependencies": ["cn", "motion"],
  "registryDependencies": ["https://jyotimoydas.in/r/liquid-glass-lib.json"],
  "files": [
    {
      "path": "src/registry/liquid-glass-slider/liquid-glass-slider.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"cn\";\nimport { motion, useMotionValue, useSpring, useTransform } from \"motion/react\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useId, useRef } from \"react\";\n\nimport { RefractionFilter } from \"@/registry/liquid-glass-lib/refraction-filter\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface LiquidGlassSliderProps {\n  // ─────────────────────────────────────────────────────────────────────────\n  // Value Control\n  // ─────────────────────────────────────────────────────────────────────────\n\n  /**\n   * Current value of the slider (controlled mode).\n   * If provided, the slider becomes controlled.\n   */\n  value?: number;\n\n  /**\n   * Default value of the slider (uncontrolled mode).\n   * @default 10\n   */\n  defaultValue?: number;\n\n  /**\n   * Minimum value of the slider.\n   * @default 0\n   */\n  min?: number;\n\n  /**\n   * Maximum value of the slider.\n   * @default 100\n   */\n  max?: number;\n\n  /**\n   * Step size for value changes.\n   * @default 1\n   */\n  step?: number;\n\n  /**\n   * Callback triggered when the slider value changes.\n   */\n  onChange?: (value: number) => void;\n\n  /**\n   * Callback triggered when dragging starts.\n   */\n  onDragStart?: () => void;\n\n  /**\n   * Callback triggered when dragging ends.\n   */\n  onDragEnd?: () => void;\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Appearance\n  // ─────────────────────────────────────────────────────────────────────────\n\n  /**\n   * Additional CSS classes.\n   */\n  className?: string;\n\n  /**\n   * Width of the slider track in pixels.\n   * @default 330\n   */\n  width?: number;\n\n  /**\n   * Color of the track fill (progress portion).\n   * @default \"#0377F7\"\n   */\n  fillColor?: string;\n\n  /**\n   * Color of the track background.\n   * @default \"#89898F66\"\n   */\n  trackColor?: string;\n\n  /**\n   * Color of the thumb when not pressed.\n   * @default \"rgba(255, 255, 255, 1)\"\n   */\n  thumbColor?: string;\n\n  /**\n   * Whether the slider is disabled.\n   * @default false\n   */\n  disabled?: boolean;\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Optical Parameters (Advanced)\n  // ─────────────────────────────────────────────────────────────────────────\n\n  /**\n   * Blur level for the glass refraction effect.\n   * @default 0\n   */\n  blur?: number;\n\n  /**\n   * Opacity of the specular highlight.\n   * @default 0.4\n   */\n  specularOpacity?: number;\n\n  /**\n   * Saturation multiplier for the specular highlight.\n   * @default 7\n   */\n  specularSaturation?: number;\n\n  /**\n   * Base refraction level (controls magnification intensity).\n   * @default 1\n   */\n  refractionLevel?: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A sleek liquid glass slider with iOS 26-inspired refraction effects.\n *\n * @example\n * ```tsx\n * <LiquidGlassSlider\n *   defaultValue={50}\n *   onChange={(value) => console.log(\"Value:\", value)}\n * />\n * ```\n *\n * @example Custom colors\n * ```tsx\n * <LiquidGlassSlider\n *   fillColor=\"#22c55e\"\n *   trackColor=\"#14532d44\"\n * />\n * ```\n */\nexport const LiquidGlassSlider: React.FC<LiquidGlassSliderProps> = ({\n  // Value Control\n  value: controlledValue,\n  defaultValue = 10,\n  min = 0,\n  max = 100,\n  step = 1,\n  onChange,\n  onDragStart,\n  onDragEnd,\n  // Appearance\n  className,\n  width = 330,\n  fillColor = \"#0377F7\",\n  trackColor = \"#89898F66\",\n  thumbColor = \"rgba(255, 255, 255, 1)\",\n  disabled = false,\n  // Optical Parameters\n  blur = 0,\n  specularOpacity = 0.4,\n  specularSaturation = 7,\n  refractionLevel = 1,\n}) => {\n  const filterId = useId();\n  const isControlled = controlledValue !== undefined;\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Dimensions\n  // ─────────────────────────────────────────────────────────────────────────\n  const sliderHeight = 14;\n  const thumbWidth = 90;\n  const thumbHeight = 60;\n  const thumbRadius = 30;\n  const SCALE_REST = 0.6;\n  const SCALE_DRAG = 1;\n  const thumbWidthRest = thumbWidth * SCALE_REST;\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Motion Values\n  // ─────────────────────────────────────────────────────────────────────────\n  const internalValue = useMotionValue(defaultValue);\n  const pointerDown = useMotionValue(0);\n\n  // Refs\n  const trackRef = useRef<HTMLDivElement>(null);\n  const thumbRef = useRef<HTMLDivElement>(null);\n\n  // Sync controlled value\n  useEffect(() => {\n    if (isControlled && controlledValue !== undefined) {\n      internalValue.set(controlledValue);\n    }\n  }, [controlledValue, isControlled, internalValue]);\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Derived Motion Values\n  // ─────────────────────────────────────────────────────────────────────────\n  const isUp = useTransform((): number => (pointerDown.get() > 0.5 ? 1 : 0));\n\n  const pressMultiplier = useTransform(isUp, [0, 1], [0.4, 0.9]);\n  const scaleRatio = useSpring(\n    useTransform([pressMultiplier], ([m]) => (Number(m) || 0) * refractionLevel)\n  );\n\n  const scaleSpring = useSpring(\n    useTransform(isUp, [0, 1], [SCALE_REST, SCALE_DRAG]),\n    { damping: 80, stiffness: 2000 }\n  );\n\n  const backgroundOpacity = useSpring(useTransform(isUp, [0, 1], [1, 0.1]), {\n    damping: 80,\n    stiffness: 2000,\n  });\n\n  const fillWidth = useTransform(internalValue, (v) => {\n    const percent = ((v - min) / (max - min)) * 100;\n    return `${Math.max(0, Math.min(100, percent))}%`;\n  });\n\n  const thumbX = useTransform(internalValue, (v) => {\n    const percent = (v - min) / (max - min);\n    const range = width - thumbWidth + thumbWidthRest / 1.5;\n    return percent * range - thumbWidthRest / 3;\n  });\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Pointer Release Handler\n  // ─────────────────────────────────────────────────────────────────────────\n  useEffect(() => {\n    function onPointerUp() {\n      pointerDown.set(0);\n    }\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  // Value Update Handler\n  // ─────────────────────────────────────────────────────────────────────────\n  const updateValue = useCallback(\n    (newValue: number) => {\n      const steppedValue = Math.round(newValue / step) * step;\n      const clampedValue = Math.max(min, Math.min(max, steppedValue));\n\n      if (!isControlled) {\n        internalValue.set(clampedValue);\n      }\n      onChange?.(clampedValue);\n    },\n    [min, max, step, isControlled, onChange, internalValue]\n  );\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Drag Handlers\n  // ─────────────────────────────────────────────────────────────────────────\n  const handleDrag = useCallback(() => {\n    if (!trackRef.current || !thumbRef.current) return;\n\n    const track = trackRef.current.getBoundingClientRect();\n    const thumb = thumbRef.current.getBoundingClientRect();\n\n    const x0 = track.left + thumbWidthRest / 2;\n    const x100 = track.right - thumbWidthRest / 2;\n    const trackInsideWidth = x100 - x0;\n    const thumbCenterX = thumb.left + thumb.width / 2;\n    const x = Math.max(x0, Math.min(x100, thumbCenterX));\n    const ratio = (x - x0) / trackInsideWidth;\n\n    updateValue(ratio * (max - min) + min);\n  }, [max, min, thumbWidthRest, updateValue]);\n\n  const handleTrackClick = useCallback(\n    (e: React.MouseEvent<HTMLDivElement>) => {\n      if (disabled || !trackRef.current) return;\n\n      const rect = trackRef.current.getBoundingClientRect();\n      const x = e.clientX - rect.left;\n      const ratio = x / rect.width;\n      updateValue(ratio * (max - min) + min);\n    },\n    [disabled, max, min, updateValue]\n  );\n\n  // ─────────────────────────────────────────────────────────────────────────\n  // Render\n  // ─────────────────────────────────────────────────────────────────────────\n  return (\n    <div\n      className={cn(\n        \"relative select-none\",\n        disabled && \"pointer-events-none opacity-50\",\n        className\n      )}\n      style={{\n        width,\n        height: thumbHeight,\n      }}\n    >\n      {/* Track */}\n      <motion.div\n        ref={trackRef}\n        style={{\n          width,\n          height: sliderHeight,\n          left: 0,\n          top: (thumbHeight - sliderHeight) / 2,\n          backgroundColor: trackColor,\n          borderRadius: sliderHeight / 2,\n          position: \"absolute\",\n          cursor: disabled ? \"not-allowed\" : \"pointer\",\n        }}\n        onClick={handleTrackClick}\n        onMouseDown={() => !disabled && pointerDown.set(1)}\n        onMouseUp={() => pointerDown.set(0)}\n      >\n        {/* Fill */}\n        <div className=\"h-full w-full overflow-hidden rounded-full\">\n          <motion.div\n            style={{\n              height: sliderHeight,\n              width: fillWidth,\n              borderRadius: 6,\n              backgroundColor: fillColor,\n            }}\n          />\n        </div>\n      </motion.div>\n\n      {/* Refraction Filter */}\n      <RefractionFilter\n        id={filterId}\n        width={thumbWidth}\n        height={thumbHeight}\n        radius={thumbRadius}\n        bezelWidth={16}\n        glassThickness={80}\n        bezelType=\"convex_squircle\"\n        refractiveIndex={1.45}\n        blur={blur}\n        scaleRatio={scaleRatio}\n        specularOpacity={specularOpacity}\n        specularSaturation={specularSaturation}\n      />\n\n      {/* Thumb */}\n      <motion.div\n        ref={thumbRef}\n        drag={disabled ? false : \"x\"}\n        dragConstraints={{\n          left: -thumbWidthRest / 3,\n          right: width - thumbWidth + thumbWidthRest / 3,\n        }}\n        dragElastic={0.02}\n        dragMomentum={false}\n        onMouseDown={() => !disabled && pointerDown.set(1)}\n        onMouseUp={() => pointerDown.set(0)}\n        onDragStart={() => {\n          pointerDown.set(1);\n          onDragStart?.();\n        }}\n        onDrag={handleDrag}\n        onDragEnd={() => {\n          pointerDown.set(0);\n          onDragEnd?.();\n        }}\n        className=\"absolute\"\n        style={{\n          height: thumbHeight,\n          width: thumbWidth,\n          top: 0,\n          x: thumbX,\n          borderRadius: thumbRadius,\n          backdropFilter: `url(#${filterId})`,\n          scale: scaleSpring,\n          cursor: disabled ? \"not-allowed\" : \"pointer\",\n          backgroundColor: useTransform(backgroundOpacity, (op) =>\n            thumbColor.replace(/[\\d.]+\\)$/, `${op})`)\n          ),\n          boxShadow: \"0 3px 14px rgba(0,0,0,0.1)\",\n        }}\n      />\n    </div>\n  );\n};\n\nexport default LiquidGlassSlider;\n",
      "type": "registry:component"
    }
  ],
  "docs": "https://jyotimoydas.in/components/liquid-glass-slider",
  "type": "registry:component"
}
